Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch th1Hooks Excluding Merge-Ins
This is equivalent to a diff from 1fdeece215 to 4f365f7b77
2013-01-07
| ||
18:58 | Improved error message handling. Fix the "fossil server" command so that it works when run as root on a repository in the root directory. check-in: baa1ebb7d9 user: drh tags: trunk | |
17:33 | Merge updates from trunk. Leaf check-in: a6647539f6 user: mistachkin tags: tclRdOnly | |
17:21 | Merge updates from trunk. Leaf check-in: 4f365f7b77 user: mistachkin tags: th1Hooks | |
17:18 | Re-sync custom MinGW makefile. check-in: 1fdeece215 user: mistachkin tags: trunk | |
15:03 | Fix "fossil revert" so that it works on files that have been renamed. check-in: 48798b2719 user: drh tags: trunk | |
2012-12-08
| ||
06:58 | Merge updates from trunk. check-in: ea529b633d user: mistachkin tags: th1Hooks | |
Changes to src/db.c.
771 771 ** it is convenient for the ~/.fossil to be attached to the main database 772 772 ** connection so that we can join between the various databases. In that 773 773 ** case, invoke this routine with useAttach as 1. 774 774 */ 775 775 void db_open_config(int useAttach){ 776 776 char *zDbName; 777 777 const char *zHome; 778 - if( g.configOpen ) return; 778 + if( g.configOpen ){ 779 + if( useAttach==g.useAttach ) return; 780 + if( g.useAttach ){ 781 + db_detach("configdb"); 782 + g.useAttach = 0; 783 + }else if( g.dbConfig ){ 784 + sqlite3_close(g.dbConfig); 785 + g.dbConfig = 0; 786 + g.zConfigDbType = 0; 787 + }else if( g.db ){ 788 + sqlite3_close(g.db); 789 + g.db = 0; 790 + g.zMainDbType = 0; 791 + } 792 + } 779 793 #if defined(_WIN32) 780 794 zHome = fossil_getenv("LOCALAPPDATA"); 781 795 if( zHome==0 ){ 782 796 zHome = fossil_getenv("APPDATA"); 783 797 if( zHome==0 ){ 784 798 char *zDrive = fossil_getenv("HOMEDRIVE"); 785 799 zHome = fossil_getenv("HOMEPATH");
Changes to src/main.c.
196 196 197 197 /* For defense against Cross-site Request Forgery attacks */ 198 198 char zCsrfToken[12]; /* Value of the anti-CSRF token */ 199 199 int okCsrf; /* Anti-CSRF token is present and valid */ 200 200 201 201 int parseCnt[10]; /* Counts of artifacts parsed */ 202 202 FILE *fDebug; /* Write debug information here, if the file exists */ 203 + int fNoThHook; /* Disable all TH1 command/webpage hooks */ 203 204 int thTrace; /* True to enable TH1 debugging output */ 204 205 Blob thLog; /* Text of the TH1 debugging output */ 205 206 206 207 int isHome; /* True if rendering the "home" page */ 207 208 208 209 /* Storage for the aux() and/or option() SQL function arguments */ 209 210 int nAux; /* Number of distinct aux() or option() values */ ................................................................................ 511 512 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; 512 513 g.fSqlStats = find_option("sqlstats", 0, 0)!=0; 513 514 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; 514 515 g.fSshTrace = find_option("sshtrace", 0, 0)!=0; 515 516 if( g.fSqlTrace ) g.fSqlStats = 1; 516 517 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; 517 518 g.fHttpTrace = find_option("httptrace", 0, 0)!=0; 519 + g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; 518 520 g.zLogin = find_option("user", "U", 1); 519 521 g.zSSLIdentity = find_option("ssl-identity", 0, 1); 520 522 if( find_option("utc",0,0) ) g.fTimeFormat = 1; 521 523 if( find_option("localtime",0,0) ) g.fTimeFormat = 2; 522 524 if( zChdir && chdir(zChdir) ){ 523 525 fossil_fatal("unable to change directories to %s", zChdir); 524 526 } ................................................................................ 534 536 g.argc++; 535 537 g.argv = zNewArgv; 536 538 } 537 539 zCmdName = g.argv[1]; 538 540 } 539 541 rc = name_search(zCmdName, aCommand, count(aCommand), &idx); 540 542 if( rc==1 ){ 541 - fossil_fatal("%s: unknown command: %s\n" 542 - "%s: use \"help\" for more information\n", 543 - g.argv[0], zCmdName, g.argv[0]); 543 + if( !g.isHTTP && !g.fNoThHook ){ 544 + rc = Th_CommandHook(zCmdName, 0); 545 + }else{ 546 + rc = TH_OK; 547 + } 548 + if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ 549 + if( rc==TH_OK || rc==TH_RETURN ){ 550 + fossil_fatal("%s: unknown command: %s\n" 551 + "%s: use \"help\" for more information\n", 552 + g.argv[0], zCmdName, g.argv[0]); 553 + } 554 + if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ 555 + Th_CommandNotify(zCmdName, 0); 556 + } 557 + } 558 + fossil_exit(0); 544 559 }else if( rc==2 ){ 545 560 int i, n; 546 561 Blob couldbe; 547 562 blob_zero(&couldbe); 548 563 n = strlen(zCmdName); 549 564 for(i=0; i<count(aCommand); i++){ 550 565 if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ ................................................................................ 554 569 fossil_print("%s: ambiguous command prefix: %s\n" 555 570 "%s: could be any of:%s\n" 556 571 "%s: use \"help\" for more information\n", 557 572 g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]); 558 573 fossil_exit(1); 559 574 } 560 575 atexit( fossil_atexit ); 561 - aCommand[idx].xFunc(); 576 + /* 577 + ** The TH1 return codes from the hook will be handled as follows: 578 + ** 579 + ** TH_OK: The xFunc() and the TH1 notification will both be executed. 580 + ** 581 + ** TH_ERROR: The xFunc() and the TH1 notification will both be skipped. 582 + ** 583 + ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped. 584 + ** 585 + ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be 586 + ** skipped. 587 + ** 588 + ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be 589 + ** executed. 590 + */ 591 + if( !g.isHTTP && !g.fNoThHook ){ 592 + rc = Th_CommandHook(aCommand[idx].zName, aCommand[idx].cmdFlags); 593 + }else{ 594 + rc = TH_OK; 595 + } 596 + if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ 597 + if( rc==TH_OK || rc==TH_RETURN ){ aCommand[idx].xFunc(); } 598 + if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ 599 + Th_CommandNotify(aCommand[idx].zName, aCommand[idx].cmdFlags); 600 + } 601 + } 562 602 fossil_exit(0); 563 603 /*NOT_REACHED*/ 564 604 return 0; 565 605 } 566 606 567 607 /* 568 608 ** The following variable becomes true while processing a fatal error ................................................................................ 1506 1546 #endif 1507 1547 { 1508 1548 @ <h1>Server Configuration Error</h1> 1509 1549 @ <p>The database schema on the server is out-of-date. Please ask 1510 1550 @ the administrator to run <b>fossil rebuild</b>.</p> 1511 1551 } 1512 1552 }else{ 1513 - aWebpage[idx].xFunc(); 1553 + /* 1554 + ** The TH1 return codes from the hook will be handled as follows: 1555 + ** 1556 + ** TH_OK: The xFunc() and the TH1 notification will both be executed. 1557 + ** 1558 + ** TH_ERROR: The xFunc() and the TH1 notification will both be skipped. 1559 + ** 1560 + ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped. 1561 + ** 1562 + ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be 1563 + ** skipped. 1564 + ** 1565 + ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be 1566 + ** executed. 1567 + */ 1568 + int rc; 1569 + if( !g.isHTTP && !g.fNoThHook ){ 1570 + rc = Th_WebpageHook(aWebpage[idx].zName, aWebpage[idx].cmdFlags); 1571 + }else{ 1572 + rc = TH_OK; 1573 + } 1574 + if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ 1575 + if( rc==TH_OK || rc==TH_RETURN ){ aWebpage[idx].xFunc(); } 1576 + if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ 1577 + Th_WebpageNotify(aWebpage[idx].zName, aWebpage[idx].cmdFlags); 1578 + } 1579 + } 1514 1580 } 1515 1581 1516 1582 /* Return the result. 1517 1583 */ 1518 1584 cgi_reply(); 1519 1585 } 1520 1586
Changes to src/th_main.c.
18 18 ** This file contains an interface between the TH scripting language 19 19 ** (an independent project) and fossil. 20 20 */ 21 21 #include "config.h" 22 22 #include "th_main.h" 23 23 #include "sqlite3.h" 24 24 25 +/* 26 +** These are the "well-known" TH1 error messages that occur when no hook is 27 +** registered to be called prior to executing a command or processing a web 28 +** page, respectively. If one of these errors is seen, it will not be sent 29 +** or displayed to the remote user or local interactive user, respectively. 30 +*/ 31 +#define NO_COMMAND_HOOK_ERROR "no such command: command_hook" 32 +#define NO_WEBPAGE_HOOK_ERROR "no such command: webpage_hook" 33 + 25 34 /* 26 35 ** Global variable counting the number of outstanding calls to malloc() 27 36 ** made by the th1 implementation. This is used to catch memory leaks 28 37 ** in the interpreter. Obviously, it also means th1 is not threadsafe. 29 38 */ 30 39 static int nOutstandingMalloc = 0; 31 40 ................................................................................ 854 863 } 855 864 if( inBracket ){ 856 865 if( z[0]!='>' ) return 0; 857 866 i += 2; 858 867 } 859 868 return i; 860 869 } 870 + 871 +/* 872 +** This function is called by Fossil just prior to dispatching a command. 873 +** Returning a value other than TH_OK from this function (i.e. via an 874 +** evaluated script raising an error or calling [break]/[continue]) will 875 +** cause the actual command execution to be skipped. 876 +*/ 877 +int Th_CommandHook( 878 + const char *zName, 879 + char cmdFlags 880 +){ 881 + int rc = TH_OK; 882 + Th_FossilInit(1, 1); 883 + Th_Store("cmd_name", zName); 884 + Th_StoreInt("cmd_flags", cmdFlags); 885 + rc = Th_Eval(g.interp, 0, "command_hook", -1); 886 + if( rc==TH_ERROR ){ 887 + int nResult = 0; 888 + char *zResult = (char*)Th_GetResult(g.interp, &nResult); 889 + /* 890 + ** Make sure that the TH1 script error was not caused by a "missing" 891 + ** command hook handler as that is not actually an error condition. 892 + */ 893 + if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ 894 + sendError(zResult, nResult, 0); 895 + } 896 + } 897 + /* 898 + ** If the script returned TH_ERROR (e.g. the "command_hook" TH1 command does 899 + ** not exist because commands are not being hooked), return TH_OK because we 900 + ** do not want to skip executing essential commands unless the called command 901 + ** (i.e. "command_hook") explicitly forbids this by successfully returning 902 + ** TH_BREAK or TH_CONTINUE. 903 + */ 904 + if( g.thTrace ){ 905 + Th_Trace("[command_hook {%h}] => %h<br />\n", zName, 906 + Th_ReturnCodeName(rc)); 907 + } 908 + return (rc != TH_ERROR) ? rc : TH_OK; 909 +} 910 + 911 +/* 912 +** This function is called by Fossil just after dispatching a command. 913 +** Returning a value other than TH_OK from this function (i.e. via an 914 +** evaluated script raising an error or calling [break]/[continue]) may 915 +** cause an error message to be displayed to the local interactive user. 916 +** Currently, TH1 error messages generated by this function are ignored. 917 +*/ 918 +int Th_CommandNotify( 919 + const char *zName, 920 + char cmdFlags 921 +){ 922 + int rc; 923 + Th_FossilInit(1, 1); 924 + Th_Store("cmd_name", zName); 925 + Th_StoreInt("cmd_flags", cmdFlags); 926 + rc = Th_Eval(g.interp, 0, "command_notify", -1); 927 + if( g.thTrace ){ 928 + Th_Trace("[command_notify {%h}] => %h<br />\n", zName, 929 + Th_ReturnCodeName(rc)); 930 + } 931 + return rc; 932 +} 933 + 934 +/* 935 +** This function is called by Fossil just prior to processing a web page. 936 +** Returning a value other than TH_OK from this function (i.e. via an 937 +** evaluated script raising an error or calling [break]/[continue]) will 938 +** cause the actual web page processing to be skipped. 939 +*/ 940 +int Th_WebpageHook( 941 + const char *zName, 942 + char cmdFlags 943 +){ 944 + int rc = TH_OK; 945 + Th_FossilInit(1, 1); 946 + Th_Store("web_name", zName); 947 + Th_StoreInt("web_flags", cmdFlags); 948 + rc = Th_Eval(g.interp, 0, "webpage_hook", -1); 949 + if( rc==TH_ERROR ){ 950 + int nResult = 0; 951 + char *zResult = (char*)Th_GetResult(g.interp, &nResult); 952 + /* 953 + ** Make sure that the TH1 script error was not caused by a "missing" 954 + ** webpage hook handler as that is not actually an error condition. 955 + */ 956 + if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ 957 + sendError(zResult, nResult, 1); 958 + } 959 + } 960 + /* 961 + ** If the script returned TH_ERROR (e.g. the "webpage_hook" TH1 command does 962 + ** not exist because commands are not being hooked), return TH_OK because we 963 + ** do not want to skip processing essential web pages unless the called 964 + ** command (i.e. "webpage_hook") explicitly forbids this by successfully 965 + ** returning TH_BREAK or TH_CONTINUE. 966 + */ 967 + if( g.thTrace ){ 968 + Th_Trace("[webpage_hook {%h}] => %h<br />\n", zName, 969 + Th_ReturnCodeName(rc)); 970 + } 971 + return (rc != TH_ERROR) ? rc : TH_OK; 972 +} 973 + 974 +/* 975 +** This function is called by Fossil just after processing a web page. 976 +** Returning a value other than TH_OK from this function (i.e. via an 977 +** evaluated script raising an error or calling [break]/[continue]) may 978 +** cause an error message to be displayed to the remote user. 979 +** Currently, TH1 error messages generated by this function are ignored. 980 +*/ 981 +int Th_WebpageNotify( 982 + const char *zName, 983 + char cmdFlags 984 +){ 985 + int rc; 986 + Th_FossilInit(1, 1); 987 + Th_Store("web_name", zName); 988 + Th_StoreInt("web_flags", cmdFlags); 989 + rc = Th_Eval(g.interp, 0, "webpage_notify", -1); 990 + if( g.thTrace ){ 991 + Th_Trace("[webpage_notify {%h}] => %h<br />\n", zName, 992 + Th_ReturnCodeName(rc)); 993 + } 994 + return rc; 995 +} 861 996 862 997 /* 863 998 ** The z[] input contains text mixed with TH1 scripts. 864 999 ** The TH1 scripts are contained within <th1>...</th1>. 865 1000 ** TH1 variables are $aaa or $<aaa>. The first form of 866 1001 ** variable is literal. The second is run through htmlize 867 1002 ** before being inserted. ................................................................................ 930 1065 usage("FILE"); 931 1066 } 932 1067 db_open_config(0); /* Needed for global "tcl" setting. */ 933 1068 blob_zero(&in); 934 1069 blob_read_from_file(&in, g.argv[2]); 935 1070 Th_Render(blob_str(&in)); 936 1071 } 1072 + 1073 +/* 1074 +** COMMAND: test-th-hook 1075 +*/ 1076 +void test_th_hook(void){ 1077 + int rc = TH_OK; 1078 + int nResult = 0; 1079 + char *zResult; 1080 + if( g.argc<5 ){ 1081 + usage("TYPE NAME FLAGS"); 1082 + } 1083 + if( fossil_stricmp(g.argv[2], "cmdhook")==0 ){ 1084 + rc = Th_CommandHook(g.argv[3], (char)atoi(g.argv[4])); 1085 + }else if( fossil_stricmp(g.argv[2], "cmdnotify")==0 ){ 1086 + rc = Th_CommandNotify(g.argv[3], (char)atoi(g.argv[4])); 1087 + }else if( fossil_stricmp(g.argv[2], "webhook")==0 ){ 1088 + rc = Th_WebpageHook(g.argv[3], (char)atoi(g.argv[4])); 1089 + }else if( fossil_stricmp(g.argv[2], "webnotify")==0 ){ 1090 + rc = Th_WebpageNotify(g.argv[3], (char)atoi(g.argv[4])); 1091 + }else{ 1092 + fossil_fatal("Unknown TH1 hook %s\n", g.argv[2]); 1093 + } 1094 + zResult = (char*)Th_GetResult(g.interp, &nResult); 1095 + sendText("RESULT (", -1, 0); 1096 + sendText(Th_ReturnCodeName(rc), -1, 0); 1097 + sendText("): ", -1, 0); 1098 + sendText(zResult, nResult, 0); 1099 + sendText("\n", -1, 0); 1100 +}