Example #1
0
        //
        // Control Event Handlers:
        //
        #region private void _extractItemStripMenuItem_Click(object sender, EventArgs e)
        /// <summary>
        /// Extracts archive item to file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _extractItemStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_archiveItemInfoListView.SelectedItems.Count == 1)
            {
                var child = _archiveItemInfoListView.SelectedItems[0].Tag as ChildDocument;

                if (child != null)
                {
                    try
                    {
                        var saveDialog = new SaveFileDialog();
                        saveDialog.Title    = "Extract Selected Item...";
                        saveDialog.FileName = child.Name;

                        if (saveDialog.ShowDialog() == DialogResult.OK)
                        {
                            using (var outStream = new FileStream(saveDialog.FileName, FileMode.CreateNew, FileAccess.ReadWrite))
                            {
                                var result = _archiveExtractor.ExtractItem((int)child.Index, outStream);

                                // Archive item is password protected:
                                if (result == ContentResult.WrongPassword)
                                {
                                    string password = null;

                                    while (_hostUI.RequestPassword(out password) == DialogResult.OK)
                                    {
                                        result = _archiveExtractor.ExtractItem((int)child.Index, outStream, password);

                                        if (result != ContentResult.WrongPassword)
                                        {
                                            break;
                                        }
                                    }

                                    if (result != ContentResult.Ok)
                                    {
                                        _hostUI.ShowMessageBox("Error extracting item, result = " + result.ToString(), "Error");
                                    }
                                }
                                else if (result != ContentResult.Ok)
                                {
                                    var msg = "Error extraction item, Error = " + result.ToString();
                                    _hostUI.ShowMessageBox(msg, "Error");
                                    _hostUI.LogMessage(msg);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _hostUI.ShowMessageBox("Error saving archive item: " + ex.Message, "Archive Extract Error");
                    }
                }
            }
        }
        /// <summary>
        /// Extracts all archive items to a root output directory using archive folder structure (if any).
        /// </summary>
        /// <remarks>
        /// **WARNING**: Outputting items to the archive folder structure does not check for illegal item name or path characters or for long file paths
        /// (i.e., paths greater than MAX_PATH), both which could cause an exception. It is left to user to write production level
        /// code to check for and replace illegal file system characters in mail store folder names and to also ensure that their
        /// application can handle paths greater than MAX_PATH.
        /// </remarks>
        /// <param name="rootOutputPath">Root folder path to extract archive items.</param>
        /// <param name="passwords">Optional list of passwords to cycle through when encountering an encrypted archive item. Archives can have archive level passwords and/or
        /// different passwords for different items.</param>
        public void ExtractItemsToDirectory(string rootOutputPath)
        {
            var stopwatch = Stopwatch.StartNew();

            TotalItemsExtracted = 0;

            _rootOutputFolder = rootOutputPath;

            if (!Directory.Exists(_rootOutputFolder))
            {
                Directory.CreateDirectory(_rootOutputFolder);
            }

            var outputDirFiles = Directory.GetFiles(_rootOutputFolder);

            if (outputDirFiles != null && outputDirFiles.Length > 0)
            {
                // This is just an example, so will play it safe:
                _hostUI.LogMessage("Extraction aborted - there are existing files in output folder, select/create a folder with no existing files.");
                _hostUI.ShowMessageBox("Extraction aborted - there are existing files in output folder, select/create a folder with no existing files.", "Error");
                return;
            }

            //
            // Create archive directory structure first (if there is one) under input argument 'rootOutputPath':
            //
            if (_archiveContent.Root != null && _archiveContent.Root.SubFolders.Count > 0)
            {
                FileSystemHelper.CreateContainerFolderDirectoryHierarchy(_rootOutputFolder, _archiveContent.Root.SubFolders);
            }

            if (_archiveExtractor.IsSolid)
            {
                //================================================================================================================
                // Solid archive: The archive has 1 or more solid compressed blocks:
                //
                // Solid compressed archives are archives (e.g., 7z or Rar) where items are compressed together is pre-defined block sizes
                // in order to improve compression ratios. It is very very inefficient to randomly extract one item at a time from a solid compressed
                // archive block, especially if there are 100's to 1000's of items in that block. The way to extract from solid block archives
                // shown here with callback delegates is the most efficient way:
                //================================================================================================================
                var blockItemResult = _archiveExtractor.ExtractSolidBlockItems(ItemGetStreamCallback, ItemFinishedCallback, _archiveContent.Password);

                if (blockItemResult == ContentResult.WrongPassword)
                {
                    var    foundPassword = false;
                    string password;

                    // Keep prompting user for passwords until result is not ContentResult.WrongPassword or until user presses "Cancel" button
                    while (_hostUI.RequestPassword(out password) == DialogResult.OK)
                    {
                        blockItemResult = _archiveExtractor.ExtractSolidBlockItems(ItemGetStreamCallback, ItemFinishedCallback, password);

                        if (blockItemResult != ContentResult.WrongPassword)
                        {
                            foundPassword = true;
                            break;
                        }
                    }

                    if (!foundPassword)
                    {
                        _hostUI.LogMessage("Could not extract items from 'solid' archive - archive solid block is encrypted and no valid password was found.");
                        _hostUI.ShowMessageBox("Could not extract items from 'solid' archive - archive solid block is encrypted and no valid password was found.", "Error");
                    }
                }
            }
            else
            {
                //================================================================================================================
                // Non-solid archives:
                //================================================================================================================
                var password = _archiveContent.Password; // Use archive level password (if there is one) for 1st try at item level password

                foreach (var childDoc in _archiveContent.ChildDocuments)
                {
                    try
                    {
                        if (string.IsNullOrWhiteSpace(childDoc.Name))
                        {
                            childDoc.Name = string.Format("item_{0}", childDoc.Index);
                        }

                        var filePath = Path.Combine(_rootOutputFolder, childDoc.ContainerRelativePath, childDoc.Name);

                        //
                        // Check for duplicate file paths and if found rename filename with like Windows does for duplicate files, e.g., "filename (2).ext"
                        // Some archive types can have duplicate named files in same directory, so this is a check for that:
                        //
                        FileSystemHelper.CheckForAndCorrectDuplicateItemFilePaths(_itemFilenameByCountDict, childDoc, ref filePath);

                        Stream stream = null;

                        try
                        {
                            stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.ReadWrite);

                            var itemExtractResult = _archiveExtractor.ExtractItem((int)childDoc.Index, stream, password);

                            if (itemExtractResult == ContentResult.Ok)
                            {
                                // Success
                                ++TotalItemsExtracted;
                                continue;
                            }
                            else if (itemExtractResult == ContentResult.WrongPassword)
                            {
                                var foundPassword = false;

                                // Keep prompting user for passwords until result is not ContentResult.WrongPassword or until user presses "Cancel" button
                                while (_hostUI.RequestPassword(out password) == DialogResult.OK)
                                {
                                    itemExtractResult = _archiveExtractor.ExtractItem((int)childDoc.Index, stream, password);

                                    if (itemExtractResult != ContentResult.WrongPassword)
                                    {
                                        foundPassword = true;
                                        break;
                                    }
                                }

                                if (foundPassword)
                                {
                                    ++TotalItemsExtracted;
                                    continue;
                                }
                                else
                                {
                                    // Set item file format identification:
                                    childDoc.FormatId = DocumentIdentifier.ContainerUnextractableResult;
                                    _hostUI.LogMessage(string.Format("Could not extract item #{0} from archive - archive item is encrypted and no valid password was found.", childDoc.Index));
                                }
                            }
                            else
                            {
                                // Some level of error has occured:
                                if (itemExtractResult == ContentResult.DataError && stream != null && (stream.Length >= (long)(0.5 * childDoc.Size) && stream.Length <= (long)(1.1 * childDoc.Size)))
                                {
                                    // We have a data error but also have at least the expanded size of data extracted from archive - attempt to process this data if
                                    // expanded stream is not too much larger than item's 'Size' property.
                                    ++TotalItemsExtracted;
                                    continue;
                                }
                                else
                                {
                                    childDoc.FormatId = DocumentIdentifier.ContainerUnextractableResult;
                                    var msg = string.Format("Could not extract item #{0} from archive - item error = '{1}'", childDoc.Index, itemExtractResult.ToString());
                                    _hostUI.LogMessage(msg);
                                }
                            }
                        }
                        finally
                        {
                            if (stream != null)
                            {
                                stream.Dispose();
                            }
                        }
                    }
                    catch
                    {
                        childDoc.FormatId = DocumentIdentifier.ContainerUnextractableResult;
                    }
                }
            }

            stopwatch.Stop();
            TotalElapsedTimeMs = stopwatch.Elapsed.TotalMilliseconds;
        }