| | @@ -40,10 +40,71 @@ |
| 40 | 40 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | 41 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | 42 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | +/* |
| 46 | +** COMMAND: callhook |
| 47 | +** %fossil callhook PUSHHOOKPATTERN ?--force|-f? |
| 48 | +** |
| 49 | +** Call the push hook command on a server, which will normally be called |
| 50 | +** after a client push (<a>setting</a> push-hook-cmd). |
| 51 | +** |
| 52 | +** If --force is used, the given pattern is not checked against the |
| 53 | +** configuration (<a>setting</a> push-hook-pattern-server). |
| 54 | +** |
| 55 | +** This command only works on the server side, it does not send a message |
| 56 | +** from a client, but executes the hook directly on the server. |
| 57 | +** |
| 58 | +** See also <a>push</a>, <a>sync</a>, <a>setting</a> |
| 59 | +*/ |
| 60 | +void callhook_cmd(void){ |
| 61 | + int forceFlag = find_option("force","f",0)!=0; |
| 62 | + |
| 63 | + db_open_config(1); |
| 64 | + db_find_and_open_repository(0); |
| 65 | + if( (g.argc!=3) || (!g.argv[2]) || (!g.argv[2][0]) ){ |
| 66 | + usage("PUSHHOOKPATTERN ?--force?"); |
| 67 | + } |
| 68 | + if( !forceFlag ){ |
| 69 | + const char *zPushHookPattern = db_get("push-hook-pattern-server", ""); |
| 70 | + int lenPushHookPattern = (zPushHookPattern && zPushHookPattern[0]) |
| 71 | + ? strlen(zPushHookPattern) : 0; |
| 72 | + if( (!lenPushHookPattern) |
| 73 | + || memcmp(g.argv[2], zPushHookPattern, lenPushHookPattern) |
| 74 | + ){ |
| 75 | + fossil_fatal("push hook pattern '%s' doesn't match configuration '%s'\n", |
| 76 | + g.argv[2],zPushHookPattern); |
| 77 | + } |
| 78 | + } |
| 79 | + post_push_hook(g.argv[2]); |
| 80 | +} |
| 81 | + |
| 82 | +/* |
| 83 | +** Let a server-side external agent know that a push has completed. /fatman |
| 84 | +*/ |
| 85 | +void post_push_hook(char const * const zPushHookLine){ |
| 86 | + /* |
| 87 | + ** TO DO: get the string cmd from a config file? Or the database local |
| 88 | + ** settings, as someone suggested? Ditto output and error logs. /fatman |
| 89 | + */ |
| 90 | + const char *zCmd = db_get("push-hook-cmd", ""); |
| 91 | + |
| 92 | + if( zCmd && zCmd[0] ){ |
| 93 | + int rc; |
| 94 | + char * zCalledCmd; |
| 95 | + |
| 96 | + zCalledCmd = mprintf("%s %s",zCmd,zPushHookLine); |
| 97 | + rc = system(zCalledCmd); |
| 98 | + if (rc != 0) { |
| 99 | + fossil_print("The post-push-hook command \"%s\" failed.", zCalledCmd); |
| 100 | + } |
| 101 | + free(zCalledCmd); |
| 102 | + }else{ |
| 103 | + fossil_print("No push hook configured, skipping call for '%s'\n", zPushHookLine); |
| 104 | + } |
| 105 | +} |
| 45 | 106 | |
| 46 | 107 | /* |
| 47 | 108 | ** The input blob contains a UUID. Convert it into a record ID. |
| 48 | 109 | ** Create a phantom record if no prior record exists and |
| 49 | 110 | ** phantomize is true. |
| | @@ -596,10 +657,13 @@ |
| 596 | 657 | int isClone = 0; |
| 597 | 658 | int nGimme = 0; |
| 598 | 659 | int size; |
| 599 | 660 | int recvConfig = 0; |
| 600 | 661 | char *zNow; |
| 662 | + const char *zPushHookPattern = db_get("push-hook-pattern-server", ""); |
| 663 | + int lenPushHookPattern = (zPushHookPattern && zPushHookPattern[0]) |
| 664 | + ? strlen(zPushHookPattern) : 0; |
| 601 | 665 | |
| 602 | 666 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 603 | 667 | fossil_redirect_home(); |
| 604 | 668 | } |
| 605 | 669 | memset(&xfer, 0, sizeof(xfer)); |
| | @@ -617,11 +681,19 @@ |
| 617 | 681 | ); |
| 618 | 682 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 619 | 683 | @ # timestamp %s(zNow) |
| 620 | 684 | manifest_crosslink_begin(); |
| 621 | 685 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 622 | | - if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 686 | + if( blob_buffer(&xfer.line)[0]=='#' ){ |
| 687 | + if( lenPushHookPattern |
| 688 | + && 0 == memcmp(blob_buffer(&xfer.line)+1, |
| 689 | + zPushHookPattern, lenPushHookPattern) |
| 690 | + ){ |
| 691 | + post_push_hook(blob_buffer(&xfer.line)+1); |
| 692 | + } |
| 693 | + continue; |
| 694 | + } |
| 623 | 695 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 624 | 696 | |
| 625 | 697 | /* file UUID SIZE \n CONTENT |
| 626 | 698 | ** file UUID DELTASRC SIZE \n CONTENT |
| 627 | 699 | ** |
| | @@ -948,10 +1020,12 @@ |
| 948 | 1020 | Blob send; /* Text we are sending to the server */ |
| 949 | 1021 | Blob recv; /* Reply we got back from the server */ |
| 950 | 1022 | Xfer xfer; /* Transfer data */ |
| 951 | 1023 | const char *zSCode = db_get("server-code", "x"); |
| 952 | 1024 | const char *zPCode = db_get("project-code", 0); |
| 1025 | + const char *zPushHookPattern = db_get("push-hook-pattern-client", ""); |
| 1026 | + |
| 953 | 1027 | |
| 954 | 1028 | if( db_get_boolean("dont-push", 0) ) pushFlag = 0; |
| 955 | 1029 | if( pushFlag + pullFlag + cloneFlag == 0 |
| 956 | 1030 | && configRcvMask==0 && configSendMask==0 ) return; |
| 957 | 1031 | |
| | @@ -1330,14 +1404,22 @@ |
| 1330 | 1404 | } |
| 1331 | 1405 | |
| 1332 | 1406 | /* If this is a clone, the go at least two rounds */ |
| 1333 | 1407 | if( cloneFlag && nCycle==1 ) go = 1; |
| 1334 | 1408 | }; |
| 1409 | + if (pushFlag && nFileSend > 0) { |
| 1410 | + if( zPushHookPattern && zPushHookPattern[0] ){ |
| 1411 | + blob_appendf(&send, "#%s\n", zPushHookPattern); |
| 1412 | + http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); |
| 1413 | + blob_reset(&send); |
| 1414 | + nCardSent++; |
| 1415 | + } |
| 1416 | + } |
| 1335 | 1417 | transport_stats(&nSent, &nRcvd, 1); |
| 1336 | 1418 | fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", |
| 1337 | 1419 | nSent, nRcvd); |
| 1338 | 1420 | transport_close(); |
| 1339 | 1421 | transport_global_shutdown(); |
| 1340 | 1422 | db_multi_exec("DROP TABLE onremote"); |
| 1341 | 1423 | manifest_crosslink_end(); |
| 1342 | 1424 | db_end_transaction(0); |
| 1343 | 1425 | } |
| 1344 | 1426 | |