Changes On Branch th1Hooks
Not logged in

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  +}