CHANGED src/db.c
Index: src/db.c
===================================================================
--- src/db.c
+++ src/db.c
@@ -1525,10 +1525,16 @@
   { "http-port",     0,               16, "8080"                },
   { "localauth",     0,                0, "0"                   },
   { "mtime-changes", 0,                0, "0"                   },
   { "pgp-command",   0,               32, "gpg --clearsign -o " },
   { "proxy",         0,               32, "off"                 },
+  { "push-hook-pattern-client",
+                     0,               32, ""                    },
+  { "push-hook-pattern-cmd",
+                     0,               32, ""                    },
+  { "push-hook-pattern-server",
+                     0,               32, ""                    },
   { "ssh-command",   0,               32, ""                    },
   { "web-browser",   0,               32, ""                    },
   { 0,0,0,0 }
 };
 
@@ -1596,10 +1602,26 @@
 **
 **    proxy         URL of the HTTP proxy.  If undefined or "off" then
 **                  the "http_proxy" environment variable is consulted.
 **                  If the http_proxy environment variable is undefined
 **                  then a direct HTTP connection is used.
+**
+**    push-hook-pattern-client
+**                  if set, a client push will sent this message to the
+**                  server, to activate the push hook command.
+**
+**    push-hook-pattern-cmd
+**                  this is the command line, that will be activated
+**                  as push hook. Output redirects should be added to
+**                  this command line.
+**                  The complete pattern, sent by the client will be
+**                  appended to the command line.
+**
+**    push-hook-pattern-server
+**                  if set, and a client send this pattern at the end of
+**                  a push, the push hook command will be executed. This
+**                  might be a prefix of the pattern, sent by the client.
 **
 **    ssh-command   Command used to talk to a remote machine with
 **                  the "ssh://" protocol.
 **
 **    web-browser   A shell command used to launch your preferred

CHANGED src/xfer.c
Index: src/xfer.c
===================================================================
--- src/xfer.c
+++ src/xfer.c
@@ -40,10 +40,32 @@
   int nDeltaRcvd;     /* Number of deltas received */
   int nDanglingFile;  /* Number of dangling deltas received */
   int mxSend;         /* Stop sending "file" with pOut reaches this size */
 };
 
+/*
+** Let a server-side external agent know that a push has completed. /fatman
+*/
+void post_push_hook(char const * const zPushHookLine){
+  /*
+  ** TO DO: get the string cmd from a config file? Or the database local
+  ** settings, as someone suggested? Ditto output and error logs. /fatman
+  */
+  const char *zCmd = db_get("push-hook-pattern-cmd", "");
+  
+  if( zCmd && zCmd[0] ){
+    int rc;
+    char * zCalledCmd;
+
+    zCalledCmd = mprintf("%s %s",zCmd,zPushHookLine);
+    rc = system(zCalledCmd);
+    if (rc != 0) {
+      fossil_print("The post-push-hook command \"%s\" failed.", zCalledCmd);
+    }
+    free(zCalledCmd);
+  }
+}
 
 /*
 ** The input blob contains a UUID.  Convert it into a record ID.
 ** Create a phantom record if no prior record exists and
 ** phantomize is true.
@@ -596,10 +618,13 @@
   int isClone = 0;
   int nGimme = 0;
   int size;
   int recvConfig = 0;
   char *zNow;
+  const char *zPushHookPattern = db_get("push-hook-pattern-server", "");
+  int lenPushHookPattern = (zPushHookPattern && zPushHookPattern[0])
+                            ? strlen(zPushHookPattern) : 0;
 
   if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
      fossil_redirect_home();
   }
   memset(&xfer, 0, sizeof(xfer));
@@ -617,11 +642,19 @@
   );
   zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
   @ # timestamp %s(zNow)
   manifest_crosslink_begin();
   while( blob_line(xfer.pIn, &xfer.line) ){
-    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
+    if( blob_buffer(&xfer.line)[0]=='#' ){
+      if(    lenPushHookPattern
+          && 0 == memcmp(blob_buffer(&xfer.line)+1,
+                         zPushHookPattern, lenPushHookPattern)
+      ){
+        post_push_hook(blob_buffer(&xfer.line)+1);
+      }
+      continue;
+    }
     xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
 
     /*   file UUID SIZE \n CONTENT
     **   file UUID DELTASRC SIZE \n CONTENT
     **
@@ -948,10 +981,12 @@
   Blob send;              /* Text we are sending to the server */
   Blob recv;              /* Reply we got back from the server */
   Xfer xfer;              /* Transfer data */
   const char *zSCode = db_get("server-code", "x");
   const char *zPCode = db_get("project-code", 0);
+  const char *zPushHookPattern = db_get("push-hook-pattern-client", "");
+
 
   if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
   if( pushFlag + pullFlag + cloneFlag == 0 
      && configRcvMask==0 && configSendMask==0 ) return;
 
@@ -1330,14 +1365,22 @@
     }
 
     /* If this is a clone, the go at least two rounds */
     if( cloneFlag && nCycle==1 ) go = 1;
   };
+  if (pushFlag && nFileSend > 0) {
+    if( zPushHookPattern && zPushHookPattern[0] ){
+      blob_appendf(&send, "#%s\n", zPushHookPattern);
+      http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
+      blob_reset(&send);
+      nCardSent++;
+    }
+  }
   transport_stats(&nSent, &nRcvd, 1);
   fossil_print("Total network traffic: %d bytes sent, %d bytes received\n",
                nSent, nRcvd);
   transport_close();
   transport_global_shutdown();
   db_multi_exec("DROP TABLE onremote");
   manifest_crosslink_end();
   db_end_transaction(0);
 }

