public static InstallerConfiguration CreateDefaultConfiguration()
 {
     InstallerConfiguration defaultConfiguration = new InstallerConfiguration ();
     defaultConfiguration.AutoDiscovery = true;
     defaultConfiguration.Certificates = new byte[0][];
     defaultConfiguration.URIs = new string[0];
     defaultConfiguration.Packages = new InstallerPackage[0];
     defaultConfiguration.Version = string.Empty;
     return defaultConfiguration;
 }
 static IDictionary<string, InstallerPackageTuple> MergePackages(InstallerConfiguration localInstallerConfiguration, InstallerConfiguration remoteInstallerConfiguration)
 {
     Dictionary<string, InstallerPackageTuple> packages = new Dictionary<string, InstallerPackageTuple> ();
     foreach (var localPackage in localInstallerConfiguration.Packages) {
         InstallerPackageTuple packageTuple = new InstallerPackageTuple ();
         packageTuple.Local = localPackage;
         packages.Add (localPackage.Name, packageTuple);
         //duplicate path, boom
         foreach (var localFile in localPackage.Files) {
             InstallerFileTuple fileTuple = new InstallerFileTuple ();
             fileTuple.Local = localFile;
             packageTuple.files.Add (localFile.Name, fileTuple);
         }
     }
     foreach (var remotePackage in remoteInstallerConfiguration.Packages) {
         InstallerPackageTuple packageTuple;
         if (!packages.TryGetValue (remotePackage.Name, out packageTuple)) {
             packageTuple = new InstallerPackageTuple ();
             packages.Add (remotePackage.Name, packageTuple);
         }
         packageTuple.Remote = remotePackage;
         foreach (var remoteFile in remotePackage.Files) {
             InstallerFileTuple fileTuple;
             if (!packageTuple.files.TryGetValue (remoteFile.Name, out fileTuple)) {
                 fileTuple = new InstallerFileTuple ();
                 packageTuple.files.Add (remoteFile.Name, fileTuple);
             }
             fileTuple.Remote = remoteFile;
         }
     }
     foreach (var p in packages) {
         if ((p.Value.Local == null) ^ (p.Value.Remote == null)) {
             p.Value.files.Clear ();
         }
     }
     return packages;
 }
        static bool Install(InstallerConfiguration localInstallerConfiguration, Uri uri)
        {
            SetCertificate (localInstallerConfiguration.Certificates);
            Stream manifestStream;
            string manifestFileName;
            Stream manifestCopy;

            Stream manifestSignatureStream;
            string manifestSignatureFileName;
            try {
                manifestStream = Fetch (uri, "manifest.xml");
                manifestFileName = Path.GetTempFileName ();
                manifestCopy = Wack (manifestStream, manifestFileName);

                manifestSignatureStream = Fetch (uri, "manifest.xml.sig");
                manifestSignatureFileName = Path.GetTempFileName ();
                using (Stream manifestSignatureTempStream = File.OpenWrite (manifestSignatureFileName)) {
                    manifestSignatureStream.CopyTo (manifestSignatureTempStream);
                }
            } catch (Exception ex) {
                Console.Error.WriteLine (ex.Message);
                Console.Error.WriteLine ("***Error while fetching manifest /sig, ABORTING INSTALLATION FROM {0}***", uri);
                return false;
            }

            try {
                Process process;
                process = Process.Start ("gpg", "--verify " + manifestSignatureFileName + " " + manifestFileName);
                process.WaitForExit ();
                int ec = process.ExitCode;
                if (ec != 0) {
                    Console.Error.WriteLine ("***GPG VERIFICATION FAILED. ABORTING INSTALLATION FROM {0}***", uri);
                    return false;
                }
            } catch (Exception ex) {
                Console.WriteLine ("'{{{0}}}:{1}' happneed when I tried to verify the gpg signature, Plesae verify that gpg is installed and all keys belonging to Behrooz0az@gmail are imported.", ex.GetType ().Name, ex.Message);
                if (Environment.OSVersion.Platform != PlatformID.Win32NT) { //windows users can't care less about security anyways
                    return false;
                }
            }

            if (File.Exists ("manifest.xml"))
                File.Delete ("manifest.xml");
            File.Move (manifestFileName, "manifest.xml");
            if (File.Exists ("manifest.xml.sig"))
                File.Delete ("manifest.xml.sig");
            File.Move (manifestSignatureFileName, "manifest.xml.sig");

            System.Xml.Serialization.XmlSerializer xmlSerializer = CreateSerializer ();
            InstallerConfiguration remoteInstallerConfiguration = (InstallerConfiguration)xmlSerializer.Deserialize (manifestCopy);
            if (string.Compare (remoteInstallerConfiguration.Version, localInstallerConfiguration.Version, StringComparison.Ordinal) < 0) {
                Console.WriteLine ("remote data too old");
                return false;
            }
            if (localInstallerConfiguration.Certificates.Length != 0) {
                List<byte[]> matches = new List<byte[]> ();
                foreach (var rcert in remoteInstallerConfiguration.Certificates) {
                    foreach (var lcert in  localInstallerConfiguration.Certificates) {
                        if (System.Collections.StructuralComparisons.StructuralEqualityComparer.Equals (rcert, lcert)) {
                            matches.Add (lcert);
                        }
                    }
                }
                if (matches.Count == 0) {
                    Console.WriteLine ("certificate changed without notice.");
                    return false;
                }
                SetCertificate (matches);
            }
            Console.WriteLine ("we have {0} packages to check", remoteInstallerConfiguration.Packages.Length);
            List<InstallerPackage> remotePackages = new List<InstallerPackage> (remoteInstallerConfiguration.Packages);
            foreach (var package in remoteInstallerConfiguration.Packages) {
                if (!PlatformSupportsPackage (package)) {
                    Console.WriteLine ("package {0}: Not Supported.", package.Name);
                    remotePackages.Remove (package);
                } else {
                    Console.WriteLine ("package {0}: OK", package.Name);
                }
            }
            remoteInstallerConfiguration.Packages = remotePackages.ToArray ();
            Console.WriteLine ("we have {0} packages to install/verify", remoteInstallerConfiguration.Packages.Length);
            IDictionary<string, InstallerPackageTuple> packages = MergePackages (localInstallerConfiguration, remoteInstallerConfiguration);
            foreach (var p in packages.Values) {
                if ((p.Local != null) && (p.Remote == null)) {
                    DeleteEntirePackage (p.Local);
                }
                if ((p.Local == null) && (p.Remote != null)) {
                    DownloadEntirePackage (uri, p.Remote);
                }
                if ((p.Local != null) && (p.Remote != null)) {
                    DownloadDeltaPackage (uri, p.Local, p.Remote, p.files);
                }
                if ((p.Local == null) && (p.Remote == null)) {
                    throw new Exception ("What the f**k");
                }
            }
            return true;
        }
        static void Configure(Queue<string> queue)
        {
            string output = null;
            InstallerConfiguration installerConfiguration = new InstallerConfiguration ();
            bool autoDiscovery = false;
            string version = null;
            List<string> uris = new List<string> ();
            List<InstallerPackage> packages = new List<InstallerPackage> ();
            List<string> certificates = new List<string> ();
            List<string> ignore = new List<string> ();
            while (queue.Count > 0) {
                switch (queue.Dequeue ()) {
                case "--output":
                    output = queue.Dequeue ();
                    break;
                case "--auto-discovery":
                    autoDiscovery = true;
                    break;
                case "--certificate":
                    certificates.Add (queue.Dequeue ());
                    break;
                case "--version":
                    version = queue.Dequeue ();
                    break;
                case "--ignore":
                    while (!queue.Peek ().StartsWith ("--", StringComparison.Ordinal)) {
                        ignore.Add (queue.Dequeue ());
                    }
                    break;
                case "--package":
                    InstallerPackage package = new InstallerPackage ();
                    while (queue.Count > 0) {
                        string current = queue.Peek ();
                        if (current.StartsWith ("--", StringComparison.Ordinal)) {
                            break;
                        }
                        queue.Dequeue ();
                        const string name = "name=";
                        if (current.StartsWith (name, StringComparison.Ordinal)) {
                            package.Name = current.Substring (name.Length);
                        }
                        const string platform = "platform=";
                        if (current.StartsWith (platform, StringComparison.Ordinal)) {
                            package.Platform = (Platform)Enum.Parse (typeof(Platform), current.Substring (platform.Length));
                        }
                        const string bits = "bits=";
                        if (current.StartsWith (bits, StringComparison.Ordinal)) {
                            package.Bits = ushort.Parse (current.Substring (bits.Length));
                        }
                        const string installPath = "install-path=";
                        if (current.StartsWith (installPath, StringComparison.Ordinal)) {
                            package.InstallPath = current.Substring (installPath.Length);
                        }
                        const string remotePath = "remote-path=";
                        if (current.StartsWith (remotePath, StringComparison.Ordinal)) {
                            package.RemotePath = current.Substring (remotePath.Length);
                        }
                        const string executable = "executable=";
                        if (current.StartsWith (executable, StringComparison.Ordinal)) {
                            package.ExecutableName = current.Substring (executable.Length);
                        }
                    }
                    packages.Add (package);
                    break;
                case "--uri":
                    uris.Add (queue.Dequeue ());
                    break;
                }
            }
            Dictionary<string, InstallerPackage> pathes = new  Dictionary<string, InstallerPackage> ();

            foreach (InstallerPackage package in packages) {
                string prp = package.RemotePath;
                if (prp == "")
                    prp = "./";
                prp = Path.GetFullPath (prp);
                int packagePathLen = prp.Length;

                List<InstallerFile> files = new List<InstallerFile> ();
                foreach (var file in Directory.GetFiles (prp, "*", SearchOption.AllDirectories)) {
                    bool belongsToMe = !pathes.ContainsKey (file);
                    if (!belongsToMe) {
                        continue;
                    }
                    pathes.Add (file, package);
                    string relativePath = file.Substring (packagePathLen);
                    if (ignore.Contains (relativePath))
                        continue;
                    InstallerFile installerFile = new InstallerFile ();
                    installerFile.Name = relativePath;
                    using (Stream stream = File.OpenRead (file)) {
                        installerFile.SHA256SUM = ComputeHash (stream);
                    }
                    files.Add (installerFile);
                }
                package.Files = files.ToArray ();
            }

            installerConfiguration.AutoDiscovery = autoDiscovery;
            installerConfiguration.Version = version;
            installerConfiguration.Packages = packages.ToArray ();
            installerConfiguration.URIs = uris.ToArray ();
            installerConfiguration.Certificates = new byte[certificates.Count][];
            for (int i = 0; i != certificates.Count; i++)
                installerConfiguration.Certificates [i] = File.ReadAllBytes (certificates [i]);
            var xmlSerializer = CreateSerializer ();
            using (Stream outputStream = File.Open (output, FileMode.CreateNew)) {
                xmlSerializer.Serialize (outputStream, installerConfiguration);
                outputStream.Flush ();
            }
        }