1 /*
2 ** Copyright (c) 2007 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
13 **   drh@hwaci.com
14 **   http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code used to cross link control files and
19 ** manifests.  The file is named "manifest.c" because it was
20 ** original only used to parse manifests.  Then later clusters
21 ** and control files and wiki pages and tickets were added.
22 */
23 #include "config.h"
24 #include "manifest.h"
25 #include <assert.h>
26 
27 #if INTERFACE
28 /*
29 ** Types of control files
30 */
31 #define CFTYPE_ANY        0
32 #define CFTYPE_MANIFEST   1
33 #define CFTYPE_CLUSTER    2
34 #define CFTYPE_CONTROL    3
35 #define CFTYPE_WIKI       4
36 #define CFTYPE_TICKET     5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT      7
39 #define CFTYPE_FORUM      8
40 
41 /*
42 ** File permissions used by Fossil internally.
43 */
44 #define PERM_REG          0     /*  regular file  */
45 #define PERM_EXE          1     /*  executable    */
46 #define PERM_LNK          2     /*  symlink       */
47 
48 /*
49 ** Flags for use with manifest_crosslink().
50 */
51 #define MC_NONE           0  /*  default handling           */
52 #define MC_PERMIT_HOOKS   1  /*  permit hooks to execute    */
53 #define MC_NO_ERRORS      2  /*  do not issue errors for a bad parse */
54 
55 /*
56 ** A single F-card within a manifest
57 */
58 struct ManifestFile {
59   char *zName;           /* Name of a file */
60   char *zUuid;           /* Artifact hash for the file */
61   char *zPerm;           /* File permissions */
62   char *zPrior;          /* Prior name if the name was changed */
63 };
64 
65 
66 /*
67 ** A parsed manifest or cluster.
68 */
69 struct Manifest {
70   Blob content;         /* The original content blob */
71   int type;             /* Type of artifact.  One of CFTYPE_xxxxx */
72   int rid;              /* The blob-id for this manifest */
73   const char *zBaseline;/* Baseline manifest.  The B card. */
74   Manifest *pBaseline;  /* The actual baseline manifest */
75   char *zComment;       /* Decoded comment.  The C card. */
76   double rDate;         /* Date and time from D card.  0.0 if no D card. */
77   char *zUser;          /* Name of the user from the U card. */
78   char *zRepoCksum;     /* MD5 checksum of the baseline content.  R card. */
79   char *zWiki;          /* Text of the wiki page.  W card. */
80   char *zWikiTitle;     /* Name of the wiki page. L card. */
81   char *zMimetype;      /* Mime type of wiki or comment text.  N card.  */
82   char *zThreadTitle;   /* The forum thread title. H card */
83   double rEventDate;    /* Date of an event.  E card. */
84   char *zEventId;       /* Artifact hash for an event.  E card. */
85   char *zTicketUuid;    /* UUID for a ticket. K card. */
86   char *zAttachName;    /* Filename of an attachment. A card. */
87   char *zAttachSrc;     /* Artifact hash for document being attached. A card. */
88   char *zAttachTarget;  /* Ticket or wiki that attachment applies to.  A card */
89   char *zThreadRoot;    /* Thread root artifact.  G card */
90   char *zInReplyTo;     /* Forum in-reply-to artifact.  I card */
91   int nFile;            /* Number of F cards */
92   int nFileAlloc;       /* Slots allocated in aFile[] */
93   int iFile;            /* Index of current file in iterator */
94   ManifestFile *aFile;  /* One entry for each F-card */
95   int nParent;          /* Number of parents. */
96   int nParentAlloc;     /* Slots allocated in azParent[] */
97   char **azParent;      /* Hashes of parents.  One for each P card argument */
98   int nCherrypick;      /* Number of entries in aCherrypick[] */
99   struct {
100     char *zCPTarget;    /* Hash for cherry-picked version w/ +|- prefix */
101     char *zCPBase;      /* Hash for cherry-pick baseline. NULL for singletons */
102   } *aCherrypick;
103   int nCChild;          /* Number of cluster children */
104   int nCChildAlloc;     /* Number of closts allocated in azCChild[] */
105   char **azCChild;      /* Hashes of referenced objects in a cluster. M cards */
106   int nTag;             /* Number of T Cards */
107   int nTagAlloc;        /* Slots allocated in aTag[] */
108   struct TagType {
109     char *zName;           /* Name of the tag */
110     char *zUuid;           /* Hash of artifact that the tag is applied to */
111     char *zValue;          /* Value if the tag is really a property */
112   } *aTag;              /* One for each T card */
113   int nField;           /* Number of J cards */
114   int nFieldAlloc;      /* Slots allocated in aField[] */
115   struct {
116     char *zName;           /* Key or field name */
117     char *zValue;          /* Value of the field */
118   } *aField;            /* One for each J card */
119 };
120 #endif
121 
122 /*
123 ** Allowed and required card types in each style of artifact
124 */
125 static struct {
126   const char *zAllowed;     /* Allowed cards.  Human-readable */
127   const char *zRequired;    /* Required cards.  Human-readable */
128 } manifestCardTypes[] = {
129   /*                           Allowed          Required    */
130   /* CFTYPE_MANIFEST   1 */ { "BCDFNPQRTUZ",   "DZ"          },
131                      /* Wants to be "CDUZ" ----^^^^
132                      ** but we must limit for historical compatibility */
133   /* CFTYPE_CLUSTER    2 */ { "MZ",            "MZ"          },
134   /* CFTYPE_CONTROL    3 */ { "DTUZ",          "DTUZ"        },
135   /* CFTYPE_WIKI       4 */ { "CDLNPUWZ",      "DLUWZ"       },
136   /* CFTYPE_TICKET     5 */ { "DJKUZ",         "DJKUZ"       },
137   /* CFTYPE_ATTACHMENT 6 */ { "ACDNUZ",        "ADZ"         },
138   /* CFTYPE_EVENT      7 */ { "CDENPTUWZ",     "DEWZ"        },
139   /* CFTYPE_FORUM      8 */ { "DGHINPUWZ",     "DUWZ"        },
140 };
141 
142 /*
143 ** Names of manifest types
144 */
145 static const char *const azNameOfMType[] = {
146   "manifest",
147   "cluster",
148   "tag",
149   "wiki",
150   "ticket",
151   "attachment",
152   "technote",
153   "forum post"
154 };
155 
156 /*
157 ** A cache of parsed manifests.  This reduces the number of
158 ** calls to manifest_parse() when doing a rebuild.
159 */
160 #define MX_MANIFEST_CACHE 6
161 static struct {
162   int nxAge;
163   int aAge[MX_MANIFEST_CACHE];
164   Manifest *apManifest[MX_MANIFEST_CACHE];
165 } manifestCache;
166 
167 /*
168 ** True if manifest_crosslink_begin() has been called but
169 ** manifest_crosslink_end() is still pending.
170 */
171 static int manifest_crosslink_busy = 0;
172 
173 /*
174 ** There are some triggers that need to fire whenever new content
175 ** is added to the EVENT table, to make corresponding changes to the
176 ** PENDING_ALERT and CHAT tables.  These are done with TEMP triggers
177 ** which are created as needed.  The reasons for using TEMP triggers:
178 **
179 **    *  A small minority of invocations of Fossil need to use those triggers.
180 **       So we save CPU cycles in the common case by not having to parse the
181 **       trigger definition
182 **
183 **    *  We don't have to worry about dangling table references inside
184 **       of triggers.  For example, we can create a trigger that adds
185 **       to the CHAT table.  But an admin can still drop that CHAT table
186 **       at any moment, since the trigger that refers to CHAT is a TEMP
187 **       trigger and won't persist to cause problems.
188 **
189 **    *  Because TEMP triggers are defined by the specific version of the
190 **       application that is running, we don't have to worry with legacy
191 **       compatibility of the triggers.
192 **
193 ** This boolean variable is set when the TEMP triggers for EVENT
194 ** have been created.
195 */
196 static int manifest_event_triggers_are_enabled = 0;
197 
198 /*
199 ** Clear the memory allocated in a manifest object
200 */
manifest_destroy(Manifest * p)201 void manifest_destroy(Manifest *p){
202   if( p ){
203     blob_reset(&p->content);
204     fossil_free(p->aFile);
205     fossil_free(p->azParent);
206     fossil_free(p->azCChild);
207     fossil_free(p->aTag);
208     fossil_free(p->aField);
209     fossil_free(p->aCherrypick);
210     if( p->pBaseline ) manifest_destroy(p->pBaseline);
211     memset(p, 0, sizeof(*p));
212     fossil_free(p);
213   }
214 }
215 
216 /*
217 ** Given a string of upper-case letters, compute a mask of the letters
218 ** present.  For example,  "ABC" computes 0x0007.  "DE" gives 0x0018".
219 */
manifest_card_mask(const char * z)220 static unsigned int manifest_card_mask(const char *z){
221   unsigned int m = 0;
222   char c;
223   while( (c = *(z++))>='A' && c<='Z' ){
224     m |= 1 << (c - 'A');
225   }
226   return m;
227 }
228 
229 /*
230 ** Given an integer mask representing letters A-Z, return the
231 ** letter which is the first bit set in the mask.  Example:
232 ** 0x03520 gives 'F' since the F-bit is the lowest.
233 */
maskToType(unsigned int x)234 static char maskToType(unsigned int x){
235   char c = 'A';
236   if( x==0 ) return '?';
237   while( (x&1)==0 ){ x >>= 1; c++; }
238   return c;
239 }
240 
241 /*
242 ** Add an element to the manifest cache using LRU replacement.
243 */
manifest_cache_insert(Manifest * p)244 void manifest_cache_insert(Manifest *p){
245   while( p ){
246     int i;
247     Manifest *pBaseline = p->pBaseline;
248     p->pBaseline = 0;
249     for(i=0; i<MX_MANIFEST_CACHE; i++){
250       if( manifestCache.apManifest[i]==0 ) break;
251     }
252     if( i>=MX_MANIFEST_CACHE ){
253       int oldest = 0;
254       int oldestAge = manifestCache.aAge[0];
255       for(i=1; i<MX_MANIFEST_CACHE; i++){
256         if( manifestCache.aAge[i]<oldestAge ){
257           oldest = i;
258           oldestAge = manifestCache.aAge[i];
259         }
260       }
261       manifest_destroy(manifestCache.apManifest[oldest]);
262       i = oldest;
263     }
264     manifestCache.aAge[i] = ++manifestCache.nxAge;
265     manifestCache.apManifest[i] = p;
266     p = pBaseline;
267   }
268 }
269 
270 /*
271 ** Try to extract a line from the manifest cache. Return 1 if found.
272 ** Return 0 if not found.
273 */
manifest_cache_find(int rid)274 static Manifest *manifest_cache_find(int rid){
275   int i;
276   Manifest *p;
277   for(i=0; i<MX_MANIFEST_CACHE; i++){
278     if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){
279       p = manifestCache.apManifest[i];
280       manifestCache.apManifest[i] = 0;
281       return p;
282     }
283   }
284   return 0;
285 }
286 
287 /*
288 ** Clear the manifest cache.
289 */
manifest_cache_clear(void)290 void manifest_cache_clear(void){
291   int i;
292   for(i=0; i<MX_MANIFEST_CACHE; i++){
293     if( manifestCache.apManifest[i] ){
294       manifest_destroy(manifestCache.apManifest[i]);
295     }
296   }
297   memset(&manifestCache, 0, sizeof(manifestCache));
298 }
299 
300 #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM
301 # define md5sum_init(X)
302 # define md5sum_step_text(X,Y)
303 #endif
304 
305 /*
306 ** Return true if z points to the first character after a blank line.
307 ** Tolerate either \r\n or \n line endings.
308 */
after_blank_line(const char * z)309 static int after_blank_line(const char *z){
310   if( z[-1]!='\n' ) return 0;
311   if( z[-2]=='\n' ) return 1;
312   if( z[-2]=='\r' && z[-3]=='\n' ) return 1;
313   return 0;
314 }
315 
316 /*
317 ** Remove the PGP signature from the artifact, if there is one.
318 */
remove_pgp_signature(const char ** pz,int * pn)319 static void remove_pgp_signature(const char **pz, int *pn){
320   const char *z = *pz;
321   int n = *pn;
322   int i;
323   if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return;
324   for(i=34; i<n && !after_blank_line(z+i); i++){}
325   if( i>=n ) return;
326   z += i;
327   n -= i;
328   *pz = z;
329   for(i=n-1; i>=0; i--){
330     if( z[i]=='\n' && strncmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){
331       n = i+1;
332       break;
333     }
334   }
335   *pn = n;
336   return;
337 }
338 
339 /*
340 ** Verify the Z-card checksum on the artifact, if there is such a
341 ** checksum.  Return 0 if there is no Z-card.  Return 1 if the Z-card
342 ** exists and is correct.  Return 2 if the Z-card exists and has the wrong
343 ** value.
344 **
345 **   0123456789 123456789 123456789 123456789
346 **   Z aea84f4f863865a8d59d0384e4d2a41c
347 */
verify_z_card(const char * z,int n,Blob * pErr)348 static int verify_z_card(const char *z, int n, Blob *pErr){
349   const char *zHash;
350   if( n<35 ) return 0;
351   if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
352   md5sum_init();
353   md5sum_step_text(z, n-35);
354   zHash = md5sum_finish(0);
355   if( memcmp(&z[n-33], zHash, 32)==0 ){
356     return 1;
357   }else{
358     if(pErr!=0){
359       blob_appendf(pErr, "incorrect Z-card cksum: expected %.32s", zHash);
360     }
361     return 2;
362   }
363 }
364 
365 /*
366 ** A structure used for rapid parsing of the Manifest file
367 */
368 typedef struct ManifestText ManifestText;
369 struct ManifestText {
370   char *z;           /* The first character of the next token */
371   char *zEnd;        /* One character beyond the end of the manifest */
372   int atEol;         /* True if z points to the start of a new line */
373 };
374 
375 /*
376 ** Return a pointer to the next token.  The token is zero-terminated.
377 ** Return NULL if there are no more tokens on the current line.
378 */
next_token(ManifestText * p,int * pLen)379 static char *next_token(ManifestText *p, int *pLen){
380   char *zStart;
381   int n;
382   if( p->atEol ) return 0;
383   zStart = p->z;
384   n = strcspn(p->z, " \n");
385   p->atEol = p->z[n]=='\n';
386   p->z[n] = 0;
387   p->z += n+1;
388   if( pLen ) *pLen = n;
389   return zStart;
390 }
391 
392 /*
393 ** Return the card-type for the next card.  Or, return 0 if there are no
394 ** more cards or if we are not at the end of the current card.
395 */
next_card(ManifestText * p)396 static char next_card(ManifestText *p){
397   char c;
398   if( !p->atEol || p->z>=p->zEnd ) return 0;
399   c = p->z[0];
400   if( p->z[1]==' ' ){
401     p->z += 2;
402     p->atEol = 0;
403   }else if( p->z[1]=='\n' ){
404     p->z += 2;
405     p->atEol = 1;
406   }else{
407     c = 0;
408   }
409   return c;
410 }
411 
412 /*
413 ** Shorthand for a control-artifact parsing error
414 */
415 #define SYNTAX(T)  {zErr=(T); goto manifest_syntax_error;}
416 
417 /*
418 ** A cache of manifest IDs which manifest_parse() has seen in this
419 ** session.
420 */
421 static Bag seenManifests =  Bag_INIT;
422 /*
423 ** Frees all memory owned by the manifest "has-seen" cache.  Intended
424 ** to be called only from the app's atexit() handler.
425 */
manifest_clear_cache()426 void manifest_clear_cache(){
427   bag_clear(&seenManifests);
428 }
429 
430 /*
431 ** Parse a blob into a Manifest object.  The Manifest object
432 ** takes over the input blob and will free it when the
433 ** Manifest object is freed.  Zeros are inserted into the blob
434 ** as string terminators so that blob should not be used again.
435 **
436 ** Return a pointer to an allocated Manifest object if the content
437 ** really is a structural artifact of some kind.  The returned Manifest
438 ** object needs to be freed by a subsequent call to manifest_destroy().
439 ** Return NULL if there are syntax errors or if the input blob does
440 ** not describe a valid structural artifact.
441 **
442 ** This routine is strict about the format of a structural artifacts.
443 ** The format must match exactly or else it is rejected.  This
444 ** rule minimizes the risk that a content artifact will be mistaken
445 ** for a structural artifact simply because they look the same.
446 **
447 ** The pContent is reset.  If a pointer is returned, then pContent will
448 ** be reset when the Manifest object is cleared.  If NULL is
449 ** returned then the Manifest object is cleared automatically
450 ** and pContent is reset before the return.
451 **
452 ** The entire input blob can be PGP clear-signed.  The signature is ignored.
453 ** The artifact consists of zero or more cards, one card per line.
454 ** (Except: the content of the W card can extend of multiple lines.)
455 ** Each card is divided into tokens by a single space character.
456 ** The first token is a single upper-case letter which is the card type.
457 ** The card type determines the other parameters to the card.
458 ** Cards must occur in lexicographical order.
459 */
manifest_parse(Blob * pContent,int rid,Blob * pErr)460 Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){
461   Manifest *p;
462   int i, lineNo=0;
463   ManifestText x;
464   char cPrevType = 0;
465   char cType;
466   char *z;
467   int n;
468   char *zUuid;
469   int sz = 0;
470   int isRepeat;
471   int nSelfTag = 0;     /* Number of T cards referring to this manifest */
472   int nSimpleTag = 0;   /* Number of T cards with "+" prefix */
473   const char *zErr = 0;
474   unsigned int m;
475   unsigned int seenCard = 0;   /* Which card types have been seen */
476   char zErrBuf[100];           /* Write error messages here */
477 
478   if( rid==0 ){
479     isRepeat = 1;
480   }else if( bag_find(&seenManifests, rid) ){
481     isRepeat = 1;
482   }else{
483     isRepeat = 0;
484     bag_insert(&seenManifests, rid);
485   }
486 
487   /* Every structural artifact ends with a '\n' character.  Exit early
488   ** if that is not the case for this artifact.
489   */
490   if( !isRepeat ) g.parseCnt[0]++;
491   z = blob_materialize(pContent);
492   n = blob_size(pContent);
493   if( n<=0 || z[n-1]!='\n' ){
494     blob_reset(pContent);
495     if(pErr!=0){
496       blob_appendf(pErr, "%s", n ? "not terminated with \\n" : "zero-length");
497     }
498     return 0;
499   }
500 
501   /* Strip off the PGP signature if there is one.
502   */
503   remove_pgp_signature((const char**)&z, &n);
504 
505   /* Verify that the first few characters of the artifact look like
506   ** a control artifact.
507   */
508   if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
509     blob_reset(pContent);
510     if(pErr!=0){
511       blob_appendf(pErr, "line 1 not recognized");
512     }
513     return 0;
514   }
515   /* Then verify the Z-card.
516   */
517 #if 1
518   /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs
519      for card-related syntax errors. */
520   if( verify_z_card(z, n, pErr)==2 ){
521     blob_reset(pContent);
522     return 0;
523   }
524 #else
525 #warning ACHTUNG - z-card check is disabled for testing purposes.
526   if(0 && verify_z_card(NULL, 0, NULL)){
527     /*avoid unused static func error*/
528   }
529 #endif
530 
531   /* Allocate a Manifest object to hold the parsed control artifact.
532   */
533   p = fossil_malloc( sizeof(*p) );
534   memset(p, 0, sizeof(*p));
535   memcpy(&p->content, pContent, sizeof(p->content));
536   p->rid = rid;
537   blob_zero(pContent);
538   pContent = &p->content;
539 
540   /* Begin parsing, card by card.
541   */
542   x.z = z;
543   x.zEnd = &z[n];
544   x.atEol = 1;
545   while( (cType = next_card(&x))!=0 ){
546     if( cType<cPrevType ){
547       /* Cards must be in increasing order.  However, out-of-order detection
548       ** was broken prior to 2021-02-10 due to a bug.  Furthermore, there
549       ** was a bug in technote generation (prior to 2021-02-10) that caused
550       ** the P card to occur before the N card.  Hence, for historical
551       ** compatibility, we do allow the N card of a technote to occur after
552       ** the P card.  See tickets 15d04de574383d61 and 5e67a7f4041a36ad.
553       */
554       if( cType!='N' || cPrevType!='P' || p->zEventId==0 ){
555         SYNTAX("cards not in lexicographical order");
556       }
557     }
558     lineNo++;
559     if( cType<'A' || cType>'Z' ) SYNTAX("bad card type");
560     seenCard |= 1 << (cType-'A');
561     cPrevType = cType;
562     switch( cType ){
563       /*
564       **     A <filename> <target> ?<source>?
565       **
566       ** Identifies an attachment to either a wiki page or a ticket.
567       ** <source> is the artifact that is the attachment.  <source>
568       ** is omitted to delete an attachment.  <target> is the name of
569       ** a wiki page or ticket to which that attachment is connected.
570       */
571       case 'A': {
572         char *zName, *zTarget, *zSrc;
573         int nTarget = 0, nSrc = 0;
574         zName = next_token(&x, 0);
575         zTarget = next_token(&x, &nTarget);
576         zSrc = next_token(&x, &nSrc);
577         if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
578         if( p->zAttachName!=0 ) goto manifest_syntax_error;
579         defossilize(zName);
580         if( !file_is_simple_pathname_nonstrict(zName) ){
581           SYNTAX("invalid filename on A-card");
582         }
583         defossilize(zTarget);
584         if( !hname_validate(zTarget,nTarget)
585            && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
586           SYNTAX("invalid target on A-card");
587         }
588         if( zSrc && !hname_validate(zSrc,nSrc) ){
589           SYNTAX("invalid source on A-card");
590         }
591         p->zAttachName = (char*)file_tail(zName);
592         p->zAttachSrc = zSrc;
593         p->zAttachTarget = zTarget;
594         p->type = CFTYPE_ATTACHMENT;
595         break;
596       }
597 
598       /*
599       **    B <uuid>
600       **
601       ** A B-line gives the artifact hash for the baseline of a delta-manifest.
602       */
603       case 'B': {
604         if( p->zBaseline ) SYNTAX("more than one B-card");
605         p->zBaseline = next_token(&x, &sz);
606         if( p->zBaseline==0 ) SYNTAX("missing hash on B-card");
607         if( !hname_validate(p->zBaseline,sz) ){
608           SYNTAX("invalid hash on B-card");
609         }
610         p->type = CFTYPE_MANIFEST;
611         break;
612       }
613 
614 
615       /*
616       **     C <comment>
617       **
618       ** Comment text is fossil-encoded.  There may be no more than
619       ** one C line.  C lines are required for manifests, are optional
620       ** for Events and Attachments, and are disallowed on all other
621       ** control files.
622       */
623       case 'C': {
624         if( p->zComment!=0 ) SYNTAX("more than one C-card");
625         p->zComment = next_token(&x, 0);
626         if( p->zComment==0 ) SYNTAX("missing comment text on C-card");
627         defossilize(p->zComment);
628         break;
629       }
630 
631       /*
632       **     D <timestamp>
633       **
634       ** The timestamp should be ISO 8601.   YYYY-MM-DDtHH:MM:SS
635       ** There can be no more than 1 D line.  D lines are required
636       ** for all control files except for clusters.
637       */
638       case 'D': {
639         if( p->rDate>0.0 ) SYNTAX("more than one D-card");
640         p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0));
641         if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card");
642         break;
643       }
644 
645       /*
646       **     E <timestamp> <uuid>
647       **
648       ** An "event" card that contains the timestamp of the event in the
649       ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event.
650       ** The event timestamp is distinct from the D timestamp.  The D
651       ** timestamp is when the artifact was created whereas the E timestamp
652       ** is when the specific event is said to occur.
653       */
654       case 'E': {
655         if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
656         p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
657         if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
658         p->zEventId = next_token(&x, &sz);
659         if( p->zEventId==0 ) SYNTAX("missing hash on E-card");
660         if( !hname_validate(p->zEventId, sz) ){
661           SYNTAX("malformed hash on E-card");
662         }
663         p->type = CFTYPE_EVENT;
664         break;
665       }
666 
667       /*
668       **     F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
669       **
670       ** Identifies a file in a manifest.  Multiple F lines are
671       ** allowed in a manifest.  F lines are not allowed in any
672       ** other control file.  The filename and old-name are fossil-encoded.
673       */
674       case 'F': {
675         char *zName, *zPerm, *zPriorName;
676         zName = next_token(&x,0);
677         if( zName==0 ) SYNTAX("missing filename on F-card");
678         defossilize(zName);
679         if( !file_is_simple_pathname_nonstrict(zName) ){
680           SYNTAX("F-card filename is not a simple path");
681         }
682         zUuid = next_token(&x, &sz);
683         if( p->zBaseline==0 || zUuid!=0 ){
684           if( zUuid==0 ) SYNTAX("missing hash on F-card");
685           if( !hname_validate(zUuid,sz) ){
686             SYNTAX("F-card hash invalid");
687           }
688         }
689         zPerm = next_token(&x,0);
690         zPriorName = next_token(&x,0);
691         if( zPriorName ){
692           defossilize(zPriorName);
693           if( !file_is_simple_pathname_nonstrict(zPriorName) ){
694             SYNTAX("F-card old filename is not a simple path");
695           }
696         }
697         if( p->nFile>=p->nFileAlloc ){
698           p->nFileAlloc = p->nFileAlloc*2 + 10;
699           p->aFile = fossil_realloc(p->aFile,
700                                     p->nFileAlloc*sizeof(p->aFile[0]) );
701         }
702         i = p->nFile++;
703         if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
704           SYNTAX("incorrect F-card sort order");
705         }
706         if( file_is_reserved_name(zName,-1) ){
707           /* If reserved names leaked into historical manifests due to
708           ** slack oversight by older versions of Fossil, simply ignore
709           ** those files */
710           p->nFile--;
711           break;
712         }
713         p->aFile[i].zName = zName;
714         p->aFile[i].zUuid = zUuid;
715         p->aFile[i].zPerm = zPerm;
716         p->aFile[i].zPrior = zPriorName;
717         p->type = CFTYPE_MANIFEST;
718         break;
719       }
720 
721       /*
722       **    G <hash>
723       **
724       ** A G-card identifies the initial root forum post for the thread
725       ** of which this post is a part.  Forum posts only.
726       */
727       case 'G': {
728         if( p->zThreadRoot!=0 ) SYNTAX("more than one G-card");
729         p->zThreadRoot = next_token(&x, &sz);
730         if( p->zThreadRoot==0 ) SYNTAX("missing hash on G-card");
731         if( !hname_validate(p->zThreadRoot,sz) ){
732           SYNTAX("Invalid hash on G-card");
733         }
734         p->type = CFTYPE_FORUM;
735         break;
736       }
737 
738       /*
739       **     H <threadtitle>
740       **
741       ** The title for a forum thread.
742       */
743       case 'H': {
744         if( p->zThreadTitle!=0 ) SYNTAX("more than one H-card");
745         p->zThreadTitle = next_token(&x,0);
746         if( p->zThreadTitle==0 ) SYNTAX("missing title on H-card");
747         defossilize(p->zThreadTitle);
748         p->type = CFTYPE_FORUM;
749         break;
750       }
751 
752       /*
753       **    I <hash>
754       **
755       ** A I-card identifies another forum post that the current forum post
756       ** is in reply to.
757       */
758       case 'I': {
759         if( p->zInReplyTo!=0 ) SYNTAX("more than one I-card");
760         p->zInReplyTo = next_token(&x, &sz);
761         if( p->zInReplyTo==0 ) SYNTAX("missing hash on I-card");
762         if( !hname_validate(p->zInReplyTo,sz) ){
763           SYNTAX("Invalid hash on I-card");
764         }
765         p->type = CFTYPE_FORUM;
766         break;
767       }
768 
769       /*
770       **     J <name> ?<value>?
771       **
772       ** Specifies a name value pair for ticket.  If the first character
773       ** of <name> is "+" then the <value> is appended to any preexisting
774       ** value.  If <value> is omitted then it is understood to be an
775       ** empty string.
776       */
777       case 'J': {
778         char *zName, *zValue;
779         zName = next_token(&x,0);
780         zValue = next_token(&x,0);
781         if( zName==0 ) SYNTAX("name missing from J-card");
782         if( zValue==0 ) zValue = "";
783         defossilize(zValue);
784         if( p->nField>=p->nFieldAlloc ){
785           p->nFieldAlloc = p->nFieldAlloc*2 + 10;
786           p->aField = fossil_realloc(p->aField,
787                                p->nFieldAlloc*sizeof(p->aField[0]) );
788         }
789         i = p->nField++;
790         p->aField[i].zName = zName;
791         p->aField[i].zValue = zValue;
792         if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
793           SYNTAX("incorrect J-card sort order");
794         }
795         p->type = CFTYPE_TICKET;
796         break;
797       }
798 
799 
800       /*
801       **    K <uuid>
802       **
803       ** A K-line gives the UUID for the ticket which this control file
804       ** is amending.
805       */
806       case 'K': {
807         if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card");
808         p->zTicketUuid = next_token(&x, &sz);
809         if( sz!=HNAME_LEN_SHA1 ) SYNTAX("K-card UUID is the wrong size");
810         if( !validate16(p->zTicketUuid, sz) ){
811           SYNTAX("invalid K-card UUID");
812         }
813         p->type = CFTYPE_TICKET;
814         break;
815       }
816 
817       /*
818       **     L <wikititle>
819       **
820       ** The wiki page title is fossil-encoded.  There may be no more than
821       ** one L line.
822       */
823       case 'L': {
824         if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card");
825         p->zWikiTitle = next_token(&x,0);
826         if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card");
827         defossilize(p->zWikiTitle);
828         if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
829           SYNTAX("L-card has malformed wiki name");
830         }
831         p->type = CFTYPE_WIKI;
832         break;
833       }
834 
835       /*
836       **    M <hash>
837       **
838       ** An M-line identifies another artifact by its hash.  M-lines
839       ** occur in clusters only.
840       */
841       case 'M': {
842         zUuid = next_token(&x, &sz);
843         if( zUuid==0 ) SYNTAX("missing hash on M-card");
844         if( !hname_validate(zUuid,sz) ){
845           SYNTAX("Invalid hash on M-card");
846         }
847         if( p->nCChild>=p->nCChildAlloc ){
848           p->nCChildAlloc = p->nCChildAlloc*2 + 10;
849           p->azCChild = fossil_realloc(p->azCChild
850                                  , p->nCChildAlloc*sizeof(p->azCChild[0]) );
851         }
852         i = p->nCChild++;
853         p->azCChild[i] = zUuid;
854         if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
855           SYNTAX("M-card in the wrong order");
856         }
857         p->type = CFTYPE_CLUSTER;
858         break;
859       }
860 
861       /*
862       **    N <uuid>
863       **
864       ** An N-line identifies the mimetype of wiki or comment text.
865       */
866       case 'N': {
867         if( p->zMimetype!=0 ) SYNTAX("more than one N-card");
868         p->zMimetype = next_token(&x,0);
869         if( p->zMimetype==0 ) SYNTAX("missing mimetype on N-card");
870         defossilize(p->zMimetype);
871         break;
872       }
873 
874       /*
875       **     P <uuid> ...
876       **
877       ** Specify one or more other artifacts which are the parents of
878       ** this artifact.  The first parent is the primary parent.  All
879       ** others are parents by merge. Note that the initial empty
880       ** check-in historically has an empty P-card, so empty P-cards
881       ** must be accepted.
882       */
883       case 'P': {
884         while( (zUuid = next_token(&x, &sz))!=0 ){
885           if( !hname_validate(zUuid, sz) ){
886              SYNTAX("invalid hash on P-card");
887           }
888           if( p->nParent>=p->nParentAlloc ){
889             p->nParentAlloc = p->nParentAlloc*2 + 5;
890             p->azParent = fossil_realloc(p->azParent,
891                                p->nParentAlloc*sizeof(char*));
892           }
893           i = p->nParent++;
894           p->azParent[i] = zUuid;
895         }
896         break;
897       }
898 
899       /*
900       **     Q (+|-)<uuid> ?<uuid>?
901       **
902       ** Specify one or a range of check-ins that are cherrypicked into
903       ** this check-in ("+") or backed out of this check-in ("-").
904       */
905       case 'Q': {
906         if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing hash on Q-card");
907         if( zUuid[0]!='+' && zUuid[0]!='-' ){
908           SYNTAX("Q-card does not begin with '+' or '-'");
909         }
910         if( !hname_validate(&zUuid[1], sz-1) ){
911           SYNTAX("invalid hash on Q-card");
912         }
913         n = p->nCherrypick;
914         p->nCherrypick++;
915         p->aCherrypick = fossil_realloc(p->aCherrypick,
916                                  p->nCherrypick*sizeof(p->aCherrypick[0]));
917         p->aCherrypick[n].zCPTarget = zUuid;
918         p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
919         if( zUuid && !hname_validate(zUuid,sz) ){
920           SYNTAX("invalid second hash on Q-card");
921         }
922         p->type = CFTYPE_MANIFEST;
923         break;
924       }
925 
926       /*
927       **     R <md5sum>
928       **
929       ** Specify the MD5 checksum over the name and content of all files
930       ** in the manifest.
931       */
932       case 'R': {
933         if( p->zRepoCksum!=0 ) SYNTAX("more than one R-card");
934         p->zRepoCksum = next_token(&x, &sz);
935         if( sz!=32 ) SYNTAX("wrong size cksum on R-card");
936         if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum");
937         p->type = CFTYPE_MANIFEST;
938         break;
939       }
940 
941       /*
942       **    T (+|*|-)<tagname> <uuid> ?<value>?
943       **
944       ** Create or cancel a tag or property.  The tagname is fossil-encoded.
945       ** The first character of the name must be either "+" to create a
946       ** singleton tag, "*" to create a propagating tag, or "-" to create
947       ** anti-tag that undoes a prior "+" or blocks propagation of of
948       ** a "*".
949       **
950       ** The tag is applied to <uuid>.  If <uuid> is "*" then the tag is
951       ** applied to the current manifest.  If <value> is provided then
952       ** the tag is really a property with the given value.
953       **
954       ** Tags are not allowed in clusters.  Multiple T lines are allowed.
955       */
956       case 'T': {
957         char *zName, *zValue;
958         zName = next_token(&x, 0);
959         if( zName==0 ) SYNTAX("missing name on T-card");
960         zUuid = next_token(&x, &sz);
961         if( zUuid==0 ) SYNTAX("missing artifact hash on T-card");
962         zValue = next_token(&x, 0);
963         if( zValue ) defossilize(zValue);
964         if( hname_validate(zUuid, sz) ){
965           /* A valid artifact hash */
966         }else if( sz==1 && zUuid[0]=='*' ){
967           zUuid = 0;
968           nSelfTag++;
969         }else{
970           SYNTAX("malformed artifact hash on T-card");
971         }
972         defossilize(zName);
973         if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
974           SYNTAX("T-card name does not begin with '-', '+', or '*'");
975         }
976         if( zName[0]=='+' ) nSimpleTag++;
977         if( validate16(&zName[1], strlen(&zName[1])) ){
978           /* Do not allow tags whose names look like a hash */
979           SYNTAX("T-card name looks like a hexadecimal hash");
980         }
981         if( p->nTag>=p->nTagAlloc ){
982           p->nTagAlloc = p->nTagAlloc*2 + 10;
983           p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
984         }
985         i = p->nTag++;
986         p->aTag[i].zName = zName;
987         p->aTag[i].zUuid = zUuid;
988         p->aTag[i].zValue = zValue;
989         if( i>0 ){
990           int c = fossil_strcmp(p->aTag[i-1].zName, zName);
991           if( c>0 || (c==0 && fossil_strcmp(p->aTag[i-1].zUuid, zUuid)>=0) ){
992             SYNTAX("T-card in the wrong order");
993           }
994         }
995         break;
996       }
997 
998       /*
999       **     U ?<login>?
1000       **
1001       ** Identify the user who created this control file by their
1002       ** login.  Only one U line is allowed.  Prohibited in clusters.
1003       ** If the user name is omitted, take that to be "anonymous".
1004       */
1005       case 'U': {
1006         if( p->zUser!=0 ) SYNTAX("more than one U-card");
1007         p->zUser = next_token(&x, 0);
1008         if( p->zUser==0 || p->zUser[0]==0 ){
1009           p->zUser = "anonymous";
1010         }else{
1011           defossilize(p->zUser);
1012         }
1013         break;
1014       }
1015 
1016       /*
1017       **     W <size>
1018       **
1019       ** The next <size> bytes of the file contain the text of the wiki
1020       ** page.  There is always an extra \n before the start of the next
1021       ** record.
1022       */
1023       case 'W': {
1024         char *zSize;
1025         unsigned size, oldsize, c;
1026         Blob wiki;
1027         zSize = next_token(&x, 0);
1028         if( zSize==0 ) SYNTAX("missing size on W-card");
1029         if( x.atEol==0 ) SYNTAX("no content after W-card");
1030         for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){
1031            size = oldsize*10 + c - '0';
1032            if( size<oldsize ) SYNTAX("size overflow on W-card");
1033            oldsize = size;
1034         }
1035         if( p->zWiki!=0 ) SYNTAX("more than one W-card");
1036         blob_zero(&wiki);
1037         if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card");
1038         p->zWiki = x.z;
1039         x.z += size;
1040         if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
1041         x.z[0] = 0;
1042         x.z++;
1043         break;
1044       }
1045 
1046 
1047       /*
1048       **     Z <md5sum>
1049       **
1050       ** MD5 checksum on this control file.  The checksum is over all
1051       ** lines (other than PGP-signature lines) prior to the current
1052       ** line.  This must be the last record.
1053       **
1054       ** This card is required for all control file types except for
1055       ** Manifest.  It is not required for manifest only for historical
1056       ** compatibility reasons.
1057       */
1058       case 'Z': {
1059         zUuid = next_token(&x, &sz);
1060         if( sz!=32 ) SYNTAX("wrong size for Z-card cksum");
1061         if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum");
1062         break;
1063       }
1064       default: {
1065         SYNTAX("unrecognized card");
1066       }
1067     }
1068   }
1069   if( x.z<x.zEnd ) SYNTAX("extra characters at end of card");
1070 
1071   /* If the artifact type has not yet been determined, then compute
1072   ** it now. */
1073   if( p->type==0 ){
1074     if( p->zComment!=0 || p->nFile>0 || p->nParent>0 ){
1075       p->type = CFTYPE_MANIFEST;
1076     }else{
1077       p->type = CFTYPE_CONTROL;
1078     }
1079   }
1080 
1081   /* Verify that no disallowed cards are present for this artifact type */
1082   m = manifest_card_mask(manifestCardTypes[p->type-1].zAllowed);
1083   if( seenCard & ~m ){
1084     sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card in %s",
1085                      maskToType(seenCard & ~m),
1086                      azNameOfMType[p->type-1]);
1087     zErr = zErrBuf;
1088     goto manifest_syntax_error;
1089   }
1090 
1091   /* Verify that all required cards are present for this artifact type */
1092   m = manifest_card_mask(manifestCardTypes[p->type-1].zRequired);
1093   if( ~seenCard & m ){
1094     sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card missing in %s",
1095                      maskToType(~seenCard & m),
1096                      azNameOfMType[p->type-1]);
1097     zErr = zErrBuf;
1098     goto manifest_syntax_error;
1099   }
1100 
1101   /* Additional checks based on artifact type */
1102   switch( p->type ){
1103     case CFTYPE_CONTROL: {
1104       if( nSelfTag ) SYNTAX("self-referential T-card in control artifact");
1105       break;
1106     }
1107     case CFTYPE_EVENT: {
1108       if( p->nTag!=nSelfTag ){
1109         SYNTAX("non-self-referential T-card in technote");
1110       }
1111       if( p->nTag!=nSimpleTag ){
1112         SYNTAX("T-card with '*' or '-' in technote");
1113       }
1114       break;
1115     }
1116     case CFTYPE_FORUM: {
1117       if( p->zThreadTitle && p->zInReplyTo ){
1118         SYNTAX("cannot have I-card and H-card in a forum post");
1119       }
1120       if( p->nParent>1 ) SYNTAX("too many arguments to P-card");
1121       break;
1122     }
1123   }
1124 
1125   md5sum_init();
1126   if( !isRepeat ) g.parseCnt[p->type]++;
1127   return p;
1128 
1129 manifest_syntax_error:
1130   {
1131     char *zUuid = rid_to_uuid(rid);
1132     if( zUuid ){
1133       if(pErr!=0){
1134         blob_appendf(pErr, "artifact [%s] ", zUuid);
1135       }
1136       fossil_free(zUuid);
1137     }
1138   }
1139   if(pErr!=0){
1140     if( zErr ){
1141       blob_appendf(pErr, "line %d: %s", lineNo, zErr);
1142     }else{
1143       blob_appendf(pErr, "unknown error on line %d", lineNo);
1144     }
1145   }
1146   md5sum_init();
1147   manifest_destroy(p);
1148   return 0;
1149 }
1150 
1151 /*
1152 ** Get a manifest given the rid for the control artifact.  Return
1153 ** a pointer to the manifest on success or NULL if there is a failure.
1154 */
manifest_get(int rid,int cfType,Blob * pErr)1155 Manifest *manifest_get(int rid, int cfType, Blob *pErr){
1156   Blob content;
1157   Manifest *p;
1158   if( !rid ) return 0;
1159   p = manifest_cache_find(rid);
1160   if( p ){
1161     if( cfType!=CFTYPE_ANY && cfType!=p->type ){
1162       manifest_cache_insert(p);
1163       p = 0;
1164     }
1165     return p;
1166   }
1167   content_get(rid, &content);
1168   p = manifest_parse(&content, rid, pErr);
1169   if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
1170     manifest_destroy(p);
1171     p = 0;
1172   }
1173   return p;
1174 }
1175 
1176 /*
1177 ** Given a check-in name, load and parse the manifest for that check-in.
1178 ** Throw a fatal error if anything goes wrong.
1179 */
manifest_get_by_name(const char * zName,int * pRid)1180 Manifest *manifest_get_by_name(const char *zName, int *pRid){
1181   int rid;
1182   Manifest *p;
1183 
1184   rid = name_to_typed_rid(zName, "ci");
1185   if( !is_a_version(rid) ){
1186     fossil_fatal("no such check-in: %s", zName);
1187   }
1188   if( pRid ) *pRid = rid;
1189   p = manifest_get(rid, CFTYPE_MANIFEST, 0);
1190   if( p==0 ){
1191     fossil_fatal("cannot parse manifest for check-in: %s", zName);
1192   }
1193   return p;
1194 }
1195 
1196 /*
1197 ** The input blob is text that may or may not be a valid Fossil
1198 ** control artifact of some kind.  This routine returns true if
1199 ** the input is a well-formed control artifact and false if it
1200 ** is not.
1201 **
1202 ** This routine is optimized to return false quickly and with minimal
1203 ** work in the common case where the input is some random file.
1204 */
manifest_is_well_formed(const char * zIn,int nIn)1205 int manifest_is_well_formed(const char *zIn, int nIn){
1206   int i;
1207   int iRes;
1208   Manifest *pManifest;
1209   Blob copy, errmsg;
1210   remove_pgp_signature(&zIn, &nIn);
1211 
1212   /* Check to see that the file begins with a "card" */
1213   if( nIn<3 ) return 0;
1214   if( zIn[0]<'A' || zIn[0]>'M' || zIn[1]!=' ' ) return 0;
1215 
1216   /* Check to see that the first card is followed by one more card */
1217   for(i=2; i<nIn && zIn[i]!='\n'; i++){}
1218   if( i>=nIn-3 ) return 0;
1219   i++;
1220   if( !fossil_isupper(zIn[i]) || zIn[i]<zIn[0] || zIn[i+1]!=' ' ) return 0;
1221 
1222   /* The checks above will eliminate most random inputs.  If these
1223   ** quick checks pass, then we could be dealing with a well-formed
1224   ** control artifact.  Make a copy, and run it through the official
1225   ** artifact parser.  This is the slow path, but it is rarely taken.
1226   */
1227   blob_init(&copy, 0, 0);
1228   blob_init(&errmsg, 0, 0);
1229   blob_append(&copy, zIn, nIn);
1230   pManifest = manifest_parse(&copy, 0, &errmsg);
1231   iRes = pManifest!=0;
1232   manifest_destroy(pManifest);
1233   blob_reset(&errmsg);
1234   return iRes;
1235 }
1236 
1237 /*
1238 ** COMMAND: test-parse-manifest
1239 **
1240 ** Usage: %fossil test-parse-manifest FILENAME ?N?
1241 **
1242 ** Parse the manifest(s) given on the command-line and report any
1243 ** errors.  If the N argument is given, run the parsing N times.
1244 */
manifest_test_parse_cmd(void)1245 void manifest_test_parse_cmd(void){
1246   Manifest *p;
1247   Blob b;
1248   int i;
1249   int n = 1;
1250   int isWF;
1251   db_find_and_open_repository(OPEN_SUBSTITUTE|OPEN_OK_NOT_FOUND,0);
1252   verify_all_options();
1253   if( g.argc!=3 && g.argc!=4 ){
1254     usage("FILENAME");
1255   }
1256   blob_read_from_file(&b, g.argv[2], ExtFILE);
1257   if( g.argc>3 ) n = atoi(g.argv[3]);
1258   isWF = manifest_is_well_formed(blob_buffer(&b), blob_size(&b));
1259   fossil_print("manifest_is_well_formed() reports the input %s\n",
1260        isWF ? "is ok" : "contains errors");
1261   for(i=0; i<n; i++){
1262     Blob b2;
1263     Blob err;
1264     blob_copy(&b2, &b);
1265     blob_zero(&err);
1266     p = manifest_parse(&b2, 0, &err);
1267     if( p==0 ){
1268       fossil_print("ERROR: %s\n", blob_str(&err));
1269     }else if( i==0 || (n==2 && i==1) ){
1270       fossil_print("manifest_parse() worked\n");
1271     }else if( i==n-1 ){
1272       fossil_print("manifest_parse() worked %d more times\n", n-1);
1273     }
1274     if( (p==0 && isWF) || (p!=0 && !isWF) ){
1275       fossil_print("ERROR: manifest_is_well_formed() and "
1276                    "manifest_parse() disagree!\n");
1277     }
1278     blob_reset(&err);
1279     manifest_destroy(p);
1280   }
1281   blob_reset(&b);
1282 }
1283 
1284 /*
1285 ** COMMAND: test-parse-all-blobs
1286 **
1287 ** Usage: %fossil test-parse-all-blobs ?OPTIONS?
1288 **
1289 ** Parse all entries in the BLOB table that are believed to be non-data
1290 ** artifacts and report any errors.  Run this test command on historical
1291 ** repositories after making any changes to the manifest_parse()
1292 ** implementation to confirm that the changes did not break anything.
1293 **
1294 ** Options:
1295 **
1296 **   --limit N            Parse no more than N artifacts before stopping
1297 **   --wellformed         Use all BLOB table entries as input, not just
1298 **                        those entries that are believed to be valid
1299 **                        artifacts, and verify that the result the
1300 **                        manifest_is_well_formed() agrees with the
1301 **                        result of manifest_parse().
1302 */
manifest_test_parse_all_blobs_cmd(void)1303 void manifest_test_parse_all_blobs_cmd(void){
1304   Manifest *p;
1305   Blob err;
1306   Stmt q;
1307   int nTest = 0;
1308   int nErr = 0;
1309   int N = 1000000000;
1310   int bWellFormed;
1311   const char *z;
1312   db_find_and_open_repository(0, 0);
1313   z = find_option("limit", 0, 1);
1314   if( z ) N = atoi(z);
1315   bWellFormed = find_option("wellformed",0,0)!=0;
1316   verify_all_options();
1317   if( bWellFormed ){
1318     db_prepare(&q, "SELECT rid FROM blob ORDER BY rid");
1319   }else{
1320     db_prepare(&q, "SELECT DISTINCT objid FROM EVENT ORDER BY objid");
1321   }
1322   while( (N--)>0 && db_step(&q)==SQLITE_ROW ){
1323     int id = db_column_int(&q,0);
1324     fossil_print("Checking %d       \r", id);
1325     nTest++;
1326     fflush(stdout);
1327     blob_init(&err, 0, 0);
1328     if( bWellFormed ){
1329       Blob content;
1330       int isWF;
1331       content_get(id, &content);
1332       isWF = manifest_is_well_formed(blob_buffer(&content),blob_size(&content));
1333       p = manifest_parse(&content, id, &err);
1334       if( isWF && p==0 ){
1335         fossil_print("%d ERROR: manifest_is_well_formed() reported true "
1336                      "but manifest_parse() reports an error: %s\n",
1337                      id, blob_str(&err));
1338         nErr++;
1339       }else if( !isWF && p!=0 ){
1340         fossil_print("%d ERROR: manifest_is_well_formed() reported false "
1341                      "but manifest_parse() found nothing wrong.\n", id);
1342         nErr++;
1343       }
1344     }else{
1345       p = manifest_get(id, CFTYPE_ANY, &err);
1346       if( p==0 ){
1347         fossil_print("%d ERROR: %s\n", id, blob_str(&err));
1348         nErr++;
1349       }
1350     }
1351     blob_reset(&err);
1352     manifest_destroy(p);
1353   }
1354   db_finalize(&q);
1355   fossil_print("%d tests with %d errors\n", nTest, nErr);
1356 }
1357 
1358 /*
1359 ** Fetch the baseline associated with the delta-manifest p.
1360 ** Return 0 on success.  If unable to parse the baseline,
1361 ** throw an error.  If the baseline is a manifest, throw an
1362 ** error if throwError is true, or record that p is an orphan
1363 ** and return 1 if throwError is false.
1364 */
fetch_baseline(Manifest * p,int throwError)1365 static int fetch_baseline(Manifest *p, int throwError){
1366   if( p->zBaseline!=0 && p->pBaseline==0 ){
1367     int rid = uuid_to_rid(p->zBaseline, 1);
1368     p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST, 0);
1369     if( p->pBaseline==0 ){
1370       if( !throwError ){
1371         db_multi_exec(
1372            "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)",
1373            p->rid, rid
1374         );
1375         return 1;
1376       }
1377       fossil_fatal("cannot access baseline manifest %S", p->zBaseline);
1378     }
1379   }
1380   return 0;
1381 }
1382 
1383 /*
1384 ** Rewind a manifest-file iterator back to the beginning of the manifest.
1385 */
manifest_file_rewind(Manifest * p)1386 void manifest_file_rewind(Manifest *p){
1387   p->iFile = 0;
1388   fetch_baseline(p, 1);
1389   if( p->pBaseline ){
1390     p->pBaseline->iFile = 0;
1391   }
1392 }
1393 
1394 /*
1395 ** Advance to the next manifest-file.
1396 **
1397 ** Return NULL for end-of-records or if there is an error.  If an error
1398 ** occurs and pErr!=0 then store 1 in *pErr.
1399 */
manifest_file_next(Manifest * p,int * pErr)1400 ManifestFile *manifest_file_next(
1401   Manifest *p,
1402   int *pErr
1403 ){
1404   ManifestFile *pOut = 0;
1405   if( pErr ) *pErr = 0;
1406   if( p->pBaseline==0 ){
1407     /* Manifest p is a baseline-manifest.  Just scan down the list
1408     ** of files. */
1409     if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
1410   }else{
1411     /* Manifest p is a delta-manifest.  Scan the baseline but amend the
1412     ** file list in the baseline with changes described by p.
1413     */
1414     Manifest *pB = p->pBaseline;
1415     int cmp;
1416     while(1){
1417       if( pB->iFile>=pB->nFile ){
1418         /* We have used all entries out of the baseline.  Return the next
1419         ** entry from the delta. */
1420         if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++];
1421         break;
1422       }else if( p->iFile>=p->nFile ){
1423         /* We have used all entries from the delta.  Return the next
1424         ** entry from the baseline. */
1425         if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++];
1426         break;
1427       }else if( (cmp = fossil_strcmp(pB->aFile[pB->iFile].zName,
1428                               p->aFile[p->iFile].zName)) < 0 ){
1429         /* The next baseline entry comes before the next delta entry.
1430         ** So return the baseline entry. */
1431         pOut = &pB->aFile[pB->iFile++];
1432         break;
1433       }else if( cmp>0 ){
1434         /* The next delta entry comes before the next baseline
1435         ** entry so return the delta entry */
1436         pOut = &p->aFile[p->iFile++];
1437         break;
1438       }else if( p->aFile[p->iFile].zUuid ){
1439         /* The next delta entry is a replacement for the next baseline
1440         ** entry.  Skip the baseline entry and return the delta entry */
1441         pB->iFile++;
1442         pOut = &p->aFile[p->iFile++];
1443         break;
1444       }else{
1445         /* The next delta entry is a delete of the next baseline
1446         ** entry.  Skip them both.  Repeat the loop to find the next
1447         ** non-delete entry. */
1448         pB->iFile++;
1449         p->iFile++;
1450         continue;
1451       }
1452     }
1453   }
1454   return pOut;
1455 }
1456 
1457 /*
1458 ** Translate a filename into a filename-id (fnid).  Create a new fnid
1459 ** if no previously exists.
1460 */
filename_to_fnid(const char * zFilename)1461 static int filename_to_fnid(const char *zFilename){
1462   static Stmt q1, s1;
1463   int fnid;
1464   db_static_prepare(&q1, "SELECT fnid FROM filename WHERE name=:fn");
1465   db_bind_text(&q1, ":fn", zFilename);
1466   fnid = 0;
1467   if( db_step(&q1)==SQLITE_ROW ){
1468     fnid = db_column_int(&q1, 0);
1469   }
1470   db_reset(&q1);
1471   if( fnid==0 ){
1472     db_static_prepare(&s1, "INSERT INTO filename(name) VALUES(:fn)");
1473     db_bind_text(&s1, ":fn", zFilename);
1474     db_exec(&s1);
1475     fnid = db_last_insert_rowid();
1476   }
1477   return fnid;
1478 }
1479 
1480 /*
1481 ** Compute an appropriate mlink.mperm integer for the permission string
1482 ** of a file.
1483 */
manifest_file_mperm(const ManifestFile * pFile)1484 int manifest_file_mperm(const ManifestFile *pFile){
1485   int mperm = PERM_REG;
1486   if( pFile && pFile->zPerm){
1487     if( strstr(pFile->zPerm,"x")!=0 ){
1488       mperm = PERM_EXE;
1489     }else if( strstr(pFile->zPerm,"l")!=0 ){
1490       mperm = PERM_LNK;
1491     }
1492   }
1493   return mperm;
1494 }
1495 
1496 /*
1497 ** Add a single entry to the mlink table.  Also add the filename to
1498 ** the filename table if it is not there already.
1499 **
1500 ** An mlink entry is always created if isPrimary is true.  But if
1501 ** isPrimary is false (meaning that pmid is a merge parent of mid)
1502 ** then the mlink entry is only created if there is already an mlink
1503 ** from primary parent for the same file.
1504 */
add_one_mlink(int pmid,const char * zFromUuid,int mid,const char * zToUuid,const char * zFilename,const char * zPrior,int isPublic,int isPrimary,int mperm)1505 static void add_one_mlink(
1506   int pmid,                 /* The parent manifest */
1507   const char *zFromUuid,    /* Artifact hash for content in parent */
1508   int mid,                  /* The record ID of the manifest */
1509   const char *zToUuid,      /* artifact hash for content in child */
1510   const char *zFilename,    /* Filename */
1511   const char *zPrior,       /* Previous filename. NULL if unchanged */
1512   int isPublic,             /* True if mid is not a private manifest */
1513   int isPrimary,            /* pmid is the primary parent of mid */
1514   int mperm                 /* 1: exec, 2: symlink */
1515 ){
1516   int fnid, pfnid, pid, fid;
1517   int doInsert;
1518   static Stmt s1, s2;
1519 
1520   fnid = filename_to_fnid(zFilename);
1521   if( zPrior==0 ){
1522     pfnid = 0;
1523   }else{
1524     pfnid = filename_to_fnid(zPrior);
1525   }
1526   if( zFromUuid==0 || zFromUuid[0]==0 ){
1527     pid = 0;
1528   }else{
1529     pid = uuid_to_rid(zFromUuid, 1);
1530   }
1531   if( zToUuid==0 || zToUuid[0]==0 ){
1532     fid = 0;
1533   }else{
1534     fid = uuid_to_rid(zToUuid, 1);
1535     if( isPublic ) content_make_public(fid);
1536   }
1537   if( isPrimary ){
1538     doInsert = 1;
1539   }else{
1540     db_static_prepare(&s2,
1541       "SELECT 1 FROM mlink WHERE mid=:m AND fnid=:n AND NOT isaux"
1542     );
1543     db_bind_int(&s2, ":m", mid);
1544     db_bind_int(&s2, ":n", fnid);
1545     doInsert = db_step(&s2)==SQLITE_ROW;
1546     db_reset(&s2);
1547   }
1548   if( doInsert ){
1549     db_static_prepare(&s1,
1550       "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)"
1551       "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)"
1552     );
1553     db_bind_int(&s1, ":m", mid);
1554     db_bind_int(&s1, ":f", fid);
1555     db_bind_int(&s1, ":pm", pmid);
1556     db_bind_int(&s1, ":p", pid);
1557     db_bind_int(&s1, ":n", fnid);
1558     db_bind_int(&s1, ":pfn", pfnid);
1559     db_bind_int(&s1, ":mp", mperm);
1560     db_bind_int(&s1, ":isaux", isPrimary==0);
1561     db_exec(&s1);
1562   }
1563   if( pid && fid ){
1564     content_deltify(pid, &fid, 1, 0);
1565   }
1566 }
1567 
1568 /*
1569 ** Do a binary search to find a file in the p->aFile[] array.
1570 **
1571 ** As an optimization, guess that the file we seek is at index p->iFile.
1572 ** That will usually be the case.  If it is not found there, then do the
1573 ** actual binary search.
1574 **
1575 ** Update p->iFile to be the index of the file that is found.
1576 */
manifest_file_seek_base(Manifest * p,const char * zName,int bBest)1577 static ManifestFile *manifest_file_seek_base(
1578   Manifest *p,             /* Manifest to search */
1579   const char *zName,       /* Name of the file we are looking for */
1580   int bBest                /* 0: exact match only.  1: closest match */
1581 ){
1582   int lwr, upr;
1583   int c;
1584   int i;
1585   if( p->aFile==0 ){
1586     return 0;
1587   }
1588   lwr = 0;
1589   upr = p->nFile - 1;
1590   if( p->iFile>=lwr && p->iFile<upr ){
1591     c = fossil_strcmp(p->aFile[p->iFile+1].zName, zName);
1592     if( c==0 ){
1593       return &p->aFile[++p->iFile];
1594     }else if( c>0 ){
1595       upr = p->iFile;
1596     }else{
1597       lwr = p->iFile+1;
1598     }
1599   }
1600   while( lwr<=upr ){
1601     i = (lwr+upr)/2;
1602     c = fossil_strcmp(p->aFile[i].zName, zName);
1603     if( c<0 ){
1604       lwr = i+1;
1605     }else if( c>0 ){
1606       upr = i-1;
1607     }else{
1608       p->iFile = i;
1609       return &p->aFile[i];
1610     }
1611   }
1612   if( bBest ){
1613     if( lwr>=p->nFile ) lwr = p->nFile-1;
1614     i = (int)strlen(zName);
1615     if( strncmp(zName, p->aFile[lwr].zName, i)==0 ) return &p->aFile[lwr];
1616   }
1617   return 0;
1618 }
1619 
1620 /*
1621 ** Locate a file named zName in the aFile[] array of the given manifest.
1622 ** Return a pointer to the appropriate ManifestFile object.  Return NULL
1623 ** if not found.
1624 **
1625 ** This routine works even if p is a delta-manifest.  The pointer
1626 ** returned might be to the baseline.
1627 **
1628 ** We assume that filenames are in sorted order and use a binary search.
1629 */
manifest_file_seek(Manifest * p,const char * zName,int bBest)1630 ManifestFile *manifest_file_seek(Manifest *p, const char *zName, int bBest){
1631   ManifestFile *pFile;
1632 
1633   pFile = manifest_file_seek_base(p, zName, p->zBaseline ? 0 : bBest);
1634   if( pFile && pFile->zUuid==0 ) return 0;
1635   if( pFile==0 && p->zBaseline ){
1636     fetch_baseline(p, 1);
1637     pFile = manifest_file_seek_base(p->pBaseline, zName,bBest);
1638   }
1639   return pFile;
1640 }
1641 
1642 /*
1643 ** Look for a file in a manifest, taking the case-sensitive option
1644 ** into account.  If case-sensitive is off, then files in any case
1645 ** will match.
1646 */
manifest_file_find(Manifest * p,const char * zName)1647 ManifestFile *manifest_file_find(Manifest *p, const char *zName){
1648   int i;
1649   Manifest *pBase;
1650   if( filenames_are_case_sensitive() ){
1651     return manifest_file_seek(p, zName, 0);
1652   }
1653   for(i=0; i<p->nFile; i++){
1654     if( fossil_stricmp(zName, p->aFile[i].zName)==0 ){
1655       return &p->aFile[i];
1656     }
1657   }
1658   if( p->zBaseline==0 ) return 0;
1659   fetch_baseline(p, 1);
1660   pBase = p->pBaseline;
1661   if( pBase==0 ) return 0;
1662   for(i=0; i<pBase->nFile; i++){
1663     if( fossil_stricmp(zName, pBase->aFile[i].zName)==0 ){
1664       return &pBase->aFile[i];
1665     }
1666   }
1667   return 0;
1668 }
1669 
1670 /*
1671 ** Add mlink table entries associated with manifest cid, pChild.  The
1672 ** parent manifest is pid, pParent.  One of either pChild or pParent
1673 ** will be NULL and it will be computed based on cid/pid.
1674 **
1675 ** A single mlink entry is added for every file that changed content,
1676 ** name, and/or permissions going from pid to cid.
1677 **
1678 ** Deleted files have mlink.fid=0.
1679 ** Added files have mlink.pid=0.
1680 ** File added by merge have mlink.pid=-1
1681 ** Edited files have both mlink.pid!=0 and mlink.fid!=0
1682 **
1683 ** Many mlink entries for merge parents will only be added if another mlink
1684 ** entry already exists for the same file from the primary parent.  Therefore,
1685 ** to ensure that all merge-parent mlink entries are properly created:
1686 **
1687 **    (1) Make this routine a no-op if pParent is a merge parent and the
1688 **        primary parent is a phantom.
1689 **    (2) Invoke this routine recursively for merge-parents if pParent is the
1690 **        primary parent.
1691 */
add_mlink(int pmid,Manifest * pParent,int mid,Manifest * pChild,int isPrim)1692 static void add_mlink(
1693   int pmid, Manifest *pParent,  /* Parent check-in */
1694   int mid,  Manifest *pChild,   /* The child check-in */
1695   int isPrim                    /* TRUE if pmid is the primary parent of mid */
1696 ){
1697   Blob otherContent;
1698   int otherRid;
1699   int i, rc;
1700   ManifestFile *pChildFile, *pParentFile;
1701   Manifest **ppOther;
1702   static Stmt eq;
1703   int isPublic;                /* True if pChild is non-private */
1704 
1705   /* If mlink table entires are already exist for the pmid-to-mid transition,
1706   ** then abort early doing no work.
1707   */
1708   db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid");
1709   db_bind_int(&eq, ":mid", mid);
1710   db_bind_int(&eq, ":pmid", pmid);
1711   rc = db_step(&eq);
1712   db_reset(&eq);
1713   if( rc==SQLITE_ROW ) return;
1714 
1715   /* Compute the value of the missing pParent or pChild parameter.
1716   ** Fetch the baseline check-ins for both.
1717   */
1718   assert( pParent==0 || pChild==0 );
1719   if( pParent==0 ){
1720     ppOther = &pParent;
1721     otherRid = pmid;
1722   }else{
1723     ppOther = &pChild;
1724     otherRid = mid;
1725   }
1726   if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1727     content_get(otherRid, &otherContent);
1728     if( blob_size(&otherContent)==0 ) return;
1729     *ppOther = manifest_parse(&otherContent, otherRid, 0);
1730     if( *ppOther==0 ) return;
1731   }
1732   if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
1733     manifest_destroy(*ppOther);
1734     return;
1735   }
1736   isPublic = !content_is_private(mid);
1737 
1738   /* If pParent is not the primary parent of pChild, and the primary
1739   ** parent of pChild is a phantom, then abort this routine without
1740   ** doing any work.  The mlink entries will be computed when the
1741   ** primary parent dephantomizes.
1742   */
1743   if( !isPrim && otherRid==mid
1744    && !db_exists("SELECT 1 FROM blob WHERE uuid=%Q AND size>0",
1745                  pChild->azParent[0])
1746   ){
1747     manifest_cache_insert(*ppOther);
1748     return;
1749   }
1750 
1751   /* Try to make the parent manifest a delta from the child, if that
1752   ** is an appropriate thing to do.  For a new baseline, make the
1753   ** previous baseline a delta from the current baseline.
1754   */
1755   if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1756     content_deltify(pmid, &mid, 1, 0);
1757   }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
1758     content_deltify(pParent->pBaseline->rid, &mid, 1, 0);
1759   }
1760 
1761   /* Remember all children less than a few seconds younger than their parent,
1762   ** as we might want to fudge the times for those children.
1763   */
1764   if( pChild->rDate<pParent->rDate+AGE_FUDGE_WINDOW
1765       && manifest_crosslink_busy
1766   ){
1767     db_multi_exec(
1768        "INSERT OR REPLACE INTO time_fudge VALUES(%d, %.17g, %d, %.17g);",
1769        pParent->rid, pParent->rDate, pChild->rid, pChild->rDate
1770     );
1771   }
1772 
1773   /* First look at all files in pChild, ignoring its baseline.  This
1774   ** is where most of the changes will be found.
1775   */
1776   for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){
1777     int mperm = manifest_file_mperm(pChildFile);
1778     if( pChildFile->zPrior ){
1779        pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0);
1780        if( pParentFile ){
1781          /* File with name change */
1782          add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1783                        pChildFile->zName, pChildFile->zPrior,
1784                        isPublic, isPrim, mperm);
1785        }else{
1786          /* File name changed, but the old name is not found in the parent!
1787          ** Treat this like a new file. */
1788          add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1789                        isPublic, isPrim, mperm);
1790        }
1791     }else{
1792        pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0);
1793        if( pParentFile==0 ){
1794          if( pChildFile->zUuid ){
1795            /* A new file */
1796            add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1797                          isPublic, isPrim, mperm);
1798          }
1799        }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0
1800               || manifest_file_mperm(pParentFile)!=mperm ){
1801          /* Changes in file content or permissions */
1802          add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1803                        pChildFile->zName, 0, isPublic, isPrim, mperm);
1804        }
1805     }
1806   }
1807   if( pParent->zBaseline && pChild->zBaseline ){
1808     /* Both parent and child are delta manifests.  Look for files that
1809     ** are deleted or modified in the parent but which reappear or revert
1810     ** to baseline in the child and show such files as being added or changed
1811     ** in the child. */
1812     for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1813       if( pParentFile->zUuid ){
1814         pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0);
1815         if( pChildFile==0 ){
1816           /* The child file reverts to baseline.  Show this as a change */
1817           pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1818           if( pChildFile ){
1819             add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1820                           pChildFile->zName, 0, isPublic, isPrim,
1821                           manifest_file_mperm(pChildFile));
1822           }
1823         }
1824       }else{
1825         pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1826         if( pChildFile ){
1827           /* File resurrected in the child after having been deleted in
1828           ** the parent.  Show this as an added file. */
1829           add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1830                         isPublic, isPrim, manifest_file_mperm(pChildFile));
1831         }
1832       }
1833     }
1834   }else if( pChild->zBaseline==0 ){
1835     /* pChild is a baseline.  Look for files that are present in pParent
1836     ** but are missing from pChild and mark them as having been deleted. */
1837     manifest_file_rewind(pParent);
1838     while( (pParentFile = manifest_file_next(pParent,0))!=0 ){
1839       pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1840       if( pChildFile==0 && pParentFile->zUuid!=0 ){
1841         add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0,
1842                       isPublic, isPrim, 0);
1843       }
1844     }
1845   }
1846   manifest_cache_insert(*ppOther);
1847 
1848   /* If pParent is the primary parent of pChild, also run this analysis
1849   ** for all merge parents of pChild
1850   */
1851   if( isPrim ){
1852     for(i=1; i<pChild->nParent; i++){
1853       pmid = uuid_to_rid(pChild->azParent[i], 0);
1854       if( pmid<=0 ) continue;
1855       add_mlink(pmid, 0, mid, pChild, 0);
1856     }
1857     for(i=0; i<pChild->nCherrypick; i++){
1858       if( pChild->aCherrypick[i].zCPTarget[0]=='+'
1859        && (pmid = uuid_to_rid(pChild->aCherrypick[i].zCPTarget+1, 0))>0
1860       ){
1861         add_mlink(pmid, 0, mid, pChild, 0);
1862       }
1863     }
1864   }
1865 }
1866 
1867 /*
1868 ** For a check-in with RID "rid" that has nParent parent check-ins given
1869 ** by the hashes in azParent[], create all appropriate plink and mlink table
1870 ** entries.
1871 **
1872 ** The primary parent is the first hash on the azParent[] list.
1873 **
1874 ** Return the RID of the primary parent.
1875 */
manifest_add_checkin_linkages(int rid,Manifest * p,int nParent,char * const * azParent)1876 static int manifest_add_checkin_linkages(
1877   int rid,                   /* The RID of the check-in */
1878   Manifest *p,               /* Manifest for this check-in */
1879   int nParent,               /* Number of parents for this check-in */
1880   char * const * azParent    /* hashes for each parent */
1881 ){
1882   int i;
1883   int parentid = 0;
1884   char zBaseId[30];    /* Baseline manifest RID for deltas.  "NULL" otherwise */
1885   Stmt q;
1886   int nLink;
1887 
1888   if( p->zBaseline ){
1889      sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
1890                       uuid_to_rid(p->zBaseline,1));
1891   }else{
1892      sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
1893   }
1894   for(i=0; i<nParent; i++){
1895     int pid = uuid_to_rid(azParent[i], 1);
1896     db_multi_exec(
1897        "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
1898         "VALUES(%d, %d, %d, %.17g, %s)",
1899        pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
1900     if( i==0 ) parentid = pid;
1901   }
1902   add_mlink(parentid, 0, rid, p, 1);
1903   nLink = nParent;
1904   for(i=0; i<p->nCherrypick; i++){
1905     if( p->aCherrypick[i].zCPTarget[0]=='+' ) nLink++;
1906   }
1907   if( nLink>1 ){
1908     /* Change MLINK.PID from 0 to -1 for files that are added by merge. */
1909     db_multi_exec(
1910       "UPDATE mlink SET pid=-1"
1911       " WHERE mid=%d"
1912       "   AND pid=0"
1913       "   AND fnid IN "
1914       "  (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid"
1915       "    HAVING count(*)<%d)",
1916       rid, rid, nLink
1917     );
1918   }
1919   db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid);
1920   while( db_step(&q)==SQLITE_ROW ){
1921     int cid = db_column_int(&q, 0);
1922     int isprim = db_column_int(&q, 1);
1923     add_mlink(rid, p, cid, 0, isprim);
1924   }
1925   db_finalize(&q);
1926   if( nParent==0 ){
1927     /* For root files (files without parents) add mlink entries
1928     ** showing all content as new. */
1929     int isPublic = !content_is_private(rid);
1930     for(i=0; i<p->nFile; i++){
1931       add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1932                     isPublic, 1, manifest_file_mperm(&p->aFile[i]));
1933     }
1934   }
1935   return parentid;
1936 }
1937 
1938 /*
1939 ** There exists a "parent" tag against checkin rid that has value zValue.
1940 ** If value is well-formed (meaning that it is a list of hashes), then use
1941 ** zValue to reparent check-in rid.
1942 */
manifest_reparent_checkin(int rid,const char * zValue)1943 void manifest_reparent_checkin(int rid, const char *zValue){
1944   int nParent = 0;
1945   char *zCopy = 0;
1946   char **azParent = 0;
1947   Manifest *p = 0;
1948   int i, j;
1949   int n = (int)strlen(zValue);
1950   int mxParent = (n+1)/(HNAME_MIN+1);
1951 
1952   if( mxParent<1 ) return;
1953   zCopy = fossil_strdup(zValue);
1954   azParent = fossil_malloc( sizeof(azParent[0])*mxParent );
1955   for(nParent=0, i=0; zCopy[i]; i++){
1956     char *z = &zCopy[i];
1957     azParent[nParent++] = z;
1958     if( nParent>mxParent ) goto reparent_abort;
1959     for(j=HNAME_MIN; z[j]>' '; j++){}
1960     if( !hname_validate(z, j) ) goto reparent_abort;
1961     if( z[j]==0 ) break;
1962     z[j] = 0;
1963     i += j;
1964   }
1965   p = manifest_get(rid, CFTYPE_MANIFEST, 0);
1966   if( p!=0 ){
1967     db_multi_exec(
1968        "DELETE FROM plink WHERE cid=%d;"
1969        "DELETE FROM mlink WHERE mid=%d;",
1970        rid, rid
1971     );
1972     manifest_add_checkin_linkages(rid,p,nParent,azParent);
1973     manifest_destroy(p);
1974   }
1975 reparent_abort:
1976   fossil_free(azParent);
1977   fossil_free(zCopy);
1978 }
1979 
1980 /*
1981 ** Setup to do multiple manifest_crosslink() calls.
1982 **
1983 ** This routine creates TEMP tables for holding information for
1984 ** processing that must be deferred until all artifacts have been
1985 ** seen at least once.  The deferred processing is accomplished
1986 ** by the call to manifest_crosslink_end().
1987 */
manifest_crosslink_begin(void)1988 void manifest_crosslink_begin(void){
1989   assert( manifest_crosslink_busy==0 );
1990   manifest_crosslink_busy = 1;
1991   manifest_create_event_triggers();
1992   db_begin_transaction();
1993   db_multi_exec(
1994      "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;"
1995      "CREATE TEMP TABLE time_fudge("
1996      "  mid INTEGER PRIMARY KEY,"    /* The rid of a manifest */
1997      "  m1 REAL,"                    /* The timestamp on mid */
1998      "  cid INTEGER,"                /* A child or mid */
1999      "  m2 REAL"                     /* Timestamp on the child */
2000      ");"
2001   );
2002 }
2003 
2004 /*
2005 ** Add a new entry to the pending_xlink table.
2006 */
add_pending_crosslink(char cType,const char * zId)2007 static void add_pending_crosslink(char cType, const char *zId){
2008   assert( manifest_crosslink_busy==1 );
2009   db_multi_exec(
2010     "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')",
2011     cType, zId
2012   );
2013 }
2014 
2015 #if INTERFACE
2016 /* Timestamps might be adjusted slightly to ensure that check-ins appear
2017 ** on the timeline in chronological order.  This is the maximum amount
2018 ** of the adjustment window, in days.
2019 */
2020 #define AGE_FUDGE_WINDOW      (2.0/86400.0)       /* 2 seconds */
2021 
2022 /* This is increment (in days) by which timestamps are adjusted for
2023 ** use on the timeline.
2024 */
2025 #define AGE_ADJUST_INCREMENT  (25.0/86400000.0)   /* 25 milliseconds */
2026 
2027 #endif /* LOCAL_INTERFACE */
2028 
2029 /*
2030 ** Finish up a sequence of manifest_crosslink calls.
2031 */
manifest_crosslink_end(int flags)2032 int manifest_crosslink_end(int flags){
2033   Stmt q, u;
2034   int i;
2035   int rc = TH_OK;
2036   int permitHooks = (flags & MC_PERMIT_HOOKS);
2037   const char *zScript = 0;
2038   assert( manifest_crosslink_busy==1 );
2039   if( permitHooks ){
2040     rc = xfer_run_common_script();
2041     if( rc==TH_OK ){
2042       zScript = xfer_ticket_code();
2043     }
2044   }
2045   db_prepare(&q,
2046      "SELECT rid, value FROM tagxref"
2047      " WHERE tagid=%d AND tagtype=1",
2048      TAG_PARENT
2049   );
2050   while( db_step(&q)==SQLITE_ROW ){
2051     int rid = db_column_int(&q,0);
2052     const char *zValue = db_column_text(&q,1);
2053     manifest_reparent_checkin(rid, zValue);
2054   }
2055   db_finalize(&q);
2056   db_prepare(&q, "SELECT id FROM pending_xlink");
2057   while( db_step(&q)==SQLITE_ROW ){
2058     const char *zId = db_column_text(&q, 0);
2059     char cType;
2060     if( zId==0 || zId[0]==0 ) continue;
2061     cType = zId[0];
2062     zId++;
2063     if( cType=='t' ){
2064       ticket_rebuild_entry(zId);
2065       if( permitHooks && rc==TH_OK ){
2066         rc = xfer_run_script(zScript, zId, 0);
2067       }
2068     }else if( cType=='w' ){
2069       backlink_wiki_refresh(zId);
2070     }
2071   }
2072   db_finalize(&q);
2073   db_multi_exec("DROP TABLE pending_xlink");
2074 
2075   /* If multiple check-ins happen close together in time, adjust their
2076   ** times by a few milliseconds to make sure they appear in chronological
2077   ** order.
2078   */
2079   db_prepare(&q,
2080       "UPDATE time_fudge SET m1=m2-:incr WHERE m1>=m2 AND m1<m2+:window"
2081   );
2082   db_bind_double(&q, ":incr", AGE_ADJUST_INCREMENT);
2083   db_bind_double(&q, ":window", AGE_FUDGE_WINDOW);
2084   db_prepare(&u,
2085       "UPDATE time_fudge SET m2="
2086          "(SELECT x.m1 FROM time_fudge AS x WHERE x.mid=time_fudge.cid)"
2087   );
2088   for(i=0; i<30; i++){
2089     db_step(&q);
2090     db_reset(&q);
2091     if( sqlite3_changes(g.db)==0 ) break;
2092     db_step(&u);
2093     db_reset(&u);
2094   }
2095   db_finalize(&q);
2096   db_finalize(&u);
2097   if( db_exists("SELECT 1 FROM time_fudge") ){
2098     db_multi_exec(
2099       "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)"
2100       " WHERE objid IN (SELECT mid FROM time_fudge)"
2101       " AND (mtime=omtime OR omtime IS NULL)"
2102     );
2103   }
2104   db_multi_exec("DROP TABLE time_fudge;");
2105 
2106   db_end_transaction(0);
2107   manifest_crosslink_busy = 0;
2108   return ( rc!=TH_ERROR );
2109 }
2110 
2111 /*
2112 ** Activate EVENT triggers if they do not already exist.
2113 */
manifest_create_event_triggers(void)2114 void manifest_create_event_triggers(void){
2115   if( manifest_event_triggers_are_enabled ){
2116     return;  /* Triggers already exists.  No-op. */
2117   }
2118   alert_create_trigger();
2119   manifest_event_triggers_are_enabled = 1;
2120 }
2121 
2122 /*
2123 ** Disable manifest event triggers.  Drop them if they exist, but mark
2124 ** them has having been created so that they won't be recreated.  This
2125 ** is used during "rebuild" to prevent triggers from firing then.
2126 */
manifest_disable_event_triggers(void)2127 void manifest_disable_event_triggers(void){
2128   alert_drop_trigger();
2129   manifest_event_triggers_are_enabled = 1;
2130 }
2131 
2132 
2133 /*
2134 ** Make an entry in the event table for a ticket change artifact.
2135 */
manifest_ticket_event(int rid,const Manifest * pManifest,int isNew,int tktTagId)2136 void manifest_ticket_event(
2137   int rid,                    /* Artifact ID of the change ticket artifact */
2138   const Manifest *pManifest,  /* Parsed content of the artifact */
2139   int isNew,                  /* True if this is the first event */
2140   int tktTagId                /* Ticket tag ID */
2141 ){
2142   int i;
2143   char *zTitle;
2144   Blob comment;
2145   Blob brief;
2146   char *zNewStatus = 0;
2147   static char *zTitleExpr = 0;
2148   static char *zStatusColumn = 0;
2149   static int once = 1;
2150 
2151   blob_zero(&comment);
2152   blob_zero(&brief);
2153   if( once ){
2154     once = 0;
2155     zTitleExpr = db_get("ticket-title-expr", "title");
2156     zStatusColumn = db_get("ticket-status-column", "status");
2157   }
2158   zTitle = db_text("unknown",
2159     "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
2160     zTitleExpr, pManifest->zTicketUuid
2161   );
2162   if( !isNew ){
2163     for(i=0; i<pManifest->nField; i++){
2164       if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
2165         zNewStatus = pManifest->aField[i].zValue;
2166       }
2167     }
2168     if( zNewStatus ){
2169       blob_appendf(&comment, "%h ticket [%!S|%S]: <i>%h</i>",
2170          zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
2171       );
2172       if( pManifest->nField>1 ){
2173         blob_appendf(&comment, " plus %d other change%s",
2174           pManifest->nField-1, pManifest->nField==2 ? "" : "s");
2175       }
2176       blob_appendf(&brief, "%h ticket [%!S|%S].",
2177                    zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid);
2178     }else{
2179       zNewStatus = db_text("unknown",
2180          "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
2181          zStatusColumn, pManifest->zTicketUuid
2182       );
2183       blob_appendf(&comment, "Ticket [%!S|%S] <i>%h</i> status still %h with "
2184            "%d other change%s",
2185            pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle, zNewStatus,
2186            pManifest->nField, pManifest->nField==1 ? "" : "s"
2187       );
2188       fossil_free(zNewStatus);
2189       blob_appendf(&brief, "Ticket [%!S|%S]: %d change%s",
2190            pManifest->zTicketUuid, pManifest->zTicketUuid, pManifest->nField,
2191            pManifest->nField==1 ? "" : "s"
2192       );
2193     }
2194   }else{
2195     blob_appendf(&comment, "New ticket [%!S|%S] <i>%h</i>.",
2196       pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
2197     );
2198     blob_appendf(&brief, "New ticket [%!S|%S].", pManifest->zTicketUuid,
2199         pManifest->zTicketUuid);
2200   }
2201   fossil_free(zTitle);
2202   manifest_create_event_triggers();
2203   db_multi_exec(
2204     "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
2205     "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
2206     tktTagId, pManifest->rDate, rid, pManifest->zUser,
2207     blob_str(&comment), blob_str(&brief)
2208   );
2209   blob_reset(&comment);
2210   blob_reset(&brief);
2211 }
2212 
2213 /*
2214 ** Add an extra line of text to the end of a manifest to prevent it being
2215 ** recognized as a valid manifest.
2216 **
2217 ** This routine is called prior to writing out the text of a manifest as
2218 ** the "manifest" file in the root of a repository when
2219 ** "fossil setting manifest on" is enabled.  That way, if the files of
2220 ** the project are imported into a different Fossil project, the manifest
2221 ** file will not be interpreted as a control artifact in that other project.
2222 **
2223 ** Normally it is sufficient to simply append the extra line of text.
2224 ** However, if the manifest is PGP signed then the extra line has to be
2225 ** inserted before the PGP signature (thus invalidating the signature).
2226 */
sterilize_manifest(Blob * p,int eType)2227 void sterilize_manifest(Blob *p, int eType){
2228   char *z, *zOrig;
2229   int n, nOrig;
2230   static const char zExtraLine[] =
2231       "# Remove this line to create a well-formed Fossil %s.\n";
2232   const char *zType = eType==CFTYPE_MANIFEST ? "manifest" : "control artifact";
2233 
2234   z = zOrig = blob_materialize(p);
2235   n = nOrig = blob_size(p);
2236   remove_pgp_signature((const char **)&z, &n);
2237   if( z==zOrig ){
2238     blob_appendf(p, zExtraLine/*works-like:"%s"*/, zType);
2239   }else{
2240     int iEnd;
2241     Blob copy;
2242     memcpy(&copy, p, sizeof(copy));
2243     blob_init(p, 0, 0);
2244     iEnd = (int)(&z[n] - zOrig);
2245     blob_append(p, zOrig, iEnd);
2246     blob_appendf(p, zExtraLine/*works-like:"%s"*/, zType);
2247     blob_append(p, &zOrig[iEnd], -1);
2248     blob_zero(&copy);
2249   }
2250 }
2251 
2252 /*
2253 ** This is the comparison function used to sort the tag array.
2254 */
tag_compare(const void * a,const void * b)2255 static int tag_compare(const void *a, const void *b){
2256   struct TagType *pA = (struct TagType*)a;
2257   struct TagType *pB = (struct TagType*)b;
2258   int c;
2259   c = fossil_strcmp(pA->zUuid, pB->zUuid);
2260   if( c==0 ){
2261     c = fossil_strcmp(pA->zName, pB->zName);
2262   }
2263   return c;
2264 }
2265 
2266 /*
2267 ** Inserts plink entries for FORUM, WIKI, and TECHNOTE manifests. May
2268 ** assert for other manifest types. If a parent entry exists, it also
2269 ** propagates any tags for that parent. This is a no-op if
2270 ** p->nParent==0.
2271 */
manifest_add_fwt_plink(int rid,Manifest * p)2272 static void manifest_add_fwt_plink(int rid, Manifest *p){
2273   int i;
2274   int parentId = 0;
2275   assert(p->type==CFTYPE_WIKI ||
2276          p->type==CFTYPE_FORUM ||
2277          p->type==CFTYPE_EVENT);
2278   for(i=0; i<p->nParent; ++i){
2279     int const pid = uuid_to_rid(p->azParent[i], 1);
2280     if(0==i){
2281       parentId = pid;
2282     }
2283     db_multi_exec(
2284                   "INSERT OR IGNORE INTO plink"
2285                   "(pid, cid, isprim, mtime, baseid)"
2286                   "VALUES(%d, %d, %d, %.17g, NULL)",
2287                   pid, rid, i==0, p->rDate);
2288   }
2289   if(parentId){
2290     tag_propagate_all(parentId);
2291   }
2292 }
2293 
2294 /*
2295 ** Scan artifact rid/pContent to see if it is a control artifact of
2296 ** any type:
2297 **
2298 **      *  Manifest
2299 **      *  Control
2300 **      *  Wiki Page
2301 **      *  Ticket Change
2302 **      *  Cluster
2303 **      *  Attachment
2304 **      *  Event
2305 **      *  Forum post
2306 **
2307 ** If the input is a control artifact, then make appropriate entries
2308 ** in the auxiliary tables of the database in order to crosslink the
2309 ** artifact.
2310 **
2311 ** If global variable g.xlinkClusterOnly is true, then ignore all
2312 ** control artifacts other than clusters.
2313 **
2314 ** This routine always resets the pContent blob before returning.
2315 **
2316 ** Historical note:  This routine original processed manifests only.
2317 ** Processing for other control artifacts was added later.  The name
2318 ** of the routine, "manifest_crosslink", and the name of this source
2319 ** file, is a legacy of its original use.
2320 */
manifest_crosslink(int rid,Blob * pContent,int flags)2321 int manifest_crosslink(int rid, Blob *pContent, int flags){
2322   int i, rc = TH_OK;
2323   Manifest *p;
2324   int parentid = 0;
2325   int permitHooks = (flags & MC_PERMIT_HOOKS);
2326   const char *zScript = 0;
2327   const char *zUuid = 0;
2328 
2329   if( g.fSqlTrace ){
2330     fossil_trace("-- manifest_crosslink(%d)\n", rid);
2331   }
2332   manifest_create_event_triggers();
2333   if( (p = manifest_cache_find(rid))!=0 ){
2334     blob_reset(pContent);
2335   }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
2336     assert( blob_is_reset(pContent) || pContent==0 );
2337     if( (flags & MC_NO_ERRORS)==0 ){
2338       char * zErrUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid);
2339       fossil_error(1, "syntax error in manifest [%S]", zErrUuid);
2340       fossil_free(zErrUuid);
2341     }
2342     return 0;
2343   }
2344   if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
2345     manifest_destroy(p);
2346     assert( blob_is_reset(pContent) );
2347     if( (flags & MC_NO_ERRORS)==0 ) fossil_error(1, "no manifest");
2348     return 0;
2349   }
2350   if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){
2351     manifest_destroy(p);
2352     assert( blob_is_reset(pContent) );
2353     if( (flags & MC_NO_ERRORS)==0 ){
2354       fossil_error(1, "cannot fetch baseline for manifest [%S]",
2355                    db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid));
2356     }
2357     return 0;
2358   }
2359   db_begin_transaction();
2360   if( p->type==CFTYPE_MANIFEST ){
2361     if( permitHooks ){
2362       zScript = xfer_commit_code();
2363       zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
2364     }
2365     if( p->nCherrypick && db_table_exists("repository","cherrypick") ){
2366       int i;
2367       for(i=0; i<p->nCherrypick; i++){
2368         db_multi_exec(
2369           "REPLACE INTO cherrypick(parentid,childid,isExclude)"
2370           " SELECT rid, %d, %d FROM blob WHERE uuid=%Q",
2371           rid, p->aCherrypick[i].zCPTarget[0]=='-',
2372           p->aCherrypick[i].zCPTarget+1
2373         );
2374       }
2375     }
2376     if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
2377       char *zCom;
2378       parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);
2379       search_doc_touch('c', rid, 0);
2380       assert( manifest_event_triggers_are_enabled );
2381       zCom = db_text(0,
2382         "REPLACE INTO event(type,mtime,objid,user,comment,"
2383                            "bgcolor,euser,ecomment,omtime)"
2384         "VALUES('ci',"
2385         "  coalesce("
2386         "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
2387         "    %.17g"
2388         "  ),"
2389         "  %d,%Q,%Q,"
2390         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
2391         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
2392         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),%.17g)"
2393         "RETURNING coalesce(ecomment,comment);",
2394         TAG_DATE, rid, p->rDate,
2395         rid, p->zUser, p->zComment,
2396         TAG_BGCOLOR, rid,
2397         TAG_USER, rid,
2398         TAG_COMMENT, rid, p->rDate
2399       );
2400       backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1);
2401       fossil_free(zCom);
2402 
2403       /* If this is a delta-manifest, record the fact that this repository
2404       ** contains delta manifests, to free the "commit" logic to generate
2405       ** new delta manifests.
2406       */
2407       if( p->zBaseline!=0 ){
2408         static int once = 1;
2409         if( once ){
2410           db_set_int("seen-delta-manifest", 1, 0);
2411           once = 0;
2412         }
2413       }
2414     }
2415   }
2416   if( p->type==CFTYPE_CLUSTER ){
2417     static Stmt del1;
2418     tag_insert("cluster", 1, 0, rid, p->rDate, rid);
2419     db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid");
2420     for(i=0; i<p->nCChild; i++){
2421       int mid;
2422       mid = uuid_to_rid(p->azCChild[i], 1);
2423       if( mid>0 ){
2424         db_bind_int(&del1, ":rid", mid);
2425         db_step(&del1);
2426         db_reset(&del1);
2427       }
2428     }
2429   }
2430   if( p->type==CFTYPE_CONTROL
2431    || p->type==CFTYPE_MANIFEST
2432    || p->type==CFTYPE_EVENT
2433   ){
2434     for(i=0; i<p->nTag; i++){
2435       int tid;
2436       int type;
2437       if( p->aTag[i].zUuid ){
2438         tid = uuid_to_rid(p->aTag[i].zUuid, 1);
2439       }else{
2440         tid = rid;
2441       }
2442       if( tid ){
2443         switch( p->aTag[i].zName[0] ){
2444           case '-':  type = 0;  break;  /* Cancel prior occurrences */
2445           case '+':  type = 1;  break;  /* Apply to target only */
2446           case '*':  type = 2;  break;  /* Propagate to descendants */
2447           default:
2448             fossil_error(1, "unknown tag type in manifest: %s", p->aTag);
2449             manifest_destroy(p);
2450             return 0;
2451         }
2452         tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
2453                    rid, p->rDate, tid);
2454       }
2455     }
2456     if( parentid ){
2457       tag_propagate_all(parentid);
2458     }
2459   }
2460   if(p->type==CFTYPE_WIKI || p->type==CFTYPE_FORUM
2461      || p->type==CFTYPE_EVENT){
2462     manifest_add_fwt_plink(rid, p);
2463   }
2464   if( p->type==CFTYPE_WIKI ){
2465     char *zTag = mprintf("wiki-%s", p->zWikiTitle);
2466     int prior = 0;
2467     char cPrefix;
2468     int nWiki;
2469     char zLength[40];
2470 
2471     while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2472     nWiki = strlen(p->zWiki);
2473     sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2474     tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2475     fossil_free(zTag);
2476     if(p->nParent){
2477       prior = fast_uuid_to_rid(p->azParent[0]);
2478     }
2479     if( prior ){
2480       content_deltify(prior, &rid, 1, 0);
2481     }
2482     if( nWiki<=0 ){
2483       cPrefix = '-';
2484     }else if( !prior ){
2485       cPrefix = '+';
2486     }else{
2487       cPrefix = ':';
2488     }
2489     search_doc_touch('w',rid,p->zWikiTitle);
2490     if( manifest_crosslink_busy ){
2491       add_pending_crosslink('w',p->zWikiTitle);
2492     }else{
2493       backlink_wiki_refresh(p->zWikiTitle);
2494     }
2495     assert( manifest_event_triggers_are_enabled );
2496     db_multi_exec(
2497       "REPLACE INTO event(type,mtime,objid,user,comment)"
2498       "VALUES('w',%.17g,%d,%Q,'%c%q');",
2499       p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
2500     );
2501   }
2502   if( p->type==CFTYPE_EVENT ){
2503     char *zTag = mprintf("event-%s", p->zEventId);
2504     int tagid = tag_findid(zTag, 1);
2505     int prior = 0, subsequent;
2506     int nWiki;
2507     char zLength[40];
2508     Stmt qatt;
2509     while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2510     nWiki = strlen(p->zWiki);
2511     sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
2512     tag_insert(zTag, 1, zLength, rid, p->rDate, rid);
2513     fossil_free(zTag);
2514     if(p->nParent){
2515       prior = fast_uuid_to_rid(p->azParent[0]);
2516     }
2517     subsequent = db_int(0,
2518       /* BUG: this check is only correct if subsequent
2519          version has already been crosslinked. */
2520       "SELECT rid FROM tagxref"
2521       " WHERE tagid=%d AND mtime>=%.17g AND rid!=%d"
2522       " ORDER BY mtime",
2523       tagid, p->rDate, rid
2524     );
2525     if( prior ){
2526       content_deltify(prior, &rid, 1, 0);
2527       if( !subsequent ){
2528         db_multi_exec(
2529           "DELETE FROM event"
2530           " WHERE type='e'"
2531           "   AND tagid=%d"
2532           "   AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)",
2533           tagid, tagid
2534         );
2535       }
2536     }
2537     if( subsequent ){
2538       content_deltify(rid, &subsequent, 1, 0);
2539     }else{
2540       search_doc_touch('e',rid,0);
2541       assert( manifest_event_triggers_are_enabled );
2542       db_multi_exec(
2543         "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
2544         "VALUES('e',%.17g,%d,%d,%Q,%Q,"
2545         "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
2546         p->rEventDate, rid, tagid, p->zUser, p->zComment,
2547         TAG_BGCOLOR, rid
2548       );
2549     }
2550     /* Locate and update comment for any attachments */
2551     db_prepare(&qatt,
2552        "SELECT attachid, src, target, filename FROM attachment"
2553        " WHERE target=%Q",
2554        p->zEventId
2555     );
2556     while( db_step(&qatt)==SQLITE_ROW ){
2557       const char *zAttachId = db_column_text(&qatt, 0);
2558       const char *zSrc = db_column_text(&qatt, 1);
2559       const char *zTarget = db_column_text(&qatt, 2);
2560       const char *zName = db_column_text(&qatt, 3);
2561       const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2562       char *zComment;
2563       if( isAdd ){
2564         zComment = mprintf(
2565              "Add attachment [/artifact/%!S|%h] to"
2566              " tech note [/technote/%!S|%S]",
2567              zSrc, zName, zTarget, zTarget);
2568       }else{
2569         zComment = mprintf(
2570              "Delete attachment \"%h\" from"
2571              " tech note [/technote/%!S|%S]",
2572              zName, zTarget, zTarget);
2573       }
2574       db_multi_exec("UPDATE event SET comment=%Q, type='e'"
2575                        " WHERE objid=%Q",
2576                     zComment, zAttachId);
2577       fossil_free(zComment);
2578     }
2579     db_finalize(&qatt);
2580   }
2581   if( p->type==CFTYPE_TICKET ){
2582     char *zTag;
2583     Stmt qatt;
2584     assert( manifest_crosslink_busy==1 );
2585     zTag = mprintf("tkt-%s", p->zTicketUuid);
2586     tag_insert(zTag, 1, 0, rid, p->rDate, rid);
2587     fossil_free(zTag);
2588     add_pending_crosslink('t',p->zTicketUuid);
2589     /* Locate and update comment for any attachments */
2590     db_prepare(&qatt,
2591        "SELECT attachid, src, target, filename FROM attachment"
2592        " WHERE target=%Q",
2593        p->zTicketUuid
2594     );
2595     while( db_step(&qatt)==SQLITE_ROW ){
2596       const char *zAttachId = db_column_text(&qatt, 0);
2597       const char *zSrc = db_column_text(&qatt, 1);
2598       const char *zTarget = db_column_text(&qatt, 2);
2599       const char *zName = db_column_text(&qatt, 3);
2600       const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
2601       char *zComment;
2602       if( isAdd ){
2603         zComment = mprintf(
2604              "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2605              zSrc, zName, zTarget, zTarget);
2606       }else{
2607         zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2608              zName, zTarget, zTarget);
2609       }
2610       db_multi_exec("UPDATE event SET comment=%Q, type='t'"
2611                        " WHERE objid=%Q",
2612                     zComment, zAttachId);
2613       fossil_free(zComment);
2614     }
2615     db_finalize(&qatt);
2616   }
2617   if( p->type==CFTYPE_ATTACHMENT ){
2618     char *zComment = 0;
2619     const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
2620     /* We assume that we're attaching to a wiki page until we
2621     ** prove otherwise (which could on a later artifact if we
2622     ** process the attachment artifact before the artifact to
2623     ** which it is attached!) */
2624     char attachToType = 'w';
2625     if( fossil_is_artifact_hash(p->zAttachTarget) ){
2626       if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
2627             p->zAttachTarget)
2628         ){
2629         attachToType = 't';          /* Attaching to known ticket */
2630       }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
2631                   p->zAttachTarget)
2632             ){
2633         attachToType = 'e';          /* Attaching to known tech note */
2634       }
2635     }
2636     db_multi_exec(
2637        "INSERT INTO attachment(attachid, mtime, src, target,"
2638                               "filename, comment, user)"
2639        "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);",
2640        rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName,
2641        (p->zComment ? p->zComment : ""), p->zUser
2642     );
2643     db_multi_exec(
2644        "UPDATE attachment SET isLatest = (mtime=="
2645           "(SELECT max(mtime) FROM attachment"
2646           "  WHERE target=%Q AND filename=%Q))"
2647        " WHERE target=%Q AND filename=%Q",
2648        p->zAttachTarget, p->zAttachName,
2649        p->zAttachTarget, p->zAttachName
2650     );
2651     if( 'w' == attachToType ){
2652       if( isAdd ){
2653         zComment = mprintf(
2654              "Add attachment [/artifact/%!S|%h] to wiki page [%h]",
2655              p->zAttachSrc, p->zAttachName, p->zAttachTarget);
2656       }else{
2657         zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
2658              p->zAttachName, p->zAttachTarget);
2659       }
2660     }else if( 'e' == attachToType ){
2661       if( isAdd ){
2662         zComment = mprintf(
2663           "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]",
2664           p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2665       }else{
2666         zComment = mprintf(
2667              "Delete attachment \"/artifact/%!S|%h\" from"
2668              " tech note [/technote/%!S|%S]",
2669              p->zAttachName, p->zAttachName,
2670              p->zAttachTarget,p->zAttachTarget);
2671       }
2672     }else{
2673       if( isAdd ){
2674         zComment = mprintf(
2675              "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
2676              p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2677       }else{
2678         zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
2679              p->zAttachName, p->zAttachTarget, p->zAttachTarget);
2680       }
2681     }
2682     assert( manifest_event_triggers_are_enabled );
2683     db_multi_exec(
2684         "REPLACE INTO event(type,mtime,objid,user,comment)"
2685         "VALUES('%c',%.17g,%d,%Q,%Q)",
2686         attachToType, p->rDate, rid, p->zUser, zComment
2687     );
2688     fossil_free(zComment);
2689   }
2690   if( p->type==CFTYPE_CONTROL ){
2691     Blob comment;
2692     int i;
2693     const char *zName;
2694     const char *zValue;
2695     const char *zTagUuid;
2696     int branchMove = 0;
2697     blob_zero(&comment);
2698     if( p->zComment ){
2699       blob_appendf(&comment, " %s.", p->zComment);
2700     }
2701     /* Next loop expects tags to be sorted on hash, so sort it. */
2702     qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare);
2703     for(i=0; i<p->nTag; i++){
2704       zTagUuid = p->aTag[i].zUuid;
2705       if( !zTagUuid ) continue;
2706       if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){
2707         blob_appendf(&comment,
2708            " Edit [%!S|%S]:",
2709            zTagUuid, zTagUuid);
2710         branchMove = 0;
2711         if( permitHooks && db_exists("SELECT 1 FROM event, blob"
2712             " WHERE event.type='ci' AND event.objid=blob.rid"
2713             " AND blob.uuid=%Q", zTagUuid) ){
2714           zScript = xfer_commit_code();
2715           zUuid = zTagUuid;
2716         }
2717       }
2718       zName = p->aTag[i].zName;
2719       zValue = p->aTag[i].zValue;
2720       if( strcmp(zName, "*branch")==0 ){
2721         blob_appendf(&comment,
2722            " Move to branch [/timeline?r=%h&nd&dp=%!S&unhide | %h].",
2723            zValue, zTagUuid, zValue);
2724         branchMove = 1;
2725         continue;
2726       }else if( strcmp(zName, "*bgcolor")==0 ){
2727         blob_appendf(&comment,
2728            " Change branch background color to \"%h\".", zValue);
2729         continue;
2730       }else if( strcmp(zName, "+bgcolor")==0 ){
2731         blob_appendf(&comment,
2732            " Change background color to \"%h\".", zValue);
2733         continue;
2734       }else if( strcmp(zName, "-bgcolor")==0 ){
2735         blob_appendf(&comment, " Cancel background color");
2736       }else if( strcmp(zName, "+comment")==0 ){
2737         blob_appendf(&comment, " Edit check-in comment.");
2738         continue;
2739       }else if( strcmp(zName, "+user")==0 ){
2740         blob_appendf(&comment, " Change user to \"%h\".", zValue);
2741         continue;
2742       }else if( strcmp(zName, "+date")==0 ){
2743         blob_appendf(&comment, " Timestamp %h.", zValue);
2744         continue;
2745       }else if( memcmp(zName, "-sym-",5)==0 ){
2746         if( !branchMove ){
2747           blob_appendf(&comment, " Cancel tag \"%h\"", &zName[5]);
2748         }else{
2749           continue;
2750         }
2751       }else if( memcmp(zName, "*sym-",5)==0 ){
2752         if( !branchMove ){
2753           blob_appendf(&comment, " Add propagating tag \"%h\"", &zName[5]);
2754         }else{
2755           continue;
2756         }
2757       }else if( memcmp(zName, "+sym-",5)==0 ){
2758         blob_appendf(&comment, " Add tag \"%h\"", &zName[5]);
2759       }else if( strcmp(zName, "+closed")==0 ){
2760         blob_append(&comment, " Mark \"Closed\"", -1);
2761       }else if( strcmp(zName, "-closed")==0 ){
2762         blob_append(&comment, " Remove the \"Closed\" mark", -1);
2763       }else {
2764         if( zName[0]=='-' ){
2765           blob_appendf(&comment, " Cancel \"%h\"", &zName[1]);
2766         }else if( zName[0]=='+' ){
2767           blob_appendf(&comment, " Add \"%h\"", &zName[1]);
2768         }else{
2769           blob_appendf(&comment, " Add propagating \"%h\"", &zName[1]);
2770         }
2771         if( zValue && zValue[0] ){
2772           blob_appendf(&comment, " with value \"%h\".", zValue);
2773         }else{
2774           blob_appendf(&comment, ".");
2775         }
2776         continue;
2777       }
2778       if( zValue && zValue[0] ){
2779         blob_appendf(&comment, " with note \"%h\".", zValue);
2780       }else{
2781         blob_appendf(&comment, ".");
2782       }
2783     }
2784     /*blob_appendf(&comment, " &#91;[/info/%S | details]&#93;");*/
2785     if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1);
2786     assert( manifest_event_triggers_are_enabled );
2787     db_multi_exec(
2788       "REPLACE INTO event(type,mtime,objid,user,comment)"
2789       "VALUES('g',%.17g,%d,%Q,%Q)",
2790       p->rDate, rid, p->zUser, blob_str(&comment)+1
2791     );
2792     blob_reset(&comment);
2793   }
2794   if( p->type==CFTYPE_FORUM ){
2795     int froot, fprev, firt;
2796     char *zFType;
2797     char *zTitle;
2798     schema_forum();
2799     search_doc_touch('f', rid, 0);
2800     froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid;
2801     fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0;
2802     firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0;
2803     db_multi_exec(
2804       "REPLACE INTO forumpost(fpid,froot,fprev,firt,fmtime)"
2805       "VALUES(%d,%d,nullif(%d,0),nullif(%d,0),%.17g)",
2806       p->rid, froot, fprev, firt, p->rDate
2807     );
2808     if( firt==0 ){
2809       /* This is the start of a new thread, either the initial entry
2810       ** or an edit of the initial entry. */
2811       zTitle = p->zThreadTitle;
2812       if( zTitle==0 || zTitle[0]==0 ){
2813         zTitle = "(Deleted)";
2814       }
2815       zFType = fprev ? "Edit" : "Post";
2816       assert( manifest_event_triggers_are_enabled );
2817       db_multi_exec(
2818         "REPLACE INTO event(type,mtime,objid,user,comment)"
2819         "VALUES('f',%.17g,%d,%Q,'%q: %q')",
2820         p->rDate, rid, p->zUser, zFType, zTitle
2821       );
2822       /*
2823       ** If this edit is the most recent, then make it the title for
2824       ** all other entries for the same thread
2825       */
2826       if( !db_exists("SELECT 1 FROM forumpost WHERE froot=%d AND firt=0"
2827                      "   AND fpid!=%d AND fmtime>%.17g", froot, rid, p->rDate)
2828       ){
2829         /* This entry establishes a new title for all entries on the thread */
2830         db_multi_exec(
2831           "UPDATE event"
2832           " SET comment=substr(comment,1,instr(comment,':')) || ' %q'"
2833           " WHERE objid IN (SELECT fpid FROM forumpost WHERE froot=%d)",
2834           zTitle, froot
2835         );
2836       }
2837     }else{
2838       /* This is a reply to a prior post.  Take the title from the root. */
2839       zTitle = db_text(0, "SELECT substr(comment,instr(comment,':')+2)"
2840                           "  FROM event WHERE objid=%d", froot);
2841       if( zTitle==0 ) zTitle = fossil_strdup("<i>Unknown</i>");
2842       if( p->zWiki[0]==0 ){
2843         zFType = "Delete reply";
2844       }else if( fprev ){
2845         zFType = "Edit reply";
2846       }else{
2847         zFType = "Reply";
2848       }
2849       assert( manifest_event_triggers_are_enabled );
2850       db_multi_exec(
2851         "REPLACE INTO event(type,mtime,objid,user,comment)"
2852         "VALUES('f',%.17g,%d,%Q,'%q: %q')",
2853         p->rDate, rid, p->zUser, zFType, zTitle
2854       );
2855       fossil_free(zTitle);
2856     }
2857     if( p->zWiki[0] ){
2858       backlink_extract(p->zWiki, p->zMimetype, rid, BKLNK_FORUM, p->rDate, 1);
2859     }
2860   }
2861 
2862   db_end_transaction(0);
2863   if( permitHooks ){
2864     rc = xfer_run_common_script();
2865     if( rc==TH_OK ){
2866       rc = xfer_run_script(zScript, zUuid, 0);
2867     }
2868   }
2869   if( p->type==CFTYPE_MANIFEST ){
2870     manifest_cache_insert(p);
2871   }else{
2872     manifest_destroy(p);
2873   }
2874   assert( blob_is_reset(pContent) );
2875   return ( rc!=TH_ERROR );
2876 }
2877 
2878 /*
2879 ** COMMAND: test-crosslink
2880 **
2881 ** Usage:  %fossil test-crosslink RECORDID
2882 **
2883 ** Run the manifest_crosslink() routine on the artifact with the given
2884 ** record ID.  This is typically done in the debugger.
2885 */
test_crosslink_cmd(void)2886 void test_crosslink_cmd(void){
2887   int rid;
2888   Blob content;
2889   db_find_and_open_repository(0, 0);
2890   if( g.argc!=3 ) usage("RECORDID");
2891   rid = name_to_rid(g.argv[2]);
2892   content_get(rid, &content);
2893   manifest_crosslink(rid, &content, MC_NONE);
2894 }
2895