Пример #1
0
        /// <summary>
        /// Performs software updates.
        /// </summary>
        /// <param name="status">software status, as retrieved via SoftwareStatus.query()</param>
        /// <param name="timeoutPerUpdate">maximum time in seconds to wait per update</param>
        /// <returns>Returns the number of updated application in case of success.
        /// Returns a negative value equal to -1 - number of updated applications, if an error occurred.</returns>
        public static int update(List <QueryEntry> status, uint timeoutPerUpdate = defaultTimeout)
        {
            if (null == status)
            {
                return(-1);
            }
            if (status.Count == 0)
            {
                logger.Info("No known software was found, so no update will be performed.");
                return(-1);
            }
            // set some reasonable timeout, if necessary
            if (timeoutPerUpdate <= minimumTimeout)
            {
                timeoutPerUpdate = defaultTimeout;
            }

            int updatedApplications = 0;

            foreach (var entry in status)
            {
                if (!entry.needsUpdate)
                {
                    continue;
                }

                InstallInfo instInfo = null;
                switch (entry.type)
                {
                case ApplicationType.Bit32:
                    instInfo = entry.software.info().install32Bit;
                    break;

                case ApplicationType.Bit64:
                    instInfo = entry.software.info().install64Bit;
                    break;

                case ApplicationType.Unknown:
                    logger.Warn("Warning: Unknown application type detected for "
                                + entry.software.info().Name + "! Update will be skipped.");
                    continue;

                default:
                    logger.Warn("Warning: Unknown application type detected for "
                                + entry.software.info().Name + "! Update will be aborted.");
                    return(-1 - updatedApplications);
                }

                // If no verification method is provided, we do not even try to download the file.
                if (!instInfo.canBeVerified())
                {
                    logger.Error("Error: No checksum and no signature information for download of "
                                 + entry.software.info().Name + " is available!");
                    logger.Error("Since installing unverified software can"
                                 + " pose a security thread to your system, the update is cancelled.");
                    return(-1 - updatedApplications);
                }

                // check for blocking processes
                if (utility.Processes.processesExist(entry.software.blockerProcesses(entry.detected)))
                {
                    logger.Warn("Warning: At least one process was found that "
                                + "blocks the update of " + entry.software.info().Name
                                + "! Update will be omitted.");
                    continue;
                }

                // download file
                if (string.IsNullOrWhiteSpace(instInfo.downloadUrl))
                {
                    logger.Error("Error: No known download URL for " + entry.software.info().Name + "!");
                    return(-1 - updatedApplications);
                }
                logger.Info("Downloading " + instInfo.downloadUrl + "...");
                string downloadedFile = download(instInfo.downloadUrl);
                if (string.IsNullOrWhiteSpace(downloadedFile))
                {
                    logger.Error("Error: Could not download installer from " + instInfo.downloadUrl + "!");
                    return(-1 - updatedApplications);
                }

                // file verification
                bool verifiedChecksum  = false;
                bool verifiedSignature = false;
                // checksum verification
                if (instInfo.hasChecksum())
                {
                    // calculate checksum
                    logger.Info("Calculating checksum of " + downloadedFile + " ...");
                    string hash = utility.Checksum.calculate(downloadedFile, instInfo.algorithm);
                    if (string.IsNullOrWhiteSpace(hash))
                    {
                        logger.Error("Error: Could not calculate checksum of file " + downloadedFile + "!");
                        File.Delete(downloadedFile);
                        return(-1 - updatedApplications);
                    }
                    if (!utility.Checksum.areEqual(hash, instInfo.checksum))
                    {
                        logger.Error("Error: Checksum of file " + downloadedFile
                                     + " is " + hash + ", but expected checksum is " + instInfo.checksum + "!");
                        File.Delete(downloadedFile);
                        return(-1 - updatedApplications);
                    }
                    logger.Info("Info: Checksum of " + downloadedFile + " is correct.");
                    verifiedChecksum = true;
                } // if checksum

                // signature verification
                if (instInfo.hasVerifiableSignature())
                {
                    logger.Info("Verifying signature of " + downloadedFile + " ...");
                    if (!utility.Verificator.verifySignature(downloadedFile, instInfo.signature.publisher))
                    {
                        logger.Error("Error: Signature of file " + downloadedFile
                                     + " is invalid or missing! The file may also have the wrong publisher.");
                        File.Delete(downloadedFile);
                        return(-1 - updatedApplications);
                    }
                    logger.Info("Info: Signature and publisher of " + downloadedFile + " are correct.");
                    verifiedSignature = true;
                } // if signature
                if (!verifiedChecksum && !verifiedSignature)
                {
                    logger.Error("Error: Downloaded file " + downloadedFile
                                 + " could not be verified to be authentic!");
                    logger.Error("Since installing unverified software can"
                                 + " pose a security thread to your system, the update is cancelled.");
                    File.Delete(downloadedFile);
                    return(-1 - updatedApplications);
                }

                // Check for blocking processes - again, because download can take
                // enough time to start some new processes.
                if (utility.Processes.processesExist(entry.software.blockerProcesses(entry.detected)))
                {
                    logger.Warn("Warning: At least one process was found that "
                                + "blocks the update of " + entry.software.info().Name
                                + "! Update will be omitted.");
                    File.Delete(downloadedFile);
                    continue;
                }

                // start update process
                try
                {
                    // preparational process needed?
                    if (entry.software.needsPreUpdateProcess(entry.detected))
                    {
                        var preProcs = entry.software.preUpdateProcess(entry.detected);
                        if (null == preProcs)
                        {
                            logger.Error("Error: Pre-update process for "
                                         + entry.software.info().Name + " is null!");
                            return(-1 - updatedApplications);
                        }

                        foreach (System.Diagnostics.Process preProc in preProcs)
                        {
                            logger.Info("Info: Starting pre-update task for "
                                        + entry.software.info().Name + "...");
                            try
                            {
                                preProc.Start();
                                uint intervalCounter = 0;
                                do
                                {
                                    System.Threading.Thread.Sleep(1000);
                                    ++intervalCounter;
                                    if (preProc.HasExited)
                                    {
                                        logger.Info("Info: Pre-update process exited after "
                                                    + intervalCounter.ToString() + " second(s) with code "
                                                    + preProc.ExitCode.ToString() + ".");
                                        break;
                                    }
                                    // only wait up to timeoutPerUpdate seconds
                                } while (intervalCounter <= timeoutPerUpdate);
                                bool success = preProc.HasExited && (preProc.ExitCode == 0);
                                // Kill it, if it is not done yet.
                                if (!preProc.HasExited)
                                {
                                    logger.Error("Error: Killing pre-update process, because timeout has been reached.");
                                    preProc.Kill();
                                    return(-1 - updatedApplications);
                                }
                                if (!success)
                                {
                                    if (!entry.software.allowPreUpdateProcessFailure(entry.detected, preProc))
                                    {
                                        logger.Error("Error: Could not perform pre-update task for "
                                                     + entry.software.info().Name + ".");
                                        return(-1 - updatedApplications);
                                    }
                                    else
                                    {
                                        logger.Warn("Info: Pre-update task for "
                                                    + entry.software.info().Name + " failed, but that is allowed.");
                                    }
                                }
                            } // try-c
                            catch (Exception ex)
                            {
                                logger.Error("Error: An exception occurred while running a pre-update tast for "
                                             + entry.software.info().Name + ": " + ex.Message);
                                return(-1 - updatedApplications);
                            }
                        } // foreach
                    }     // if preparational process is needed

                    var proc = instInfo.createInstallProccess(downloadedFile, entry.detected);
                    if (null == proc)
                    {
                        // error while creating install process - should never happen
                        logger.Error("Error: Could not create install process for "
                                     + entry.software.info().Name + "!");
                        return(-1 - updatedApplications);
                    }

                    try
                    {
                        logger.Info("Info: Starting update of " + entry.software.info().Name + "...");
                        logger.Debug("Command line: " + proc.StartInfo.FileName + " " + proc.StartInfo.Arguments);
                        bool startedNew      = proc.Start();
                        uint intervalCounter = 0;
                        do
                        {
                            System.Threading.Thread.Sleep(1000);
                            ++intervalCounter;
                            if (proc.HasExited)
                            {
                                logger.Info("Info: Update process exited after "
                                            + intervalCounter.ToString() + " second(s) with code "
                                            + proc.ExitCode.ToString() + ".");
                                break;
                            }
                            // only wait up to timeoutPerUpdate seconds
                        } while (intervalCounter <= timeoutPerUpdate);
                        // Update was successful, if process has exited already,
                        // i.e. there was no timeout.
                        // Additionally, the exit code must be zero.
                        // However, for MSI processes the exit code 3010 means
                        // the the update succeeded, but a reboot is required.
                        bool success = proc.HasExited &&
                                       ((proc.ExitCode == 0) ||
                                        ((proc.ExitCode == InstallInfoMsi.successRebootRequired) && (instInfo is InstallInfoMsi)));
                        // Kill it, if it is not done yet.
                        if (!proc.HasExited)
                        {
                            logger.Warn("Warning: Killing update process, because timeout has been reached.");
                            proc.Kill();
                        }
                        if (success)
                        {
                            logger.Info("Info: Update of " + entry.software.info().Name + " was successful.");
                            ++updatedApplications;
                            if ((instInfo is InstallInfoMsi) && (proc.ExitCode == InstallInfoMsi.successRebootRequired))
                            {
                                logger.Warn("Warning: A reboot is required to"
                                            + " finish the update of " + entry.software.info().Name + ".");
                            } // if MSI installer requires reboot
                        }     // if success
                        else
                        {
                            logger.Error("Error: Could not update " + entry.software.info().Name + ".");
                            return(-1 - updatedApplications);
                        }
                    } // try-c
                    catch (Exception ex)
                    {
                        logger.Error("Error: Exception occurred while updating "
                                     + entry.software.info().Name + ": " + ex.Message);
                        return(-1 - updatedApplications);
                    } // try-catch
                }     // try-fin
                finally
                {
                    try
                    {
                        File.Delete(downloadedFile);
                    }
                    catch (Exception ex)
                    {
                        logger.Error("Error: Could not delete installer file "
                                     + downloadedFile + " after update: " + ex.Message);
                    }
                } // try-finally
            }     // foreach

            return(updatedApplications);
        }