Diff
Not logged in

Differences From Artifact [6ea3f8b43a]:

To Artifact [06fd835b87]:


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
728
729
730
731
732
733
734
735
736
737
738
739
740









741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759

760

761
762
763
764
765
766
767
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
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
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
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818

819
820
821
822
823
824
825
826









+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
+
+



-
+





-
+















+
+
+
+
+
+
+
+
+



















+
-
+







  glob_free(pExclude);
  if( listFlag ) fflush(stdout);
  if( zOut ){
    blob_write_to_file(&tarball, zOut);
    blob_reset(&tarball);
  }
}

/*
** This is a helper routine for tar_uuid_from_name().  It handles
** the case where *pzName contains no "/" character.  Check for
** format (3).  Return the hash if the name matches format (3),
** or return NULL if it does not.
*/
static char *format_three_parser(const char *zName){
  int iDot = 0;    /* Index in zName[] of the first '.' */
  int iDash1 = 0;  /* Index in zName[] of the '-' before the timestamp */
  int iDash2 = 0;  /* Index in zName[] of the '-' between timestamp and hash */
  int nHash;       /* Size of the hash */
  char *zHash;     /* A copy of the hash value */
  char *zDate;     /* Copy of the timestamp */
  char *zUuid;     /* Final result */
  int i;           /* Loop query */
  Stmt q;          /* Query to verify that hash and timestamp agree */

  for(i=0; zName[i]; i++){
    char c = zName[i];
    if( c=='.' ){ iDot = i;  break; }
    if( c=='-' ){ iDash1 = iDash2; iDash2 = i; }
    if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; }
  }
  if( iDot==0 ) return 0;
  if( iDash1==0 ) return 0;
  nHash = iDot - iDash2 - 1;
  if( nHash<8 ) return 0;                /* HASH value too short */  
  if( (iDash2 - iDash1)!=15 ) return 0;  /* Wrong timestamp size */
  zHash = fossil_strndup(&zName[iDash2+1], nHash);
  zDate = fossil_strndup(&zName[iDash1+1], 14);
  db_prepare(&q, 
    "SELECT blob.uuid"
    "  FROM blob JOIN event ON event.objid=blob.rid"
    " WHERE blob.uuid GLOB '%q*'"
    "   AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'", 
    zHash, zDate
  );
  fossil_free(zHash);
  fossil_free(zDate);
  if( db_step(&q)==SQLITE_ROW ){
    zUuid = fossil_strdup(db_column_text(&q,0));
  }else{
    zUuid = 0;
  }
  db_finalize(&q);
  return zUuid;
}

/*
** Check to see if the input string is of one of the following
** two the forms:
**
**        check-in-name/filename.ext                       (1)
**        tag-name/check-in-name/filename.txt              (2)
**        tag-name/check-in-name/filename.ext              (2)
**        project-datetime-hash.ext                        (3)
**
** In other words, check to see if the input string contains either
** a check-in name or a tag-name and a check-in name separated by
** a slash.  There must be either 1 or 2 "/" characters.  In the
** a slash.  There must be between 0 or 2 "/" characters.  In the
** second form, tag-name must be an individual tag (not a branch-tag)
** that is found on the check-in identified by the check-in-name.
**
** If the condition is true, then:
**
**   *  Make *pzName point to the fielname suffix only
**   *  Make *pzName point to the filename suffix only
**   *  return a copy of the check-in name in memory from mprintf().
**
** If the condition is false, leave *pzName unchanged and return either
** NULL or an empty string.  Normally NULL is returned, however an
** empty string is returned for format (2) if check-in-name does not
** match tag-name.
**
** Format (2) is specifically designed to allow URLs like this:
**
**      /tarball/release/UUID/PROJECT.tar.gz
**
** Such URLs will pass through most anti-robot filters because of the
** "/tarball/release" prefix will match the suggested "robot-exception"
** pattern and can still refer to an historic release rather than just
** the most recent release.
**
** Format (3) is designed to allow URLs like this:
**
**     /tarball/fossil-20251018193920-d6c9aee97df.tar.gz
**
** In other words, filename itself contains sufficient information to
** uniquely identify the check-in, including a timestamp of the form
** YYYYMMDDHHMMSS and a prefix of the check-in hash.  The timestamp
** and hash must immediately preceed the first "." in the name.
*/
char *tar_uuid_from_name(char **pzName){
  char *zName = *pzName;      /* Original input */
  int n1 = 0;                 /* Bytes in first prefix (tag-name) */
  int n2 = 0;                 /* Bytes in second prefix (check-in-name) */
  int n = 0;                  /* max(n1,n2) */
  int i;                      /* Loop counter */
  for(i=n1=n2=0; zName[i]; i++){
    if( zName[i]=='/' ){
      if( n1==0 ){
        n = n1 = i;
      }else if( n2==0 ){
        n = n2 = i;
      }else{
        return 0;   /* More than two "/" characters seen */
      }
    }
  }
  if( n1==0 ){
    /* Check for format (3) */
    return 0;    /* No prefix of any kind */
    return format_three_parser(*pzName);
  }
  if( zName[n+1]==0 ){
    return 0;    /* No filename suffix */
  }
  if( n2==0 ){
    /* Format (1): check-in name only.  The check-in-name is not verified */
    zName[n1] = 0;
1000
1001
1002
1003
1004
1005
1006
1007

1008
1009

1010
1011
1012
1013
1014
1015
1016
1059
1060
1061
1062
1063
1064
1065

1066
1067

1068
1069
1070
1071
1072
1073
1074
1075







-
+

-
+







      }
      if( (tmFlags & TIMELINE_GRAPH)==0 && fossil_strcmp(zBrName,"trunk")!=0 ){
        @ branch:&nbsp;\
        @   %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a>
      }
    }
    zNm = archive_base_name(rid);
    @ %z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zNm))\
    @ %z(href("%R/tarball/%s.tar.gz",zNm))\
    @    <button>Tarball</button></a>
    @  %z(href("%R/zip/%!S/%s.zip",zUuid,zNm))\
    @  %z(href("%R/zip/%s.zip",zNm))\
    @    <button>ZIP&nbsp;Archive</button></a>
    fossil_free(zBrName);
    fossil_free(zNm);
  }
}

/*
1174
1175
1176
1177
1178
1179
1180
1181
1182


1183
1184
1185
1186
1187
1188


1189
1190
1191
1192
1193
1194


1195
1196
1197
1198
1199
1200
1201
1202
1233
1234
1235
1236
1237
1238
1239


1240
1241
1242
1243
1244
1245


1246
1247
1248
1249
1250
1251


1252
1253
1254
1255
1256
1257
1258
1259
1260
1261







-
-
+
+




-
-
+
+




-
-
+
+








  zBase = archive_base_name(rid);
  @ <div class="section accordion">Downloads for check-in \
  @ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div>
  @ <div class="accordion_panel">
  @ <table class="label-value">
  @ <tr>
  @ <th>Tarball:</th>
  @ <td>%z(href("%R/tarball/%!S/%s.tar.gz",zUuid,zBase))\
  @ %s(g.zBaseURL)/tarball/%!S(zUuid)/%s(zBase).tar.gz</a></td>
  @ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\
  @ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td>
  @ </tr>
  @
  @ <tr>
  @ <th>ZIP:</th>
  @ <td>%z(href("%R/zip/%!S/%s.zip",zUuid,zBase))\
  @ %s(g.zBaseURL)/zip/%!S(zUuid)/%s(zBase).zip</a></td>
  @ <td>%z(href("%R/zip/%s.zip",zBase))\
  @ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td>
  @ </tr>
  @
  @ <tr>
  @ <th>SQLAR:</th>
  @ <td>%z(href("%R/sqlar/%!S/%s.sqlar",zUuid,zBase))\
  @ %s(g.zBaseURL)/sqlar/%!S(zUuid)/%s(zBase).sqlar</a></td>
  @ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\
  @ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td>
  @ </tr>
  @ </table></div>
  fossil_free(zBase);
  @ <div class="section accordion">Context</div><div class="accordion_panel">
  render_checkin_context(rid, 0, 0, 0);
  @ </div>
  style_finish_page();
}