Fossil

Changes On Branch 12c53e1cb6f714bc
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch tkt-change-hook Through [12c53e1cb6] Excluding Merge-Ins

This is equivalent to a diff from 5c123de48c to 12c53e1cb6

2013-10-14
08:28
Fix compiler warnings. check-in: c932fa47ef user: mistachkin tags: trunk
2013-10-13
15:19
don't hardcode default value of "ticket-change" code check-in: 0e1f94732c user: jan.nijtmans tags: tkt-change-hook
15:13
Put back ticket-change in JSON part as well check-in: 12c53e1cb6 user: jan.nijtmans tags: tkt-change-hook
15:03
One more place where failing run_common_script() should not prevent running manifest_crosslink(). If run_common_script() fails, let the following run_script() output its error-message. check-in: cc5466b8d4 user: jan.nijtmans tags: tkt-change-hook
09:53
merge trunk check-in: a4327ba0b6 user: jan.nijtmans tags: tkt-change-hook
2013-10-11
20:19
Improved the help text for /reports. Started 1.28 changelog entries. check-in: 5c123de48c user: stephan tags: trunk
19:58
Added sub-submenu to /reports for selecting type of event to filter on. check-in: 3e915d420a user: stephan tags: trunk

Changes to src/branch.c.
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
  }

  brid = content_put_ex(&branch, 0, 0, 0, isPrivate);
  if( brid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);

  if( manifest_crosslink(brid, &branch)==0 ){
    fossil_fatal("unable to install new manifest");
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
  fossil_print("New branch: %s\n", zUuid);
  if( g.argc==3 ){
    fossil_print(







>

|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  }

  brid = content_put_ex(&branch, 0, 0, 0, isPrivate);
  if( brid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  run_common_script();
  if( manifest_crosslink(brid, &branch)==0 ){
    fossil_fatal("%s\n", g.zErrMsg);
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
  fossil_print("New branch: %s\n", zUuid);
  if( g.argc==3 ){
    fossil_print(
Changes to src/checkin.c.
1804
1805
1806
1807
1808
1809
1810


1811



1812
1813
1814
1815
1816
1817
1818
  }

  nvid = content_put(&manifest);
  if( nvid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);


  manifest_crosslink(nvid, &manifest);



  assert( blob_is_reset(&manifest) );
  content_deltify(vid, nvid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);

  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){







>
>
|
>
>
>







1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
  }

  nvid = content_put(&manifest);
  if( nvid==0 ){
    fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
  if( !dryRunFlag ){
    run_common_script();
    if( manifest_crosslink(nvid, &manifest)==0 ){
      fossil_fatal("%s\n", g.zErrMsg);
    }
  }
  assert( blob_is_reset(&manifest) );
  content_deltify(vid, nvid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);

  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){
Changes to src/configure.c.
129
130
131
132
133
134
135


136
137
138
139
140
141
142

  { "@concealed",             CONFIGSET_ADDR },

  { "@shun",                  CONFIGSET_SHUN },

  { "xfer-common-script",     CONFIGSET_XFER },
  { "xfer-push-script",       CONFIGSET_XFER },



};
static int iConfig = 0;

/*
** Return name of first configuration property matching the given mask.
*/







>
>







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

  { "@concealed",             CONFIGSET_ADDR },

  { "@shun",                  CONFIGSET_SHUN },

  { "xfer-common-script",     CONFIGSET_XFER },
  { "xfer-push-script",       CONFIGSET_XFER },
  { "xfer-commit-script",     CONFIGSET_XFER },
  { "xfer-ticket-script",     CONFIGSET_XFER },

};
static int iConfig = 0;

/*
** Return name of first configuration property matching the given mask.
*/
Changes to src/db.c.
2119
2120
2121
2122
2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
  { "diff-command",  0,               40, 0, ""                    },
  { "dont-push",     0,                0, 0, "off"                 },
  { "editor",        0,               32, 0, ""                    },
  { "empty-dirs",    0,               40, 1, ""                    },
  { "encoding-glob",  0,              40, 1, ""                    },
  { "gdiff-command", 0,               40, 0, "gdiff"               },
  { "gmerge-command",0,               40, 0, ""                    },

  { "http-port",     0,               16, 0, "8080"                },
  { "https-login",   0,                0, 0, "off"                 },
  { "ignore-glob",   0,               40, 1, ""                    },
  { "keep-glob",     0,               40, 1, ""                    },
  { "localauth",     0,                0, 0, "off"                 },
  { "main-branch",   0,               40, 0, "trunk"               },
  { "manifest",      0,                0, 1, "off"                 },







>







2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
  { "diff-command",  0,               40, 0, ""                    },
  { "dont-push",     0,                0, 0, "off"                 },
  { "editor",        0,               32, 0, ""                    },
  { "empty-dirs",    0,               40, 1, ""                    },
  { "encoding-glob",  0,              40, 1, ""                    },
  { "gdiff-command", 0,               40, 0, "gdiff"               },
  { "gmerge-command",0,               40, 0, ""                    },
  { "http-allow-regexp",0,            40, 0, ""                    },
  { "http-port",     0,               16, 0, "8080"                },
  { "https-login",   0,                0, 0, "off"                 },
  { "ignore-glob",   0,               40, 1, ""                    },
  { "keep-glob",     0,               40, 1, ""                    },
  { "localauth",     0,                0, 0, "off"                 },
  { "main-branch",   0,               40, 0, "trunk"               },
  { "manifest",      0,                0, 1, "off"                 },
2248
2249
2250
2251
2252
2253
2254




2255
2256
2257
2258
2259
2260
2261
**                     diff. If undefined, text diff will be used.
**
**    gmerge-command   A graphical merge conflict resolver command operating
**                     on four files.
**                     Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output"
**                     Ex: xxdiff "%original" "%baseline" "%merge" -M "%output"
**                     Ex: meld "%baseline" "%original" "%merge" "%output"




**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    https-login      Send login credentials using HTTPS instead of HTTP
**                     even if the login page request came via HTTP.
**







>
>
>
>







2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
**                     diff. If undefined, text diff will be used.
**
**    gmerge-command   A graphical merge conflict resolver command operating
**                     on four files.
**                     Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output"
**                     Ex: xxdiff "%original" "%baseline" "%merge" -M "%output"
**                     Ex: meld "%baseline" "%original" "%merge" "%output"
**
**    http-allow-regexp Specify which URL's are allowed in http requests for
**                     commit and ticket hooks. If empty, no http requests
**                     are allowed whatsoever. Default: "".
**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    https-login      Send login credentials using HTTPS instead of HTTP
**                     even if the login page request came via HTTP.
**
Changes to src/info.c.
2176
2177
2178
2179
2180
2181
2182

2183
2184
2185
2186
2187
2188
2189
      Blob cksum;
      blob_appendf(&ctrl, "U %F\n", g.zLogin);
      md5sum_blob(&ctrl, &cksum);
      blob_appendf(&ctrl, "Z %b\n", &cksum);
      db_begin_transaction();
      g.markPrivate = content_is_private(rid);
      nrid = content_put(&ctrl);

      manifest_crosslink(nrid, &ctrl);
      assert( blob_is_reset(&ctrl) );
      db_end_transaction(0);
    }
    cgi_redirectf("ci?name=%s", zUuid);
  }
  blob_zero(&comment);







>







2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
      Blob cksum;
      blob_appendf(&ctrl, "U %F\n", g.zLogin);
      md5sum_blob(&ctrl, &cksum);
      blob_appendf(&ctrl, "Z %b\n", &cksum);
      db_begin_transaction();
      g.markPrivate = content_is_private(rid);
      nrid = content_put(&ctrl);
      run_common_script();
      manifest_crosslink(nrid, &ctrl);
      assert( blob_is_reset(&ctrl) );
      db_end_transaction(0);
    }
    cgi_redirectf("ci?name=%s", zUuid);
  }
  blob_zero(&comment);
Changes to src/json_branch.c.
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304
  blob_appendf(&branch, "Z %b\n", &mcksum);

  brid = content_put(&branch);
  if( brid==0 ){
    fossil_fatal("Problem committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);

  if( manifest_crosslink(brid, &branch)==0 ){
    fossil_fatal("unable to install new manifest");
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  if( zNewRid ){
    *zNewRid = brid;
  }








>

|







289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  blob_appendf(&branch, "Z %b\n", &mcksum);

  brid = content_put(&branch);
  if( brid==0 ){
    fossil_fatal("Problem committing manifest: %s", g.zErrMsg);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  run_common_script();
  if( manifest_crosslink(brid, &branch)==0 ){
    fossil_fatal("%s\n", g.zErrMsg);
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  if( zNewRid ){
    *zNewRid = brid;
  }

Changes to src/manifest.c.
1653
1654
1655
1656
1657
1658
1659


1660
1661
1662
1663
1664

1665
1666
1667
1668
1669

1670
1671
1672
1673
1674

1675
1676
1677
1678


1679
1680
1681
1682
1683
1684
1685
** file, is a legacy of its original use.
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest *p;
  Stmt q;
  int parentid = 0;



  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
  }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
    assert( blob_is_reset(pContent) || pContent==0 );

    return 0;
  }
  if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
    manifest_destroy(p);
    assert( blob_is_reset(pContent) );

    return 0;
  }
  if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){
    manifest_destroy(p);
    assert( blob_is_reset(pContent) );

    return 0;
  }
  db_begin_transaction();
  if( p->type==CFTYPE_MANIFEST ){


    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      for(i=0; i<p->nParent; i++){
        int pid = uuid_to_rid(p->azParent[i], 1);
        db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
        if( i==0 ){







>
>





>





>





>




>
>







1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
** file, is a legacy of its original use.
*/
int manifest_crosslink(int rid, Blob *pContent){
  int i;
  Manifest *p;
  Stmt q;
  int parentid = 0;
  const char *hook = 0;
  const char *zUuid = 0;

  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
  }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
    assert( blob_is_reset(pContent) || pContent==0 );
    fossil_error(1, "syntax error in manifest");
    return 0;
  }
  if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
    manifest_destroy(p);
    assert( blob_is_reset(pContent) );
    fossil_error(1, "no manifest");
    return 0;
  }
  if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){
    manifest_destroy(p);
    assert( blob_is_reset(pContent) );
    fossil_error(1, "cannot fetch baseline manifest");
    return 0;
  }
  db_begin_transaction();
  if( p->type==CFTYPE_MANIFEST ){
    hook = "xfer-commit-script";
    zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      for(i=0; i<p->nParent; i++){
        int pid = uuid_to_rid(p->azParent[i], 1);
        db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
                      "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate);
        if( i==0 ){
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
      }
      if( tid ){
        switch( p->aTag[i].zName[0] ){
          case '-':  type = 0;  break;  /* Cancel prior occurrences */
          case '+':  type = 1;  break;  /* Apply to target only */
          case '*':  type = 2;  break;  /* Propagate to descendants */
          default:
            fossil_fatal("unknown tag type in manifest: %s", p->aTag);
            return 0;
        }
        tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, 
                   rid, p->rDate, tid);
      }
    }
    if( parentid ){







|







1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
      }
      if( tid ){
        switch( p->aTag[i].zName[0] ){
          case '-':  type = 0;  break;  /* Cancel prior occurrences */
          case '+':  type = 1;  break;  /* Apply to target only */
          case '*':  type = 2;  break;  /* Propagate to descendants */
          default:
            fossil_error(1, "unknown tag type in manifest: %s", p->aTag);
            return 0;
        }
        tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, 
                   rid, p->rDate, tid);
      }
    }
    if( parentid ){
1868
1869
1870
1871
1872
1873
1874


1875
1876
1877
1878
1879
1880
1881
        TAG_BGCOLOR, rid
      );
    }
  }
  if( p->type==CFTYPE_TICKET ){
    char *zTag;



    assert( manifest_crosslink_busy==1 );
    zTag = mprintf("tkt-%s", p->zTicketUuid);
    tag_insert(zTag, 1, 0, rid, p->rDate, rid);
    free(zTag);
    db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
                  p->zTicketUuid);
  }







>
>







1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
        TAG_BGCOLOR, rid
      );
    }
  }
  if( p->type==CFTYPE_TICKET ){
    char *zTag;

    hook = "xfer-ticket-script";
    zUuid = p->zTicketUuid;
    assert( manifest_crosslink_busy==1 );
    zTag = mprintf("tkt-%s", p->zTicketUuid);
    tag_insert(zTag, 1, 0, rid, p->rDate, rid);
    free(zTag);
    db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
                  p->zTicketUuid);
  }
2019
2020
2021
2022
2023
2024
2025

2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('g',%.17g,%d,%Q,%Q)",
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }
  db_end_transaction(0);

  if( p->type==CFTYPE_MANIFEST ){
    manifest_cache_insert(p);
  }else{
    manifest_destroy(p);
  }
  assert( blob_is_reset(pContent) );
  return 1;
}

/*
** COMMAND: test-crosslink
**
** Usage:  %fossil test-crosslink RECORDID
**







>






|







2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('g',%.17g,%d,%Q,%Q)",
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }
  db_end_transaction(0);
  i = run_script(hook, zUuid)==0;
  if( p->type==CFTYPE_MANIFEST ){
    manifest_cache_insert(p);
  }else{
    manifest_destroy(p);
  }
  assert( blob_is_reset(pContent) );
  return i;
}

/*
** COMMAND: test-crosslink
**
** Usage:  %fossil test-crosslink RECORDID
**
Changes to src/tag.c.
324
325
326
327
328
329
330

331
332
333
334
335
336
337
  }else{
    blob_appendf(&ctrl, "\n");
  }
  blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(&ctrl, &cksum);
  blob_appendf(&ctrl, "Z %b\n", &cksum);
  nrid = content_put(&ctrl);

  manifest_crosslink(nrid, &ctrl);
  assert( blob_is_reset(&ctrl) );
}

/*
** COMMAND: tag
** Usage: %fossil tag SUBCOMMAND ...







>







324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
  }else{
    blob_appendf(&ctrl, "\n");
  }
  blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin);
  md5sum_blob(&ctrl, &cksum);
  blob_appendf(&ctrl, "Z %b\n", &cksum);
  nrid = content_put(&ctrl);
  run_common_script();
  manifest_crosslink(nrid, &ctrl);
  assert( blob_is_reset(&ctrl) );
}

/*
** COMMAND: tag
** Usage: %fossil tag SUBCOMMAND ...
Changes to src/tkt.c.
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

/*
** Create the TH1 interpreter and load the "change" code.
*/
int ticket_change(void){
  const char *zConfig;
  Th_FossilInit(TH_INIT_DEFAULT);
  zConfig = ticket_change_code();
  return Th_Eval(g.interp, 0, zConfig, -1);
}

/*
** Recreate the TICKET and TICKETCHNG tables.
*/
void ticket_create_table(int separateConnection){







|







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

/*
** Create the TH1 interpreter and load the "change" code.
*/
int ticket_change(void){
  const char *zConfig;
  Th_FossilInit(TH_INIT_DEFAULT);
  zConfig = db_get("ticket-change", "return\n");
  return Th_Eval(g.interp, 0, zConfig, -1);
}

/*
** Recreate the TICKET and TICKETCHNG tables.
*/
void ticket_create_table(int separateConnection){
511
512
513
514
515
516
517
518
519
520
521
522

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
  aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]);
  return TH_OK;
}

/*
** Write a ticket into the repository.
*/
static void ticket_put(
  Blob *pTicket,           /* The text of the ticket change record */
  const char *zTktId,      /* The ticket to which this change is applied */
  int needMod              /* True if moderation is needed */
){

  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
  if( rid==0 ){
    fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
  }
  if( needMod ){
    moderation_table_create();
    db_multi_exec(
      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
      rid, zTktId
    );
  }else{
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
  }
  manifest_crosslink_begin();
  manifest_crosslink(rid, pTicket);
  assert( blob_is_reset(pTicket) );
  manifest_crosslink_end();

}

/*
** Subscript command:   submit_ticket
**
** Construct and submit a new ticket artifact.  The fields of the artifact
** are the names of the columns in the TICKET table.  The content is







|




>















|


>







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
  aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]);
  return TH_OK;
}

/*
** Write a ticket into the repository.
*/
static int ticket_put(
  Blob *pTicket,           /* The text of the ticket change record */
  const char *zTktId,      /* The ticket to which this change is applied */
  int needMod              /* True if moderation is needed */
){
  int result;
  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
  if( rid==0 ){
    fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
  }
  if( needMod ){
    moderation_table_create();
    db_multi_exec(
      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
      rid, zTktId
    );
  }else{
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
  }
  manifest_crosslink_begin();
  result = manifest_crosslink(rid, pTicket)==0;
  assert( blob_is_reset(pTicket) );
  manifest_crosslink_end();
  return result;
}

/*
** Subscript command:   submit_ticket
**
** Construct and submit a new ticket artifact.  The fields of the artifact
** are the names of the columns in the TICKET table.  The content is
678
679
680
681
682
683
684


685
686
687
688
689
690
691
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_footer();


}

/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.







>
>







680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_footer();
  run_common_script();
  run_script("xfer-ticket-script", zNewUuid);
}

/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.
746
747
748
749
750
751
752


753
754
755
756
757
758
759
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_footer();


}

/*
** Check the ticket table schema in zSchema to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.







>
>







750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_footer();
  run_common_script();
  run_script("xfer-ticket-script", zName);
}

/*
** Check the ticket table schema in zSchema to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.
1341
1342
1343
1344
1345
1346
1347
1348


1349
1350

1351
1352
1353
                       aField[i].zName, strlen(zValue), zValue);
        }
      }
      blob_appendf(&tktchng, "K %s\n", zTktUuid);
      blob_appendf(&tktchng, "U %F\n", zUser);
      md5sum_blob(&tktchng, &cksum);
      blob_appendf(&tktchng, "Z %b\n", &cksum);
      ticket_put(&tktchng, zTktUuid, 0);


      printf("ticket %s succeeded for %s\n",
             (eCmd==set?"set":"add"),zTktUuid);

    }
  }
}







|
>
>
|

>



1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
                       aField[i].zName, strlen(zValue), zValue);
        }
      }
      blob_appendf(&tktchng, "K %s\n", zTktUuid);
      blob_appendf(&tktchng, "U %F\n", zUser);
      md5sum_blob(&tktchng, &cksum);
      blob_appendf(&tktchng, "Z %b\n", &cksum);
      if( run_common_script() || ticket_put(&tktchng, zTktUuid, 0)){
        fossil_fatal("%s\n", g.zErrMsg);
      }else{
        fossil_print("ticket %s succeeded for %s\n",
             (eCmd==set?"set":"add"),zTktUuid);
      }
    }
  }
}
Changes to src/xfer.c.
818
819
820
821
822
823
824



























































































825
826
827
828

829
830
831
832




833





834
835
836
837

838
839




840



841

842
843
844
845
846
847
848
849
850
851
852
853
854
** from a server without authorization.
*/
static void server_private_xfer_not_authorized(void){
  @ error not\sauthorized\sto\ssync\sprivate\scontent
}

/*



























































































** Run the specified TH1 script, if any, and returns the return code or TH_OK
** when there is no script.
*/
static int run_script(const char *zScript){

  if( !zScript ){
    return TH_OK; /* No script, return success. */
  }
  Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */




  return Th_Eval(g.interp, 0, zScript, -1);





}

/*
** Run the pre-transfer TH1 script, if any, and returns the return code.

*/
static int run_common_script(void){




  return run_script(db_get("xfer-common-script", 0));



}


/*
** Run the post-push TH1 script, if any, and returns the return code.
*/
static int run_push_script(void){
  return run_script(db_get("xfer-push-script", 0));
}

/*
** If this variable is set, disable login checks.  Used for debugging
** only.
*/
static int disableLogin = 0;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<

|
>
|
|

<
>
>
>
>
|
>
>
>
>
>




>

|
>
>
>
>
|
>
>
>
|
>
|
<
<
<
<
|







818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916

917
918
919
920
921
922

923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950




951
952
953
954
955
956
957
958
** from a server without authorization.
*/
static void server_private_xfer_not_authorized(void){
  @ error not\sauthorized\sto\ssync\sprivate\scontent
}

/*
** TH command:      http -async URL ?PAYLOAD?
**
** Do a HTTP request to specified URL. If PAYLOAD is present
** it will be POST'ed as text/plain, otherwise it's a GET
*/
static int httpCmd(
  Th_Interp *interp,
  void *p,
  int argc,
  const char **argv,
  int *argl
){
  int i;
  const char *zSep, *type, *regexp, *params;
  Blob hdr, payload;
  ReCompiled *pRe = 0;

  if( (argc>1) && strcmp(argv[1],"-async") ){
    Th_ErrorMessage(interp, "synchronous http requests not yet implemented", 0, 0);
    return TH_ERROR;
  }
  ++argv;
  --argc;
  blob_zero(&payload);
  if( argc!=2 ){
    if( argc != 3 ){
      return Th_WrongNumArgs(interp, "http -async url ?payload?");
    }
    blob_append(&payload, argv[2], -1);
    type = "POST";
  }else{
    type = "GET";
  }
  params = strrchr(argv[1], '?');
  url_parse(argv[1], 0);
  if( g.urlIsSsh || g.urlIsFile ){
    Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
    return TH_ERROR;
  }
  regexp = db_get("http-allow-regexp", 0);
  if( regexp && regexp[0] ){
    const char * zErr = re_compile(&pRe, regexp, 0);
    if( zErr ){
      Th_SetResult(interp, zErr, -1);
      return TH_ERROR;
    }
  }
  if (!pRe || !re_match(pRe, (const unsigned char *)argv[1], -1) ){
    Th_SetResult(interp, "url not allowed", -1);
    return TH_ERROR;
  }
  re_free(pRe);
  if( transport_open() ){
    Th_ErrorMessage(interp, transport_errmsg(), 0, 0);
    return TH_ERROR;
  }
  blob_zero(&hdr);
  i = strlen(g.urlPath);
  if( (i>0) && (params!=argv[1]) ){
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", type, zSep, g.urlPath, params?params:"");
  if( g.urlProxyAuth ){
    blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", g.urlProxyAuth);
  }
  if( g.urlPasswd && g.urlUser && g.urlPasswd[0]=='#' ){
    char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]);
    char *zEncoded = encode64(zCredentials, -1);
    blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded);
    fossil_free(zEncoded);
    fossil_free(zCredentials);
  }
  blob_appendf(&hdr, "Host: %s\r\n", g.urlHostname);
  blob_appendf(&hdr, "User-Agent: Fossil/" RELEASE_VERSION
                     " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n");
  blob_appendf(&hdr, "Content-Type: text/plain\r\n");
  blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));

  transport_send(&hdr);
  transport_send(&payload);
  transport_close();
  g.urlProtocol=0; /* Make sure the url is not re-used. */
  Th_SetResult(interp, "", -1);
  return TH_OK;
}

static int commonScriptRan = 0;

/*
** Run the specified TH1 script, if any, and returns 1 on error.

*/
int run_script(const char *zScript, const char *zUuid){
  int result = 0;
  if( !commonScriptRan || !zScript || !(zScript = db_get(zScript, 0))){
    return 0; /* No script or common script didn't run, return success. */
  }

  if( commonScriptRan == 1 ){
    if( zUuid ){
      Th_SetVar(g.interp, "uuid", -1, zUuid, strlen(zUuid));
    }
    result = Th_Eval(g.interp, 0, zScript, -1) != TH_OK;
  }else{
    result = TH_ERROR;
  }
  if (result) fossil_error(1, "%s", Th_GetResult(g.interp, 0));
  return result;
}

/*
** Run the pre-transfer TH1 script, if any, and returns the return code.
** Prepare the "http" command for use in other hook scripts.
*/
int run_common_script(void){
  int result = 0;
  if( !commonScriptRan ){
    Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */
    commonScriptRan = 1; /* enable run_script to do something */
    result = run_script("xfer-common-script", 0);
    if( result == TH_ERROR ){
      /* Error message is left in th interpreter. */
      commonScriptRan = 2;
    }
    Th_CreateCommand(g.interp, "http", httpCmd, 0, 0);
  }




  return result;
}

/*
** If this variable is set, disable login checks.  Used for debugging
** only.
*/
static int disableLogin = 0;
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
  g.xferPanic = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
  );
  manifest_crosslink_begin();
  if( run_common_script()==TH_ERROR ){
    cgi_reset_content();
    @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
    nErr++;
  }
  while( blob_line(xfer.pIn, &xfer.line) ){
    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
    if( blob_size(&xfer.line)==0 ) continue;







|







1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
  g.xferPanic = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
  );
  manifest_crosslink_begin();
  if( run_common_script() ){
    cgi_reset_content();
    @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
    nErr++;
  }
  while( blob_line(xfer.pIn, &xfer.line) ){
    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
    if( blob_size(&xfer.line)==0 ) continue;
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
      cgi_reset_content();
      @ error bad\scommand:\s%F(blob_str(&xfer.line))
    }
    blobarray_reset(xfer.aToken, xfer.nToken);
    blob_reset(&xfer.line);
  }
  if( isPush ){
    if( run_push_script()==TH_ERROR ){
      cgi_reset_content();
      @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
      nErr++;
    }
    request_phantoms(&xfer, 500);
  }
  if( isClone && nGimme==0 ){
    /* The initial "clone" message from client to server contains no
    ** "gimme" cards. On that initial message, send the client an "igot"







|

|







1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
      cgi_reset_content();
      @ error bad\scommand:\s%F(blob_str(&xfer.line))
    }
    blobarray_reset(xfer.aToken, xfer.nToken);
    blob_reset(&xfer.line);
  }
  if( isPush ){
    if( run_script("xfer-push-script", 0) ){
      cgi_reset_content();
      @ error push\sscript\sfailed:\s%F(g.zErrMsg)
      nErr++;
    }
    request_phantoms(&xfer, 500);
  }
  if( isClone && nGimme==0 ){
    /* The initial "clone" message from client to server contains no
    ** "gimme" cards. On that initial message, send the client an "igot"
Changes to src/xfersetup.c.
34
35
36
37
38
39
40




41
42
43
44
45
46
47

  style_header("Transfer Setup");
  @ <table border="0" cellspacing="20">
  setup_menu_entry("Common", "xfersetup_com",
    "Common TH1 code run before all transfer request processing.");
  setup_menu_entry("Push", "xfersetup_push",
    "Specific TH1 code to run after \"push\" transfer requests.");




  @ </table>
  style_footer();
}

/*
** Common implementation for the transfer setup editor pages.
*/







>
>
>
>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

  style_header("Transfer Setup");
  @ <table border="0" cellspacing="20">
  setup_menu_entry("Common", "xfersetup_com",
    "Common TH1 code run before all transfer request processing.");
  setup_menu_entry("Push", "xfersetup_push",
    "Specific TH1 code to run after \"push\" transfer requests.");
  setup_menu_entry("Commit", "xfersetup_commit",
    "The TH1 code run after a commit.");
  setup_menu_entry("Ticket", "xfersetup_ticket",
    "The TH1 code run after a ticket change.");
  @ </table>
  style_footer();
}

/*
** Common implementation for the transfer setup editor pages.
*/
138
139
140
141
142
143
144
145
146
147
148








































  ;
  xfersetup_generic(
    "Transfer Push Script",
    "xfer-push-script",
    zDefaultXferPush,
    zDesc,
    0,
    0,
    30
  );
}



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  ;
  xfersetup_generic(
    "Transfer Push Script",
    "xfer-push-script",
    zDefaultXferPush,
    zDesc,
    0,
    0,
    30
  );
}

static const char *zDefaultXferCommit = 0;

/*
** WEBPAGE: xfersetup_commit
*/
void xfersetup_commit_page(void){
  static const char zDesc[] =
  @ Enter TH1 script that runs for each "commit" in a transfer requests.
  ;
  xfersetup_generic(
    "Transfer Commit Script",
    "xfer-commit-script",
    zDefaultXferCommit,
    zDesc,
    0,
    0,
    30
  );
}

static const char *zDefaultXferTicket = 0;

/*
** WEBPAGE: xfersetup_ticket
*/
void xfersetup_ticket_page(void){
  static const char zDesc[] =
  @ Enter TH1 script that runs for each "ticket" change in a transfer requests.
  ;
  xfersetup_generic(
    "Transfer Ticket Script",
    "xfer-ticket-script",
    zDefaultXferTicket,
    zDesc,
    0,
    0,
    30
  );
}