コード例 #1
0
        public PackageData AddPackage(string archiveFileName)
        {
            // uncompress and verify package content
            string tempFolder = Path.Combine(Utility.GetTmpFolder(), Path.GetFileNameWithoutExtension(archiveFileName));

            Utility.FolderCleanUp(tempFolder);
            Utility.UncompressZip(archiveFileName, tempFolder);
            string packageJson = File.ReadAllText(Path.Combine(tempFolder, "package.json"));
            var    packageData = JsonConvert.DeserializeObject <PackageData>(packageJson);
            // TODO: Data normalization and check
            string programsDatabase = Path.Combine(tempFolder, "programs.xml");
            var    serializer1      = new XmlSerializer(typeof(List <ProgramBlock>));

            using (var reader = new StreamReader(programsDatabase))
            {
                // verify if package data checksum matches the one in programs.xml file
                var programs = (List <ProgramBlock>)serializer1.Deserialize(reader);
                foreach (var prg in packageData.Programs)
                {
                    // Get the program from programs.xml
                    var p1 = programs.Find((p) => p.Address.ToString() == prg.Hid);
                    if (p1 != null)
                    {
                        p1.PackageInfo.Checksum = Utility.GetObjectChecksum(new
                        {
                            setup  = p1.ScriptSetup,
                            source = p1.ScriptSource
                        });
                        if (p1.PackageInfo.Checksum != prg.Checksum)
                        {
                            //TODO: Integrity check failed, should abort package importing
                            //Console.WriteLine(p1.PackageInfo.Checksum);
                            //Console.WriteLine(prg.Checksum);
                        }
                    }
                }
            }
            // copy the extracted package folder to the repository folder
            string repositoryFolder = Path.Combine(Utility.GetDataFolder(), "packages", packageData.Repository);

            if (!Directory.Exists(repositoryFolder))
            {
                Directory.CreateDirectory(repositoryFolder);
            }
            string pkgFolder = Path.Combine(repositoryFolder, packageData.Id);

            if (Directory.Exists(pkgFolder))
            {
                Directory.Delete(pkgFolder, true);
            }
            Directory.Move(tempFolder, pkgFolder);
            return(packageData);
        }
コード例 #2
0
ファイル: UpdateChecker.cs プロジェクト: orf53975/HomeGenie
        public bool UpdatePrograms(string file)
        {
            bool success = true;

            try
            {
                var serializer     = new XmlSerializer(typeof(List <ProgramBlock>));
                var reader         = new StreamReader(file);
                var newProgramList = (List <ProgramBlock>)serializer.Deserialize(reader);
                reader.Close();
                //
                if (newProgramList.Count > 0)
                {
                    bool configChanged = false;
                    foreach (var program in newProgramList)
                    {
                        // Only system programs are to be updated
                        if (program.Address < ProgramManager.USERSPACE_PROGRAMS_START)
                        {
                            ProgramBlock oldProgram = homegenie.ProgramManager.Programs.Find(p => p.Address == program.Address);
                            if (oldProgram != null)
                            {
                                // Check new program against old one to find out if they differ
                                bool changed = ProgramsDiff(oldProgram, program);
                                if (!changed)
                                {
                                    continue;
                                }

                                // Preserve IsEnabled status if program already exist
                                program.IsEnabled = oldProgram.IsEnabled;
                                LogMessage("* Updating Automation Program: " + program.Name + " (" + program.Address + ")");
                                homegenie.ProgramManager.ProgramRemove(oldProgram);
                            }
                            else
                            {
                                LogMessage("+ Adding Automation Program: " + program.Name + " (" + program.Address + ")");
                            }

                            // Try copying the new program files (binary dll or arduino sketch files)
                            try
                            {
                                if (program.Type.ToLower() == "csharp")
                                {
                                    File.Copy(Path.Combine(UpdateBaseFolder, "programs", program.Address + ".dll"), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "programs", program.Address + ".dll"), true);
                                }
                                else if (program.Type.ToLower() == "arduino")
                                {
                                    // copy arduino project files...
                                    // TODO: this is untested yet
                                    string sourceFolder  = Path.Combine(UpdateBaseFolder, "programs", "arduino", program.Address.ToString());
                                    string arduinoFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "programs", "arduino", program.Address.ToString());
                                    Utility.FolderCleanUp(arduinoFolder);
                                    foreach (string newPath in Directory.GetFiles(sourceFolder))
                                    {
                                        File.Copy(newPath, newPath.Replace(sourceFolder, arduinoFolder), true);
                                        LogMessage("* Updating Automation Program: " + program.Name + " (" + program.Address + ") - " + Path.GetFileName(newPath));
                                    }
                                }
                            }
                            catch
                            {
                            }

                            // Add the new program to the ProgramEngine
                            homegenie.ProgramManager.ProgramAdd(program);

                            if (!configChanged)
                            {
                                configChanged = true;
                            }
                        }
                    }

                    if (configChanged)
                    {
                        // Save new programs config
                        homegenie.UpdateProgramsDatabase();
                    }
                }
                //
                File.Delete(file);
                if (Directory.Exists(Path.Combine(homegenie.UpdateChecker.UpdateBaseFolder, "programs")))
                {
                    Directory.Delete(Path.Combine(homegenie.UpdateChecker.UpdateBaseFolder, "programs"), true);
                }
            }
            catch
            {
                success = false;
            }

            if (!success)
            {
                LogMessage("+ ERROR updating Automation Programs");
            }
            return(success);
        }
コード例 #3
0
ファイル: UpdateChecker.cs プロジェクト: orf53975/HomeGenie
        public InstallStatus InstallFiles()
        {
            var    status            = InstallStatus.Success;
            bool   restartRequired   = false;
            string oldFilesPath      = Path.Combine("_update", "oldfiles");
            string newFilesPath      = Path.Combine("_update", "files", "HomeGenie_update");
            string fullReleaseFolder = Path.Combine("_update", "files", "homegenie");

            if (Directory.Exists(fullReleaseFolder))
            {
                Directory.Move(fullReleaseFolder, newFilesPath);
            }
            Utility.FolderCleanUp(oldFilesPath);
            if (Directory.Exists(newFilesPath))
            {
                LogMessage("= Copying new files...");
                foreach (string file in Directory.EnumerateFiles(newFilesPath, "*", SearchOption.AllDirectories))
                {
                    bool doNotCopy = false;

                    string destinationFolder = Path.GetDirectoryName(file).Replace(newFilesPath, "").TrimStart('/').TrimStart('\\');
                    string destinationFile   = Path.Combine(destinationFolder, Path.GetFileName(file)).TrimStart(Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()).ToArray()).TrimStart('/').TrimStart('\\');

                    // Update file only if different from local one
                    bool processFile = false;
                    if (File.Exists(destinationFile))
                    {
                        using (var md5 = MD5.Create())
                        {
                            string localHash, remoteHash = "";
                            try
                            {
                                // Try getting files' hash
                                using (var stream = File.OpenRead(destinationFile))
                                {
                                    localHash = BitConverter.ToString(md5.ComputeHash(stream));
                                }
                                using (var stream = File.OpenRead(file))
                                {
                                    remoteHash = BitConverter.ToString(md5.ComputeHash(stream));
                                }
                                if (localHash != remoteHash)
                                {
                                    processFile = true;
                                    //Console.WriteLine("CHANGED {0}", destinationFile);
                                    //Console.WriteLine("   - LOCAL  {0}", localHash);
                                    //Console.WriteLine("   - REMOTE {0}", remoteHash);
                                }
                            }
                            catch (Exception e)
                            {
                                // this mostly happen if the destinationFile is un use and cannot be opened,
                                // file is then ignored if hash cannot be calculated
                            }
                        }
                    }
                    else
                    {
                        processFile = true;
                        //Console.WriteLine("NEW FILE {0}", file);
                    }

                    if (!processFile)
                    {
                        continue;
                    }

                    // Some files needs to be handled differently than just copying
                    if (destinationFile.EndsWith(".xml") && File.Exists(destinationFile))
                    {
                        switch (destinationFile)
                        {
                        case "automationgroups.xml":
                            doNotCopy = true;
                            status    = UpdateAutomationGroups(file) ? InstallStatus.Success : InstallStatus.Error;;
                            break;

                        case "groups.xml":
                            doNotCopy = true;
                            status    = UpdateGroups(file) ? InstallStatus.Success : InstallStatus.Error;
                            break;

                        case "lircconfig.xml":
                            doNotCopy = true;
                            break;

                        case "modules.xml":
                            doNotCopy = true;
                            break;

                        case "programs.xml":
                            doNotCopy = true;
                            status    = UpdatePrograms(file) ? InstallStatus.Success : InstallStatus.Error;;
                            break;

                        case "scheduler.xml":
                            doNotCopy = true;
                            status    = UpdateScheduler(file) ? InstallStatus.Success : InstallStatus.Error;;
                            break;

                        case "systemconfig.xml":
                            doNotCopy = true;
                            status    = UpdateSystemConfig(file) ? InstallStatus.Success : InstallStatus.Error;;
                            break;
                        }
                        if (status == InstallStatus.Error)
                        {
                            break;
                        }
                    }
                    else if (destinationFile.EndsWith("homegenie_stats.db"))
                    {
                        doNotCopy = true;
                    }

                    if (doNotCopy)
                    {
                        continue;
                    }

                    // Update the file
                    if (destinationFile.EndsWith(".exe") || destinationFile.EndsWith(".dll") || destinationFile.EndsWith(".so"))
                    {
                        restartRequired = true;
                    }

                    if (!String.IsNullOrWhiteSpace(destinationFolder) && !Directory.Exists(destinationFolder))
                    {
                        Directory.CreateDirectory(destinationFolder);
                    }

                    // backup current file before replacing it
                    if (File.Exists(destinationFile))
                    {
                        string oldFile = Path.Combine(oldFilesPath, destinationFile);
                        Directory.CreateDirectory(Path.GetDirectoryName(oldFile));

                        LogMessage("+ Backup file '" + oldFile + "'");

                        // TODO: delete oldFilesPath before starting update
                        //File.Delete(oldFile);

                        if (destinationFile.EndsWith(".exe") || destinationFile.EndsWith(".dll"))
                        {
                            // this will allow replace of new exe and dll files
                            File.Move(destinationFile, oldFile);
                        }
                        else
                        {
                            File.Copy(destinationFile, oldFile);
                        }
                    }

                    try
                    {
                        LogMessage("+ Copying file '" + destinationFile + "'");
                        if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(destinationFile)) && !Directory.Exists(Path.GetDirectoryName(destinationFile)))
                        {
                            try
                            {
                                Directory.CreateDirectory(Path.GetDirectoryName(destinationFile));
                                LogMessage("+ Created folder '" + Path.GetDirectoryName(destinationFile) + "'");
                            }
                            catch
                            {
                            }
                        }
                        File.Copy(file, destinationFile, true);
                    }
                    catch (Exception e)
                    {
                        LogMessage("! Error copying file '" + destinationFile + "' (" + e.Message + ")");
                        status = InstallStatus.Error;
                        break;
                    }
                }

                if (status == InstallStatus.Error)
                {
                    // TODO: should revert!
                    LogMessage("! ERROR update aborted.");
                }
                else if (restartRequired)
                {
                    status = InstallStatus.RestartRequired;
                }
            }

            return(status);
        }
コード例 #4
0
ファイル: UpdateChecker.cs プロジェクト: xzflin/HomeGenie
        public bool InstallFiles()
        {
            bool success = true;

            string oldFilesPath = Path.Combine("_update", "oldfiles");

            Utility.FolderCleanUp(oldFilesPath);
            if (Directory.Exists(Path.Combine("_update", "files", "HomeGenie")))
            {
                LogMessage("= Copying new files...");
                foreach (string file in Directory.EnumerateFiles(Path.Combine("_update", "files", "HomeGenie"), "*", SearchOption.AllDirectories))
                {
                    bool doNotCopy = false;

                    string destinationFolder = Path.GetDirectoryName(file).Replace(Path.Combine("_update", "files", "HomeGenie"), "").TrimStart('/').TrimStart('\\');
                    if (!String.IsNullOrWhiteSpace(destinationFolder) && !Directory.Exists(destinationFolder))
                    {
                        Directory.CreateDirectory(destinationFolder);
                    }
                    string destinationFile = Path.Combine(destinationFolder, Path.GetFileName(file)).TrimStart(Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()).ToArray()).TrimStart('/').TrimStart('\\');

                    // backup current file before replacing it
                    if (File.Exists(destinationFile))
                    {
                        string oldFile = Path.Combine(oldFilesPath, destinationFile);
                        Directory.CreateDirectory(Path.GetDirectoryName(oldFile));

                        LogMessage("+ Backup file '" + oldFile + "'");

                        // TODO: delete oldFilesPath before starting update
                        //File.Delete(oldFile);

                        if (destinationFile.EndsWith(".exe") || destinationFile.EndsWith(".dll"))
                        {
                            // this will allow replace of new exe and dll files
                            File.Move(destinationFile, oldFile);
                        }
                        else
                        {
                            File.Copy(destinationFile, oldFile);
                        }
                    }

                    if (destinationFile.EndsWith(".xml") && File.Exists(destinationFile))
                    {
                        switch (destinationFile)
                        {
                        case "automationgroups.xml":
                            doNotCopy = true;
                            success   = UpdateAutomationGroups(file);
                            break;

                        case "groups.xml":
                            doNotCopy = true;
                            success   = UpdateGroups(file);
                            break;

                        case "lircconfig.xml":
                            doNotCopy = true;
                            break;

                        case "modules.xml":
                            doNotCopy = true;
                            break;

                        case "programs.xml":
                            doNotCopy = true;
                            success   = UpdatePrograms(file);
                            break;

                        case "scheduler.xml":
                            doNotCopy = true;
                            success   = UpdateScheduler(file);
                            break;

                        case "systemconfig.xml":
                            doNotCopy = true;
                            success   = UpdateSystemConfig(file);
                            break;
                        }
                        if (!success)
                        {
                            break;
                        }
                    }

                    if (!doNotCopy)
                    {
                        try
                        {
                            LogMessage("+ Copying file '" + destinationFile + "'");
                            if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName(destinationFile)) && !Directory.Exists(Path.GetDirectoryName(destinationFile)))
                            {
                                try
                                {
                                    Directory.CreateDirectory(Path.GetDirectoryName(destinationFile));
                                    LogMessage("+ Created folder '" + Path.GetDirectoryName(destinationFile) + "'");
                                }
                                catch { }
                            }
                            File.Copy(file, destinationFile, true);
                        }
                        catch (Exception e)
                        {
                            LogMessage("! Error copying file '" + destinationFile + "' (" + e.Message + ")");
                            success = false;
                            break;
                        }
                    }
                }

                if (!success)
                {
                    // TODO: should revert!
                    LogMessage("! ERROR update aborted.");
                }
            }

            return(success);
        }
コード例 #5
0
        public bool InstallPackage(string pkgFolderUrl, string tempFolderPath)
        {
            string  installFolder = Path.Combine(tempFolderPath, "pkg");
            dynamic pkgData       = null;
            bool    success       = true;

            // Download package specs
            homegenie.RaiseEvent(
                Domains.HomeGenie_System,
                Domains.HomeGenie_PackageInstaller,
                SourceModule.Master,
                "HomeGenie Package Installer",
                Properties.InstallProgressMessage,
                "= Downloading: package.json"
                );
            using (var client = new WebClient())
            {
                try
                {
                    string pkgJson = "[" + client.DownloadString(pkgFolderUrl + "/package.json") + "]";
                    pkgData = (JsonConvert.DeserializeObject(pkgJson) as JArray)[0];
                }
                catch (Exception e)
                {
                    homegenie.RaiseEvent(
                        Domains.HomeGenie_System,
                        Domains.HomeGenie_PackageInstaller,
                        SourceModule.Master,
                        "HomeGenie Package Installer",
                        Properties.InstallProgressMessage,
                        "= ERROR: '" + e.Message + "'"
                        );
                    success = false;
                }
                client.Dispose();
            }
            // Download and install package files
            if (success && pkgData != null)
            {
                // Import Automation Programs in package
                foreach (var program in pkgData.programs)
                {
                    homegenie.RaiseEvent(
                        Domains.HomeGenie_System,
                        Domains.HomeGenie_PackageInstaller,
                        SourceModule.Master,
                        "HomeGenie Package Installer",
                        Properties.InstallProgressMessage,
                        "= Downloading: " + program.file.ToString()
                        );
                    Utility.FolderCleanUp(installFolder);
                    string programFile = Path.Combine(installFolder, program.file.ToString());
                    if (File.Exists(programFile))
                    {
                        File.Delete(programFile);
                    }
                    using (var client = new WebClient())
                    {
                        try
                        {
                            client.DownloadFile(pkgFolderUrl + "/" + program.file.ToString(), programFile);
                        }
                        catch (Exception e)
                        {
                            homegenie.RaiseEvent(
                                Domains.HomeGenie_System,
                                Domains.HomeGenie_PackageInstaller,
                                SourceModule.Master,
                                "HomeGenie Package Installer",
                                Properties.InstallProgressMessage,
                                "= ERROR: '" + e.Message + "'"
                                );
                            success = false;
                        }
                        client.Dispose();
                    }
                    if (success)
                    {
                        homegenie.RaiseEvent(
                            Domains.HomeGenie_System,
                            Domains.HomeGenie_PackageInstaller,
                            SourceModule.Master,
                            "HomeGenie Package Installer",
                            Properties.InstallProgressMessage,
                            "= Installing: " + program.name.ToString()
                            );
                        int pid = int.Parse(program.uid.ToString());
                        // by default enable package programs after installing them
                        var enabled    = true;
                        var oldProgram = homegenie.ProgramManager.ProgramGet(pid);
                        if (oldProgram != null)
                        {
                            homegenie.RaiseEvent(
                                Domains.HomeGenie_System,
                                Domains.HomeGenie_PackageInstaller,
                                SourceModule.Master,
                                "HomeGenie Package Installer",
                                Properties.InstallProgressMessage,
                                "= Replacing: '" + oldProgram.Name + "' with pid " + pid
                                );
                            // if the program was already installed, inherit IsEnabled
                            enabled = oldProgram.IsEnabled;
                            homegenie.ProgramManager.ProgramRemove(oldProgram);
                        }
                        var programBlock = ProgramImport(pid, programFile, program.group.ToString());
                        if (programBlock != null)
                        {
                            string groupName = programBlock.Group;
                            if (!String.IsNullOrWhiteSpace(groupName))
                            {
                                // Add automation program group if does not exist
                                Group newGroup = new Group()
                                {
                                    Name = groupName
                                };
                                if (homegenie.AutomationGroups.Find(g => g.Name == newGroup.Name) == null)
                                {
                                    homegenie.AutomationGroups.Add(newGroup);
                                    homegenie.UpdateGroupsDatabase("Automation");
                                }
                            }
                            programBlock.IsEnabled = enabled;
                            homegenie.RaiseEvent(
                                Domains.HomeGenie_System,
                                Domains.HomeGenie_PackageInstaller,
                                SourceModule.Master,
                                "HomeGenie Package Installer",
                                Properties.InstallProgressMessage,
                                "= Installed: '" + program.name.ToString() + "' as pid " + pid
                                );
                        }
                        else
                        {
                            // TODO: report error and stop the package install procedure
                            success = false;
                        }
                    }
                }
                // Import Widgets in package
                foreach (var widget in pkgData.widgets)
                {
                    homegenie.RaiseEvent(
                        Domains.HomeGenie_System,
                        Domains.HomeGenie_PackageInstaller,
                        SourceModule.Master,
                        "HomeGenie Package Installer",
                        Properties.InstallProgressMessage,
                        "= Downloading: " + widget.file.ToString()
                        );
                    Utility.FolderCleanUp(installFolder);
                    string widgetFile = Path.Combine(installFolder, widget.file.ToString());
                    if (File.Exists(widgetFile))
                    {
                        File.Delete(widgetFile);
                    }
                    using (var client = new WebClient())
                    {
                        try
                        {
                            client.DownloadFile(pkgFolderUrl + "/" + widget.file.ToString(), widgetFile);
                        }
                        catch (Exception e)
                        {
                            homegenie.RaiseEvent(
                                Domains.HomeGenie_System,
                                Domains.HomeGenie_PackageInstaller,
                                SourceModule.Master,
                                "HomeGenie Package Installer",
                                Properties.InstallProgressMessage,
                                "= ERROR: '" + e.Message + "'"
                                );
                            success = false;
                        }
                        client.Dispose();
                    }
                    if (success && WidgetImport(widgetFile, installFolder))
                    {
                        homegenie.RaiseEvent(
                            Domains.HomeGenie_System,
                            Domains.HomeGenie_PackageInstaller,
                            SourceModule.Master,
                            "HomeGenie Package Installer",
                            Properties.InstallProgressMessage,
                            "= Installed: '" + widget.name.ToString() + "'"
                            );
                    }
                    else
                    {
                        // TODO: report error and stop the package install procedure
                        success = false;
                    }
                }
                // Import MIG Interfaces in package
                foreach (var migface in pkgData.interfaces)
                {
                    homegenie.RaiseEvent(
                        Domains.HomeGenie_System,
                        Domains.HomeGenie_PackageInstaller,
                        SourceModule.Master,
                        "HomeGenie Package Installer",
                        Properties.InstallProgressMessage,
                        "= Downloading: " + migface.file.ToString()
                        );
                    Utility.FolderCleanUp(installFolder);
                    string migfaceFile = Path.Combine(installFolder, migface.file.ToString());
                    if (File.Exists(migfaceFile))
                    {
                        File.Delete(migfaceFile);
                    }
                    using (var client = new WebClient())
                    {
                        try
                        {
                            client.DownloadFile(pkgFolderUrl + "/" + migface.file.ToString(), migfaceFile);
                            Utility.UncompressZip(migfaceFile, installFolder);
                            File.Delete(migfaceFile);
                        }
                        catch (Exception e)
                        {
                            homegenie.RaiseEvent(
                                Domains.HomeGenie_System,
                                Domains.HomeGenie_PackageInstaller,
                                SourceModule.Master,
                                "HomeGenie Package Installer",
                                Properties.InstallProgressMessage,
                                "= ERROR: '" + e.Message + "'"
                                );
                            success = false;
                        }
                        client.Dispose();
                    }
                    if (success && InterfaceInstall(installFolder))
                    {
                        homegenie.RaiseEvent(
                            Domains.HomeGenie_System,
                            Domains.HomeGenie_PackageInstaller,
                            SourceModule.Master,
                            "HomeGenie Package Installer",
                            Properties.InstallProgressMessage,
                            "= Installed: '" + migface.name.ToString() + "'"
                            );
                    }
                    else
                    {
                        // TODO: report error and stop the package install procedure
                        success = false;
                    }
                }
            }
            else
            {
                success = false;
            }
            if (success)
            {
                pkgData.folder_url   = pkgFolderUrl;
                pkgData.install_date = DateTime.UtcNow;
                AddInstalledPackage(pkgData);
                homegenie.RaiseEvent(
                    Domains.HomeGenie_System,
                    Domains.HomeGenie_PackageInstaller,
                    SourceModule.Master,
                    "HomeGenie Package Installer",
                    Properties.InstallProgressMessage,
                    "= Status: Package Install Successful"
                    );
            }
            else
            {
                homegenie.RaiseEvent(
                    Domains.HomeGenie_System,
                    Domains.HomeGenie_PackageInstaller,
                    SourceModule.Master,
                    "HomeGenie Package Installer",
                    Properties.InstallProgressMessage,
                    "= Status: Package Install Error"
                    );
            }

            return(success);
        }
コード例 #6
0
        public string CreatePackage(PackageData package)
        {
            string programsFile  = Path.Combine(Utility.GetTmpFolder(), "programs.xml");
            string modulesFile   = Path.Combine(Utility.GetTmpFolder(), "modules.xml");
            string groupsFile    = Path.Combine(Utility.GetTmpFolder(), "groups.xml");
            string schedulesFile = Path.Combine(Utility.GetTmpFolder(), "schedules.xml");
            string packageFile   = Path.Combine(Utility.GetTmpFolder(), "package.json");
            string bundleFile    = Path.Combine(Utility.GetTmpFolder(), package.Id + "-" + package.Version + ".zip");

            try
            {
                // Clean-up
                File.Delete(programsFile);
                File.Delete(packageFile);
                File.Delete(bundleFile);
                // collect programs and modules
                bool saveProgramsRequired = false;
                var  packagePrograms      = new List <ProgramBlock>();
                var  packageModules       = new List <Module>();
                foreach (var item in package.Programs)
                {
                    if (item.Repository != package.Repository || item.PackageId != package.Id)
                    {
                        // item is an external dependency belonging to some other repository/package
                        continue;
                    }
                    var program = homegenie.ProgramManager.ProgramGet(int.Parse(item.Hid));
                    if (program != null)
                    {
                        saveProgramsRequired = true;
                        //if (program.PackageInfo.Repository == null)
                        {
                            program.PackageInfo.Repository = package.Repository;
                        }
                        //if (program.PackageInfo.PackageId == null)
                        {
                            program.PackageInfo.PackageId = package.Id;
                        }
                        // update package version only if repository/package id match
                        if (program.PackageInfo.Repository == package.Repository && program.PackageInfo.PackageId == package.Id)
                        {
                            program.PackageInfo.PackageVersion = package.Version;
                        }
                        if (program.PackageInfo.Id == null)
                        {
                            program.PackageInfo.Id = item.Id;
                        }
                        program.PackageInfo.Version  = item.Version;
                        program.PackageInfo.Required = item.Required;
                        item.Checksum = program.PackageInfo.Checksum = Utility.GetObjectChecksum(new
                        {
                            setup  = program.ScriptSetup,
                            source = program.ScriptSource
                        });
                        packagePrograms.Add(program);
                        // lookup for modules belonging to this program
                        packageModules.AddRange(homegenie.Modules.FindAll((m) =>
                        {
                            var vm = Utility.ModuleParameterGet(m, Properties.VirtualModuleParentId);
                            return((m.Domain == Domains.HomeAutomation_HomeGenie && m.Address == program.Address.ToString()) ||
                                   (vm != null && vm.Value == program.Address.ToString()));
                        }));
                    }
                }
                Utility.UpdateXmlDatabase(packagePrograms, programsFile, null);
                Utility.UpdateXmlDatabase(packageModules, modulesFile, null);
                // collect control groups
                var packageGroups = new List <Group>();
                foreach (var item in package.Groups)
                {
                    var group = homegenie.GetGroups("Control").Find((g) => g.Name == item.Hid);
                    if (group != null)
                    {
                        packageGroups.Add(group);
                    }
                }
                Utility.UpdateXmlDatabase(packageGroups, groupsFile, null);
                // collect schedules
                var packageSchedules = new List <SchedulerItem>();
                foreach (var item in package.Schedules)
                {
                    var schedule = homegenie.ProgramManager.SchedulerService.Get(item.Hid);
                    if (schedule != null)
                    {
                        packageSchedules.Add(schedule);
                    }
                }
                Utility.UpdateXmlDatabase(packageSchedules, schedulesFile, null);
                // add files to zip bundle
                File.WriteAllText(packageFile, JsonConvert.SerializeObject(package));
                Utility.AddFileToZip(bundleFile, packageFile, "package.json");
                Utility.AddFileToZip(bundleFile, programsFile, "programs.xml");
                Utility.AddFileToZip(bundleFile, modulesFile, "modules.xml");
                Utility.AddFileToZip(bundleFile, groupsFile, "groups.xml");
                Utility.AddFileToZip(bundleFile, schedulesFile, "schedules.xml");
                // move files to package folder in data/packages
                string packageFolder = Path.Combine(Utility.GetDataFolder(), "packages", package.Repository, package.Id);
                Utility.FolderCleanUp(packageFolder);
                File.Move(packageFile, Path.Combine(packageFolder, "package.json"));
                File.Move(programsFile, Path.Combine(packageFolder, "programs.xml"));
                File.Move(modulesFile, Path.Combine(packageFolder, "modules.xml"));
                File.Move(groupsFile, Path.Combine(packageFolder, "groups.xml"));
                File.Move(schedulesFile, Path.Combine(packageFolder, "schedules.xml"));
                // update programs db if required
                if (saveProgramsRequired)
                {
                    homegenie.UpdateProgramsDatabase();
                }
            }
            catch (Exception e)
            {
                homegenie.RaiseEvent(
                    Domains.HomeGenie_System,
                    Domains.HomeGenie_PackageInstaller,
                    SourceModule.Master,
                    "HomeGenie Package Installer",
                    Properties.InstallProgressMessage,
                    "= Error: " + e.Message
                    );
                return(null);
            }
            // TODO: cleanup temp files and folders
            return(bundleFile);
        }