/// <summary>
        ///     Saves the text of a file version to a temp file.
        /// </summary>
        /// <param name="version"> The version to save. </param>
        /// <returns> The name of the file, or null if it fails. </returns>
        private TempFile SaveToTempFile(FileVersion version, bool ignoreBase = false)
        {
            var reader = GetFileStream(version, ignoreBase);
            if (reader == null)
                return null;

            var file = new TempFile();

            using (reader)
            using (var writer = new StreamWriter(file.FullName))
            {
                foreach (var line in reader.ReadLines())
                    writer.WriteLine(line);
            }

            // We created the temp file, but because of the security settings for %TEMP% might not have access to it.
            // Grant it explicitly.
            AddFileSecurity(file.FullName, WellKnownSidType.AuthenticatedUserSid, FileSystemRights.Read,
                            AccessControlType.Allow);
            AddFileSecurity(file.FullName, WellKnownSidType.CreatorOwnerSid, FileSystemRights.FullControl,
                            AccessControlType.Allow);

            return file;
        }
        /// <summary>
        /// Computes the displayed name of the file version. Similar to ComputeMoniker, but without the action.
        /// </summary>
        /// <param name="name"> The base name of a file (excluding the path). </param>
        /// <param name="version"> The version. </param>
        /// <returns> The string to display. </returns>
        private static string FileDisplayName(string name, FileVersion version)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(name);
            sb.Append('#');
            sb.Append(version.Revision.ToString());
            if (!version.IsRevisionBase && version.TimeStamp != null)
                sb.Append(" " + version.TimeStamp);

            return sb.ToString();
        }
        /// <summary>
        ///     Produces a stream for a file version. This may involve reading the database for the base for the version,
        ///     if the version is a diff.
        /// </summary>
        /// <param name="version"> The version to stream. </param>
        /// <returns> The resultant full-text stream. </returns>
        private StreamCombiner GetFileStream(FileVersion version, bool ignoreBase = false)
        {
            if (version.IsFullText)
                return new StreamCombiner(version.Text);

            var baseVersionQuery = from fl in DataContext.FileVersions
                                   where
                                       fl.FileId == version.FileId &&
                                       fl.Revision == version.Revision && fl.IsRevisionBase
                                   select fl;
            if (baseVersionQuery.Count() != 1)
            {
                if (ignoreBase)
                    return new StreamCombiner(version.Text);

                throw new Exception("Base revision not found. This can happen if the file changed type from binary to text " +
                         "as part of this change. If this is not the case, it might be a bug - report it!");
            }

            var baseVersion = baseVersionQuery.Single();

            return new StreamCombiner(baseVersion.Text, version.Text);
        }
 public void GenerateDiffInfoFile(string clName, FileVersion left, FileVersion right, int baseReviewId)
 {
     var js = new JavaScriptSerializer();
     var jsonFile = string.Format(@"{0}\{1}\{2}\{3}.{4}.json", _diffFolderRoot, clName, left.FileId, left.Id, right.Id);
     var json = JsonConvert.SerializeObject(new DiffFileDto(left, right, clName, baseReviewId), Formatting.Indented);
     File.WriteAllText(jsonFile, json);
 }
        /// <summary>
        ///     Computes and displays the diff between two distinct versions. Uses unix "diff" command to actually
        ///     produce the diff. This is relatively slow operation and involves spooling the data into two temp
        ///     files and running an external process.
        /// </summary>
        public string GenerateDiffFile(FileVersion left, FileVersion right, string clName, int baseReviewId, UserSettingsDto settings, bool force = false)
        {
            string outputFile = null;
            if (settings.diff.DefaultSettings)
            {
                outputFile = string.Format(@"{0}\{1}\{2}\{3}.{4}.htm", _diffFolderRoot, clName, left.FileId, left.Id, right.Id);
                if (File.Exists(outputFile))
                {
                    if (!force)
                        return outputFile;
                    File.Delete(outputFile);
                }
            }

            var useLeft = true;
            var useRight = true;
            var ignoreBase = false;

            if (left.Id == right.Id)
            {
                ignoreBase = true;
                var actionType = (SourceControlAction)Enum.ToObject(typeof(SourceControlAction), left.Action);
                switch (actionType)
                {
                    case SourceControlAction.Add:
                    case SourceControlAction.Branch:
                    case SourceControlAction.Edit:
                    case SourceControlAction.Integrate:
                    case SourceControlAction.Rename:
                    default:
                        useLeft = false;
                        break;

                    case SourceControlAction.Delete:
                        useRight = false;
                        break;
                }
            }

            using (var leftFile = useLeft ? SaveToTempFile(left, ignoreBase) : EmptyTempFile())
            using (var rightFile = useRight ? SaveToTempFile(right, ignoreBase) : EmptyTempFile())
            {
                if (leftFile == null || rightFile == null)
                    throw new ApplicationException(string.Format("Could not save temporary file for diffs. change list: {0} left file id: {1} right file id: {2}", clName, left.FileId, right.FileId));

                string stderr = null;
                string result = null;

                if (ConfigurationManager.AppSettings.LookupValue("preDiff", false) && settings.diff.preDiff)
                {
                    PreDiffFile(leftFile.FullName, rightFile.FullName);
                }

                result = DiffFiles(leftFile.FullName, rightFile.FullName, settings.diff.ignoreWhiteSpace, ref stderr);
                if (!string.IsNullOrEmpty(stderr))
                {
                    ErrorOut("Diff failed.");
                    ErrorOut(stderr);

                    return "Error Failed to generate diff. " + stderr;
                }

                using (var leftStream = new StreamCombiner(new StreamReader(leftFile.FullName)))
                using (var rightStream = new StreamCombiner(new StreamReader(rightFile.FullName)))
                using (var rawDiffStream = new StreamCombiner(result))
                {
                    var leftName = string.Format("{0}_{1}", Path.GetFileName(left.ChangeFile.ServerFileName), left.Id);
                    var rightName = string.Format("{0}_{1}", Path.GetFileName(right.ChangeFile.ServerFileName),
                                                  right.Id);

                    if (string.IsNullOrEmpty(outputFile))
                        outputFile = string.Format(@"{0}\{1}\{2}\{3}.{4}.delete.{5}.htm", _diffFolderRoot, clName, left.FileId, left.Id, right.Id, this._random.Next());

                    GenerateFileDiffView(left.ReviewRevision, right.ReviewRevision, settings,
                                                leftStream, left.Id, leftName,
                                                rightStream, right.Id, rightName,
                                                rawDiffStream, clName, outputFile);

                    GenerateDiffInfoFile(clName, left, right, baseReviewId);

                    return outputFile;
                }
            }
        }
        public FileVersionDto(FileVersion input, bool complete)
        {
            if (input == null)
                return;

            this.action = input.Action;
            this.fileId = input.FileId;
            this.id = input.Id;
            this.isFullText = input.IsFullText;
            this.isRevisionBase = input.IsRevisionBase;
            this.isText = input.IsText;
            this.revision = input.Revision;
            this.reviewRevision = input.ReviewRevision;
            this.timeStamp = input.TimeStamp;
            this.name = string.Format("{0}_{1}", System.IO.Path.GetFileName(input.ChangeFile.ServerFileName), input.Id);

            if (complete)
            {
                changeFile = new ChangeFileDto(input.ChangeFile, false);
            }
        }
 public DiffFileDto(FileVersion left, FileVersion right, string CL, int baseReviewId)
 {
     this.left = new FileVersionDto(left, false);
     this.right = new FileVersionDto(right, false);
     this.CL = CL;
     this.baseReviewId = baseReviewId;
 }