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.

drh 2025-10-03 16:05 trunk
Commit 57c8bec8169e0725b02bdd67555b25d3e68982f6db8395970182dce7e8424814
1 file changed +92 -25
+92 -25
--- src/tar.c
+++ src/tar.c
@@ -675,45 +675,112 @@
675675
blob_reset(&tarball);
676676
}
677677
}
678678
679679
/*
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.
691710
*/
692711
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++){
696718
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
+ }
706770
}
707771
708772
/*
709773
** 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
711777
**
712778
** Generate a compressed tarball for the check-in specified by VERSION.
713779
** 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.
715782
**
716783
** The optional VERSION element defaults to "trunk" per the r= rules below.
717784
** All of the following URLs are equivalent:
718785
**
719786
** /tarball/release/xyz.tar.gz
720787
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button