Fossil SCM
Allow URLs of the form /tarball/TAG/VERSION/NAME.tar.gz for the tarball page where TAG must be a non-branch tag associated with VERSION. This is so that the robot-exception pattern can match things like /tarball/release while will allowing access to historical releases via the VERSION section of the URL.
Commit
57c8bec8169e0725b02bdd67555b25d3e68982f6db8395970182dce7e8424814
Parent
871f6b36e9aa12d…
1 file changed
+92
-25
+92
-25
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -675,45 +675,112 @@ | ||
| 675 | 675 | blob_reset(&tarball); |
| 676 | 676 | } |
| 677 | 677 | } |
| 678 | 678 | |
| 679 | 679 | /* |
| 680 | -** Check to see if the input string is of the form: | |
| 681 | -** | |
| 682 | -** check-in-name/filename.ext | |
| 683 | -** | |
| 684 | -** In other words, check to see if the input contains a single '/' | |
| 685 | -** character that separates a valid check-in name from a filename. | |
| 686 | -** | |
| 687 | -** If the condition is true, return the check-in name and set the | |
| 688 | -** input string to be the filename. | |
| 689 | -** | |
| 690 | -** If the condition is false, return NULL | |
| 680 | +** Check to see if the input string is of one of the following | |
| 681 | +** two the forms: | |
| 682 | +** | |
| 683 | +** check-in-name/filename.ext (1) | |
| 684 | +** tag-name/check-in-name/filename.txt (2) | |
| 685 | +** | |
| 686 | +** In other words, check to see if the input string contains either | |
| 687 | +** a check-in name or a tag-name and a check-in name separated by | |
| 688 | +** a slash. There must be either 1 or 2 "/" characters. In the | |
| 689 | +** second form, tag-name must be an individual tag (not a branch-tag) | |
| 690 | +** that is found on the check-in identified by the check-in-name. | |
| 691 | +** | |
| 692 | +** If the condition is true, then: | |
| 693 | +** | |
| 694 | +** * Make *pzName point to the fielname suffix only | |
| 695 | +** * return a copy of the check-in name in memory from mprintf(). | |
| 696 | +** | |
| 697 | +** If the condition is false, leave *pzName unchanged and return either | |
| 698 | +** NULL or an empty string. Normally NULL is returned, however an | |
| 699 | +** empty string is returned for format (2) if check-in-name does not | |
| 700 | +** match tag-name. | |
| 701 | +** | |
| 702 | +** Format (2) is specifically designed to allow URLs like this: | |
| 703 | +** | |
| 704 | +** /tarball/release/UUID/PROJECT.tar.gz | |
| 705 | +** | |
| 706 | +** Such URLs will pass through most anti-robot filters because of the | |
| 707 | +** "/tarball/release" prefix will match the suggested "robot-exception" | |
| 708 | +** pattern and can still refer to an historic release rather than just | |
| 709 | +** the most recent release. | |
| 691 | 710 | */ |
| 692 | 711 | char *tar_uuid_from_name(char **pzName){ |
| 693 | - char *zName = *pzName; | |
| 694 | - int i, n; | |
| 695 | - for(i=n=0; zName[i]; i++){ | |
| 712 | + char *zName = *pzName; /* Original input */ | |
| 713 | + int n1 = 0; /* Bytes in first prefix (tag-name) */ | |
| 714 | + int n2 = 0; /* Bytes in second prefix (check-in-name) */ | |
| 715 | + int n = 0; /* max(n1,n2) */ | |
| 716 | + int i; /* Loop counter */ | |
| 717 | + for(i=n1=n2=0; zName[i]; i++){ | |
| 696 | 718 | if( zName[i]=='/' ){ |
| 697 | - if( n==0 ) n = i; | |
| 698 | - else return 0; | |
| 699 | - } | |
| 700 | - } | |
| 701 | - if( n==0 ) return 0; | |
| 702 | - if( zName[n+1]==0 ) return 0; | |
| 703 | - zName[n] = 0; | |
| 704 | - *pzName = fossil_strdup(&zName[n+1]); | |
| 705 | - return zName; | |
| 719 | + if( n1==0 ){ | |
| 720 | + n = n1 = i; | |
| 721 | + }else if( n2==0 ){ | |
| 722 | + n = n2 = i; | |
| 723 | + }else{ | |
| 724 | + return 0; /* More than two "/" characters seen */ | |
| 725 | + } | |
| 726 | + } | |
| 727 | + } | |
| 728 | + if( n1==0 ){ | |
| 729 | + return 0; /* No prefix of any kind */ | |
| 730 | + } | |
| 731 | + if( zName[n+1]==0 ){ | |
| 732 | + return 0; /* No filename suffix */ | |
| 733 | + } | |
| 734 | + if( n2==0 ){ | |
| 735 | + /* Format (1): check-in name only. The check-in-name is not verified */ | |
| 736 | + zName[n1] = 0; | |
| 737 | + *pzName = fossil_strdup(&zName[n1+1]); | |
| 738 | + return zName; | |
| 739 | + }else if( n2>n1+1 ){ | |
| 740 | + /* Format (2): tag-name/check-in-name. Verify that check-in-name is real | |
| 741 | + ** and that the check-in has the tag named by tag-name. | |
| 742 | + */ | |
| 743 | + char *zCkin = mprintf("%.*s", n2-n1-1, &zName[n1+1]); | |
| 744 | + char *zTag; | |
| 745 | + int rid = symbolic_name_to_rid(zCkin,"ci"); | |
| 746 | + int hasTag; | |
| 747 | + if( rid<=0 ){ | |
| 748 | + fossil_free(zCkin); | |
| 749 | + return fossil_strdup(""); | |
| 750 | + } | |
| 751 | + zTag = mprintf("%.*s", n1, zName); | |
| 752 | + hasTag = db_exists( | |
| 753 | + "SELECT 1 FROM tagxref, tag" | |
| 754 | + " WHERE tagxref.rid=%d" | |
| 755 | + " AND tag.tagid=tagxref.tagid" | |
| 756 | + " AND tagxref.tagtype=1" | |
| 757 | + " AND tag.tagname='sym-%q'", | |
| 758 | + rid, zTag | |
| 759 | + ); | |
| 760 | + fossil_free(zTag); | |
| 761 | + if( !hasTag ){ | |
| 762 | + fossil_free(zCkin); | |
| 763 | + return fossil_strdup(""); | |
| 764 | + } | |
| 765 | + *pzName = fossil_strdup(&zName[n2+1]); | |
| 766 | + return zCkin; | |
| 767 | + }else{ | |
| 768 | + return 0; | |
| 769 | + } | |
| 706 | 770 | } |
| 707 | 771 | |
| 708 | 772 | /* |
| 709 | 773 | ** WEBPAGE: tarball |
| 710 | -** URL: /tarball/[VERSION/]NAME.tar.gz | |
| 774 | +** URL: /tarball/NAME.tar.gz | |
| 775 | +** or: /tarball/VERSION/NAME.tar.gz | |
| 776 | +** or: /tarball/TAG/VERSION/NAME.tar.gz | |
| 711 | 777 | ** |
| 712 | 778 | ** Generate a compressed tarball for the check-in specified by VERSION. |
| 713 | 779 | ** The tarball is called NAME.tar.gz and has a top-level directory called |
| 714 | -** NAME. | |
| 780 | +** NAME. If TAG is provided, then VERSION must hold TAG or else an error | |
| 781 | +** is returned. | |
| 715 | 782 | ** |
| 716 | 783 | ** The optional VERSION element defaults to "trunk" per the r= rules below. |
| 717 | 784 | ** All of the following URLs are equivalent: |
| 718 | 785 | ** |
| 719 | 786 | ** /tarball/release/xyz.tar.gz |
| 720 | 787 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -675,45 +675,112 @@ | |
| 675 | blob_reset(&tarball); |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | ** Check to see if the input string is of the form: |
| 681 | ** |
| 682 | ** check-in-name/filename.ext |
| 683 | ** |
| 684 | ** In other words, check to see if the input contains a single '/' |
| 685 | ** character that separates a valid check-in name from a filename. |
| 686 | ** |
| 687 | ** If the condition is true, return the check-in name and set the |
| 688 | ** input string to be the filename. |
| 689 | ** |
| 690 | ** If the condition is false, return NULL |
| 691 | */ |
| 692 | char *tar_uuid_from_name(char **pzName){ |
| 693 | char *zName = *pzName; |
| 694 | int i, n; |
| 695 | for(i=n=0; zName[i]; i++){ |
| 696 | if( zName[i]=='/' ){ |
| 697 | if( n==0 ) n = i; |
| 698 | else return 0; |
| 699 | } |
| 700 | } |
| 701 | if( n==0 ) return 0; |
| 702 | if( zName[n+1]==0 ) return 0; |
| 703 | zName[n] = 0; |
| 704 | *pzName = fossil_strdup(&zName[n+1]); |
| 705 | return zName; |
| 706 | } |
| 707 | |
| 708 | /* |
| 709 | ** WEBPAGE: tarball |
| 710 | ** URL: /tarball/[VERSION/]NAME.tar.gz |
| 711 | ** |
| 712 | ** Generate a compressed tarball for the check-in specified by VERSION. |
| 713 | ** The tarball is called NAME.tar.gz and has a top-level directory called |
| 714 | ** NAME. |
| 715 | ** |
| 716 | ** The optional VERSION element defaults to "trunk" per the r= rules below. |
| 717 | ** All of the following URLs are equivalent: |
| 718 | ** |
| 719 | ** /tarball/release/xyz.tar.gz |
| 720 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -675,45 +675,112 @@ | |
| 675 | blob_reset(&tarball); |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | ** Check to see if the input string is of one of the following |
| 681 | ** two the forms: |
| 682 | ** |
| 683 | ** check-in-name/filename.ext (1) |
| 684 | ** tag-name/check-in-name/filename.txt (2) |
| 685 | ** |
| 686 | ** In other words, check to see if the input string contains either |
| 687 | ** a check-in name or a tag-name and a check-in name separated by |
| 688 | ** a slash. There must be either 1 or 2 "/" characters. In the |
| 689 | ** second form, tag-name must be an individual tag (not a branch-tag) |
| 690 | ** that is found on the check-in identified by the check-in-name. |
| 691 | ** |
| 692 | ** If the condition is true, then: |
| 693 | ** |
| 694 | ** * Make *pzName point to the fielname suffix only |
| 695 | ** * return a copy of the check-in name in memory from mprintf(). |
| 696 | ** |
| 697 | ** If the condition is false, leave *pzName unchanged and return either |
| 698 | ** NULL or an empty string. Normally NULL is returned, however an |
| 699 | ** empty string is returned for format (2) if check-in-name does not |
| 700 | ** match tag-name. |
| 701 | ** |
| 702 | ** Format (2) is specifically designed to allow URLs like this: |
| 703 | ** |
| 704 | ** /tarball/release/UUID/PROJECT.tar.gz |
| 705 | ** |
| 706 | ** Such URLs will pass through most anti-robot filters because of the |
| 707 | ** "/tarball/release" prefix will match the suggested "robot-exception" |
| 708 | ** pattern and can still refer to an historic release rather than just |
| 709 | ** the most recent release. |
| 710 | */ |
| 711 | char *tar_uuid_from_name(char **pzName){ |
| 712 | char *zName = *pzName; /* Original input */ |
| 713 | int n1 = 0; /* Bytes in first prefix (tag-name) */ |
| 714 | int n2 = 0; /* Bytes in second prefix (check-in-name) */ |
| 715 | int n = 0; /* max(n1,n2) */ |
| 716 | int i; /* Loop counter */ |
| 717 | for(i=n1=n2=0; zName[i]; i++){ |
| 718 | if( zName[i]=='/' ){ |
| 719 | if( n1==0 ){ |
| 720 | n = n1 = i; |
| 721 | }else if( n2==0 ){ |
| 722 | n = n2 = i; |
| 723 | }else{ |
| 724 | return 0; /* More than two "/" characters seen */ |
| 725 | } |
| 726 | } |
| 727 | } |
| 728 | if( n1==0 ){ |
| 729 | return 0; /* No prefix of any kind */ |
| 730 | } |
| 731 | if( zName[n+1]==0 ){ |
| 732 | return 0; /* No filename suffix */ |
| 733 | } |
| 734 | if( n2==0 ){ |
| 735 | /* Format (1): check-in name only. The check-in-name is not verified */ |
| 736 | zName[n1] = 0; |
| 737 | *pzName = fossil_strdup(&zName[n1+1]); |
| 738 | return zName; |
| 739 | }else if( n2>n1+1 ){ |
| 740 | /* Format (2): tag-name/check-in-name. Verify that check-in-name is real |
| 741 | ** and that the check-in has the tag named by tag-name. |
| 742 | */ |
| 743 | char *zCkin = mprintf("%.*s", n2-n1-1, &zName[n1+1]); |
| 744 | char *zTag; |
| 745 | int rid = symbolic_name_to_rid(zCkin,"ci"); |
| 746 | int hasTag; |
| 747 | if( rid<=0 ){ |
| 748 | fossil_free(zCkin); |
| 749 | return fossil_strdup(""); |
| 750 | } |
| 751 | zTag = mprintf("%.*s", n1, zName); |
| 752 | hasTag = db_exists( |
| 753 | "SELECT 1 FROM tagxref, tag" |
| 754 | " WHERE tagxref.rid=%d" |
| 755 | " AND tag.tagid=tagxref.tagid" |
| 756 | " AND tagxref.tagtype=1" |
| 757 | " AND tag.tagname='sym-%q'", |
| 758 | rid, zTag |
| 759 | ); |
| 760 | fossil_free(zTag); |
| 761 | if( !hasTag ){ |
| 762 | fossil_free(zCkin); |
| 763 | return fossil_strdup(""); |
| 764 | } |
| 765 | *pzName = fossil_strdup(&zName[n2+1]); |
| 766 | return zCkin; |
| 767 | }else{ |
| 768 | return 0; |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | /* |
| 773 | ** WEBPAGE: tarball |
| 774 | ** URL: /tarball/NAME.tar.gz |
| 775 | ** or: /tarball/VERSION/NAME.tar.gz |
| 776 | ** or: /tarball/TAG/VERSION/NAME.tar.gz |
| 777 | ** |
| 778 | ** Generate a compressed tarball for the check-in specified by VERSION. |
| 779 | ** The tarball is called NAME.tar.gz and has a top-level directory called |
| 780 | ** NAME. If TAG is provided, then VERSION must hold TAG or else an error |
| 781 | ** is returned. |
| 782 | ** |
| 783 | ** The optional VERSION element defaults to "trunk" per the r= rules below. |
| 784 | ** All of the following URLs are equivalent: |
| 785 | ** |
| 786 | ** /tarball/release/xyz.tar.gz |
| 787 |