private int cbHeader; // Size of the serialized package header /// <summary> /// Instantiates an empty file package for writing. Call <see cref="Create(string)" /> /// to indicate where the package should be written. /// </summary> public Package() { this.entries = new Hashtable(); this.root = new PackageEntry(this, null, string.Empty, true); this.packageIn = null; this.packageOut = null; entries.Add(string.Empty, root); }
/// <summary> /// Instantiates a folder or file package entry. /// </summary> /// <param name="package">The package that owns this entry.</param> /// <param name="parent">The parent folder (or <c>null</c> for the root folder).</param> /// <param name="name">The entry's local name.</param> /// <param name="isFolder"><c>true</c> if this is a folder, false for a file.</param> internal PackageEntry(Package package, PackageEntry parent, string name, bool isFolder) { this.package = package; this.type = isFolder ? FOLDER : FILE; this.parent = parent; this.name = name; this.fullName = GetFullName(); this.size = 0; this.children = new ArrayList(); }
/// <summary> /// Adds the entry passed to the set of this folder's children. /// </summary> /// <param name="entry">The entry to be added.</param> internal void AddChild(PackageEntry entry) { if (type != FOLDER) { throw new PackageException(Package.NotFolder); } if (children == null) { children = new ArrayList(); } children.Add(entry); }
/// <summary> /// Adds a file entry to the package by copying data from a stream. /// </summary> /// <param name="packagePath">The fully qualified name of the file to be added.</param> /// <param name="input">The input stream.</param> /// <param name="size">The number of bytes to copy.</param> /// <returns>The package entry added.</returns> /// <remarks> /// The input stream's position will be advanced past the last /// byte read. /// </remarks> public PackageEntry AddFile(string packagePath, Stream input, int size) { PackageEntry entry; byte[] buffer; int cb, cbRemain; if (packageOut == null) { throw new PackageException(ReadOnly); } CheckPath(packagePath); if (entries[packagePath.ToUpper()] != null) { throw new PackageException(AlreadyExists, packagePath); } entry = new PackageEntry(this, packagePath, false); entry.SetSize(size); AddEntry(entry); entry.Serialize(packageOut); buffer = new byte[CopyBufSize]; cbRemain = entry.Size; while (cbRemain > 0) { cb = cbRemain; if (cb > CopyBufSize) { cb = CopyBufSize; } input.Read(buffer, 0, cb); packageOut.Write(buffer, 0, cb); cbRemain -= cb; } return(entry); }
/// <summary> /// Instantiates a package entry with a fully qualified name. /// </summary> /// <param name="package">The package that owns this entry.</param> /// <param name="fullName">The entry's fully qualified name.</param> /// <param name="isFolder">Pass <c>true</c> if this entry is a folder.</param> internal PackageEntry(Package package, string fullName, bool isFolder) { int pos; pos = fullName.LastIndexOf('/'); if (pos == -1) { name = fullName; } else { name = fullName.Substring(pos + 1); } this.package = package; this.type = isFolder ? FOLDER : FILE; this.parent = null; this.fullName = fullName; this.size = 0; this.children = new ArrayList(); }
/// <summary> /// Adds the package passed to the entry hierarchy, creating folders /// as necessary. /// </summary> /// <param name="entry">The new entry.</param> private void AddEntry(PackageEntry entry) { PackageEntry parent; string path; int pos, posEnd; // Make sure that all of the folders up // the hierarchy exist. parent = root; pos = 1; while (true) { posEnd = entry.FullName.IndexOf('/', pos); if (posEnd == -1) { break; } path = entry.FullName.Substring(0, posEnd); parent = this[path]; if (parent != null && !parent.IsFolder) { throw new PackageException(MustBeFolder, path); } else if (parent == null) { parent = AddFolder(path); } pos = posEnd + 1; } entry.SetParent(parent); parent.AddChild(entry); entries.Add(entry.FullName.ToUpper(), entry); }
/// <summary> /// Adds a folder entry to the package. /// </summary> /// <param name="packagePath">The fully wualified name of the folder to be added.</param> /// <returns>The package entry added.</returns> public PackageEntry AddFolder(string packagePath) { PackageEntry entry; if (packageOut == null) { throw new PackageException(ReadOnly); } CheckPath(packagePath); if (entries[packagePath.ToUpper()] != null) { throw new PackageException(AlreadyExists, packagePath); } entry = new PackageEntry(this, packagePath, true); entry.SetSize(0); AddEntry(entry); entry.Serialize(packageOut); return(entry); }
/// <summary> /// Adds a file entry to the package. /// </summary> /// <param name="packagePath">The fully qualified name of the file to be added.</param> /// <param name="buffer">The entry data to be added.</param> /// <returns>The package entry added.</returns> public PackageEntry AddFile(string packagePath, byte[] buffer) { PackageEntry entry; if (packageOut == null) { throw new PackageException(ReadOnly); } CheckPath(packagePath); if (entries[packagePath.ToUpper()] != null) { throw new PackageException(AlreadyExists, packagePath); } entry = new PackageEntry(this, packagePath, false); entry.SetSize(buffer.Length); AddEntry(entry); entry.Serialize(packageOut); packageOut.Write(buffer, 0, buffer.Length); return(entry); }
/// <summary> /// Sets the entry's parent. /// </summary> /// <param name="parent">The parent being assigned.</param> internal void SetParent(PackageEntry parent) { this.parent = parent; }
/// <summary> /// Scans the input stream, validating its structure and loading /// the package entries. /// </summary> private void Load() { PackageHeader header; byte[] hash; packageIn.Position = 0; header = new PackageHeader(packageIn); cbHeader = (int)packageIn.Position; // Compute a MD5 hash on the rest of then file and compare // the result to what we read from the header. hash = MD5Hasher.Compute(packageIn, packageIn.Length - cbHeader); if (hash.Length != header.Hash.Length) { throw new PackageException(CorruptPackage); } for (int i = 0; i < hash.Length; i++) { if (hash[i] != header.Hash[i]) { throw new PackageException(CorruptPackage); } } // Read the package entry headers. packageIn.Position = cbHeader; while (true) { var entry = new PackageEntry(this, packageIn); if (entry.IsEol) { break; } entries.Add(entry.FullName.ToUpper(), entry); } // Walk back through the entries and link each entry to // its parent folder. foreach (PackageEntry entry in entries.Values) { PackageEntry parent; int pos; if (entry == root) { continue; } pos = entry.FullName.LastIndexOf('/'); if (pos == -1) { throw new PackageException(CorruptPackage); } if (pos == 0) { parent = root; } else { parent = (PackageEntry)entries[entry.FullName.Substring(0, pos).ToUpper()]; if (parent == null) { throw new PackageException(CorruptPackage); } } entry.SetParent(parent); parent.AddChild(entry); } }