Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch fossil-2.0 Excluding Merge-Ins
This is equivalent to a diff from 41af304e to b9a1a3b9
2017-03-01
| ||
16:00 | Add the ability to read and understand SHA3 name hashes. ... (check-in: fd9b7bd9 user: drh tags: trunk) | |
15:35 | Change references to "SHA1 hash" in comments and UI labels to be "artifact hash" or similar. ... (Closed-Leaf check-in: b9a1a3b9 user: drh tags: fossil-2.0) | |
15:09 | Do not create the ALIAS table as it turns out not to be needed. ... (check-in: 3259aa54 user: drh tags: fossil-2.0) | |
00:35 | Merge updates from trunk. ... (check-in: fcc9116a user: drh tags: fossil-2.0) | |
00:25 | On a check-out the manifest.uuid file is determined by the display name, not the SHA1 hash of the manifest. ... (check-in: 41af304e user: drh tags: trunk) | |
2017-02-27
| ||
14:46 | The smallest SHA3 hash size is 224 bits, not 228 bits. ... (check-in: 5ed8477b user: drh tags: trunk) | |
Changes to VERSION.
|
| | | 1 | 2.0 |
Changes to src/attach.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ |
Changes to src/blob.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ | |||
645 646 647 648 649 650 651 | if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* | | > > > > > | < | | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 | if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* ** Return true if the blob contains a valid base16 identifier artifact hash. ** ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the ** hash is valid. Both of these are non-zero and therefore "true". ** If the hash is not valid, then HNAME_ERROR is returned, which is zero or ** false. */ int blob_is_hname(Blob *pBlob){ return hname_validate(blob_buffer(pBlob), blob_size(pBlob)); } /* ** Return true if the blob contains a valid filename */ int blob_is_filename(Blob *pBlob){ return file_is_simple_pathname(blob_str(pBlob), 1); |
︙ | ︙ |
Changes to src/browse.c.
︙ | ︙ | |||
313 314 315 316 317 318 319 | FileTreeNode *pNext; /* Next entry in an ordered list of them all */ FileTreeNode *pParent; /* Directory containing this entry */ FileTreeNode *pSibling; /* Next element in the same subdirectory */ FileTreeNode *pChild; /* List of child nodes */ FileTreeNode *pLastChild; /* Last child on the pChild list */ char *zName; /* Name of this entry. The "tail" */ char *zFullName; /* Full pathname of this entry */ | | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | FileTreeNode *pNext; /* Next entry in an ordered list of them all */ FileTreeNode *pParent; /* Directory containing this entry */ FileTreeNode *pSibling; /* Next element in the same subdirectory */ FileTreeNode *pChild; /* List of child nodes */ FileTreeNode *pLastChild; /* Last child on the pChild list */ char *zName; /* Name of this entry. The "tail" */ char *zFullName; /* Full pathname of this entry */ char *zUuid; /* Artifact hash of this file. May be NULL. */ double mtime; /* Modification time for this entry */ unsigned nFullName; /* Length of zFullName */ unsigned iLevel; /* Levels of parent directories */ }; /* ** A complete file hierarchy |
︙ | ︙ | |||
343 344 345 346 347 348 349 | ** When constructing a list of FileTreeNodes, all entries that have ** a common directory prefix must be added consecutively in order for ** the tree to be constructed properly. */ static void tree_add_node( FileTree *pTree, /* Tree into which nodes are added */ const char *zPath, /* The full pathname of file to add */ | | | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | ** When constructing a list of FileTreeNodes, all entries that have ** a common directory prefix must be added consecutively in order for ** the tree to be constructed properly. */ static void tree_add_node( FileTree *pTree, /* Tree into which nodes are added */ const char *zPath, /* The full pathname of file to add */ const char *zUuid, /* Hash of the file. Might be NULL. */ double mtime /* Modification time for this entry */ ){ int i; FileTreeNode *pParent; /* Parent (directory) of the next node to insert */ /* Make pParent point to the most recent ancestor of zPath, or ** NULL if there are no prior entires that are a container for zPath. |
︙ | ︙ | |||
366 367 368 369 370 371 372 | i = pParent ? pParent->nFullName+1 : 0; while( zPath[i] ){ FileTreeNode *pNew; int iStart = i; int nByte; while( zPath[i] && zPath[i]!='/' ){ i++; } nByte = sizeof(*pNew) + i + 1; | | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | i = pParent ? pParent->nFullName+1 : 0; while( zPath[i] ){ FileTreeNode *pNew; int iStart = i; int nByte; while( zPath[i] && zPath[i]!='/' ){ i++; } nByte = sizeof(*pNew) + i + 1; if( zUuid!=0 && zPath[i]==0 ) nByte += HNAME_MAX+1; pNew = fossil_malloc( nByte ); memset(pNew, 0, sizeof(*pNew)); pNew->zFullName = (char*)&pNew[1]; memcpy(pNew->zFullName, zPath, i); pNew->zFullName[i] = 0; pNew->nFullName = i; if( zUuid!=0 && zPath[i]==0 ){ pNew->zUuid = pNew->zFullName + i + 1; memcpy(pNew->zUuid, zUuid, strlen(zUuid)+1); } pNew->zName = pNew->zFullName + iStart; if( pTree->pLast ){ pTree->pLast->pNext = pNew; }else{ pTree->pFirst = pNew; } |
︙ | ︙ |
Changes to src/bundle.c.
︙ | ︙ | |||
33 34 35 36 37 38 39 | static const char zBundleInit[] = @ CREATE TABLE IF NOT EXISTS "%w".bconfig( @ bcname TEXT, @ bcvalue ANY @ ); @ CREATE TABLE IF NOT EXISTS "%w".bblob( @ blobid INTEGER PRIMARY KEY, -- Blob ID | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | static const char zBundleInit[] = @ CREATE TABLE IF NOT EXISTS "%w".bconfig( @ bcname TEXT, @ bcvalue ANY @ ); @ CREATE TABLE IF NOT EXISTS "%w".bblob( @ blobid INTEGER PRIMARY KEY, -- Blob ID @ uuid TEXT NOT NULL, -- hash of expanded blob @ sz INT NOT NULL, -- Size of blob after expansion @ delta ANY, -- Delta compression basis, or NULL @ notes TEXT, -- Description of content @ data BLOB -- compressed content @ ); ; |
︙ | ︙ | |||
437 438 439 440 441 442 443 | "SELECT uuid, data, bblob.delta, bix.blobid" " FROM bix, bblob" " WHERE bix.delta=%d" " AND bix.blobid=bblob.blobid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ | | | | < | < < | 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | "SELECT uuid, data, bblob.delta, bix.blobid" " FROM bix, bblob" " WHERE bix.delta=%d" " AND bix.blobid=bblob.blobid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ Blob h1, c1, c2; int rid; blob_zero(&h1); db_column_blob(&q, 0, &h1); blob_zero(&c1); db_column_blob(&q, 1, &c1); blob_uncompress(&c1, &c1); blob_zero(&c2); if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)>=HNAME_MIN ){ Blob basis; rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q", db_column_text(&q,2)); content_get(rid, &basis); blob_delta_apply(&basis, &c1, &c2); blob_reset(&basis); blob_reset(&c1); }else if( pBasis ){ blob_delta_apply(pBasis, &c1, &c2); blob_reset(&c1); }else{ c2 = c1; } if( hname_verify_hash(&c2, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("artifact hash error on %b", &h1); } rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv); if( rid==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ if( !isPriv ) content_make_public(rid); content_get(rid, &c1); manifest_crosslink(rid, &c1, MC_NO_ERRORS); |
︙ | ︙ | |||
489 490 491 492 493 494 495 | ** Extract an item from content from the bundle */ static void bundle_extract_item( int blobid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; | | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | ** Extract an item from content from the bundle */ static void bundle_extract_item( int blobid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; Blob x, basis, h1; static Bag busy; db_prepare(&q, "SELECT uuid, delta, data FROM bblob" " WHERE blobid=%d", blobid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); fossil_fatal("no such item: %d", blobid); |
︙ | ︙ | |||
523 524 525 526 527 528 529 | blob_reset(&basis); blob_reset(&x); }else{ *pOut = x; } blob_zero(&h1); db_column_blob(&q, 0, &h1); | | < | < < | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | blob_reset(&basis); blob_reset(&x); }else{ *pOut = x; } blob_zero(&h1); db_column_blob(&q, 0, &h1); if( hname_verify_hash(pOut, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect hash for artifact %b", &h1); } blob_reset(&h1); bag_remove(&busy, blobid); db_finalize(&q); } /* fossil bundle cat BUNDLE UUID... ** ** Write elements of a bundle on standard output |
︙ | ︙ | |||
595 596 597 598 599 600 601 | /* If the bundle contains deltas with a basis that is external to the ** bundle and those external basis files are missing from the local ** repo, then the delta encodings cannot be decoded and the bundle cannot ** be extracted. */ zMissingDeltas = db_text(0, "SELECT group_concat(substr(delta,1,10),' ')" " FROM bblob" | | | > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | /* If the bundle contains deltas with a basis that is external to the ** bundle and those external basis files are missing from the local ** repo, then the delta encodings cannot be decoded and the bundle cannot ** be extracted. */ zMissingDeltas = db_text(0, "SELECT group_concat(substr(delta,1,10),' ')" " FROM bblob" " WHERE typeof(delta)='text' AND length(delta)>=%d" " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)", HNAME_MIN); if( zMissingDeltas && zMissingDeltas[0] ){ fossil_fatal("delta basis artifacts not found in repository: %s", zMissingDeltas); } db_begin_transaction(); db_multi_exec( |
︙ | ︙ |
Changes to src/checkin.c.
︙ | ︙ | |||
375 376 377 378 379 380 381 | ** can be overridden by using one or more filter options (listed below), ** in which case only files with the specified change type(s) are shown. ** As a special case, the --no-merge option does not inhibit this default. ** This default shows exactly the set of changes that would be checked ** in by the commit command. ** ** If no filter options are used, or if the --merge option is used, the | | | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | ** can be overridden by using one or more filter options (listed below), ** in which case only files with the specified change type(s) are shown. ** As a special case, the --no-merge option does not inhibit this default. ** This default shows exactly the set of changes that would be checked ** in by the commit command. ** ** If no filter options are used, or if the --merge option is used, the ** artifact hash of each merge contributor check-in version is displayed at ** the end of the report. The --no-merge option is useful to display the ** default set of changed files without the merge contributors. ** ** If change type classification is enabled, each output line starts with ** a code describing the file's change type, e.g. EDITED or RENAMED. It ** is enabled by default unless exactly one change type is selected. For ** the purposes of determining the default, --changed counts as selecting |
︙ | ︙ | |||
410 411 412 413 414 415 416 | ** ** The "fossil changes --extra" command is equivalent to "fossil extras". ** ** General options: ** --abs-paths Display absolute pathnames. ** --rel-paths Display pathnames relative to the current working ** directory. | | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | ** ** The "fossil changes --extra" command is equivalent to "fossil extras". ** ** General options: ** --abs-paths Display absolute pathnames. ** --rel-paths Display pathnames relative to the current working ** directory. ** --hash Verify file status using hashing rather than ** relying on file mtimes. ** --case-sensitive <BOOL> Override case-sensitive setting. ** --dotfiles Include unmanaged files beginning with a dot. ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns. ** --no-dir-symlinks Disables support for directory symlinks. ** ** Options specific to the changes command: |
︙ | ︙ | |||
462 463 464 465 466 467 468 | {"classify", C_CLASSIFY}, }, noFlagDefs[] = { {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, }; Blob report = BLOB_INITIALIZER; enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | {"classify", C_CLASSIFY}, }, noFlagDefs[] = { {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, }; Blob report = BLOB_INITIALIZER; enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; int useHash = find_option("hash", 0, 0)!=0; int showHdr = command==CHANGES && find_option("header", 0, 0); int verboseFlag = command==CHANGES && find_option("verbose", "v", 0); const char *zIgnoreFlag = find_option("ignore", 0, 1); unsigned scanFlags = 0; unsigned flags = 0; int vid, i; |
︙ | ︙ | |||
526 527 528 529 530 531 532 | scanFlags = SCAN_ALL; } /* We should be done with options. */ verify_all_options(); /* Check for changed files. */ | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | scanFlags = SCAN_ALL; } /* We should be done with options. */ verify_all_options(); /* Check for changed files. */ vfile_check_signature(vid, useHash ? CKSIG_HASH : 0); /* Search for unmanaged files if requested. */ if( flags & C_EXTRA ){ Glob *pIgnore = glob_create(zIgnoreFlag); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); glob_free(pIgnore); } |
︙ | ︙ | |||
1983 1984 1985 1986 1987 1988 1989 | ** blank commit message. The default value is "N", do not commit. ** ** The --private option creates a private check-in that is never synced. ** Children of private check-ins are automatically private. ** ** The --tag option applies the symbolic tag name to the check-in. ** | | | | 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 | ** blank commit message. The default value is "N", do not commit. ** ** The --private option creates a private check-in that is never synced. ** Children of private check-ins are automatically private. ** ** The --tag option applies the symbolic tag name to the check-in. ** ** The --hash option detects edited files by computing each file's ** artifact hash rather than just checking for changes to its size or mtime. ** ** Options: ** --allow-conflict allow unresolved merge conflicts ** --allow-empty allow a commit with no changes ** --allow-fork allow the commit to fork ** --allow-older allow a commit older than its ancestor ** --baseline use a baseline manifest in the commit process |
︙ | ︙ | |||
2008 2009 2010 2011 2012 2013 2014 | ** -n|--dry-run If given, display instead of run actions ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings omit all warnings about file contents ** --nosign do not attempt to sign this commit with gpg ** --private do not sync changes and their descendants | | | 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 | ** -n|--dry-run If given, display instead of run actions ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings omit all warnings about file contents ** --nosign do not attempt to sign this commit with gpg ** --private do not sync changes and their descendants ** --hash verify file status using hashing rather ** than relying on file mtimes ** --tag TAG-NAME assign given tag TAG-NAME to the check-in ** --date-override DATETIME DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by |
︙ | ︙ | |||
2031 2032 2033 2034 2035 2036 2037 | int vid; /* blob-id of parent version */ int nrid; /* blob-id of a modified file */ int nvid; /* Blob-id of the new check-in */ Blob comment; /* Check-in comment */ const char *zComment; /* Check-in comment */ Stmt q; /* Various queries */ char *zUuid; /* UUID of the new check-in */ | | | 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 | int vid; /* blob-id of parent version */ int nrid; /* blob-id of a modified file */ int nvid; /* Blob-id of the new check-in */ Blob comment; /* Check-in comment */ const char *zComment; /* Check-in comment */ Stmt q; /* Various queries */ char *zUuid; /* UUID of the new check-in */ int useHash = 0; /* True to verify file status using hashing */ int noSign = 0; /* True to omit signing the manifest using GPG */ int isAMerge = 0; /* True if checking in a merge */ int noWarningFlag = 0; /* True if skipping all warnings */ int noPrompt = 0; /* True if skipping all prompts */ int forceFlag = 0; /* Undocumented: Disables all checks */ int forceDelta = 0; /* Force a delta-manifest */ int forceBaseline = 0; /* Force a baseline-manifest */ |
︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 | int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; Blob ans; char cReply; memset(&sCiInfo, 0, sizeof(sCiInfo)); url_proxy_options(); | | | 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 | int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; Blob ans; char cReply; memset(&sCiInfo, 0, sizeof(sCiInfo)); url_proxy_options(); useHash = find_option("hash", 0, 0)!=0; noSign = find_option("nosign",0,0)!=0; forceDelta = find_option("delta",0,0)!=0; forceBaseline = find_option("baseline",0,0)!=0; if( forceDelta && forceBaseline ){ fossil_fatal("cannot use --delta and --baseline together"); } dryRunFlag = find_option("dry-run","n",0)!=0; |
︙ | ︙ | |||
2230 2231 2232 2233 2234 2235 2236 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } | | | 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0); db_begin_transaction(); db_record_repository_filename(0); if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ fossil_fatal("nothing has changed; use --allow-empty to override"); } /* If none of the files that were named on the command line have |
︙ | ︙ |
Changes to src/content.c.
︙ | ︙ | |||
310 311 312 313 314 315 316 | } /* ** COMMAND: artifact* ** ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? ** | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | } /* ** COMMAND: artifact* ** ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? ** ** Extract an artifact by its artifact hash and write the results on ** standard output, or if the optional 4th argument is given, in ** the named output file. ** ** Options: ** -R|--repository FILE Extract artifacts from repository FILE ** ** See also: finfo |
︙ | ︙ | |||
495 496 497 498 499 500 501 | ** ** The original content of pBlob is not disturbed. The caller continues ** to be responsible for pBlob. This routine does *not* take over ** responsibility for freeing pBlob. */ int content_put_ex( Blob *pBlob, /* Content to add to the repository */ | | > > > > > > > > | > > < | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 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 | ** ** The original content of pBlob is not disturbed. The caller continues ** to be responsible for pBlob. This routine does *not* take over ** responsibility for freeing pBlob. */ int content_put_ex( Blob *pBlob, /* Content to add to the repository */ const char *zUuid, /* artifact hash of reconstructed pBlob */ int srcId, /* pBlob is a delta from this entry */ int nBlob, /* pBlob is compressed. Original size is this */ int isPrivate /* The content should be marked private */ ){ int size; int rid; Stmt s1; Blob cmpr; Blob hash; int markAsUnclustered = 0; int isDephantomize = 0; assert( g.repositoryOpen ); assert( pBlob!=0 ); assert( srcId==0 || zUuid!=0 ); db_begin_transaction(); if( zUuid==0 ){ assert( nBlob==0 ); /* First check the auxiliary hash to see if there is already an artifact ** that uses the auxiliary hash name */ hname_hash(pBlob, 1, &hash); rid = fast_uuid_to_rid(blob_str(&hash)); if( rid==0 ){ /* No existing artifact with the auxiliary hash name. Therefore, use ** the primary hash name. */ blob_reset(&hash); hname_hash(pBlob, 0, &hash); } }else{ blob_init(&hash, zUuid, -1); } if( nBlob ){ size = nBlob; }else{ size = blob_size(pBlob); if( srcId ){ size = delta_output_size(blob_buffer(pBlob), size); } } /* Check to see if the entry already exists and if it does whether ** or not the entry is a phantom */ db_prepare(&s1, "SELECT rid, size FROM blob WHERE uuid=%B", &hash); if( db_step(&s1)==SQLITE_ROW ){ rid = db_column_int(&s1, 0); |
︙ | ︙ | |||
866 867 868 869 870 871 872 | ** ** --parse Parse all manifests, wikis, tickets, events, and ** so forth, reporting any errors found. */ void test_integrity(void){ Stmt q; Blob content; | < | 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | ** ** --parse Parse all manifests, wikis, tickets, events, and ** so forth, reporting any errors found. */ void test_integrity(void){ Stmt q; Blob content; int n1 = 0; int n2 = 0; int nErr = 0; int total; int nCA = 0; int anCA[10]; int bParse = find_option("parse",0,0)!=0; |
︙ | ︙ | |||
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 | db_finalize(&q); db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); total = db_int(0, "SELECT max(rid) FROM blob"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); int size = db_column_int(&q, 2); n1++; fossil_print(" %d/%d\r", n1, total); fflush(stdout); if( size<0 ){ fossil_print("skip phantom %d %s\n", rid, zUuid); continue; /* Ignore phantoms */ } content_get(rid, &content); if( blob_size(&content)!=size ){ fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", rid, size, blob_size(&content)); nErr++; } | > | < | < | < | 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 959 960 961 962 963 964 965 966 967 968 969 970 971 972 | db_finalize(&q); db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); total = db_int(0, "SELECT max(rid) FROM blob"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); int nUuid = db_column_bytes(&q, 1); int size = db_column_int(&q, 2); n1++; fossil_print(" %d/%d\r", n1, total); fflush(stdout); if( size<0 ){ fossil_print("skip phantom %d %s\n", rid, zUuid); continue; /* Ignore phantoms */ } content_get(rid, &content); if( blob_size(&content)!=size ){ fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", rid, size, blob_size(&content)); nErr++; } if( !hname_verify_hash(&content, zUuid, nUuid) ){ fossil_print("wrong hash on artifact %d\n",rid); nErr++; } if( bParse && looks_like_control_artifact(&content) ){ Blob err; int i, n; char *z; Manifest *p; char zFirstLine[400]; blob_zero(&err); z = blob_buffer(&content); n = blob_size(&content); for(i=0; i<n && z[i] && z[i]!='\n' && i<sizeof(zFirstLine)-1; i++){} memcpy(zFirstLine, z, i); zFirstLine[i] = 0; p = manifest_parse(&content, 0, &err); if( p==0 ){ fossil_print("manifest_parse failed for %s:\n%s\n", zUuid, blob_str(&err)); if( strncmp(blob_str(&err), "line 1:", 7)==0 ){ fossil_print("\"%s\"\n", zFirstLine); } }else{ anCA[p->type]++; manifest_destroy(p); nCA++; } blob_reset(&err); }else{ blob_reset(&content); } n2++; } db_finalize(&q); fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", n2, n1, nErr); if( bParse ){ static const char *const azType[] = { 0, "manifest", "cluster", |
︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 | ** WARNING: This command destroys data and can cause you to lose work. ** Make sure you have a backup copy before using this command! ** ** WARNING: You must run "fossil rebuild" after this command to rebuild ** the metadata. ** ** Note that the arguments are the integer raw RID values from the BLOB table, | | | 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 | ** WARNING: This command destroys data and can cause you to lose work. ** Make sure you have a backup copy before using this command! ** ** WARNING: You must run "fossil rebuild" after this command to rebuild ** the metadata. ** ** Note that the arguments are the integer raw RID values from the BLOB table, ** not artifact hashs or labels. */ void test_content_erase(void){ int i; Blob x; char c; Stmt q; prompt_user("This command erases information from the repository and\n" |
︙ | ︙ |
Changes to src/db.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ | |||
1484 1485 1486 1487 1488 1489 1490 | db_open_or_attach(g.zRepositoryName, "repository"); g.repositoryOpen = 1; /* Cache "allow-symlinks" option, because we'll need it on every stat call */ g.allowSymlinks = db_get_boolean("allow-symlinks", db_allow_symlinks_by_default()); g.zAuxSchema = db_get("aux-schema",""); | | < | < < < < < < | < < < < < < < < < < < < < | 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 | db_open_or_attach(g.zRepositoryName, "repository"); g.repositoryOpen = 1; /* Cache "allow-symlinks" option, because we'll need it on every stat call */ g.allowSymlinks = db_get_boolean("allow-symlinks", db_allow_symlinks_by_default()); g.zAuxSchema = db_get("aux-schema",""); /* If the ALIAS table is not present, then some on-the-fly schema ** updates might be required. */ rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */ } /* ** Flags for the db_find_and_open_repository() function. */ #if INTERFACE #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */ |
︙ | ︙ | |||
2080 2081 2082 2083 2084 2085 2086 | assert( rc==0 || rc==1 ); if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; sqlite3_result_value(context, argv[2-rc]); } } /* | | | | | | | 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 | assert( rc==0 || rc==1 ); if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; sqlite3_result_value(context, argv[2-rc]); } } /* ** Convert the input string into a artifact hash. Make a notation in the ** CONCEALED table so that the hash can be undo using the db_reveal() ** function at some later time. ** ** The value returned is stored in static space and will be overwritten ** on subsequent calls. ** ** If zContent is already a well-formed artifact hash, then return a copy ** of that hash, not a hash of the hash. ** ** The CONCEALED table is meant to obscure email addresses. Every valid ** email address will contain a "@" character and "@" is not valid within ** a SHA1 hash so there is no chance that a valid email address will go ** unconcealed. */ char *db_conceal(const char *zContent, int n){ static char zHash[HNAME_MAX+1]; Blob out; if( hname_validate(zContent, n) ){ memcpy(zHash, zContent, n); zHash[n] = 0; }else{ sha1sum_step_text(zContent, n); sha1sum_finish(&out); sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out)); blob_reset(&out); |
︙ | ︙ |
Changes to src/diff.c.
︙ | ︙ | |||
2276 2277 2278 2279 2280 2281 2282 | ** WEBPAGE: praise ** ** URL: /annotate?checkin=ID&filename=FILENAME ** URL: /blame?checkin=ID&filename=FILENAME ** URL: /praise?checkin=ID&filename=FILENAME ** ** Show the most recent change to each line of a text file. /annotate shows | | | 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 | ** WEBPAGE: praise ** ** URL: /annotate?checkin=ID&filename=FILENAME ** URL: /blame?checkin=ID&filename=FILENAME ** URL: /praise?checkin=ID&filename=FILENAME ** ** Show the most recent change to each line of a text file. /annotate shows ** the date of the changes and the check-in hash (with a link to the ** check-in). /blame and /praise also show the user who made the check-in. ** ** Query parameters: ** ** checkin=ID The manifest ID at which to start the annotation ** filename=FILENAME The filename. ** filevers Show file versions rather than check-in versions |
︙ | ︙ |
Changes to src/doc.c.
︙ | ︙ | |||
525 526 527 528 529 530 531 | /* ** WEBPAGE: uv ** WEBPAGE: doc ** URL: /uv/FILE ** URL: /doc/CHECKIN/FILE ** | | | 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 | /* ** WEBPAGE: uv ** WEBPAGE: doc ** URL: /uv/FILE ** URL: /doc/CHECKIN/FILE ** ** CHECKIN can be either tag or hash prefix or timestamp identifying a ** particular check, or the name of a branch (meaning the most recent ** check-in on that branch) or one of various magic words: ** ** "tip" means the most recent check-in ** ** "ckout" means the current check-out, if the server is run from ** within a check-out, otherwise it is the same as "tip" |
︙ | ︙ |
Changes to src/event.c.
︙ | ︙ | |||
382 383 384 385 386 387 388 | } zTag = mprintf("event-%s", zId); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB '%q*')" " ORDER BY mtime DESC", zTag ); | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | } zTag = mprintf("event-%s", zId); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB '%q*')" " ORDER BY mtime DESC", zTag ); if( rid && strlen(zId)<HNAME_MIN ){ zId = db_text(0, "SELECT substr(tagname,7) FROM tag WHERE tagname GLOB '%q*'", zTag ); } free(zTag); |
︙ | ︙ |
Changes to src/export.c.
︙ | ︙ | |||
259 260 261 262 263 264 265 | } cur_tok = strtok(NULL, " \t"); if( !cur_tok ){ /* This mark was generated by an older version of Fossil and doesn't ** include the mark name and uuid. create_mark() will name the new mark ** exactly as it was when exported to git, so that we should have a | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | } cur_tok = strtok(NULL, " \t"); if( !cur_tok ){ /* This mark was generated by an older version of Fossil and doesn't ** include the mark name and uuid. create_mark() will name the new mark ** exactly as it was when exported to git, so that we should have a ** valid mapping from git hash<->mark name<->fossil hash. */ unsigned int mid; if( type_=='c' ){ mid = COMMITMARK(mark->rid); } else{ mid = BLOBMARK(mark->rid); } |
︙ | ︙ |
Changes to src/foci.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | ** SELECT * FROM files_of_checkin('trunk'); ** ** The "schema" for the temp.foci table is: ** ** CREATE TABLE files_of_checkin( ** checkinID INTEGER, -- RID for the check-in manifest ** filename TEXT, -- Name of a file | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** SELECT * FROM files_of_checkin('trunk'); ** ** The "schema" for the temp.foci table is: ** ** CREATE TABLE files_of_checkin( ** checkinID INTEGER, -- RID for the check-in manifest ** filename TEXT, -- Name of a file ** uuid TEXT, -- hash of the file ** previousName TEXT, -- Name of the file in previous check-in ** perm TEXT, -- Permissions on the file ** symname TEXT HIDDEN -- Symbolic name of the check-in. ** ); ** ** The hidden symname column is (optionally) used as a query parameter to ** identify the particular check-in to parse. The checkinID parameter |
︙ | ︙ | |||
52 53 54 55 56 57 58 | /* ** The schema for the virtual table: */ static const char zFociSchema[] = @ CREATE TABLE files_of_checkin( @ checkinID INTEGER, -- RID for the check-in manifest @ filename TEXT, -- Name of a file | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /* ** The schema for the virtual table: */ static const char zFociSchema[] = @ CREATE TABLE files_of_checkin( @ checkinID INTEGER, -- RID for the check-in manifest @ filename TEXT, -- Name of a file @ uuid TEXT, -- hash of the file @ previousName TEXT, -- Name of the file in previous check-in @ perm TEXT, -- Permissions on the file @ symname TEXT HIDDEN -- Symbolic name of the check-in @ ); ; #define FOCI_CHECKINID 0 |
︙ | ︙ |
Changes to src/fusefs.c.
︙ | ︙ | |||
292 293 294 295 296 297 298 | ** Usage: %fossil fusefs [--debug] DIRECTORY ** ** This command uses the Fuse Filesystem (FuseFS) to mount a directory ** at DIRECTORY that contains the content of all check-ins in the ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH ** where DIRECTORY is the root of the mount, VERSION is any valid ** check-in name (examples: "trunk" or "tip" or a tag or any unique | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ** Usage: %fossil fusefs [--debug] DIRECTORY ** ** This command uses the Fuse Filesystem (FuseFS) to mount a directory ** at DIRECTORY that contains the content of all check-ins in the ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH ** where DIRECTORY is the root of the mount, VERSION is any valid ** check-in name (examples: "trunk" or "tip" or a tag or any unique ** prefix of an artifact hash, etc) and PATH is the pathname of the file in ** the check-in. If DIRECTORY does not exist, then an attempt is made ** to create it. ** ** The DIRECTORY/checkins directory is not searchable so one cannot ** do "ls DIRECTORY/checkins" to get a listing of all possible check-in ** names. There are countless variations on check-in names and it is ** impractical to list them all. But all other directories are searchable |
︙ | ︙ |
Changes to src/graph.c.
︙ | ︙ | |||
177 178 179 180 181 182 183 | int graph_add_row( GraphContext *p, /* The context to which the row is added */ int rid, /* RID for the check-in */ int nParent, /* Number of parents */ int *aParent, /* Array of parents */ const char *zBranch, /* Branch for this check-in */ const char *zBgClr, /* Background color. NULL or "" for white. */ | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | int graph_add_row( GraphContext *p, /* The context to which the row is added */ int rid, /* RID for the check-in */ int nParent, /* Number of parents */ int *aParent, /* Array of parents */ const char *zBranch, /* Branch for this check-in */ const char *zBgClr, /* Background color. NULL or "" for white. */ const char *zUuid, /* hash name of the object being graphed */ int isLeaf /* True if this row is a leaf */ ){ GraphRow *pRow; int nByte; if( p->nErr ) return 0; nByte = sizeof(GraphRow); |
︙ | ︙ |
Added src/hname.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* ** Copyright (c) 2017 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains generic code for dealing with hashes used for ** naming artifacts. Specific hash algorithms are implemented separately ** (for example in sha1.c and sha3.c). This file contains the generic ** interface code. */ #include "config.h" #include "hname.h" #if INTERFACE /* ** Code numbers for the allowed hash algorithms. */ #define HNAME_ERROR 0 /* Not a valid hash */ #define HNAME_SHA1 1 /* SHA1 */ #define HNAME_K256 2 /* SHA3-256 */ /* ** Minimum and maximum lengths for a hash value when hex encoded. */ #define HNAME_MIN 40 /* Length for SHA1 */ #define HNAME_MAX 64 /* Length for SHA3-256 */ /* ** Hash lengths for the various algorithms */ #define HNAME_LEN_SHA1 40 #define HNAME_LEN_K256 64 /* ** The number of distinct hash algorithms: */ #define HNAME_COUNT 2 /* Just SHA1 and SHA3-256. Let's keep it that way! */ #endif /* INTERFACE */ /* ** Return the integer hash algorithm code number (ex: HNAME_K224) for ** the hash string provided. Or return HNAME_ERROR (0) if the input string ** is not a valid artifact hash string. */ int hname_validate(const char *zHash, int nHash){ int id; switch( nHash ){ case HNAME_LEN_SHA1: id = HNAME_SHA1; break; case HNAME_LEN_K256: id = HNAME_K256; break; default: return HNAME_ERROR; } if( !validate16(zHash, nHash) ) return HNAME_ERROR; return id; } /* ** Verify that zHash is a valid hash for the content in pContent. ** Return true if the hash is correct. Return false if the content ** does not match the hash. ** ** Actually, the returned value is one of the hash algorithm constants ** corresponding to the hash that matched if the hash is correct. ** (Examples: HNAME_SHA1 or HNAME_K224). And the return is HNAME_ERROR ** if the hash does not match. */ int hname_verify_hash(Blob *pContent, const char *zHash, int nHash){ int id = HNAME_ERROR; switch( nHash ){ case HNAME_LEN_SHA1: { Blob hash; sha1sum_blob(pContent, &hash); if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; blob_reset(&hash); break; } case HNAME_LEN_K256: { sha3sum_init(256); sha3sum_step_blob(pContent); if( memcmp(sha3sum_finish(0),zHash,64)==0 ) id = HNAME_K256; break; } } return id; } /* ** Verify that zHash is a valid hash for the content of a file on ** disk named zFile. ** ** Return true if the hash is correct. Return false if the content ** does not match the hash. ** ** Actually, the returned value is one of the hash algorithm constants ** corresponding to the hash that matched if the hash is correct. ** (Examples: HNAME_SHA1 or HNAME_K224). And the return is HNAME_ERROR ** if the hash does not match. */ int hname_verify_file_hash(const char *zFile, const char *zHash, int nHash){ int id = HNAME_ERROR; switch( nHash ){ case HNAME_LEN_SHA1: { Blob hash; sha1sum_file(zFile, &hash); if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; blob_reset(&hash); break; } case HNAME_LEN_K256: { Blob hash; sha3sum_file(zFile, 256, &hash); if( memcmp(blob_buffer(&hash),zHash,64)==0 ) id = HNAME_LEN_K256; blob_reset(&hash); break; } } return id; } /* ** Compute a hash on blob pContent. Write the hash into blob pHashOut. ** This routine assumes that pHashOut is uninitialized. ** ** The preferred hash is used for iHType==0, and various alternative hashes ** are used for iHType>0 && iHType<NHAME_COUNT. */ void hname_hash(const Blob *pContent, unsigned int iHType, Blob *pHashOut){ #if RELEASE_VERSION_NUMBER>=20100 /* For Fossil 2.1 and later, the preferred hash algorithm is SHA3-256 and ** SHA1 is the secondary hash algorithm. */ switch( iHType ){ case 0: sha3sum_blob(pContent, 256, pHashOut); break; case 1: sha1sum_blob(pContent, pHashOut); break; } #else /* Prior to Fossil 2.1, the preferred hash algorithm is SHA1 (for backwards ** compatibility with Fossil 1.x) and SHA3-256 is the only auxiliary ** algorithm */ switch( iHType ){ case 0: sha1sum_blob(pContent, pHashOut); break; case 1: sha3sum_blob(pContent, 256, pHashOut); break; } #endif } |
Changes to src/import.c.
︙ | ︙ | |||
147 148 149 150 151 152 153 | ** ** If saveUuid is true, then pContent is a commit record. Record its ** UUID in gg.zPrevCheckin. */ static int fast_insert_content( Blob *pContent, /* Content to insert */ const char *zMark, /* Label using this mark, if not NULL */ | | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | ** ** If saveUuid is true, then pContent is a commit record. Record its ** UUID in gg.zPrevCheckin. */ static int fast_insert_content( Blob *pContent, /* Content to insert */ const char *zMark, /* Label using this mark, if not NULL */ int saveUuid, /* Save artifact hash in gg.zPrevCheckin */ int doParse /* Invoke manifest_crosslink() */ ){ Blob hash; Blob cmpr; int rid; hname_hash(pContent, 0, &hash); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); if( rid==0 ){ static Stmt ins; db_static_prepare(&ins, "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)" ); db_bind_text(&ins, ":uuid", blob_str(&hash)); |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | " FROM attachment" " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" " ORDER BY mtime DESC /*sort*/", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTarget = db_column_text(&q, 0); const char *zFilename = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); /* const char *zSrc = db_column_text(&q, 4); */ if( cnt>0 ){ @ Also attachment "%h(zFilename)" to }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; | > | | 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | " FROM attachment" " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" " ORDER BY mtime DESC /*sort*/", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTarget = db_column_text(&q, 0); int nTarget = db_column_bytes(&q, 0); const char *zFilename = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); /* const char *zSrc = db_column_text(&q, 4); */ if( cnt>0 ){ @ Also attachment "%h(zFilename)" to }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; if( nTarget==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ if ( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ if( g.perm.Hyperlink && g.anon.RdTkt ){ @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)</a>] }else{ @ ticket [%S(zTarget)] |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
48 49 50 51 52 53 54 | #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ # include "json_detail.h" #endif /* | | > > > > > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ # include "json_detail.h" #endif /* ** Size of a UUID in characters. A UUID is a randomly generated ** lower-case hexadecimal number used to identify tickets. ** ** In Fossil 1.x, UUID also referred to a SHA1 artifact hash. But that ** usage is now obsolete. The term UUID should now mean only a very large ** random number used as a unique identifier for tickets or other objects. */ #define UUID_SIZE 40 /* ** Maximum number of auxiliary parameters on reports */ #define MX_AUX 5 |
︙ | ︙ |
Changes to src/main.mk.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/json.c \ | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/json.c \ |
︙ | ︙ | |||
226 227 228 229 230 231 232 233 234 235 236 237 238 239 | $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/json_.c \ | > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/json_.c \ |
︙ | ︙ | |||
351 352 353 354 355 356 357 358 359 360 361 362 363 364 | $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/json.o \ | > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/json.o \ |
︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 | $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ $(OBJDIR)/import_.c:$(OBJDIR)/import.h \ $(OBJDIR)/info_.c:$(OBJDIR)/info.h \ $(OBJDIR)/json_.c:$(OBJDIR)/json.h \ | > | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 | $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ $(OBJDIR)/import_.c:$(OBJDIR)/import.h \ $(OBJDIR)/info_.c:$(OBJDIR)/info.h \ $(OBJDIR)/json_.c:$(OBJDIR)/json.h \ |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c | > > > > > > > > | 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 | $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/hname_.c: $(SRCDIR)/hname.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/hname.c >$@ $(OBJDIR)/hname.o: $(OBJDIR)/hname_.c $(OBJDIR)/hname.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hname.o -c $(OBJDIR)/hname_.c $(OBJDIR)/hname.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c |
︙ | ︙ |
Changes to src/makemake.tcl.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | finfo foci fshell fusefs glob graph gzip http http_socket http_transport import info json json_artifact | > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | finfo foci fshell fusefs glob graph gzip hname http http_socket http_transport import info json json_artifact |
︙ | ︙ |
Changes to src/manifest.c.
︙ | ︙ | |||
52 53 54 55 56 57 58 | #define MC_NO_ERRORS 2 /* do not issue errors for a bad parse */ /* ** A single F-card within a manifest */ struct ManifestFile { char *zName; /* Name of a file */ | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #define MC_NO_ERRORS 2 /* do not issue errors for a bad parse */ /* ** A single F-card within a manifest */ struct ManifestFile { char *zName; /* Name of a file */ char *zUuid; /* Artifact hash for the file */ char *zPerm; /* File permissions */ char *zPrior; /* Prior name if the name was changed */ }; /* ** A parsed manifest or cluster. |
︙ | ︙ | |||
75 76 77 78 79 80 81 | double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zMimetype; /* Mime type of wiki or comment text. N card. */ double rEventDate; /* Date of an event. E card. */ | | | | | | | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zMimetype; /* Mime type of wiki or comment text. N card. */ double rEventDate; /* Date of an event. E card. */ char *zEventId; /* Artifact hash for an event. E card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* Artifact hash for document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ int nFile; /* Number of F cards */ int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* Hashes of parents. One for each P card argument */ int nCherrypick; /* Number of entries in aCherrypick[] */ struct { char *zCPTarget; /* Hash for cherry-picked version w/ +|- prefix */ char *zCPBase; /* Hash for cherry-pick baseline. NULL for singletons */ } *aCherrypick; int nCChild; /* Number of cluster children */ int nCChildAlloc; /* Number of closts allocated in azCChild[] */ char **azCChild; /* Hashes of referenced objects in a cluster. M cards */ int nTag; /* Number of T Cards */ int nTagAlloc; /* Slots allocated in aTag[] */ struct TagType { char *zName; /* Name of the tag */ char *zUuid; /* Hash of artifact that the tag is applied to */ char *zValue; /* Value if the tag is really a property */ } *aTag; /* One for each T card */ int nField; /* Number of J cards */ int nFieldAlloc; /* Slots allocated in aField[] */ struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ |
︙ | ︙ | |||
325 326 327 328 329 330 331 | /* ** Parse a blob into a Manifest object. The Manifest object ** takes over the input blob and will free it when the ** Manifest object is freed. Zeros are inserted into the blob ** as string terminators so that blob should not be used again. ** ** Return a pointer to an allocated Manifest object if the content | | | | > | | | | | < | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | /* ** Parse a blob into a Manifest object. The Manifest object ** takes over the input blob and will free it when the ** Manifest object is freed. Zeros are inserted into the blob ** as string terminators so that blob should not be used again. ** ** Return a pointer to an allocated Manifest object if the content ** really is a structural artifact of some kind. The returned Manifest ** object needs to be freed by a subsequent call to manifest_destroy(). ** Return NULL if there are syntax errors or if the input blob does ** not describe a valid structural artifact. ** ** This routine is strict about the format of a structural artifacts. ** The format must match exactly or else it is rejected. This ** rule minimizes the risk that a content artifact will be mistaken ** for a structural artifact simply because they look the same. ** ** The pContent is reset. If a pointer is returned, then pContent will ** be reset when the Manifest object is cleared. If NULL is ** returned then the Manifest object is cleared automatically ** and pContent is reset before the return. ** ** The entire input blob can be PGP clear-signed. The signature is ignored. ** The artifact consists of zero or more cards, one card per line. ** (Except: the content of the W card can extend of multiple lines.) ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ Manifest *p; int seenZ = 0; int i, lineNo=0; ManifestText x; char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; int isRepeat, hasSelfRefTag = 0; static Bag seen; const char *zErr = 0; if( rid==0 ){ isRepeat = 1; }else if( bag_find(&seen, rid) ){ isRepeat = 1; }else{ isRepeat = 0; bag_insert(&seen, rid); } /* Every structural artifact ends with a '\n' character. Exit early ** if that is not the case for this artifact. */ if( !isRepeat ) g.parseCnt[0]++; z = blob_materialize(pContent); n = blob_size(pContent); if( n<=0 || z[n-1]!='\n' ){ blob_reset(pContent); |
︙ | ︙ | |||
404 405 406 407 408 409 410 | */ if( verify_z_card(z, n)==2 ){ blob_reset(pContent); blob_appendf(pErr, "incorrect Z-card cksum"); return 0; } | < < < < < | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | */ if( verify_z_card(z, n)==2 ){ blob_reset(pContent); blob_appendf(pErr, "incorrect Z-card cksum"); return 0; } /* Allocate a Manifest object to hold the parsed control artifact. */ p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); memcpy(&p->content, pContent, sizeof(p->content)); p->rid = rid; blob_zero(pContent); |
︙ | ︙ | |||
447 448 449 450 451 452 453 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; if( p->zAttachName!=0 ) goto manifest_syntax_error; defossilize(zName); if( !file_is_simple_pathname(zName, 0) ){ SYNTAX("invalid filename on A-card"); } defossilize(zTarget); | | | | | | | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; if( p->zAttachName!=0 ) goto manifest_syntax_error; defossilize(zName); if( !file_is_simple_pathname(zName, 0) ){ SYNTAX("invalid filename on A-card"); } defossilize(zTarget); if( !hname_validate(zTarget,nTarget) && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ SYNTAX("invalid target on A-card"); } if( zSrc && !hname_validate(zSrc,nSrc) ){ SYNTAX("invalid source on A-card"); } p->zAttachName = (char*)file_tail(zName); p->zAttachSrc = zSrc; p->zAttachTarget = zTarget; break; } /* ** B <uuid> ** ** A B-line gives the artifact hash for the baseline of a delta-manifest. */ case 'B': { if( p->zBaseline ) SYNTAX("more than one B-card"); p->zBaseline = next_token(&x, &sz); if( p->zBaseline==0 ) SYNTAX("missing hash on B-card"); if( !hname_validate(p->zBaseline,sz) ){ SYNTAX("invalid hash on B-card"); } break; } /* ** C <comment> |
︙ | ︙ | |||
520 521 522 523 524 525 526 | ** is when the specific event is said to occur. */ case 'E': { if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); p->zEventId = next_token(&x, &sz); | | | | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | ** is when the specific event is said to occur. */ case 'E': { if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); p->zEventId = next_token(&x, &sz); if( !hname_validate(p->zEventId, sz) ){ SYNTAX("malformed hash on E-card"); } break; } /* ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? ** |
︙ | ︙ | |||
543 544 545 546 547 548 549 | if( zName==0 ) SYNTAX("missing filename on F-card"); defossilize(zName); if( !file_is_simple_pathname(zName, 0) ){ SYNTAX("F-card filename is not a simple path"); } zUuid = next_token(&x, &sz); if( p->zBaseline==0 || zUuid!=0 ){ | | | > | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | if( zName==0 ) SYNTAX("missing filename on F-card"); defossilize(zName); if( !file_is_simple_pathname(zName, 0) ){ SYNTAX("F-card filename is not a simple path"); } zUuid = next_token(&x, &sz); if( p->zBaseline==0 || zUuid!=0 ){ if( !hname_validate(zUuid,sz) ){ SYNTAX("F-card hash invalid"); } } zPerm = next_token(&x,0); zPriorName = next_token(&x,0); if( zPriorName ){ defossilize(zPriorName); if( !file_is_simple_pathname(zPriorName, 0) ){ SYNTAX("F-card old filename is not a simple path"); |
︙ | ︙ | |||
634 635 636 637 638 639 640 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ SYNTAX("L-card has malformed wiki name"); } break; } /* | | | | | | > | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ SYNTAX("L-card has malformed wiki name"); } break; } /* ** M <hash> ** ** An M-line identifies another artifact by its hash. M-lines ** occur in clusters only. */ case 'M': { zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing hash on M-card"); if( !hname_validate(zUuid,sz) ){ SYNTAX("Invalid hash on M-card"); } if( p->nCChild>=p->nCChildAlloc ){ p->nCChildAlloc = p->nCChildAlloc*2 + 10; p->azCChild = fossil_realloc(p->azCChild , p->nCChildAlloc*sizeof(p->azCChild[0]) ); } i = p->nCChild++; p->azCChild[i] = zUuid; |
︙ | ︙ | |||
681 682 683 684 685 686 687 | ** this artifact. The first parent is the primary parent. All ** others are parents by merge. Note that the initial empty ** check-in historically has an empty P-card, so empty P-cards ** must be accepted. */ case 'P': { while( (zUuid = next_token(&x, &sz))!=0 ){ | | | > | < | | | < < | < | 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | ** this artifact. The first parent is the primary parent. All ** others are parents by merge. Note that the initial empty ** check-in historically has an empty P-card, so empty P-cards ** must be accepted. */ case 'P': { while( (zUuid = next_token(&x, &sz))!=0 ){ if( !hname_validate(zUuid, sz) ){ SYNTAX("invalid hash on P-card"); } if( p->nParent>=p->nParentAlloc ){ p->nParentAlloc = p->nParentAlloc*2 + 5; p->azParent = fossil_realloc(p->azParent, p->nParentAlloc*sizeof(char*)); } i = p->nParent++; p->azParent[i] = zUuid; } break; } /* ** Q (+|-)<uuid> ?<uuid>? ** ** Specify one or a range of check-ins that are cherrypicked into ** this check-in ("+") or backed out of this check-in ("-"). */ case 'Q': { if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing hash on Q-card"); if( zUuid[0]!='+' && zUuid[0]!='-' ){ SYNTAX("Q-card does not begin with '+' or '-'"); } if( !hname_validate(&zUuid[1], sz-1) ){ SYNTAX("invalid hash on Q-card"); } n = p->nCherrypick; p->nCherrypick++; p->aCherrypick = fossil_realloc(p->aCherrypick, p->nCherrypick*sizeof(p->aCherrypick[0])); p->aCherrypick[n].zCPTarget = zUuid; p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); if( zUuid && !hname_validate(zUuid,sz) ){ SYNTAX("invalid second hash on Q-card"); } break; } /* ** R <md5sum> ** |
︙ | ︙ | |||
758 759 760 761 762 763 764 | ** Tags are not allowed in clusters. Multiple T lines are allowed. */ case 'T': { char *zName, *zValue; zName = next_token(&x, 0); if( zName==0 ) SYNTAX("missing name on T-card"); zUuid = next_token(&x, &sz); | | | | | | | | 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 | ** Tags are not allowed in clusters. Multiple T lines are allowed. */ case 'T': { char *zName, *zValue; zName = next_token(&x, 0); if( zName==0 ) SYNTAX("missing name on T-card"); zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing artifact hash on T-card"); zValue = next_token(&x, 0); if( zValue ) defossilize(zValue); if( hname_validate(zUuid, sz) ){ /* A valid artifact hash */ if( p->zEventId ) SYNTAX("non-self-referential T-card in event"); }else if( sz==1 && zUuid[0]=='*' ){ zUuid = 0; hasSelfRefTag = 1; if( p->zEventId && zName[0]!='+' ){ SYNTAX("propagating T-card in event"); } }else{ SYNTAX("malformed artifact hash on T-card"); } defossilize(zName); if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ SYNTAX("T-card name does not begin with '-', '+', or '*'"); } if( validate16(&zName[1], strlen(&zName[1])) ){ /* Do not allow tags whose names look like a hash */ SYNTAX("T-card name looks like a hexadecimal hash"); } if( p->nTag>=p->nTagAlloc ){ p->nTagAlloc = p->nTagAlloc*2 + 10; p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); } i = p->nTag++; p->aTag[i].zName = zName; |
︙ | ︙ | |||
949 950 951 952 953 954 955 | if( p->rDate<=0.0 ) SYNTAX("missing date on control"); if( p->zMimetype ) SYNTAX("N-card in control"); if( !seenZ ) SYNTAX("missing Z-card on control"); p->type = CFTYPE_CONTROL; } md5sum_init(); if( !isRepeat ) g.parseCnt[p->type]++; | < > > | | | > | 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 | if( p->rDate<=0.0 ) SYNTAX("missing date on control"); if( p->zMimetype ) SYNTAX("N-card in control"); if( !seenZ ) SYNTAX("missing Z-card on control"); p->type = CFTYPE_CONTROL; } md5sum_init(); if( !isRepeat ) g.parseCnt[p->type]++; return p; manifest_syntax_error: { char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid ){ blob_appendf(pErr, "manifest [%s] ", zUuid); fossil_free(zUuid); } } if( zErr ){ blob_appendf(pErr, "line %d: %s", lineNo, zErr); }else{ blob_appendf(pErr, "unknown error on line %d", lineNo); } md5sum_init(); |
︙ | ︙ | |||
1191 1192 1193 1194 1195 1196 1197 | ** An mlink entry is always created if isPrimary is true. But if ** isPrimary is false (meaning that pmid is a merge parent of mid) ** then the mlink entry is only created if there is already an mlink ** from primary parent for the same file. */ static void add_one_mlink( int pmid, /* The parent manifest */ | | | | 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 | ** An mlink entry is always created if isPrimary is true. But if ** isPrimary is false (meaning that pmid is a merge parent of mid) ** then the mlink entry is only created if there is already an mlink ** from primary parent for the same file. */ static void add_one_mlink( int pmid, /* The parent manifest */ const char *zFromUuid, /* Artifact hash for content in parent */ int mid, /* The record ID of the manifest */ const char *zToUuid, /* artifact hash for content in child */ const char *zFilename, /* Filename */ const char *zPrior, /* Previous filename. NULL if unchanged */ int isPublic, /* True if mid is not a private manifest */ int isPrimary, /* pmid is the primary parent of mid */ int mperm /* 1: exec, 2: symlink */ ){ int fnid, pfnid, pid, fid; |
︙ | ︙ | |||
1543 1544 1545 1546 1547 1548 1549 | add_mlink(pmid, 0, mid, pChild, 0); } } } /* ** For a check-in with RID "rid" that has nParent parent check-ins given | | | | | 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 | add_mlink(pmid, 0, mid, pChild, 0); } } } /* ** For a check-in with RID "rid" that has nParent parent check-ins given ** by the hashes in azParent[], create all appropriate plink and mlink table ** entries. ** ** The primary parent is the first hash on the azParent[] list. ** ** Return the RID of the primary parent. */ static int manifest_add_checkin_linkages( int rid, /* The RID of the check-in */ Manifest *p, /* Manifest for this check-in */ int nParent, /* Number of parents for this check-in */ char **azParent /* hashes for each parent */ ){ int i; int parentid = 0; char zBaseId[30]; /* Baseline manifest RID for deltas. "NULL" otherwise */ Stmt q; if( p->zBaseline ){ |
︙ | ︙ | |||
1609 1610 1611 1612 1613 1614 1615 | } } return parentid; } /* ** There exists a "parent" tag against checkin rid that has value zValue. | | | | | | | | | | | | > > | > > < | > | 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 | } } return parentid; } /* ** There exists a "parent" tag against checkin rid that has value zValue. ** If value is well-formed (meaning that it is a list of hashes), then use ** zValue to reparent check-in rid. */ void manifest_reparent_checkin(int rid, const char *zValue){ int nParent = 0; char *zCopy = 0; char **azParent = 0; Manifest *p = 0; int i, j; int n = (int)strlen(zValue); int mxParent = (n+1)/(HNAME_MIN+1); if( mxParent<1 ) return; zCopy = fossil_strdup(zValue); azParent = fossil_malloc( sizeof(azParent[0])*mxParent ); for(nParent=0, i=0; zCopy[i]; i++){ char *z = &zCopy[i]; azParent[nParent++] = z; if( nParent>mxParent ) goto reparent_abort; for(j=HNAME_MIN; z[j]>' '; j++){} if( !hname_validate(z, j) ) goto reparent_abort; if( z[j]==0 ) break; z[j] = 0; i += j; } if( !db_exists("SELECT 1 FROM plink WHERE cid=%d AND pid=%d", rid, uuid_to_rid(azParent[0],0)) ){ p = manifest_get(rid, CFTYPE_MANIFEST, 0); } if( p!=0 ){ db_multi_exec( "DELETE FROM plink WHERE cid=%d;" "DELETE FROM mlink WHERE mid=%d;", rid, rid ); manifest_add_checkin_linkages(rid,p,nParent,azParent); } manifest_destroy(p); reparent_abort: fossil_free(azParent); fossil_free(zCopy); } /* ** Setup to do multiple manifest_crosslink() calls. ** |
︙ | ︙ | |||
2278 2279 2280 2281 2282 2283 2284 | const char *zValue; const char *zTagUuid; int branchMove = 0; blob_zero(&comment); if( p->zComment ){ blob_appendf(&comment, " %s.", p->zComment); } | | | 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 | const char *zValue; const char *zTagUuid; int branchMove = 0; blob_zero(&comment); if( p->zComment ){ blob_appendf(&comment, " %s.", p->zComment); } /* Next loop expects tags to be sorted on hash, so sort it. */ qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); for(i=0; i<p->nTag; i++){ zTagUuid = p->aTag[i].zUuid; if( !zTagUuid ) continue; if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ blob_appendf(&comment, " Edit [%!S|%S]:", |
︙ | ︙ |
Changes to src/mkversion.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; | | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; int i, j, x, d; int vn[3]; char b[1000]; char vx[1000]; memset(b,0,sizeof(b)); memset(vx,0,sizeof(vx)); u = fopen(argv[1],"r"); if( fgets(b, sizeof(b)-1,u)==0 ){ fprintf(stderr, "malformed manifest.uuid file: %s\n", argv[1]); |
︙ | ︙ | |||
43 44 45 46 47 48 49 | exit(1); } fclose(v); for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} *z = 0; printf("#define RELEASE_VERSION \"%s\"\n", b); x=0; | | > < | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | exit(1); } fclose(v); for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} *z = 0; printf("#define RELEASE_VERSION \"%s\"\n", b); x=0; i=j=0; z=b; vn[0] = vn[1] = vn[2] = 0; while(1){ if( z[0]>='0' && z[0]<='9' ){ x = x*10 + z[0] - '0'; }else{ if( j<3 ) vn[j++] = x; x = 0; if( z[0]==0 ) break; } z++; } for(z=vx; z[0]=='0'; z++){} printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]); memset(vx,0,sizeof(vx)); strcpy(vx,b); d = 0; for(z=vx; z[0]; z++){ if( z[0]=='-' ){ z[0] = 0; break; |
︙ | ︙ |
Changes to src/name.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to resolved user-supplied object names. */ #include "config.h" #include "name.h" #include <assert.h> /* ** Return TRUE if the string begins with something that looks roughly |
︙ | ︙ | |||
78 79 80 81 82 83 84 | db_finalize(&q); return rid; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | db_finalize(&q); return rid; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** ** * artifact hash ** * 4-character or larger prefix of a artifact ** * Symbolic Name ** * "tag:" + symbolic name ** * Date or date-time ** * "date:" + Date or date-time ** * symbolic-name ":" date-time ** * "tip" ** |
︙ | ︙ | |||
103 104 105 106 107 108 109 | ** The zType parameter specifies the type of artifact: ci, t, w, e, g. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. ** zType is "ci" in most use cases since we are usually searching for ** a check-in. ** | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ** The zType parameter specifies the type of artifact: ci, t, w, e, g. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. ** zType is "ci" in most use cases since we are usually searching for ** a check-in. ** ** Note that the input zTag for types "t" and "e" is the artifact hash of ** the ticket-change or event-change artifact, not the randomly generated ** hexadecimal identifier assigned to tickets and events. Those identifiers ** live in a separate namespace. */ int symbolic_name_to_rid(const char *zTag, const char *zType){ int vid; int rid = 0; |
︙ | ︙ | |||
230 231 232 233 234 235 236 | " AND event.mtime<=julianday(%Q)" " AND event.type GLOB '%q'", zTagBase, zDate, zType ); return rid; } | | | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | " AND event.mtime<=julianday(%Q)" " AND event.type GLOB '%q'", zTagBase, zDate, zType ); return rid; } /* artifact hash or prefix */ if( nTag>=4 && nTag<=HNAME_MAX && validate16(zTag, nTag) ){ Stmt q; char zUuid[HNAME_MAX+1]; memcpy(zUuid, zTag, nTag+1); canonical16(zUuid, nTag); rid = 0; if( zType[0]=='*' ){ db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid); }else{ db_prepare(&q, |
︙ | ︙ | |||
352 353 354 355 356 357 358 | ** collisions of a given UUID based on its length on UUIDs no shorter ** than 4 characters in length. */ int name_collisions(const char *zName){ int c = 0; /* count of collisions for zName */ int nLen; /* length of zName */ nLen = strlen(zName); | | | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | ** collisions of a given UUID based on its length on UUIDs no shorter ** than 4 characters in length. */ int name_collisions(const char *zName){ int c = 0; /* count of collisions for zName */ int nLen; /* length of zName */ nLen = strlen(zName); if( nLen>=4 && nLen<=HNAME_MAX && validate16(zName, nLen) ){ c = db_int(0, "SELECT" " (SELECT count(*) FROM ticket" " WHERE tkt_uuid GLOB '%q*') +" " (SELECT count(*) FROM tag" " WHERE tagname GLOB 'event-%q*') +" " (SELECT count(*) FROM blob" |
︙ | ︙ | |||
394 395 396 397 398 399 400 | } } /* ** Convert a name to a rid. If the name can be any of the various forms ** accepted: ** | | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | } } /* ** Convert a name to a rid. If the name can be any of the various forms ** accepted: ** ** * artifact hash or prefix thereof ** * symbolic name ** * date ** * label:date ** * prev, previous ** * next ** * tip ** |
︙ | ︙ | |||
423 424 425 426 427 428 429 | } int name_to_rid(const char *zName){ return name_to_typed_rid(zName, "*"); } /* ** WEBPAGE: ambiguous | | | | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | } int name_to_rid(const char *zName){ return name_to_typed_rid(zName, "*"); } /* ** WEBPAGE: ambiguous ** URL: /ambiguous?name=NAME&src=WEBPAGE ** ** The NAME given by the name parameter is ambiguous. Display a page ** that shows all possible choices and let the user select between them. */ void ambiguous_page(void){ Stmt q; const char *zName = P("name"); const char *zSrc = P("src"); char *z; |
︙ | ︙ | |||
666 667 668 669 670 671 672 | } /* ** COMMAND: whatis* ** ** Usage: %fossil whatis NAME ** | | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 | } /* ** COMMAND: whatis* ** ** Usage: %fossil whatis NAME ** ** Resolve the symbol NAME into its canonical artifact hash ** artifact name and provide a description of what role that artifact ** plays. ** ** Options: ** ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', ** 'w', 'g', or 'e'). |
︙ | ︙ | |||
742 743 744 745 746 747 748 | /* ** COMMAND: test-ambiguous ** ** Usage: %fossil test-ambiguous [--minsize N] ** | | | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | /* ** COMMAND: test-ambiguous ** ** Usage: %fossil test-ambiguous [--minsize N] ** ** Show a list of ambiguous artifact hash abbreviations of N characters or ** more where N defaults to 4. Change N to a different value using ** the "--minsize N" command-line option. */ void test_ambiguous_cmd(void){ Stmt q, ins; int i; int minSize = 4; |
︙ | ︙ | |||
792 793 794 795 796 797 798 | /* ** Schema for the description table */ static const char zDescTab[] = @ CREATE TEMP TABLE IF NOT EXISTS description( @ rid INTEGER PRIMARY KEY, -- RID of the object | | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | /* ** Schema for the description table */ static const char zDescTab[] = @ CREATE TEMP TABLE IF NOT EXISTS description( @ rid INTEGER PRIMARY KEY, -- RID of the object @ uuid TEXT, -- hash of the object @ ctime DATETIME, -- Time of creation @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts @ type TEXT, -- file, checkin, wiki, ticket, etc. @ summary TEXT, -- Summary comment for the object @ detail TEXT -- File name, check-in comment, etc @ ); ; |
︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 | " datetime(description.ctime)" " FROM description, blob LEFT JOIN delta ON delta.rid=blob.rid" " WHERE description.rid=blob.rid" " ORDER BY length(content) DESC" ); @ <table cellpadding="2" cellspacing="0" border="1" id="bigblobtab"> @ <thead><tr><th align="right">Size<th align="right">RID | | | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | " datetime(description.ctime)" " FROM description, blob LEFT JOIN delta ON delta.rid=blob.rid" " WHERE description.rid=blob.rid" " ORDER BY length(content) DESC" ); @ <table cellpadding="2" cellspacing="0" border="1" id="bigblobtab"> @ <thead><tr><th align="right">Size<th align="right">RID @ <th align="right">Delta From<th>Hash<th>Description<th>Date</tr></thead> @ <tbody> while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q,0); const char *zUuid = db_column_text(&q, 1); const char *zDesc = db_column_text(&q, 2); int sz = db_column_int(&q,3); const char *zSrcId = db_column_text(&q,4); |
︙ | ︙ | |||
1145 1146 1147 1148 1149 1150 1151 | describe_artifacts_to_stdout("IN (SELECT rid FROM blob WHERE size<0)", 0); } /* Maximum number of collision examples to remember */ #define MAX_COLLIDE 25 /* | | | | | | | | | 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 | describe_artifacts_to_stdout("IN (SELECT rid FROM blob WHERE size<0)", 0); } /* Maximum number of collision examples to remember */ #define MAX_COLLIDE 25 /* ** Generate a report on the number of collisions in artifact hashes ** generated by the SQL given in the argument. */ static void collision_report(const char *zSql){ int i, j, kk; int nHash = 0; Stmt q; char zPrev[HNAME_MAX+1]; struct { int cnt; char *azHit[MAX_COLLIDE]; char z[HNAME_MAX+1]; } aCollide[HNAME_MAX+1]; memset(aCollide, 0, sizeof(aCollide)); memset(zPrev, 0, sizeof(zPrev)); db_prepare(&q,"%s",zSql/*safe-for-%s*/); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q,0); int n = db_column_bytes(&q,0); int i; nHash++; for(i=0; zPrev[i] && zPrev[i]==zUuid[i]; i++){} if( i>0 && i<=HNAME_MAX ){ if( i>=4 && aCollide[i].cnt<MAX_COLLIDE ){ aCollide[i].azHit[aCollide[i].cnt] = mprintf("%.*s", i, zPrev); } aCollide[i].cnt++; if( aCollide[i].z[0]==0 ) memcpy(aCollide[i].z, zPrev, n+1); } memcpy(zPrev, zUuid, n+1); } db_finalize(&q); @ <table border=1><thead> @ <tr><th>Length<th>Instances<th>First Instance</tr> @ </thead><tbody> for(i=1; i<=HNAME_MAX; i++){ if( aCollide[i].cnt==0 ) continue; @ <tr><td>%d(i)<td>%d(aCollide[i].cnt)<td>%h(aCollide[i].z)</tr> } @ </tbody></table> @ <p>Total number of hashes: %d(nHash)</p> kk = 0; for(i=HNAME_MAX; i>=4; i--){ if( aCollide[i].cnt==0 ) continue; if( aCollide[i].cnt>200 ) break; kk += aCollide[i].cnt; if( aCollide[i].cnt<25 ){ @ <p>Collisions of length %d(i): }else{ @ <p>First 25 collisions of length %d(i): |
︙ | ︙ | |||
1217 1218 1219 1220 1221 1222 1223 | ** WEBPAGE: hash-collisions ** ** Show the number of hash collisions for hash prefixes of various lengths. */ void hash_collisions_webpage(void){ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | | | 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 | ** WEBPAGE: hash-collisions ** ** Show the number of hash collisions for hash prefixes of various lengths. */ void hash_collisions_webpage(void){ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Hash Prefix Collisions"); style_submenu_element("Activity Reports", "reports"); style_submenu_element("Stats", "stat"); @ <h1>Hash Prefix Collisions on Check-ins</h1> collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)" " FROM event WHERE event.type='ci'" " ORDER BY 1"); @ <h1>Hash Prefix Collisions on All Artifacts</h1> collision_report("SELECT uuid FROM blob ORDER BY 1"); style_footer(); } |
Changes to src/printf.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 | #include "printf.h" #if defined(_WIN32) # include <io.h> # include <fcntl.h> #endif #include <time.h> | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include "printf.h" #if defined(_WIN32) # include <io.h> # include <fcntl.h> #endif #include <time.h> /* Two custom conversions are used to show a prefix of artifact hashes: ** ** %!S Prefix of a length appropriate for URLs ** %S Prefix of a length appropriate for human display ** ** The following macros help determine those lengths. FOSSIL_HASH_DIGITS ** is the default number of digits to display to humans. This value can ** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL ** is the minimum number of digits to be used in URLs. The number used ** will always be at least 6 more than the number used for human output, ** or 40 if the number of digits in human output is 34 or more. */ #ifndef FOSSIL_HASH_DIGITS # define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */ #endif #ifndef FOSSIL_HASH_DIGITS_URL # define FOSSIL_HASH_DIGITS_URL 16 /* For %!S (embedded in URLs) */ #endif /* ** Return the number of artifact hash digits to display. The number is for ** human output if the bForUrl is false and is destined for a URL if ** bForUrl is false. */ static int hashDigits(int bForUrl){ static int nDigitHuman = 0; static int nDigitUrl = 0; if( nDigitHuman==0 ){ |
︙ | ︙ |
Changes to src/purge.c.
︙ | ︙ | |||
41 42 43 44 45 46 47 | @ ctime DATETIME, -- When purge occurred. Seconds since 1970. @ pnotes TEXT -- Human-readable notes about the purge event @ ); @ CREATE TABLE IF NOT EXISTS "%w".purgeitem( @ piid INTEGER PRIMARY KEY, -- ID for the purge item @ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event @ orid INTEGER, -- Original RID before purged | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | @ ctime DATETIME, -- When purge occurred. Seconds since 1970. @ pnotes TEXT -- Human-readable notes about the purge event @ ); @ CREATE TABLE IF NOT EXISTS "%w".purgeitem( @ piid INTEGER PRIMARY KEY, -- ID for the purge item @ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event @ orid INTEGER, -- Original RID before purged @ uuid TEXT NOT NULL, -- hash of the purged artifact @ srcid INTEGER, -- Basis purgeitem for delta compression @ isPrivate BOOLEAN, -- True if artifact was originally private @ sz INT NOT NULL, -- Uncompressed size of the purged artifact @ desc TEXT, -- Brief description of this artifact @ data BLOB -- Compressed artifact content @ ); ; |
︙ | ︙ | |||
345 346 347 348 349 350 351 | */ static int purge_extract_item( int piid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; int srcid; | | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | */ static int purge_extract_item( int piid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; int srcid; Blob h1, x; static Bag busy; db_prepare(&q, "SELECT uuid, srcid, data FROM purgeitem" " WHERE piid=%d", piid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); fossil_fatal("missing purge-item %d", piid); |
︙ | ︙ | |||
374 375 376 377 378 379 380 | blob_reset(pOut); *pOut = out; blob_reset(&baseline); } bag_remove(&busy, piid); blob_zero(&h1); db_column_blob(&q, 0, &h1); | | < | < < | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | blob_reset(pOut); *pOut = out; blob_reset(&baseline); } bag_remove(&busy, piid); blob_zero(&h1); db_column_blob(&q, 0, &h1); if( hname_verify_hash(pOut, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect artifact hash on %b", &h1); } blob_reset(&h1); db_finalize(&q); return 0; } /* ** There is a TEMP table ix(piid,srcid) containing a set of purgeitems ** that need to be transferred to the BLOB table. This routine does |
︙ | ︙ | |||
409 410 411 412 413 414 415 | "SELECT uuid, data, isPrivate, ix.piid" " FROM ix, purgeitem" " WHERE ix.srcid=%d" " AND ix.piid=purgeitem.piid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ | | | < | < < | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | "SELECT uuid, data, isPrivate, ix.piid" " FROM ix, purgeitem" " WHERE ix.srcid=%d" " AND ix.piid=purgeitem.piid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ Blob h1, c1, c2; int isPriv, rid; blob_zero(&h1); db_column_blob(&q, 0, &h1); blob_zero(&c1); db_column_blob(&q, 1, &c1); blob_uncompress(&c1, &c1); blob_zero(&c2); if( pBasis ){ blob_delta_apply(pBasis, &c1, &c2); blob_reset(&c1); }else{ c2 = c1; } if( hname_verify_hash(&c2, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect hash on %b", &h1); } isPriv = db_column_int(&q, 2); rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv); if( rid==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ if( !isPriv ) content_make_public(rid); content_get(rid, &c1); |
︙ | ︙ |
Changes to src/rebuild.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "rebuild.h" #include <assert.h> #include <errno.h> /* | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > | > > > > > > > > > > > | > > > > | | | > | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | */ #include "config.h" #include "rebuild.h" #include <assert.h> #include <errno.h> /* ** Update the schema as necessary */ static void rebuild_update_schema(void){ /* Verify that the PLINK table has a new column added by the ** 2014-11-28 schema change. Create it if necessary. This code ** can be removed in the future, once all users have upgraded to the ** 2014-11-28 or later schema. */ if( !db_table_has_column("repository","plink","baseid") ){ db_multi_exec( "ALTER TABLE repository.plink ADD COLUMN baseid;" ); } /* Verify that the MLINK table has the newer columns added by the ** 2015-01-24 schema change. Create them if necessary. This code ** can be removed in the future, once all users have upgraded to the ** 2015-01-24 or later schema. */ if( !db_table_has_column("repository","mlink","isaux") ){ db_begin_transaction(); db_multi_exec( "ALTER TABLE repository.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" "ALTER TABLE repository.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;" ); db_end_transaction(0); } /* Add the user.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "user", "mtime") ){ db_multi_exec( "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" "DROP TABLE user;" "CREATE TABLE user(\n" " uid INTEGER PRIMARY KEY,\n" " login TEXT UNIQUE,\n" " pw TEXT,\n" |
︙ | ︙ | |||
107 108 109 110 111 112 113 | "INSERT OR IGNORE INTO user" " SELECT uid, login, pw, cap, cookie," " ipaddr, cexpire, info, now(), photo FROM temp_user;" "DROP TABLE temp_user;" ); } | < | > | < | > > | | > > > > > > | > > > > > > | > | < | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 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 | "INSERT OR IGNORE INTO user" " SELECT uid, login, pw, cap, cookie," " ipaddr, cexpire, info, now(), photo FROM temp_user;" "DROP TABLE temp_user;" ); } /* Add the config.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "config", "mtime") ){ db_multi_exec( "ALTER TABLE config ADD COLUMN mtime INTEGER;" "UPDATE config SET mtime=now();" ); } /* Add the shun.mtime and shun.scom columns if they are missing. ** (2011-04-27) */ if( !db_table_has_column("repository", "shun", "mtime") ){ db_multi_exec( "ALTER TABLE shun ADD COLUMN mtime INTEGER;" "ALTER TABLE shun ADD COLUMN scom TEXT;" "UPDATE shun SET mtime=now();" ); } /* Add the reportfmt.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "reportfmt", "mtime") ){ static const char zCreateReportFmtTable[] = @ -- An entry in this table describes a database query that generates a @ -- table of tickets. @ -- @ CREATE TABLE IF NOT EXISTS reportfmt( @ rn INTEGER PRIMARY KEY, -- Report number @ owner TEXT, -- Owner of this report format (not used) @ title TEXT UNIQUE, -- Title of this report @ mtime INTEGER, -- Time last modified. Seconds since 1970 @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); ; db_multi_exec( "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;" "DROP TABLE reportfmt;" ); db_multi_exec("%s", zCreateReportFmtTable/*safe-for-%s*/); db_multi_exec( "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)" " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;" "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)" " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()" " FROM old_fmt;" ); } /* Add the concealed.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "concealed", "mtime") ){ db_multi_exec( "ALTER TABLE concealed ADD COLUMN mtime INTEGER;" "UPDATE concealed SET mtime=now();" ); } /* Do the fossil-2.0 updates to the schema. (2017-02-28) */ rebuild_schema_update_2_0(); } /* ** Update the repository schema for Fossil version 2.0. (2017-02-28) ** (1) Change the CHECK constraint on BLOB.UUID so that the length ** is greater than or equal to 40, not exactly equal to 40. */ void rebuild_schema_update_2_0(void){ char *z = db_text(0, "SELECT sql FROM repository.sqlite_master WHERE name='blob'"); if( z ){ /* Search for: length(uuid)==40 ** 0123456789 12345 */ int i; for(i=10; z[i]; i++){ if( z[i]=='=' && strncmp(&z[i-6],"(uuid)==40",10)==0 ){ z[i] = '>'; db_multi_exec( "PRAGMA writable_schema=ON;" "UPDATE repository.sqlite_master SET sql=%Q WHERE name LIKE 'blob';" "PRAGMA writable_schema=OFF;", z ); break; } } fossil_free(z); } } /* ** Variables used to store state information about an on-going "rebuild" ** or "deconstruct". */ static int totalSize; /* Total number of artifacts to process */ |
︙ | ︙ | |||
328 329 330 331 332 333 334 | ** ** If the randomize parameter is true, then the BLOBs are deliberately ** extracted in a random order. This feature is used to test the ** ability of fossil to accept records in any order and still ** construct a sane repository. */ int rebuild_db(int randomize, int doOut, int doClustering){ | | > | | | | | | | | | | | | | < > > > | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | ** ** If the randomize parameter is true, then the BLOBs are deliberately ** extracted in a random order. This feature is used to test the ** ability of fossil to accept records in any order and still ** construct a sane repository. */ int rebuild_db(int randomize, int doOut, int doClustering){ Stmt s, q; int errCnt = 0; char *zTable; int incrSize; Blob sql; bag_init(&bagDone); ttyOutput = doOut; processCnt = 0; if (ttyOutput && !g.fQuiet) { percent_complete(0); } rebuild_update_schema(); blob_init(&sql, 0, 0); db_prepare(&q, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," "'purgeevent','purgeitem','unversioned')" " AND name NOT GLOB 'sqlite_*'" " AND name NOT GLOB 'fx_*'" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&sql, "DROP TABLE \"%w\";\n", db_column_text(&q,0)); } db_finalize(&q); db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); blob_reset(&sql); db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/); ticket_create_table(0); shun_artifacts(); db_multi_exec( "INSERT INTO unclustered" " SELECT rid FROM blob EXCEPT SELECT rid FROM private" |
︙ | ︙ | |||
992 993 994 995 996 997 998 | ** ** Usage %fossil deconstruct ?OPTIONS? DESTINATION ** ** ** This command exports all artifacts of a given repository and ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where | | | 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | ** ** Usage %fossil deconstruct ?OPTIONS? DESTINATION ** ** ** This command exports all artifacts of a given repository and ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where ** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters. ** If -L|--prefixlength is given, the length (default 2) of the directory ** prefix can be set to 0,1,..,9 characters. ** ** Options: ** -R|--repository REPOSITORY deconstruct given REPOSITORY ** -L|--prefixlength N set the length of the names of the DESTINATION ** subdirectories to N |
︙ | ︙ |
Changes to src/schema.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | @ -- The blob and delta tables collectively hold the "global state" of @ -- a Fossil repository. @ -- @ CREATE TABLE blob( @ rid INTEGER PRIMARY KEY, -- Record ID @ rcvid INTEGER, -- Origin of this record @ size INTEGER, -- Size of content. -1 for a phantom. | | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | @ -- The blob and delta tables collectively hold the "global state" of @ -- a Fossil repository. @ -- @ CREATE TABLE blob( @ rid INTEGER PRIMARY KEY, -- Record ID @ rcvid INTEGER, -- Origin of this record @ size INTEGER, -- Size of content. -1 for a phantom. @ uuid TEXT UNIQUE NOT NULL, -- hash of the content @ content BLOB, -- Compressed content of this record @ CHECK( length(uuid)>=40 AND rid>0 ) @ ); @ CREATE TABLE delta( @ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed @ srcid INTEGER NOT NULL REFERENCES blob -- Baseline for delta-compression @ ); @ CREATE INDEX delta_i1 ON delta(srcid); @ |
︙ | ︙ |
Changes to src/sha1.c.
1 2 3 4 5 6 7 8 | /* ** This implementation of SHA1. */ #include "config.h" #include <sys/types.h> #include "sha1.h" #ifdef FOSSIL_ENABLE_SSL | > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This implementation of SHA1. */ #include "config.h" #include <sys/types.h> #include "sha1.h" #ifdef FOSSIL_ENABLE_SSL |
︙ | ︙ |
Changes to src/sha3.c.
1 2 3 4 5 6 7 8 | /* ** This file contains an implementation of SHA3 (Keccak) hashing. */ #include "config.h" #include "sha3.h" /* ** Macros to determine whether the machine is big or little endian, | > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* ** Copyright (c) 2017 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains an implementation of SHA3 (Keccak) hashing. */ #include "config.h" #include "sha3.h" /* ** Macros to determine whether the machine is big or little endian, |
︙ | ︙ | |||
399 400 401 402 403 404 405 | SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ | | | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){ for(; i+7<nData; i+=8){ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; p->nLoaded += 8; if( p->nLoaded>=p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } |
︙ | ︙ | |||
606 607 608 609 610 611 612 | ** Usage: %fossil sha3sum FILE... ** ** Compute an SHA3 checksum of all files named on the command-line. ** If a file is named "-" then take its content from standard input. ** ** Options: ** | | | | | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | ** Usage: %fossil sha3sum FILE... ** ** Compute an SHA3 checksum of all files named on the command-line. ** If a file is named "-" then take its content from standard input. ** ** Options: ** ** --224 Compute a SHA3-224 hash ** --256 Compute a SHA3-256 hash (the default) ** --384 Compute a SHA3-384 hash ** --512 Compute a SHA3-512 hash ** --size N An N-bit hash. N must be a multiple of 32 between 128 ** and 512. */ void sha3sum_test(void){ int i; Blob in; Blob cksum; int iSize = 256; if( find_option("224",0,0)!=0 ) iSize = 224; else if( find_option("256",0,0)!=0 ) iSize = 256; else if( find_option("384",0,0)!=0 ) iSize = 384; else if( find_option("512",0,0)!=0 ) iSize = 512; else{ const char *zN = find_option("size",0,1); |
︙ | ︙ |
Changes to src/shun.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 | db_reset(&q); return rc==SQLITE_ROW; } /* ** WEBPAGE: shun ** | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | db_reset(&q); return rc==SQLITE_ROW; } /* ** WEBPAGE: shun ** ** View the hashes of all shunned artifacts. Add new hashes ** to the shun set. Requires Admin privilege. */ void shun_page(void){ Stmt q; int cnt = 0; const char *zUuid = P("uuid"); const char *zShun = P("shun"); |
︙ | ︙ | |||
82 83 84 85 86 87 88 | } i++; } zCanonical[j+1] = zCanonical[j] = 0; p = zCanonical; while( *p ){ int nUuid = strlen(p); | | | | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | } i++; } zCanonical[j+1] = zCanonical[j] = 0; p = zCanonical; while( *p ){ int nUuid = strlen(p); if( !hname_validate(p, nUuid) ){ @ <p class="generalError">Error: Bad artifact IDs.</p> fossil_free(zCanonical); zCanonical = 0; break; }else{ canonical16(p, nUuid); p += nUuid+1; } } zUuid = zCanonical; } style_header("Shunned Artifacts"); if( zUuid && P("sub") ){ const char *p = zUuid; int allExist = 1; login_verify_csrf_secret(); while( *p ){ db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p); if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){ allExist = 0; } admin_log("Unshunned %Q", p); p += strlen(p)+1; } if( allExist ){ @ <p class="noMoreShun">Artifact(s)<br /> for( p = zUuid ; *p ; p += strlen(p)+1 ){ @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> } @ are no longer being shunned.</p> }else{ @ <p class="noMoreShun">Artifact(s)<br /> for( p = zUuid ; *p ; p += strlen(p)+1 ){ @ %s(p)<br /> } @ will no longer be shunned. But they may not exist in the repository. @ It may be necessary to rebuild the repository using the @ <b>fossil rebuild</b> command-line before the artifact content @ can pulled in from other repositories.</p> } |
︙ | ︙ | |||
144 145 146 147 148 149 150 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p); if( tagid ){ db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p); db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid); db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); } admin_log("Shunned %Q", p); | | | | | | | | 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 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p); if( tagid ){ db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p); db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid); db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); } admin_log("Shunned %Q", p); p += strlen(p)+1; } @ <p class="shunned">Artifact(s)<br /> for( p = zUuid ; *p ; p += strlen(p)+1 ){ @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> } @ have been shunned. They will no longer be pushed. @ They will be removed from the repository the next time the repository @ is rebuilt using the <b>fossil rebuild</b> command-line</p> } if( zRcvid ){ nRcvid = atoi(zRcvid); numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d", nRcvid); } @ <p>A shunned artifact will not be pushed nor accepted in a pull and the @ artifact content will be purged from the repository the next time the @ repository is rebuilt. A list of shunned artifacts can be seen at the @ bottom of this page.</p> @ @ <a name="addshun"></a> @ <p>To shun artifacts, enter their artifact hashes (the 40- or @ 64-character lowercase hexadecimal hash of the artifact content) in the @ following box and press the "Shun" button. This will cause the artifacts @ to be removed from the repository and will prevent the artifacts from being @ readded to the repository by subsequent sync operation.</p> @ @ <p>Note that you must enter the full 40- or 64-character artifact hashes, @ not an abbreviation or a symbolic tag.</p> @ @ <p>Warning: Shunning should only be used to remove inappropriate content @ from the repository. Inappropriate content includes such things as @ spam added to Wiki, files that violate copyright or patent agreements, @ or artifacts that by design or accident interfere with the processing @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> |
︙ | ︙ |
Changes to src/sitemap.c.
︙ | ︙ | |||
109 110 111 112 113 114 115 | if( srchFlags ){ @ <li>%z(href("%R/search"))Full-Text Search</a></li> } @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li> if( g.perm.Read ){ @ <li>%z(href("%R/stat"))Repository Status</a> @ <ul> | | < | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | if( srchFlags ){ @ <li>%z(href("%R/search"))Full-Text Search</a></li> } @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li> if( g.perm.Read ){ @ <li>%z(href("%R/stat"))Repository Status</a> @ <ul> @ <li>%z(href("%R/hash-collisions"))Collisions on hash prefixes</a></li> if( g.perm.Admin ){ @ <li>%z(href("%R/urllist"))List of URLs used to access @ this repository</a></li> } @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li> @ </ul> |
︙ | ︙ |
Changes to src/sqlcmd.c.
︙ | ︙ | |||
183 184 185 186 187 188 189 | ** ** WARNING: Careless use of this command can corrupt a Fossil repository ** in ways that are unrecoverable. Be sure you know what you are doing before ** running any SQL commands that modify the repository database. ** ** The following extensions to the usual SQLite commands are provided: ** | | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | ** ** WARNING: Careless use of this command can corrupt a Fossil repository ** in ways that are unrecoverable. Be sure you know what you are doing before ** running any SQL commands that modify the repository database. ** ** The following extensions to the usual SQLite commands are provided: ** ** content(X) Return the content of artifact X. X can be an ** artifact hash or prefix or a tag. ** ** compress(X) Compress text X. ** ** decompress(X) Decompress text X. Undoes the work of ** compress(X). ** ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) |
︙ | ︙ |
Changes to src/stat.c.
︙ | ︙ | |||
74 75 76 77 78 79 80 | style_adunit_config(ADUNIT_RIGHT_OK); if( g.perm.Admin ){ style_submenu_element("URLs", "urllist"); style_submenu_element("Schema", "repo_schema"); style_submenu_element("Web-Cache", "cachestat"); } style_submenu_element("Activity Reports", "reports"); | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | style_adunit_config(ADUNIT_RIGHT_OK); if( g.perm.Admin ){ style_submenu_element("URLs", "urllist"); style_submenu_element("Schema", "repo_schema"); style_submenu_element("Web-Cache", "cachestat"); } style_submenu_element("Activity Reports", "reports"); style_submenu_element("Hash Collisions", "hash-collisions"); if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){ style_submenu_element("Table Sizes", "repo-tabsize"); } if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){ style_submenu_element("Environment", "test_env"); } @ <table class="label-value"> |
︙ | ︙ |
Changes to src/tag.c.
︙ | ︙ | |||
625 626 627 628 629 630 631 | } rid = name_to_typed_rid(g.argv[2], "ci"); blob_init(&value, 0, 0); for(i=3; i<g.argc; i++){ int pid = name_to_typed_rid(g.argv[i], "ci"); if( i>3 ) blob_append(&value, " ", 1); zUuid = rid_to_uuid(pid); | | | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | } rid = name_to_typed_rid(g.argv[2], "ci"); blob_init(&value, 0, 0); for(i=3; i<g.argc; i++){ int pid = name_to_typed_rid(g.argv[i], "ci"); if( i>3 ) blob_append(&value, " ", 1); zUuid = rid_to_uuid(pid); blob_append(&value, zUuid, strlen(zUuid)); fossil_free(zUuid); } if( bTest && !dryRun ){ tag_insert("parent", 1, blob_str(&value), -1, 0.0, rid); }else{ zUuid = rid_to_uuid(rid); tag_add_artifact("","parent",zUuid,blob_str(&value),1|dryRun,0,0); |
︙ | ︙ |
Changes to src/tar.c.
︙ | ︙ | |||
474 475 476 477 478 479 480 481 482 483 484 485 486 487 | ){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; char *zName = 0; unsigned int mTime; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pTar); return; } | > | | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | ){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; char *zName = 0; char *zUuid; unsigned int mTime; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pTar); return; } blob_set_dynamic(&hash, rid_to_uuid(rid)); blob_zero(&filename); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); |
︙ | ︙ | |||
518 519 520 521 522 523 524 | } if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){ if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); } | < < < < | 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 | } if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){ if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); } if( eflg & MFESTFLG_RAW ) { sterilize_manifest(&mfile); tar_add_file(zName, &mfile, 0, mTime); } } blob_reset(&mfile); if( eflg & MFESTFLG_UUID ){ blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); tar_add_file(zName, &hash, 0, mTime); } if( eflg & MFESTFLG_TAGS ){ Blob tagslist; blob_zero(&tagslist); get_checkin_taglist(rid, &tagslist); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.tags", -1); |
︙ | ︙ | |||
562 563 564 565 566 567 568 | blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime); blob_reset(&file); } } }else{ | < > | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime); blob_reset(&file); } } }else{ blob_append(&filename, blob_str(&hash), 16); zName = blob_str(&filename); mTime = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0;"); tar_begin(mTime); tar_add_file(zName, &mfile, 0, mTime); } manifest_destroy(pManifest); blob_reset(&mfile); blob_reset(&hash); blob_reset(&filename); tar_finish(pTar); } /* ** COMMAND: tarball* ** |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
721 722 723 724 725 726 727 | ** is the rail on which the riser should run and the second integer ** is the id of the node upto which the riser should run. ** mi: "merge-in". An array of integer rail positions from which ** merge arrows should be drawn into this node. If the value is ** negative, then the rail position is the absolute value of mi[] ** and a thin merge-arrow descender is drawn to the bottom of ** the screen. | | | 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 | ** is the rail on which the riser should run and the second integer ** is the id of the node upto which the riser should run. ** mi: "merge-in". An array of integer rail positions from which ** merge arrows should be drawn into this node. If the value is ** negative, then the rail position is the absolute value of mi[] ** and a thin merge-arrow descender is drawn to the bottom of ** the screen. ** h: The artifact hash of the object being graphed */ cgi_printf("var rowinfo = [\n"); for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:", pRow->idx, /* id */ pRow->zBgClr, /* bg */ pRow->iRail, /* r */ |
︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 | ** ng No Graph. ** nd Do not highlight the focus check-in ** v Show details of files changed ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** shortest ... show only the shortest path | | | 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 | ** ng No Graph. ** nd Do not highlight the focus check-in ** v Show details of files changed ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** shortest ... show only the shortest path ** uf=FILE_HASH Show only check-ins that contain the given file version ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color from branch name ** ubg Background color from user ** namechng Show only check-ins that have filename changes ** forks Show only forks and their children ** ym=YYYY-MM Show only events for the given year/month |
︙ | ︙ |
Changes to src/verify.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 | static void verify_rid(int rid){ Blob uuid, hash, content; if( content_size(rid, 0)<0 ){ return; /* No way to verify phantoms */ } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); | | | < < | | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | static void verify_rid(int rid){ Blob uuid, hash, content; if( content_size(rid, 0)<0 ){ return; /* No way to verify phantoms */ } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_fatal("not a valid rid: %d", rid); } if( content_get(rid, &content) ){ if( !hname_verify_hash(&content, blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_fatal("hash of rid %d does not match its uuid (%b)", rid, &uuid); } blob_reset(&content); } blob_reset(&uuid); } /* ** The following bag holds the rid for every record that needs ** to be verified. |
︙ | ︙ |
Changes to src/vfile.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> /* | | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> /* ** The input is guaranteed to be a 40- or 64-character well-formed ** artifact hash. Find its rid. */ int fast_uuid_to_rid(const char *zUuid){ static Stmt q; int rid; db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
49 50 51 52 53 54 55 | ** ** If the UUID is not found and phantomize is 1 or 2, then attempt to ** create a phantom record. A private phantom is created for 2 and ** a public phantom is created for 1. */ int uuid_to_rid(const char *zUuid, int phantomize){ int rid, sz; | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | ** ** If the UUID is not found and phantomize is 1 or 2, then attempt to ** create a phantom record. A private phantom is created for 2 and ** a public phantom is created for 1. */ int uuid_to_rid(const char *zUuid, int phantomize){ int rid, sz; char z[HNAME_MAX+1]; sz = strlen(zUuid); if( !hname_validate(zUuid, sz) ){ return 0; /* Not a valid hash */ } memcpy(z, zUuid, sz+1); canonical16(z, sz); rid = fast_uuid_to_rid(z); if( rid==0 && phantomize ){ rid = content_new(zUuid, phantomize-1); } return rid; } |
︙ | ︙ | |||
128 129 130 131 132 133 134 | #if INTERFACE /* ** The cksigFlags parameter to vfile_check_signature() is an OR-ed ** combination of the following bits: */ #define CKSIG_ENOTFILE 0x001 /* non-file FS objects throw an error */ | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | #if INTERFACE /* ** The cksigFlags parameter to vfile_check_signature() is an OR-ed ** combination of the following bits: */ #define CKSIG_ENOTFILE 0x001 /* non-file FS objects throw an error */ #define CKSIG_HASH 0x002 /* Verify file content using hashing */ #define CKSIG_SETMTIME 0x004 /* Set mtime to last check-out time */ #endif /* INTERFACE */ /* ** Look at every VFILE entry with the given vid and update VFILE.CHNGED field ** according to whether or not the file has changed. |
︙ | ︙ | |||
157 158 159 160 161 162 163 | ** the file has changed without having the check the size, mtime, ** or on-disk content. ** ** If the size of the file has changed, then we always know that the file ** changed without having to look at the mtime or on-disk content. ** ** The mtime of the file is only a factor if the mtime-changes setting | | | | | 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 | ** the file has changed without having the check the size, mtime, ** or on-disk content. ** ** If the size of the file has changed, then we always know that the file ** changed without having to look at the mtime or on-disk content. ** ** The mtime of the file is only a factor if the mtime-changes setting ** is false and the CKSIG_HASH flag is false. If the mtime-changes ** setting is true (or undefined - it defaults to true) or if CKSIG_HASH ** is true, then we do not trust the mtime and will examine the on-disk ** content to determine if a file really is the same. ** ** If the mtime is used, it is used only to determine if files are the same. ** If the mtime of a file has changed, we still examine the on-disk content ** to see whether or not the edit was a null-edit. */ void vfile_check_signature(int vid, unsigned int cksigFlags){ int nErr = 0; Stmt q; Blob fileCksum, origCksum; int useMtime = (cksigFlags & CKSIG_HASH)==0 && db_get_boolean("mtime-changes", 1); db_begin_transaction(); db_prepare(&q, "SELECT id, %Q || pathname," " vfile.mrid, deleted, chnged, uuid, size, mtime," " CASE WHEN isexe THEN %d WHEN islink THEN %d ELSE %d END" " FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid" |
︙ | ︙ | |||
220 221 222 223 224 225 226 | nErr++; } chnged = 1; } if( origSize!=currentSize ){ if( chnged!=1 ){ /* A file size change is definitive - the file has changed. No | | > > < < < < | < < | | > > < < < < < | < < < | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | nErr++; } chnged = 1; } if( origSize!=currentSize ){ if( chnged!=1 ){ /* A file size change is definitive - the file has changed. No ** need to check the mtime or hash */ chnged = 1; } }else if( chnged==1 && rid!=0 && !isDeleted ){ /* File is believed to have changed but it is the same size. ** Double check that it really has changed by looking at content. */ const char *zUuid = db_column_text(&q, 5); int nUuid = db_column_bytes(&q, 5); assert( origSize==currentSize ); if( hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 0; }else if( (chnged==0 || chnged==2 || chnged==4) && (useMtime==0 || currentMtime!=oldMtime) ){ /* For files that were formerly believed to be unchanged or that were ** changed by merging, if their mtime changes, or unconditionally ** if --hash is used, check to see if they have been edited by ** looking at their artifact hashes */ const char *zUuid = db_column_text(&q, 5); int nUuid = db_column_bytes(&q, 5); assert( origSize==currentSize ); if( !hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 1; } if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2 || chnged==4) ){ i64 desiredMtime; if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){ if( currentMtime!=desiredMtime ){ file_set_mtime(zName, desiredMtime); currentMtime = file_wd_mtime(zName); |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
1120 1121 1122 1123 1124 1125 1126 | ** timestamp. Returns 0 if there is no such item and -1 if the details ** are ambiguous and could refer to multiple items. */ int wiki_technote_to_rid(const char *zETime) { int rid=0; /* Artifact ID of the tech note */ int nETime = strlen(zETime); Stmt q; | | | | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 | ** timestamp. Returns 0 if there is no such item and -1 if the details ** are ambiguous and could refer to multiple items. */ int wiki_technote_to_rid(const char *zETime) { int rid=0; /* Artifact ID of the tech note */ int nETime = strlen(zETime); Stmt q; if( nETime>=4 && hname_validate(zETime, nETime) ){ char zUuid[HNAME_MAX+1]; memcpy(zUuid, zETime, nETime+1); canonical16(zUuid, nETime); db_prepare(&q, "SELECT e.objid" " FROM event e, tag t" " WHERE e.type='e' AND e.tagid IS NOT NULL AND t.tagid=e.tagid" " AND t.tagname GLOB 'event-%q*'", |
︙ | ︙ |
Changes to src/wikiformat.c.
︙ | ︙ | |||
1065 1066 1067 1068 1069 1070 1071 | /* ** If the input string corresponds to an existing baseline, ** return true. */ static int is_valid_uuid(const char *z){ int n = strlen(z); | | | 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | /* ** If the input string corresponds to an existing baseline, ** return true. */ static int is_valid_uuid(const char *z){ int n = strlen(z); if( n<4 || n>HNAME_MAX ) return 0; if( !validate16(z, n) ) return 0; return 1; } /* ** Return TRUE if a UUID corresponds to an artifact in this ** repository. |
︙ | ︙ |
Changes to src/xfer.c.
︙ | ︙ | |||
52 53 54 55 56 57 58 | u8 syncPrivate; /* True to enable syncing private content */ u8 nextIsPrivate; /* If true, next "file" received is a private */ time_t maxTime; /* Time when this transfer should be finished */ }; /* | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | u8 syncPrivate; /* True to enable syncing private content */ u8 nextIsPrivate; /* If true, next "file" received is a private */ time_t maxTime; /* Time when this transfer should be finished */ }; /* ** The input blob contains an artifact. Convert it into a record ID. ** Create a phantom record if no prior record exists and ** phantomize is true. ** ** Compare to uuid_to_rid(). This routine takes a blob argument ** and does less error checking. */ static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){ |
︙ | ︙ | |||
98 99 100 101 102 103 104 | /* ** The aToken[0..nToken-1] blob array is a parse of a "file" line ** message. This routine finishes parsing that message and does ** a record insert of the file. ** ** The file line is in one of the following two forms: ** | | | | > | | < > | | | | | | | | < | < < | | < | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | /* ** The aToken[0..nToken-1] blob array is a parse of a "file" line ** message. This routine finishes parsing that message and does ** a record insert of the file. ** ** The file line is in one of the following two forms: ** ** file HASH SIZE \n CONTENT ** file HASH DELTASRC SIZE \n CONTENT ** ** The content is SIZE bytes immediately following the newline. ** If DELTASRC exists, then the CONTENT is a delta against the ** content of DELTASRC. ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ static void xfer_accept_file( Xfer *pXfer, int cloneFlag, char **pzUuidList, int *pnUuidList ){ int n; int rid; int srcid = 0; Blob content; int isPriv; Blob *pUuid; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<3 || pXfer->nToken>4 || !blob_is_hname(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n) || n<0 || (pXfer->nToken==4 && !blob_is_hname(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed file line"); return; } blob_zero(&content); blob_extract(pXfer->pIn, n, &content); pUuid = &pXfer->aToken[1]; if( !cloneFlag && uuid_is_shunned(blob_str(pUuid)) ){ /* Ignore files that have been shunned */ blob_reset(&content); return; } if( isPriv && !g.perm.Private ){ /* Do not accept private files if not authorized */ blob_reset(&content); return; } if( cloneFlag ){ if( pXfer->nToken==4 ){ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(pUuid), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); remote_has(rid); blob_reset(&content); return; } if( pXfer->nToken==4 ){ Blob src, next; srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); if( content_get(srcid, &src)==0 ){ rid = content_put_ex(&content, blob_str(pUuid), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); pXfer->nDanglingFile++; db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); if( !isPriv ) content_make_public(rid); blob_reset(&src); blob_reset(&content); return; } pXfer->nDeltaRcvd++; blob_delta_apply(&src, &content, &next); blob_reset(&src); blob_reset(&content); content = next; }else{ pXfer->nFileRcvd++; } if( hname_verify_hash(&content, blob_buffer(pUuid), blob_size(pUuid))==0 ){ blob_appendf(&pXfer->err, "wrong hash on received artifact: %b", pUuid); } rid = content_put_ex(&content, blob_str(pUuid), 0, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); if( rid==0 ){ blob_appendf(&pXfer->err, "%s", g.zErrMsg); blob_reset(&content); }else{ if( !isPriv ) content_make_public(rid); manifest_crosslink(rid, &content, MC_NO_ERRORS); } assert( blob_is_reset(&content) ); remote_has(rid); } /* ** The aToken[0..nToken-1] blob array is a parse of a "cfile" line ** message. This routine finishes parsing that message and does ** a record insert of the file. The difference between "file" and ** "cfile" is that with "cfile" the content is already compressed. ** ** The file line is in one of the following two forms: ** ** cfile HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** The content is CSIZE bytes immediately following the newline. ** If DELTASRC exists, then the CONTENT is a delta against the ** content of DELTASRC. ** ** The original size of the HASH artifact is USIZE. ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ |
︙ | ︙ | |||
246 247 248 249 250 251 252 | Blob content; int isPriv; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<4 || pXfer->nToken>5 | | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | Blob content; int isPriv; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<4 || pXfer->nToken>5 || !blob_is_hname(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC) || szC<0 || szU<0 || (pXfer->nToken==5 && !blob_is_hname(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed cfile line"); return; } if( isPriv && !g.perm.Private ){ /* Do not accept private files if not authorized */ return; |
︙ | ︙ | |||
295 296 297 298 299 300 301 | ** uvfile NAME MTIME HASH SIZE FLAGS ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the 0x0001 bit of FLAGS is set, that means the file has been ** deleted, SIZE is zero, the HASH is "-", and the "\n CONTENT" is omitted. ** ** SIZE is the number of bytes of CONTENT. The CONTENT is uncompressed. | | < | < | < | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | ** uvfile NAME MTIME HASH SIZE FLAGS ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the 0x0001 bit of FLAGS is set, that means the file has been ** deleted, SIZE is zero, the HASH is "-", and the "\n CONTENT" is omitted. ** ** SIZE is the number of bytes of CONTENT. The CONTENT is uncompressed. ** HASH is the artifact hash of CONTENT. ** ** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted. ** The sender might have omitted the content because it is too big to ** transmit, or because it is unchanged and this record exists purely ** to update the MTIME. */ static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){ sqlite3_int64 mtime; /* The MTIME */ Blob *pHash; /* The HASH value */ int sz; /* The SIZE */ int flags; /* The FLAGS */ Blob content; /* The CONTENT */ Blob x; /* Compressed content */ Stmt q; /* SQL statements for comparison and insert */ int isDelete; /* HASH is "-" indicating this is a delete */ int nullContent; /* True of CONTENT is NULL */ int iStatus; /* Result from unversioned_status() */ pHash = &pXfer->aToken[3]; if( pXfer->nToken==5 || !blob_is_filename(&pXfer->aToken[1]) || !blob_is_int64(&pXfer->aToken[2], &mtime) || (!blob_eq(pHash,"-") && !blob_is_hname(pHash)) || !blob_is_int(&pXfer->aToken[4], &sz) || !blob_is_int(&pXfer->aToken[5], &flags) ){ blob_appendf(&pXfer->err, "malformed uvfile line"); return; } blob_init(&content, 0, 0); blob_init(&x, 0, 0); if( sz>0 && (flags & 0x0005)==0 ){ blob_extract(pXfer->pIn, sz, &content); nullContent = 0; if( hname_verify_hash(&content, blob_buffer(pHash), blob_size(pHash))==0 ){ blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT"); goto end_accept_unversioned_file; } }else{ nullContent = 1; } |
︙ | ︙ | |||
397 398 399 400 401 402 403 | db_step(&q); db_finalize(&q); db_unset("uv-hash", 0); end_accept_unversioned_file: blob_reset(&x); blob_reset(&content); | < | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | db_step(&q); db_finalize(&q); db_unset("uv-hash", 0); end_accept_unversioned_file: blob_reset(&x); blob_reset(&content); } /* ** Try to send a file as a delta against its parent. ** If successful, return the number of bytes in the delta. ** If we cannot generate an appropriate delta, then send ** nothing and return zero. ** ** Never send a delta against a private artifact. */ static int send_delta_parent( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ int isPrivate, /* True if rid is a private artifact */ Blob *pContent, /* The content of the file to send */ Blob *pUuid /* The HASH of the file to send */ ){ static const char *const azQuery[] = { "SELECT pid FROM plink x" " WHERE cid=%d" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid" |
︙ | ︙ | |||
467 468 469 470 471 472 473 | ** ** Never send a delta against a private artifact. */ static int send_delta_native( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ int isPrivate, /* True if rid is a private artifact */ | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | ** ** Never send a delta against a private artifact. */ static int send_delta_native( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ int isPrivate, /* True if rid is a private artifact */ Blob *pUuid /* The HASH of the file to send */ ){ Blob src, delta; int size = 0; int srcId; srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid); if( srcId>0 |
︙ | ︙ | |||
502 503 504 505 506 507 508 | } return size; } /* ** Send the file identified by rid. ** | | | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | } return size; } /* ** Send the file identified by rid. ** ** The pUuid can be NULL in which case the correct hash is computed ** from the rid. ** ** Try to send the file as a native delta if nativeDelta is true, or ** as a parent delta if nativeDelta is false. ** ** It should never be the case that rid is a private artifact. But ** as a precaution, this routine does check on rid and if it is private |
︙ | ︙ | |||
726 727 728 729 730 731 732 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); pXfer->nGimmeSent++; } db_finalize(&q); } /* | | > > > | < < | | 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); pXfer->nGimmeSent++; } db_finalize(&q); } /* ** Compute an hash on the tail of pMsg. Verify that it matches the ** the hash given in pHash. Return non-zero for an error and 0 on success. ** ** The type of hash computed (SHA1, SHA3-224, SHA3-256) is determined by ** the length of the input hash in pHash. */ static int check_tail_hash(Blob *pHash, Blob *pMsg){ Blob tail; Blob h2; int rc; blob_tail(pMsg, &tail); rc = hname_verify_hash(&tail, blob_buffer(pHash), blob_size(pHash)); blob_reset(&tail); return rc==HNAME_ERROR; } /* ** Check the signature on an application/x-fossil payload received by ** the HTTP server. The signature is a line of the following form: ** ** login LOGIN NONCE SIGNATURE |
︙ | ︙ | |||
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 | int rc; const char *zScript = 0; char *zUuidList = 0; int nUuidList = 0; char **pzUuidList = 0; int *pnUuidList = 0; int uvCatalogSent = 0; if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } g.zLogin = "anonymous"; login_set_anon_nobody_capabilities(); login_check_credentials(); | > | 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | int rc; const char *zScript = 0; char *zUuidList = 0; int nUuidList = 0; char **pzUuidList = 0; int *pnUuidList = 0; int uvCatalogSent = 0; int clientVersion = 0; /* Version number of the client */ if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } g.zLogin = "anonymous"; login_set_anon_nobody_capabilities(); login_check_credentials(); |
︙ | ︙ | |||
1196 1197 1198 1199 1200 1201 1202 | pnUuidList = &nUuidList; } while( blob_line(xfer.pIn, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ) continue; if( blob_size(&xfer.line)==0 ) continue; xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); | | | | | | 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | pnUuidList = &nUuidList; } while( blob_line(xfer.pIn, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ) continue; if( blob_size(&xfer.line)==0 ) continue; xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); /* file HASH SIZE \n CONTENT ** file HASH DELTASRC SIZE \n CONTENT ** ** Accept a file from the client. */ if( blob_eq(&xfer.aToken[0], "file") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; break; } xfer_accept_file(&xfer, 0, pzUuidList, pnUuidList); if( blob_size(&xfer.err) ){ cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else /* cfile HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** Accept a file from the client. */ if( blob_eq(&xfer.aToken[0], "cfile") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite |
︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 | cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else | | | | 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 | cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else /* gimme HASH ** ** Client is requesting a file. Send it. */ if( blob_eq(&xfer.aToken[0], "gimme") && xfer.nToken==2 && blob_is_hname(&xfer.aToken[1]) ){ nGimme++; if( isPull ){ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid ){ send_file(&xfer, rid, &xfer.aToken[1], deltaFlag); } |
︙ | ︙ | |||
1280 1281 1282 1283 1284 1285 1286 | if( blob_eq(&xfer.aToken[0], "uvgimme") && xfer.nToken==2 && blob_is_filename(&xfer.aToken[1]) ){ send_unversioned_file(&xfer, blob_str(&xfer.aToken[1]), 0); }else | | | | < | | 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 | if( blob_eq(&xfer.aToken[0], "uvgimme") && xfer.nToken==2 && blob_is_filename(&xfer.aToken[1]) ){ send_unversioned_file(&xfer, blob_str(&xfer.aToken[1]), 0); }else /* igot HASH ?ISPRIVATE? ** ** Client announces that it has a particular file. If the ISPRIVATE ** argument exists and is non-zero, then the file is a private file. */ if( xfer.nToken>=2 && blob_eq(&xfer.aToken[0], "igot") && blob_is_hname(&xfer.aToken[1]) ){ if( isPush ){ if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){ rid_from_uuid(&xfer.aToken[1], 1, 0); }else if( g.perm.Private ){ rid_from_uuid(&xfer.aToken[1], 1, 1); }else{ server_private_xfer_not_authorized(); } } }else /* pull SERVERCODE PROJECTCODE ** push SERVERCODE PROJECTCODE ** ** The client wants either send or receive. The server should ** verify that the project code matches. The server code is ignored. */ if( xfer.nToken==3 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) && blob_is_hname(&xfer.aToken[2]) ){ const char *zPCode; zPCode = db_get("project-code", 0); if( zPCode==0 ){ fossil_panic("missing project code"); } if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){ |
︙ | ︙ | |||
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 | /* pragma send-catalog ** ** Send igot cards for all known artifacts. */ if( blob_eq(&xfer.aToken[1], "send-catalog") ){ xfer.resync = 0x7fffffff; } /* pragma uv-hash HASH ** ** The client wants to make sure that unversioned files are all synced. ** If the HASH does not match, send a complete catalog of ** "uvigot" cards. */ if( blob_eq(&xfer.aToken[1], "uv-hash") | > > > > > > > > | | 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 | /* pragma send-catalog ** ** Send igot cards for all known artifacts. */ if( blob_eq(&xfer.aToken[1], "send-catalog") ){ xfer.resync = 0x7fffffff; } /* pragma client-version VERSION ** ** Let the server know what version of Fossil is running on the client. */ if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){ clientVersion = atoi(blob_str(&xfer.aToken[2])); } /* pragma uv-hash HASH ** ** The client wants to make sure that unversioned files are all synced. ** If the HASH does not match, send a complete catalog of ** "uvigot" cards. */ if( blob_eq(&xfer.aToken[1], "uv-hash") && blob_is_hname(&xfer.aToken[2]) ){ if( !uvCatalogSent ){ if( g.perm.Read && g.perm.WrUnver ){ @ pragma uv-push-ok send_unversioned_catalog(&xfer); }else if( g.perm.Read ){ @ pragma uv-pull-only |
︙ | ︙ | |||
1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 | " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" ); } /* ** Always begin with a clone, pull, or push message */ if( syncFlags & SYNC_CLONE ){ blob_appendf(&send, "clone 3 %d\n", cloneSeqno); syncFlags &= ~(SYNC_PUSH|SYNC_PULL); nCardSent++; /* TBD: Request all transferable configuration values */ content_enable_dephantomize(0); zOpType = "Clone"; | > | 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 | " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" ); } /* ** Always begin with a clone, pull, or push message */ blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER); if( syncFlags & SYNC_CLONE ){ blob_appendf(&send, "clone 3 %d\n", cloneSeqno); syncFlags &= ~(SYNC_PUSH|SYNC_PULL); nCardSent++; /* TBD: Request all transferable configuration values */ content_enable_dephantomize(0); zOpType = "Clone"; |
︙ | ︙ | |||
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 | char *zRandomness; db_begin_transaction(); db_record_repository_filename(0); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); /* Send back the most recently received cookie. Let the server ** figure out if this is a cookie that it cares about. */ zCookie = db_get("cookie", 0); if( zCookie ){ blob_appendf(&send, "cookie %s\n", zCookie); | > | 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 | char *zRandomness; db_begin_transaction(); db_record_repository_filename(0); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); /* Send back the most recently received cookie. Let the server ** figure out if this is a cookie that it cares about. */ zCookie = db_get("cookie", 0); if( zCookie ){ blob_appendf(&send, "cookie %s\n", zCookie); |
︙ | ︙ | |||
2026 2027 2028 2029 2030 2031 2032 | if( pctDone!=lastPctDone ){ fossil_print("\rprocessed: %d%% ", pctDone); lastPctDone = pctDone; fflush(stdout); } } | | | | | | 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 | if( pctDone!=lastPctDone ){ fossil_print("\rprocessed: %d%% ", pctDone); lastPctDone = pctDone; fflush(stdout); } } /* file HASH SIZE \n CONTENT ** file HASH DELTASRC SIZE \n CONTENT ** ** Receive a file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"file") ){ xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0); nArtifactRcvd++; }else /* cfile HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** Receive a compressed file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"cfile") ){ xfer_accept_compressed_file(&xfer, 0, 0); nArtifactRcvd++; }else |
︙ | ︙ | |||
2060 2061 2062 2063 2064 2065 2066 | nUvFileRcvd++; if( syncFlags & SYNC_VERBOSE ){ fossil_print("\rUnversioned-file received: %s\n", blob_str(&xfer.aToken[1])); } }else | | | | | | 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 | nUvFileRcvd++; if( syncFlags & SYNC_VERBOSE ){ fossil_print("\rUnversioned-file received: %s\n", blob_str(&xfer.aToken[1])); } }else /* gimme HASH ** ** Server is requesting a file. If the file is a manifest, assume ** that the server will also want to know all of the content files ** associated with the manifest and send those too. */ if( blob_eq(&xfer.aToken[0], "gimme") && xfer.nToken==2 && blob_is_hname(&xfer.aToken[1]) ){ if( syncFlags & SYNC_PUSH ){ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0); } }else /* igot HASH ?PRIVATEFLAG? ** ** Server announces that it has a particular file. If this is ** not a file that we have and we are pulling, then create a ** phantom to cause this file to be requested on the next cycle. ** Always remember that the server has this file so that we do ** not transmit it by accident. ** ** If the PRIVATE argument exists and is 1, then the file is ** private. Pretend it does not exists if we are not pulling ** private files. */ if( xfer.nToken>=2 && blob_eq(&xfer.aToken[0], "igot") && blob_is_hname(&xfer.aToken[1]) ){ int rid; int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1"); rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid>0 ){ if( !isPriv ) content_make_public(rid); }else if( isPriv && !g.perm.Private ){ |
︙ | ︙ | |||
2124 2125 2126 2127 2128 2129 2130 | ** then do the deletion. */ if( xfer.nToken==5 && blob_eq(&xfer.aToken[0], "uvigot") && blob_is_filename(&xfer.aToken[1]) && blob_is_int64(&xfer.aToken[2], &mtime) && blob_is_int(&xfer.aToken[4], &size) | | | 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 | ** then do the deletion. */ if( xfer.nToken==5 && blob_eq(&xfer.aToken[0], "uvigot") && blob_is_filename(&xfer.aToken[1]) && blob_is_int64(&xfer.aToken[2], &mtime) && blob_is_int(&xfer.aToken[4], &size) && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3])) ){ const char *zName = blob_str(&xfer.aToken[1]); const char *zHash = blob_str(&xfer.aToken[3]); int iStatus; iStatus = unversioned_status(zName, mtime, zHash); if( (syncFlags & SYNC_UV_REVERT)!=0 ){ if( iStatus==4 ) iStatus = 2; |
︙ | ︙ | |||
2186 2187 2188 2189 2190 2191 2192 | ** ** Should only happen in response to a clone. This message tells ** the client what product to use for the new database. */ if( blob_eq(&xfer.aToken[0],"push") && xfer.nToken==3 && (syncFlags & SYNC_CLONE)!=0 | | | 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 | ** ** Should only happen in response to a clone. This message tells ** the client what product to use for the new database. */ if( blob_eq(&xfer.aToken[0],"push") && xfer.nToken==3 && (syncFlags & SYNC_CLONE)!=0 && blob_is_hname(&xfer.aToken[2]) ){ if( zPCode==0 ){ zPCode = mprintf("%b", &xfer.aToken[2]); db_set("project-code", zPCode, 0); } if( cloneSeqno>0 ) blob_appendf(&send, "clone 3 %d\n", cloneSeqno); nCardSent++; |
︙ | ︙ |
Changes to src/zip.c.
︙ | ︙ | |||
337 338 339 340 341 342 343 | int nPrefix; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pZip); return; } | | | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | int nPrefix; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pZip); return; } blob_set_dynamic(&hash, rid_to_uuid(rid)); blob_zero(&filename); zip_open(); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); |
︙ | ︙ | |||
376 377 378 379 380 381 382 | if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){ if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); zip_add_folders(zName); } | < < < < | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){ if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); zip_add_folders(zName); } if( eflg & MFESTFLG_RAW ){ sterilize_manifest(&mfile); zip_add_file(zName, &mfile, 0); } } blob_reset(&mfile); if( eflg & MFESTFLG_UUID ){ blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); zip_add_folders(zName); zip_add_file(zName, &hash, 0); } if( eflg & MFESTFLG_TAGS ){ Blob tagslist; blob_zero(&tagslist); get_checkin_taglist(rid, &tagslist); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.tags", -1); |
︙ | ︙ | |||
427 428 429 430 431 432 433 434 435 436 437 438 439 440 | } } }else{ blob_reset(&mfile); } manifest_destroy(pManifest); blob_reset(&filename); zip_close(pZip); } /* ** COMMAND: zip* ** ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS] | > | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | } } }else{ blob_reset(&mfile); } manifest_destroy(pManifest); blob_reset(&filename); blob_reset(&hash); zip_close(pZip); } /* ** COMMAND: zip* ** ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS] |
︙ | ︙ |
Changes to win/Makefile.dmc.
︙ | ︙ | |||
26 27 28 29 30 31 32 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c sha3_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd dispatch doc encode event export file finfo foci fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 sha3 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c |
︙ | ︙ | |||
354 355 356 357 358 359 360 361 362 363 364 365 366 367 | +translate$E $** > $@ $(OBJDIR)\gzip$O : gzip_.c gzip.h $(TCC) -o$@ -c gzip_.c gzip_.c : $(SRCDIR)\gzip.c +translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) -o$@ -c http_.c http_.c : $(SRCDIR)\http.c +translate$E $** > $@ | > > > > > > | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | +translate$E $** > $@ $(OBJDIR)\gzip$O : gzip_.c gzip.h $(TCC) -o$@ -c gzip_.c gzip_.c : $(SRCDIR)\gzip.c +translate$E $** > $@ $(OBJDIR)\hname$O : hname_.c hname.h $(TCC) -o$@ -c hname_.c hname_.c : $(SRCDIR)\hname.c +translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) -o$@ -c http_.c http_.c : $(SRCDIR)\http.c +translate$E $** > $@ |
︙ | ︙ | |||
860 861 862 863 864 865 866 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h | | | 866 867 868 869 870 871 872 873 874 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
︙ | ︙ | |||
459 460 461 462 463 464 465 466 467 468 469 470 471 472 | $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/json.c \ | > | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/json.c \ |
︙ | ︙ | |||
635 636 637 638 639 640 641 642 643 644 645 646 647 648 | $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/json_.c \ | > | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/json_.c \ |
︙ | ︙ | |||
760 761 762 763 764 765 766 767 768 769 770 771 772 773 | $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/json.o \ | > | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/json.o \ |
︙ | ︙ | |||
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 | $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ $(OBJDIR)/import_.c:$(OBJDIR)/import.h \ $(OBJDIR)/info_.c:$(OBJDIR)/info.h \ $(OBJDIR)/json_.c:$(OBJDIR)/json.h \ | > | 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ $(OBJDIR)/import_.c:$(OBJDIR)/import.h \ $(OBJDIR)/info_.c:$(OBJDIR)/info.h \ $(OBJDIR)/json_.c:$(OBJDIR)/json.h \ |
︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c | > > > > > > > > | 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 | $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/hname_.c: $(SRCDIR)/hname.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/hname.c >$@ $(OBJDIR)/hname.o: $(OBJDIR)/hname_.c $(OBJDIR)/hname.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hname.o -c $(OBJDIR)/hname_.c $(OBJDIR)/hname.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c |
︙ | ︙ |
Changes to win/Makefile.msc.
︙ | ︙ | |||
384 385 386 387 388 389 390 391 392 393 394 395 396 397 | finfo_.c \ foci_.c \ fshell_.c \ fusefs_.c \ glob_.c \ graph_.c \ gzip_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ import_.c \ info_.c \ json_.c \ | > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | finfo_.c \ foci_.c \ fshell_.c \ fusefs_.c \ glob_.c \ graph_.c \ gzip_.c \ hname_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ import_.c \ info_.c \ json_.c \ |
︙ | ︙ | |||
559 560 561 562 563 564 565 566 567 568 569 570 571 572 | $(OX)\finfo$O \ $(OX)\foci$O \ $(OX)\fshell$O \ $(OX)\fusefs$O \ $(OX)\glob$O \ $(OX)\graph$O \ $(OX)\gzip$O \ $(OX)\http$O \ $(OX)\http_socket$O \ $(OX)\http_ssl$O \ $(OX)\http_transport$O \ $(OX)\import$O \ $(OX)\info$O \ $(OX)\json$O \ | > | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | $(OX)\finfo$O \ $(OX)\foci$O \ $(OX)\fshell$O \ $(OX)\fusefs$O \ $(OX)\glob$O \ $(OX)\graph$O \ $(OX)\gzip$O \ $(OX)\hname$O \ $(OX)\http$O \ $(OX)\http_socket$O \ $(OX)\http_ssl$O \ $(OX)\http_transport$O \ $(OX)\import$O \ $(OX)\info$O \ $(OX)\json$O \ |
︙ | ︙ | |||
743 744 745 746 747 748 749 750 751 752 753 754 755 756 | echo $(OX)\finfo.obj >> $@ echo $(OX)\foci.obj >> $@ echo $(OX)\fshell.obj >> $@ echo $(OX)\fusefs.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\http.obj >> $@ echo $(OX)\http_socket.obj >> $@ echo $(OX)\http_ssl.obj >> $@ echo $(OX)\http_transport.obj >> $@ echo $(OX)\import.obj >> $@ echo $(OX)\info.obj >> $@ echo $(OX)\json.obj >> $@ | > | 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | echo $(OX)\finfo.obj >> $@ echo $(OX)\foci.obj >> $@ echo $(OX)\fshell.obj >> $@ echo $(OX)\fusefs.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\hname.obj >> $@ echo $(OX)\http.obj >> $@ echo $(OX)\http_socket.obj >> $@ echo $(OX)\http_ssl.obj >> $@ echo $(OX)\http_transport.obj >> $@ echo $(OX)\import.obj >> $@ echo $(OX)\info.obj >> $@ echo $(OX)\json.obj >> $@ |
︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 | translate$E $** > $@ $(OX)\gzip$O : gzip_.c gzip.h $(TCC) /Fo$@ -c gzip_.c gzip_.c : $(SRCDIR)\gzip.c translate$E $** > $@ $(OX)\http$O : http_.c http.h $(TCC) /Fo$@ -c http_.c http_.c : $(SRCDIR)\http.c translate$E $** > $@ | > > > > > > | 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 | translate$E $** > $@ $(OX)\gzip$O : gzip_.c gzip.h $(TCC) /Fo$@ -c gzip_.c gzip_.c : $(SRCDIR)\gzip.c translate$E $** > $@ $(OX)\hname$O : hname_.c hname.h $(TCC) /Fo$@ -c hname_.c hname_.c : $(SRCDIR)\hname.c translate$E $** > $@ $(OX)\http$O : http_.c http.h $(TCC) /Fo$@ -c http_.c http_.c : $(SRCDIR)\http.c translate$E $** > $@ |
︙ | ︙ | |||
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 | finfo_.c:finfo.h \ foci_.c:foci.h \ fshell_.c:fshell.h \ fusefs_.c:fusefs.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ http_.c:http.h \ http_socket_.c:http_socket.h \ http_ssl_.c:http_ssl.h \ http_transport_.c:http_transport.h \ import_.c:import.h \ info_.c:info.h \ json_.c:json.h \ | > | 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 | finfo_.c:finfo.h \ foci_.c:foci.h \ fshell_.c:fshell.h \ fusefs_.c:fusefs.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ hname_.c:hname.h \ http_.c:http.h \ http_socket_.c:http_socket.h \ http_ssl_.c:http_ssl.h \ http_transport_.c:http_transport.h \ import_.c:import.h \ info_.c:info.h \ json_.c:json.h \ |
︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | | | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <title>Change Log</title> <a name='v2_0'></a> <h2>Changes for Version 2.0 (2017-03-??)</h2> * Added support for SHA3 hashes used as [./fileformat.wiki#names|artifact names]. * Added support for the [/help?cmd=sha3sum|sha3sum] command. * Update the built-in SQLite to version 3.17.0. <a name='v1_37'></a> <h2>Changes for Version 1.37 (2017-01-16)</h2> * Add checkbox widgets to various web pages. See [/technote/8d18bf27e9| this technote] for more information. To get the checkboxes to look as |
︙ | ︙ |
Changes to www/fileformat.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Fossil File Formats</title> <h1 align="center"> Fossil File Formats </h1> The global state of a fossil repository is kept simple so that it can endure in useful form for decades or centuries. A fossil repository is intended to be readable, searchable, and extensible by people not yet born. The global state of a fossil repository is an unordered set of <i>artifacts</i>. An artifact might be a source code file, the text of a wiki page, | | > > > > | | > | > > > | | | > | > > > > > > > > | > > > > > > > > | > > > > > > | > | > > > > > > > > > > > > | < < < | | | | < < < < < < < < < < < < < < < < < < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | <title>Fossil File Formats</title> <h1 align="center"> Fossil File Formats </h1> The global state of a fossil repository is kept simple so that it can endure in useful form for decades or centuries. A fossil repository is intended to be readable, searchable, and extensible by people not yet born. The global state of a fossil repository is an unordered set of <i>artifacts</i>. An artifact might be a source code file, the text of a wiki page, part of a trouble ticket, a description of a check-in including all the files in that check-in with the check-in comment and so forth. Artifacts are broadly grouped into two types: content artifacts and structural artifacts. Content artifacts are the raw project source-code files that are checked into the repository. Structural artifacts have special formatting rules and are used to show the relationships between other artifacts in the repository. It is possible for an artifact to be both a structure artifact and a content artifact, though this is rare. Artifacts can be text or binary. In addition to the global state, each fossil repository also contains local state. The local state consists of web-page formatting preferences, authorized users, ticket display and reporting formats, and so forth. The global state is shared in common among all repositories for the same project, whereas the local state is often different in separate repositories. The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. <a name="names"></a> <h2>1.0 Artifact Names</h2> Each artifact in the repository is named by a hash of its content. No prefixes, suffixes, or other information is added to an artifact before the hash is computed. The artifact name is just the (lower-case hexadecimal) hash of the raw artifact. Fossil supports multiple hash algorithms including SHA1 and various lengths of SHA3. Because an artifact can be hashed using multiple algorithms, a single artifact can have multiple names. Usually, Fossil knows each artifact by just a single name called the "display name". But it is possible for Fossil to know an artifact by multiple names from different hashes. In that case, Fossil uses the display name for output, but continues to accept the alternative names as command-line arguments or as parameters to webpage URLs. When referring to artifacts in using tty commands or webpage URLs, it is sufficient to specify a unique prefix for the artifact name. If the input prefix is not unique, Fossil will show an error. Within a structural artifact, however, all references to other artifacts must be the complete hash. Prior to Fossil version 2.0, all names were formed from the SHA1 hash of the artifact. The key innovation in Fossil 2.0 was adding support for alternative hash algorithms. <a name="structural"></a> <h2>2.0 Structural Artifacts</h2> A structural artifact is an artifact that has a particular format and that is used to define the relationships between other artifacts in the repository. Fossil recognizes the following kinds of structural artifacts: <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | TechNotes] </li> </ul> These seven structural artifact types are described in subsections below. Structural artifacts are ASCII text. The artifact may be PGP clearsigned. After removal of the PGP clearsign header and suffix (if any) a structural artifact consists of one or more "cards" separated by a single newline (ASCII: 0x0a) character. Each card begins with a single character "card type". Zero or more arguments may follow the card type. All arguments are separated from each other and from the card-type character by a single space character. There is no surplus white space between arguments and no leading or trailing whitespace except for the newline character that acts as the card separator. All cards must be in strict lexicographical order. There may not be any duplicate cards. In the current implementation (as of 2017-02-27) the artifacts that make up a fossil repository are stored as delta- and zlib-compressed blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This is an implementation detail and might change in a future release. For the purpose of this article "file format" means the format of the artifacts, not how the artifacts are stored on disk. It is the artifact format that is intended to be enduring. The specifics of how artifacts are stored on disk, though stable, is not intended to live as long as the artifact format. <a name="manifest"></a> <h3>2.1 The Manifest</h3> A manifest defines a check-in. A manifest contains a list of artifacts for each file in the project and the corresponding filenames, as well as information such as parent check-ins, the username of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated with the check-in. Allowed cards in the manifest are as follows: <blockquote> <b>B</b> <i>baseline-manifest</i><br> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> ?<i>hash</i>? ?<i>permissions</i>? ?<i>old-name</i>?<br> <b>N</b> <i>mimetype</i><br> <b>P</b> <i>artifact-hash</i>+<br> <b>Q</b> (<b>+</b>|<b>-</b>)<i>artifact-hash</i> ?<i>artifact-hash</i>?<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <b>*</b> ?<i>value</i>?<br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </blockquote> A manifest may optionally have a single B-card. The B-card specifies |
︙ | ︙ | |||
143 144 145 146 147 148 149 | that is part of the check-in. There are one, two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in C-card comment text. Backslash characters and newlines are not allowed within filenames. The directory separator character is a forward slash (ASCII 0x2F). The second argument to the | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | that is part of the check-in. There are one, two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in C-card comment text. Backslash characters and newlines are not allowed within filenames. The directory separator character is a forward slash (ASCII 0x2F). The second argument to the F-card is the lower-case hexadecimal artifact hash of the content artifact. The second argument is required for baseline manifests but is optional for delta manifests. When the second argument to the F-card is omitted, it means that the file has been deleted relative to the baseline (files removed in baseline manifests versions are <em>not</em> added as F-cards). The optional 3rd argument defines any special access permissions associated with the file. This can be defined as "x" to mean that the file is executable or "l" |
︙ | ︙ | |||
165 166 167 168 169 170 171 | A manifest has zero or one N-cards. The N-card specifies the mimetype for the text in the comment of the C-card. If the N-card is omitted, a default mimetype is used. A manifest has zero or one P-cards. Most manifests have one P-card. The P-card has a varying number of arguments that define other manifests from which the current manifest | | | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | A manifest has zero or one N-cards. The N-card specifies the mimetype for the text in the comment of the C-card. If the N-card is omitted, a default mimetype is used. A manifest has zero or one P-cards. Most manifests have one P-card. The P-card has a varying number of arguments that define other manifests from which the current manifest is derived. Each argument is a lowercase hexadecimal artifact hash of a predecessor manifest. All arguments to the P-card must be unique within that card. The first argument is the artifact hash of the direct ancestor of the manifest. Other arguments define manifests with which the first was merged to yield the current manifest. Most manifests have a P-card with a single argument. The first manifest in the project has no ancestors and thus has no P-card or (depending on the Fossil version) an empty P-card (no arguments). A manifest has zero or more Q-cards. A Q-card is similar to a P-card |
︙ | ︙ | |||
227 228 229 230 231 232 233 | the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the C-card. A manifest must have a single Z-card as its last line. The argument to the Z-card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline | | > | < < < < < < < < < < < < < < < | | < < < < < < | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the C-card. A manifest must have a single Z-card as its last line. The argument to the Z-card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline character that immediately precedes the "Z", excluding any PGP clear-signing prefix. The Z-card is a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <a name="cluster"></a> <h3>2.2 Clusters</h3> A cluster is an artifact that declares the existence of other artifacts. Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. Allowed cards in the cluster are as follows: <blockquote> <b>M</b> <i>artifact-id</i><br /> <b>Z</b> <i>checksum</i> </blockquote> A cluster contains one or more "M" cards followed by a single "Z" card. Each M card has a single argument which is the artifact ID of another artifact in the repository. The Z card works exactly like the Z card of a manifest. The argument to the Z card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. The Z-card is required. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. <a name="ctrl"></a> <h3>2.3 Control Artifacts</h3> Control artifacts are used to assign properties to other artifacts within the repository. Allowed cards in a control artifact are as follows: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i><br /> |
︙ | ︙ | |||
336 337 338 339 340 341 342 | The U card is the name of the user that created the control artifact. The Z card is the usual required artifact checksum. An example control artifacts can be seen [/info/9d302ccda8 | here]. <a name="wikichng"></a> | | > | < < | | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | The U card is the name of the user that created the control artifact. The Z card is the usual required artifact checksum. An example control artifacts can be seen [/info/9d302ccda8 | here]. <a name="wikichng"></a> <h3>2.4 Wiki Pages</h3> A wiki artifact defines a single version of a single wiki page. Wiki artifacts accept the following card types: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>L</b> <i>wiki-title</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> |
︙ | ︙ | |||
373 374 375 376 377 378 379 | that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <a name="tktchng"></a> | | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | that terminates the W card. The wiki text is always followed by one extra newline. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <a name="tktchng"></a> <h3>2.5 Ticket Changes</h3> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br /> |
︙ | ︙ | |||
419 420 421 422 423 424 425 | The field name and value are both encoded using the character escapes defined for the C card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> | | | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | The field name and value are both encoded using the character escapes defined for the C card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h3>2.6 Attachments</h3> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or technical note to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: |
︙ | ︙ | |||
461 462 463 464 465 466 467 | If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. The Z card is required. <a name="event"></a> | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. The Z card is required. <a name="event"></a> <h3>2.7 Technical Notes</h3> A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an technote artifact: |
︙ | ︙ | |||
530 531 532 533 534 535 536 | technote. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="summary"></a> | | | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | technote. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="summary"></a> <h2>3.0 Card Summary</h2> The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. |
︙ | ︙ | |||
737 738 739 740 741 742 743 | <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> </table> <a name="addenda"></a> | | | | 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 | <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> </table> <a name="addenda"></a> <h2>4.0 Addenda</h2> This section contains additional information which may be useful when implementing algorithms described above. <h3>4.1 R-Card Hash Calculation</h3> Given a manifest file named <tt>MF</tt>, the following Bash shell code demonstrates how to compute the value of the R card in that manifest. This example uses manifest [28987096ac]. Lines starting with <tt>#</tt> are shell input and other lines are output. This demonstration assumes that the file versions represented by the input manifest are checked out under the current directory. |
︙ | ︙ |