示例#1
0
        /// <summary>
        /// Determines the download URL for a FileHippo application with the given ID.
        /// </summary>
        /// <param name="avoidBeta">Whether or not to avoid beta versions. If only beta versions are available, they will be downloaded anyways.</param>
        public static string FileHippoDownloadUrl(string fileId, bool avoidBeta)
        {
            fileId = fileId.ToLower();
            string url = GetFileHippoBaseDownloadUrl(fileId);

            // On the overview page, find the link to the
            // download page of the latest version
            string overviewPage;

            using (WebClient client = new WebClient())
            {
                overviewPage = client.DownloadString(url);

                // If FileHippo redirects to a new name, extract it the actual ID.
                if (client.ResponseUri != null)
                {
                    string newId = GetFileHippoIdFromUrl(client.ResponseUri.ToString());
                    if (!string.IsNullOrEmpty(newId) && GetFileHippoBaseDownloadUrl(newId) != url && newId != client.ResponseUri.ToString())
                    {
                        LogDialog.Log(string.Format("FileHippo ID '{0}' has been renamed to '{1}'.", fileId, newId));
                        fileId = newId;
                    }
                }
            }

            if (avoidBeta && FileHippoIsBeta(overviewPage))
            {
                overviewPage = GetNonBetaPageContent(overviewPage, fileId, false);
            }

            string findUrl = string.Format("/download_{0}/download/", GetFileHippoCleanFileId(fileId));
            int    pos     = overviewPage.IndexOf(findUrl);

            if (pos < 0)
            {
                throw new WebException("FileHippo ID '" + fileId + "' does not exist.", WebExceptionStatus.ReceiveFailure);
            }
            pos += findUrl.Length;

            string downloadUrl = GetFileHippoBaseDownloadUrl(fileId) + string.Format("download/{0}/", overviewPage.Substring(pos, 32)) + "?direct";

            // Now on the download page, find the link which redirects to the latest file
            string downloadPage;

            using (WebClient client = new WebClient())
            {
                downloadPage = client.DownloadString(downloadUrl);
            }

            findUrl = "/download/file/";
            pos     = downloadPage.IndexOf(findUrl);
            if (pos < 0)
            {
                return(string.Empty);
            }
            pos += findUrl.Length;
            string redirectUrl = string.Format("http://www.filehippo.com/download/file/{0}", downloadPage.Substring(pos, 64));

            return(redirectUrl);
        }
示例#2
0
        /// <summary>
        /// Checks for which of the given applications updates
        /// are available. Fires an event when finished.
        /// </summary>
        private void CheckForOnlineUpdates(object argument)
        {
            ApplicationJob[] jobs = argument as ApplicationJob[];

            // Build an array containing all GUIDs and dates
            List <RpcAppGuidAndDate> sendInfo = new List <RpcAppGuidAndDate>();

            foreach (ApplicationJob job in jobs.Where(job => !job.CanBeShared))
            {
                sendInfo.Add(new RpcAppGuidAndDate(job.Guid, job.DownloadDate));
            }

            if (sendInfo.Count == 0)
            {
                // Nothing to do
                return;
            }

            try
            {
                IGeGeekRpc proxy       = XmlRpcProxyGen.Create <IGeGeekRpc>();
                string[]   updatedApps = proxy.GetUpdatedApplications(sendInfo.ToArray());
                OnUpdatesFound(updatedApps);
            }
            catch (Exception ex)
            {
                // If updating fails, it does not hurt and should not annoy anyone.
                // Just write a log entry, just in case
                LogDialog.Log("Failed checking for online database updates", ex);
            }
        }
示例#3
0
        /// <summary>
        /// Determines the download URL for a FileHippo application with the given ID.
        /// </summary>
        /// <param name="avoidBeta">Whether or not to avoid beta versions. If only beta versions are available, they will be downloaded anyways.</param>
        public static string FileHippoDownloadUrl(string fileId, bool avoidBeta)
        {
            fileId = fileId.ToLower();
            string url = GetFileHippoBaseDownloadUrl(fileId);

            // On the overview page, find the link to the
            // download page of the latest version
            string overviewPage;

            using (WebClient client = new WebClient())
            {
                overviewPage = client.DownloadString(url);

                // If FileHippo redirects to a new name, extract it the actual ID.
                if (client.ResponseUri != null)
                {
                    string newId = GetFileHippoIdFromUrl(client.ResponseUri.ToString());
                    if (!string.IsNullOrEmpty(newId) && GetFileHippoBaseDownloadUrl(newId) != url && newId != client.ResponseUri.ToString())
                    {
                        LogDialog.Log(string.Format("FileHippo ID '{0}' has been renamed to '{1}'.", fileId, newId));
                        fileId = newId;
                    }
                }
            }

            if (avoidBeta && FileHippoIsBeta(overviewPage))
            {
                overviewPage = GetNonBetaPageContent(overviewPage, fileId, false);
            }

            string findUrl = string.Format("/download_{0}/post_download/", GetFileHippoCleanFileId(fileId));
            int    pos     = overviewPage.IndexOf(findUrl);

            if (pos < 0)
            {
                throw new WebException("FileHippo ID '" + fileId + "' does not exist.", WebExceptionStatus.ReceiveFailure);
            }

            using (WebClient client = new WebClient())
            {
                overviewPage = client.DownloadString($"https://filehippo.com/download_{GetFileHippoCleanFileId(fileId)}/post_download/");
            }

            // Now on the download page, find the link which redirects to the latest file
            // setTimeout(function() { downloadIframe.src = 'https://filehippo.com/launch_download/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
            string searchText = "downloadIframe.src";

            pos = overviewPage.IndexOf(searchText);
            int urlEndPos = overviewPage.IndexOf("'", pos + 30);

            if (pos < 0)
            {
                throw new WebException("Download URL for FileHippo ID '" + fileId + "' cannot be found.", WebExceptionStatus.ReceiveFailure);
            }

            string downloadUrl = overviewPage.Substring(pos + searchText.Length, urlEndPos - pos - searchText.Length).Trim().Trim('\'', '=', ' ');

            return(downloadUrl);
        }
示例#4
0
        internal void Execute(ApplicationJob application, ApplicationJobError errorInfo = null)
        {
            using (PowerShell powerShell = PowerShell.Create())
            {
                powerShell.AddScript(this.scriptText);

                // Make application object available to the script.
                if (application != null)
                {
                    powerShell.Runspace.SessionStateProxy.SetVariable("app", application);
                }

                if (errorInfo != null)
                {
                    powerShell.Runspace.SessionStateProxy.SetVariable("apperror", errorInfo);
                }

                powerShell.Runspace.SessionStateProxy.SetVariable("globalvars", UrlVariable.GlobalVariables);

                // Output all information we can get.
                powerShell.Streams.Debug.DataAdded   += this.DebugDataAdded;
                powerShell.Streams.Warning.DataAdded += this.WarningDataAdded;
                try
                {
                    powerShell.Streams.Information.DataAdded += this.InfoDataAdded;
                }
                catch (MissingMethodException)
                {
                    // Only supported in PS 5.0 and higher
                }

                Collection <PSObject> psOutput = powerShell.Invoke();

                foreach (PSObject outputItem in psOutput)
                {
                    // if null object was dumped to the pipeline during the script then a null
                    // object may be present here. check for null to prevent potential NRE.
                    if (outputItem != null)
                    {
                        this.LastOutput = outputItem.ToString();

                        LogDialog.Log("PowerShell: " + outputItem);
                    }
                }

                if (powerShell.HadErrors)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (ErrorRecord error in powerShell.Streams.Error)
                    {
                        sb.AppendLine(error.Exception.Message);
                    }

                    throw new ApplicationException(sb.ToString());
                }
            }
        }
        public static WebRequest CreateRequest(Uri location, ApplicationJob job, CookieContainer cookies)
        {
            WebRequest req = WebRequest.CreateDefault(WebClient.FixNoProtocolUri(location));

            req.Timeout = Convert.ToInt32(Settings.GetValue("ConnectionTimeout", 10)) * 1000;
            // 10 seconds by default

            HttpWebRequest httpRequest = req as HttpWebRequest;

            if (httpRequest != null)
            {
                httpRequest.Accept = "*/*";

                // Store cookies for future requests. Some sites
                // check for previously stored cookies before allowing to download.
                if (httpRequest.CookieContainer == null)
                {
                    httpRequest.CookieContainer = cookies;
                }
                else
                {
                    httpRequest.CookieContainer.Add(cookies.GetCookies(httpRequest.RequestUri));
                }

                // If we have an HTTP request, some sites may require a correct referer
                // for the download.
                // If there are variables defined (from which most likely the download link
                // or version is being extracted), we'll just use the first variable's URL as referer.
                // The user still has the option to set a custom referer.
                // Note: Some websites don't "like" certain referers
                if (!NoAutoReferer.Contains(GetBaseHost(req.RequestUri)))
                {
                    foreach (UrlVariable urlVar in job.Variables.Values)
                    {
                        httpRequest.Referer = urlVar.Url;
                        break;
                    }
                }

                if (!string.IsNullOrEmpty(job.HttpReferer))
                {
                    httpRequest.Referer = job.Variables.ReplaceAllInString(job.HttpReferer);
                }

                LogDialog.Log(job,
                              "Using referer: " + (string.IsNullOrEmpty(httpRequest.Referer) ? "(none)" : httpRequest.Referer));
                httpRequest.UserAgent = string.IsNullOrEmpty(job.UserAgent)
                    ? WebClient.DefaultUserAgent
                    : job.Variables.ReplaceAllInString(job.UserAgent);

                // PAD files may be compressed
                httpRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            }

            return(req);
        }
示例#6
0
        public override RemoteFileInfo GetFileInfo(ResourceLocation rl, out Stream stream)
        {
            WebRequest request = this.GetRequest(rl);

            RemoteFileInfo result   = new RemoteFileInfo();
            WebResponse    response = null;

            HttpWebRequest httpRequest = request as HttpWebRequest;

            if (httpRequest != null)
            {
                response            = request.GetResponse();
                result.MimeType     = response.ContentType;
                result.LastModified = ((HttpWebResponse)response).LastModified;
                result.FileSize     = response.ContentLength;
                result.AcceptRanges = string.Compare(response.Headers["Accept-Ranges"], "bytes", StringComparison.OrdinalIgnoreCase) == 0;
            }

            FtpWebRequest ftpRequest = request as FtpWebRequest;

            if (ftpRequest != null)
            {
                result.AcceptRanges = true;
                ftpRequest.Method   = WebRequestMethods.Ftp.GetFileSize;

                using (FtpWebResponse sizeResponse = (FtpWebResponse)ftpRequest.GetResponse())
                {
                    result.FileSize = sizeResponse.ContentLength;
                }

                ftpRequest = (FtpWebRequest)this.GetRequest(rl);

                ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp;
                using (FtpWebResponse dateResponse = (FtpWebResponse)ftpRequest.GetResponse())
                {
                    result.LastModified = dateResponse.LastModified;
                }

                // For FTP: Don't create download stream yet.
            }

            if (!result.AcceptRanges || response == null)
            {
                LogDialog.Log(this.job, $"Server for {rl.Url} does not support segmented transfer");
            }

            stream = response?.GetResponseStream();

            return(result);
        }
示例#7
0
        /// <summary>
        /// Handles download failure (set failed state, add to errors) and executes the "update failed"
        /// command for additional control.
        /// </summary>
        private void HandleUpdateFailed(ApplicationJob job, ApplicationJobError error)
        {
            // Execute: Default update failed command
            string     updateFailedCommand   = Settings.GetValue("UpdateFailedCommand", "") as string;
            ScriptType defaultPreCommandType = Command.ConvertToScriptType(Settings.GetValue("UpdateFailedCommandType", ScriptType.Batch.ToString()) as string);

            m_Status[job] = Status.Failure;

            if (!string.IsNullOrEmpty(updateFailedCommand))
            {
                int exitCode = new Command(updateFailedCommand, defaultPreCommandType).Execute(job, null, error);

                // Do not show failure in error window.
                if (exitCode == 1)
                {
                    LogDialog.Log(job, "Update failed command returned '1', ignoring error");
                    return;
                }
            }

            m_Errors.Add(error);
        }
示例#8
0
        /// <summary>
        /// Applies a function (if given) to content and returns the
        /// modified content.
        /// </summary>
        /// <param name="function">A function specification, for example "replace:a:b"</param>
        /// <param name="content">The usual variable content</param>
        private static string ReplaceFunction(string function, string content)
        {
            function = function.TrimStart(':');
            if (string.IsNullOrEmpty(function))
            {
                return(content);
            }

            string[] parts = SplitEscaped(function, ':');
            if (parts.Length == 0)
            {
                return(content);
            }

            switch (parts[0])
            {
            case "empty":
                // Useful, if you want to load, but not use a variable
                return(string.Empty);

            case "regexreplace":
                try
                {
                    if (parts.Length > 2)
                    {
                        Regex regex = new Regex(parts[1], RegexOptions.Singleline | RegexOptions.IgnoreCase);
                        return(regex.Replace(content, delegate(Match match) {
                            string result = parts[2];
                            for (int i = 0; i < match.Groups.Count; i++)
                            {
                                result = result.Replace("$" + i, match.Groups[i].Value);
                            }
                            return result;
                        }));
                    }
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regexreplace'.", ex);
                }
                return(string.Empty);

            case "multireplace":
            case "multireplacei":
                if (parts.Length > 3)
                {
                    if (string.IsNullOrEmpty(parts[1]))
                    {
                        break;
                    }

                    // Exmaple: multireplace:,:a,b,c:1,2,3
                    char delimiter = parts[1][0];

                    string[] search  = parts[2].Split(delimiter);
                    string[] replace = parts[3].Split(delimiter);
                    for (int i = 0; i < search.Length; i++)
                    {
                        string replaceValue = (replace.Length > i) ? replace[i] : string.Empty;
                        if (parts[0] == "multireplacei")
                        {
                            content = ReplaceEx(content, search[i], replaceValue);
                        }
                        else
                        {
                            content = content.Replace(search[i], replaceValue);
                        }
                    }

                    return(content);
                }
                break;

            case "regex":
                try
                {
                    Regex regex = new Regex(parts[1], RegexOptions.Singleline);
                    Match match = regex.Match(content);
                    if (parts.Length > 2)
                    {
                        int groupNum = Conversion.ToInt(parts[2]);
                        if (groupNum >= 0 && groupNum < match.Groups.Count)
                        {
                            return(match.Groups[groupNum].Value);
                        }
                    }
                    return((match.Success) ? match.Value : string.Empty);
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regex'.", ex);
                    return(string.Empty);
                }

            case "regexrandom":
                try
                {
                    Regex           regex   = new Regex(parts[1], RegexOptions.Singleline);
                    MatchCollection matches = regex.Matches(content);
                    if (matches.Count > 0)
                    {
                        int randomPos = random.Next(0, matches.Count - 1);
                        int groupNum  = (parts.Length > 2) ? Conversion.ToInt(parts[2]) : -1;

                        if (groupNum >= 0 && groupNum < matches[randomPos].Groups.Count)
                        {
                            return(matches[randomPos].Groups[groupNum].Value);
                        }
                        else
                        {
                            return(matches[randomPos].Value);
                        }
                    }
                    return(string.Empty);
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regex'.", ex);
                    return(string.Empty);
                }

            case "ext":
                return(Path.GetExtension(content).TrimStart('.'));

            case "basefile":
                return(Path.GetFileNameWithoutExtension(content));

            case "directory":
                try
                {
                    if (content.StartsWith("\"") && content.EndsWith("\""))
                    {
                        return("\"" + Path.GetDirectoryName(content.Trim('"')) + "\"");
                    }
                    else
                    {
                        return(Path.GetDirectoryName(content.Trim('"')));
                    }
                }
                catch
                {
                    return(content);
                }

            case "filename":
                try
                {
                    return(Path.GetFileName(content));
                }
                catch
                {
                    return(content);
                }

            case "filenameWithoutExtension":
                try
                {
                    return(Path.GetFileNameWithoutExtension(content));
                }
                catch
                {
                    return(content);
                }

            case "toupper":
                return(content.ToUpper());

            case "tolower":
                return(content.ToLower());

            case "split":
                if (parts.Length >= 3)
                {
                    string[] contentParts = content.Split(parts[1][0]);
                    int      partNum;
                    if (Int32.TryParse(parts[2], out partNum))
                    {
                        if (partNum < 0)
                        {
                            // Negative number: Count from the end
                            partNum = contentParts.Length + partNum;
                        }
                        if (partNum >= 0 && partNum < contentParts.Length)
                        {
                            return(contentParts[partNum]);
                        }
                    }
                }
                break;

            case "trim":
                if (parts.Length >= 2)
                {
                    return(content.Trim(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.Trim());
                }

            case "trimend":
                if (parts.Length >= 2)
                {
                    return(content.TrimEnd(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.TrimEnd());
                }

            case "trimstart":
                if (parts.Length >= 2)
                {
                    return(content.TrimStart(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.TrimStart());
                }

            case "replace":
                if (parts.Length >= 3)
                {
                    return(content.Replace(parts[1], parts[2]));
                }
                break;

            case "formatfilesize":
                return(CDBurnerXP.IO.FormatFileSize.Format(Conversion.ToInt(content)));

            case "startuppath":
                return(System.Windows.Forms.Application.StartupPath);

            case "urldecode":
                return(HttpUtility.UrlDecode(content));

            case "urlencode":
                return(HttpUtility.UrlEncode(content));
            }

            return(content);
        }
示例#9
0
        /// <summary>
        /// Executes a given command for the given application (also resolves variables).
        /// </summary>
        /// <returns>Exit code of the command, if not run in background</returns>
        private static int ExecuteBatchCommand(ApplicationJob job, string commandText, string targetFileName)
        {
            // Ignore empty commands
            if (string.IsNullOrEmpty(commandText))
            {
                return(0);
            }

            commandText = commandText.Replace("\r\n", "\n");

            // Job specific data
            if (job != null)
            {
                commandText = job.Variables.ReplaceAllInString(commandText, DateTime.MinValue, targetFileName, false);
            }
            else
            {
                commandText = UrlVariable.GlobalVariables.ReplaceAllInString(commandText);
            }

            // Replace variable: root
            try
            {
                commandText = UrlVariable.Replace(commandText, "root", Path.GetPathRoot(Application.StartupPath));
            }
            catch (ArgumentException) { }

            // Feed cmd.exe with our commands
            ProcessStartInfo cmdExe = new ProcessStartInfo("cmd.exe");

            cmdExe.RedirectStandardInput  = true;
            cmdExe.UseShellExecute        = false;
            cmdExe.CreateNoWindow         = true;
            cmdExe.RedirectStandardOutput = true;
            cmdExe.RedirectStandardError  = true;

            bool executeBackground = commandText.EndsWith("&");

            commandText = commandText.TrimEnd('&');

            using (Process proc = Process.Start(cmdExe))
            {
                StringBuilder commandResult = new StringBuilder();

                // Set the event handler to asynchronously read the command output.
                proc.OutputDataReceived += new DataReceivedEventHandler(delegate(object sendingProcess, DataReceivedEventArgs outLine)
                {
                    if (!string.IsNullOrEmpty(outLine.Data))
                    {
                        commandResult.AppendLine(outLine.Data);
                    }
                });
                proc.ErrorDataReceived += new DataReceivedEventHandler(delegate(object sendingProcess, DataReceivedEventArgs outLine)
                {
                    if (!string.IsNullOrEmpty(outLine.Data))
                    {
                        commandResult.AppendLine(outLine.Data);
                    }
                });

                // Start the asynchronous read of the command output stream.
                proc.BeginOutputReadLine();
                proc.BeginErrorReadLine();

                // Input commands
                using (proc.StandardInput)
                {
                    string[] commands = commandText.Split('\n');
                    foreach (string command in commands)
                    {
                        if (!string.IsNullOrEmpty(command))
                        {
                            LogDialog.Log(job, "Executing command: " + command);
                        }
                        proc.StandardInput.WriteLine(command);
                    }
                }

                // Read output
                if (!executeBackground)
                {
                    proc.WaitForExit();
                    string commandResultString = commandResult.ToString();
                    if (!string.IsNullOrEmpty(commandResultString))
                    {
                        LogDialog.Log(job, "Command result: " + commandResultString);
                    }
                    return(proc.ExitCode);
                }
            }

            return(0);
        }
示例#10
0
 private void DebugDataAdded(object sender, DataAddedEventArgs e)
 {
     LogDialog.Log("PowerShell (debug): " + ((PSDataCollection <DebugRecord>)sender)[e.Index].Message);
 }
示例#11
0
        /// <summary>
        /// Executes the actual download from an URL. Does not handle exceptions,
        /// but takes care of proper cleanup.
        /// </summary>
        /// <exception cref="NonBinaryFileException">This exception is thrown, if the resulting file is not of a binary type</exception>
        /// <exception cref="TargetPathInvalidException">This exception is thrown, if the resulting target path of an application is not valid</exception>
        /// <param name="job">The job to process</param>
        /// <param name="urlToRequest">URL from which should be downloaded</param>
        /// <returns>true, if a new update has been found and downloaded, false otherwise</returns>
        protected Status DoDownload(ApplicationJob job, Uri urlToRequest)
        {
            // Determine number of segments to create
            int segmentCount = Convert.ToInt32(Settings.GetValue("SegmentCount", 1));

            job.Variables.ResetDownloadCount();

            WebRequest req = GeGeekProtocolProvider.CreateRequest(urlToRequest, job, this.m_Cookies);

            AddRequestToCancel(req);

            using (WebResponse response = WebClient.GetResponse(req))
            {
                LogDialog.Log(job, "Server source file: " + req.RequestUri.AbsolutePath);

                // Occasionally, websites are not available and an error page is encountered
                // For the case that the content type is just plain wrong, ignore it if the size is higher than 500KB
                HttpWebResponse httpResponse = response as HttpWebResponse;
                if (httpResponse != null && response.ContentLength < 500000)
                {
                    if (response.ContentType.StartsWith("text/xml") || response.ContentType.StartsWith("application/xml"))
                    {
                        // If an XML file is served, maybe we have a PAD file
                        ApplicationJob padJob = ApplicationJob.ImportFromPad(httpResponse);
                        if (padJob != null)
                        {
                            job.CachedPadFileVersion = padJob.CachedPadFileVersion;
                            return(this.DoDownload(job, new Uri(padJob.FixedDownloadUrl)));
                        }
                    }

                    if (response.ContentType.StartsWith("text/html"))
                    {
                        bool avoidNonBinary = (bool)Settings.GetValue("AvoidDownloadingNonBinaryFiles", true);
                        if (httpResponse.StatusCode != HttpStatusCode.OK || avoidNonBinary)
                        {
                            throw NonBinaryFileException.Create(response.ContentType, httpResponse.StatusCode);
                        }
                    }
                }

                long fileSize = GetContentLength(response);
                if (fileSize == 0)
                {
                    throw new IOException("Source file on server is empty (ContentLength = 0).");
                }

                string targetFileName = job.GetTargetFile(response, urlToRequest.AbsoluteUri);

                LogDialog.Log(job, "Determined target file name: " + targetFileName);

                // Only download, if the file size or date has changed
                if (!ForceDownload && !job.RequiresDownload(response, targetFileName))
                {
                    // If file already exists (created by user),
                    // the download is not necessary. We still need to
                    // set the file name.
                    // If the file exists, but not at the target location
                    // (after renaming), do not reset the previous location.
                    if (File.Exists(targetFileName))
                    {
                        job.PreviousLocation = targetFileName;
                    }
                    job.Save();
                    return(Status.NoUpdate);
                }

                // Skip downloading!
                // Installing also requires a forced download
                if (!ForceDownload && !m_InstallUpdated && (m_OnlyCheck || (job.CheckForUpdatesOnly && !IgnoreCheckForUpdatesOnly)))
                {
                    LogDialog.Log(job, "Skipped downloading updates");
                    return(Status.UpdateAvailable);
                }

                // Execute: Default pre-update command
                string defaultPreCommand = Settings.GetValue("PreUpdateCommand", "") as string;
                // For starting external download managers: {preupdate-url}
                defaultPreCommand = UrlVariable.Replace(defaultPreCommand, "preupdate-url", urlToRequest.ToString(), job);
                ScriptType defaultPreCommandType = Command.ConvertToScriptType(Settings.GetValue("PreUpdateCommandType", ScriptType.Batch.ToString()) as string);

                int exitCode = new Command(defaultPreCommand, defaultPreCommandType).Execute(job, targetFileName);
                if (exitCode == 1)
                {
                    LogDialog.Log(job, "Default pre-update command returned '1', download aborted");
                    throw new CommandErrorException();
                }
                else if (exitCode == 2)
                {
                    LogDialog.Log(job, "Default pre-update command returned '2', download skipped");
                    return(Status.UpdateAvailable);
                }

                // Execute: Application pre-update command
                exitCode = new Command(UrlVariable.Replace(job.ExecutePreCommand, "preupdate-url", urlToRequest.ToString(), job), job.ExecutePreCommandType).Execute(job, targetFileName);
                if (exitCode == 1)
                {
                    LogDialog.Log(job, "Pre-update command returned '1', download aborted");
                    throw new CommandErrorException();
                }
                else if (exitCode == 2)
                {
                    LogDialog.Log(job, "Pre-update command returned '2', download skipped");
                    return(Status.UpdateAvailable);
                }
                else if (exitCode == 3)
                {
                    LogDialog.Log(job, "Pre-update command returned '3', external download");
                    job.LastUpdated = DateTime.Now;
                    job.Save();
                    return(Status.UpdateSuccessful);
                }

                // Read all file contents to a temporary location
                string   tmpLocation   = Path.GetTempFileName();
                DateTime lastWriteTime = ApplicationJob.GetLastModified(response);

                // Only use segmented downloader with more than one segment.
                if (segmentCount > 1)
                {
                    // Response can be closed now, new one will be created.
                    response.Dispose();

                    m_Size[job] = fileSize;

                    Downloader d = new Downloader(new ResourceLocation {
                        Url = urlToRequest.AbsoluteUri, ProtocolProvider = new GeGeekProtocolProvider(job, m_Cookies)
                    }, null, tmpLocation, segmentCount);
                    d.Start();

                    while (d.State < DownloaderState.Ended)
                    {
                        if (m_CancelUpdates)
                        {
                            d.Pause();
                            break;
                        }

                        this.OnProgressChanged(d.Segments.Sum(x => x.Transfered), fileSize, job);
                        Thread.Sleep(250);
                    }

                    if (d.State == DownloaderState.EndedWithError)
                    {
                        throw d.LastError;
                    }
                }
                else
                {
                    // Read contents from the web and put into file
                    using (Stream sourceFile = response.GetResponseStream())
                    {
                        using (FileStream targetFile = File.Create(tmpLocation))
                        {
                            long byteCount = 0;
                            int  readBytes;
                            m_Size[job] = fileSize;

                            // Only create buffer once and re-use.
                            const int bufferSize = 1024 * 1024;
                            byte[]    buffer     = new byte[bufferSize];

                            do
                            {
                                if (m_CancelUpdates)
                                {
                                    break;
                                }

                                // Some adjustment for SCP download: Read only up to the max known bytes
                                int maxRead = (fileSize > 0) ? (int)Math.Min(fileSize - byteCount, bufferSize) : bufferSize;
                                if (maxRead == 0)
                                {
                                    break;
                                }

                                readBytes = sourceFile.Read(buffer, 0, maxRead);
                                if (readBytes > 0)
                                {
                                    targetFile.Write(buffer, 0, readBytes);
                                }
                                byteCount += readBytes;

                                this.OnProgressChanged(byteCount, fileSize, job);
                            } while (readBytes > 0);
                        }
                    }
                }

                if (m_CancelUpdates)
                {
                    m_Progress[job] = 0;
                    OnStatusChanged(job);
                    return(Status.Failure);
                }

                // If each version has a different file name (version number),
                // we might only want to keep one of them. Also, we might
                // want to free some space on the target location.
                if (job.DeletePreviousFile)
                {
                    PathEx.TryDeleteFiles(job.PreviousLocation);
                }

                try
                {
                    File.SetLastWriteTime(tmpLocation, lastWriteTime);
                }
                catch (ArgumentException)
                {
                    // Invalid file date. Ignore and just use DateTime.Now
                }

                // File downloaded. Now let's check if the hash value is valid or abort otherwise!
                if (!string.IsNullOrEmpty(job.HashVariable) && job.HashType != HashType.None)
                {
                    string varName      = job.HashVariable.Trim('{', '}');
                    string expectedHash = job.Variables.ReplaceAllInString("{" + varName + "}").Trim();

                    // Compare online hash with actual current hash.
                    if (!string.IsNullOrEmpty(expectedHash))
                    {
                        string currentHash = job.GetFileHash(tmpLocation);
                        if (string.Compare(expectedHash, currentHash, StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            LogDialog.Log(job, string.Format("File downloaded, but hash of downloaded file {0} does not match the expected hash {1}.", currentHash, expectedHash));
                            File.Delete(tmpLocation);
                            throw new IOException("Hash verification failed.");
                        }
                    }
                }

                try
                {
                    FileInfo downloadedFileInfo = new FileInfo(tmpLocation);
                    job.LastFileSize = downloadedFileInfo.Length;
                    job.LastFileDate = downloadedFileInfo.LastWriteTime;
                }
                catch (Exception ex)
                {
                    LogDialog.Log(job, ex);
                }

                try
                {
                    // Before copying, we might have to create the directory
                    Directory.CreateDirectory(Path.GetDirectoryName(targetFileName));

                    // Copying might fail if variables have been replaced with bad values.
                    // However, we cannot rely on functions to clean up the path, since they
                    // might actually parse the path incorrectly and return an even worse path.
                    File.Copy(tmpLocation, targetFileName, true);
                }
                catch (ArgumentException)
                {
                    throw new TargetPathInvalidException(targetFileName);
                }
                catch (NotSupportedException)
                {
                    throw new TargetPathInvalidException(targetFileName);
                }

                File.Delete(tmpLocation);

                // At this point, the update is complete
                job.LastUpdated      = DateTime.Now;
                job.PreviousLocation = targetFileName;
            }

            job.Save();

            job.ExecutePostUpdateCommands();

            return(Status.UpdateSuccessful);
        }
示例#12
0
        /// <summary>
        /// Performs the update process of a single application.
        /// Catches most exceptions and stores them for later use.
        /// </summary>
        private void StartNewThread(object paramJob)
        {
            ApplicationJob job = paramJob as ApplicationJob;

            m_Status[job] = Status.Downloading;
            OnStatusChanged(job);

            string requestedUrl = string.Empty;
            int    numTries     = 0;
            int    maxTries     = Convert.ToInt32(Settings.GetValue("RetryCount", 1));

            try
            {
                while (numTries < maxTries)
                {
                    try
                    {
                        numTries++;
                        m_Status[job] = DoDownload(job, out requestedUrl);

                        // If there is a custom column variable, and it has not been been downloaded yet,
                        // make sure that we fetch it now "unnecessarily" so that the column contains a current value.
                        Dictionary <string, string> customColumns = SettingsDialog.CustomColumns;
                        foreach (KeyValuePair <string, string> column in customColumns)
                        {
                            if (!string.IsNullOrEmpty(column.Value) && !job.Variables.HasVariableBeenDownloaded(column.Value))
                            {
                                job.Variables.ReplaceAllInString("{" + column.Value.TrimStart('{').TrimEnd('}') + "}");
                            }
                        }
                        if (customColumns.Count > 0)
                        {
                            job.Save(); // cached variable content
                        }

                        // Install if updated
                        if (m_InstallUpdated && m_Status[job] == Status.UpdateSuccessful)
                        {
                            job.Install(null);
                        }

                        // If no exception happened, we immediately leave the loop
                        break;
                    }
                    catch (SQLiteException ex)
                    {
                        // If "locked" exception (slow USB device eg.) continue trying
                        if (ex.ErrorCode == (int)SQLiteErrorCode.Locked)
                        {
                            numTries--;
                            LogDialog.Log(job, ex);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    catch (Exception ex)
                    {
                        WebException webException = ex as WebException;
                        if (webException != null && webException.Status == WebExceptionStatus.RequestCanceled)
                        {
                            // User cancelled the process -> Do nothing
                            m_Status[job] = Status.Failure;
                            break;
                        }

                        // Only throw an exception if we have run out of tries
                        if (numTries == maxTries)
                        {
                            throw;
                        }
                        else
                        {
                            LogDialog.Log(job, ex);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                LogDialog.Log(job, ex);

                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, (ex.Response != null) ? ex.Response.ResponseUri.ToString() : requestedUrl));
            }
            catch (FileNotFoundException ex)
            {
                // Executing command failed
                LogDialog.Log(job, ex);
                m_Errors.Add(new ApplicationJobError(job, ex));
            }
            catch (Win32Exception ex)
            {
                // Executing command failed
                LogDialog.Log(job, ex);
                m_Errors.Add(new ApplicationJobError(job, ex));
            }
            catch (IOException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex));
            }
            catch (UnauthorizedAccessException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex));
            }
            catch (UriFormatException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }
            catch (NotSupportedException ex)
            {
                // Invalid URI prefix
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }
            catch (NonBinaryFileException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }
            catch (TargetPathInvalidException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }
            catch (CommandErrorException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }
            catch (ApplicationException ex)
            {
                // Error executing custom C# script
                LogDialog.Log(job, ex);
                m_Errors.Add(new ApplicationJobError(job, ex));
            }
            catch (SQLiteException ex)
            {
                LogDialog.Log(job, ex);
                this.HandleUpdateFailed(job, new ApplicationJobError(job, ex, requestedUrl));
            }

            m_Progress[job] = 100;
            OnStatusChanged(job);
        }
示例#13
0
        /// <summary>
        /// Performs the actual update check for the current applications.
        /// Starts multiple threads if necessary.
        /// </summary>
        private void UpdateApplications()
        {
            m_CancelUpdates = false;
            m_Errors        = new List <ApplicationJobError>();
            LogDialog.Log(string.Format("Update started with {0} application(s)", m_Jobs.Length));

            try
            {
                ApplicationJob previousJob = null;

                foreach (ApplicationJob job in m_Jobs)
                {
                    // Skip if disabled
                    if (!job.Enabled && m_Jobs.Length > 1)
                    {
                        continue;
                    }

                    // Wait until we can start a new thread:
                    // - Thread limit is not reached
                    // - The next application is not to be downloaded exclusively
                    // - The application previously started is not to be downloaded exclusively
                    // - Setup is taking place
                    while (m_Threads.Count >= m_ThreadLimit || (m_Threads.Count > 0 && (m_InstallUpdated || job.ExclusiveDownload || (previousJob != null && previousJob.ExclusiveDownload))))
                    {
                        Thread.Sleep(200);

                        foreach (Thread activeThread in m_Threads)
                        {
                            if (!activeThread.IsAlive)
                            {
                                m_Threads.Remove(activeThread);
                                break;
                            }
                        }
                    }

                    // Stop if cancelled
                    if (m_CancelUpdates)
                    {
                        break;
                    }

                    Thread newThread = new Thread(this.StartNewThread);
                    previousJob = job;
                    newThread.Start(job);
                    m_Threads.Add(newThread);
                }

                // Now, wait until all threads have finished
                while (m_Threads.Count > 0)
                {
                    Thread.Sleep(200);

                    foreach (Thread activeThread in m_Threads)
                    {
                        if (!activeThread.IsAlive)
                        {
                            m_Threads.Remove(activeThread);
                            break;
                        }
                    }
                }

                try
                {
                    string     postUpdateCommand     = Settings.GetValue("PostUpdateCommand", "") as string;
                    ScriptType postUpdateCommandType = Command.ConvertToScriptType(Settings.GetValue("PostUpdateCommandType", ScriptType.Batch.ToString()) as string);
                    new Command(postUpdateCommand, postUpdateCommandType).Execute(null);
                }
                catch (ApplicationException ex)
                {
                    LogDialog.Log("Post update command failed.", ex);
                }

                LogDialog.Log("Update finished");
            }
            finally
            {
                IsBusy = false;
                m_Progress.Clear();
                m_Size.Clear();
                OnUpdateCompleted();
            }
        }
 private void InfoDataAdded(object sender, DataAddedEventArgs e)
 {
     LogDialog.Log("PowerShell (info): " + ((PSDataCollection <InformationRecord>)sender)[e.Index].MessageData);
 }
示例#15
0
        /// <summary>
        /// Applies a function (if given) to content and returns the
        /// modified content.
        /// </summary>
        /// <param name="function">A function specification, for example "replace:a:b"</param>
        /// <param name="content">The usual variable content</param>
        /// <param name="context">ApplicationJob context for referencing values of other variables</param>
        private static string ReplaceFunction(string function, string content, ApplicationJob context = null)
        {
            function = function.TrimStart(':');
            if (string.IsNullOrEmpty(function))
            {
                return(content);
            }

            string[] parts = SplitEscaped(function, ':');
            if (parts.Length == 0)
            {
                return(content);
            }

            switch (parts[0])
            {
            case "runpowershell":
            case "ps":
                // PowerShell is broken because Stream "Information" not supportable.
                LogDialog.Log(context, "PowerShell commands not supported at present.");
                return(string.Empty);

            /*
             * try
             * {
             *  if (context != null && !context.CanBeShared)
             *  {
             *      LogDialog.Log(context, "PowerShell command of downloaded application is not executed for security reasons.");
             *      return string.Empty;
             *  }
             *
             *  PowerShellScript psScript = new PowerShellScript(content);
             *  psScript.Execute(context);
             *  return psScript.LastOutput;
             * }
             * catch
             * {
             *  return string.Empty;
             * }
             */

            case "empty":
                // Useful, if you want to load, but not use a variable
                return(string.Empty);

            case "ifempty":
                if (string.IsNullOrEmpty(content) && context != null && parts.Length > 1)
                {
                    return(context.Variables.ReplaceAllInString("{" + parts[1] + "}"));
                }

                return(content);

            case "ifemptythenerror":
                if (string.IsNullOrEmpty(content))
                {
                    throw new VariableIsEmptyException();
                }
                return(content);

            case "regexreplace":
                try
                {
                    if (parts.Length > 2)
                    {
                        Regex regex = new Regex(parts[1], RegexOptions.Singleline | RegexOptions.IgnoreCase);
                        return(regex.Replace(content, delegate(Match match) {
                            string result = parts[2];
                            for (int i = 0; i < match.Groups.Count; i++)
                            {
                                result = result.Replace("$" + i, match.Groups[i].Value);
                            }
                            return result;
                        }));
                    }
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regexreplace'.", ex);
                }
                return(string.Empty);

            case "multireplace":
            case "multireplacei":
                if (parts.Length > 3)
                {
                    if (string.IsNullOrEmpty(parts[1]))
                    {
                        break;
                    }

                    // Exmaple: multireplace:,:a,b,c:1,2,3
                    char delimiter = parts[1][0];

                    string[] search  = parts[2].Split(delimiter);
                    string[] replace = parts[3].Split(delimiter);
                    for (int i = 0; i < search.Length; i++)
                    {
                        string replaceValue = (replace.Length > i) ? replace[i] : string.Empty;
                        content = parts[0] == "multireplacei" ? ReplaceEx(content, search[i], replaceValue) : content.Replace(search[i], replaceValue);
                    }

                    return(content);
                }
                break;

            case "regex":
                try
                {
                    Regex regex = new Regex(parts[1], RegexOptions.Singleline);
                    Match match = regex.Match(content);
                    if (parts.Length > 2)
                    {
                        int groupNum = Conversion.ToInt(parts[2]);
                        if (groupNum >= 0 && groupNum < match.Groups.Count)
                        {
                            return(match.Groups[groupNum].Value);
                        }
                    }
                    return((match.Success) ? match.Value : string.Empty);
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regex'.", ex);
                    return(string.Empty);
                }

            case "regexrandom":
                try
                {
                    Regex           regex   = new Regex(parts[1], RegexOptions.Singleline);
                    MatchCollection matches = regex.Matches(content);
                    if (matches.Count > 0)
                    {
                        int randomPos = random.Next(0, matches.Count - 1);
                        int groupNum  = (parts.Length > 2) ? Conversion.ToInt(parts[2]) : -1;

                        if (groupNum >= 0 && groupNum < matches[randomPos].Groups.Count)
                        {
                            return(matches[randomPos].Groups[groupNum].Value);
                        }
                        else
                        {
                            return(matches[randomPos].Value);
                        }
                    }
                    return(string.Empty);
                }
                catch (ArgumentException ex)
                {
                    LogDialog.Log("Could not process the function 'regex'.", ex);
                    return(string.Empty);
                }

            case "ext":
                return(Path.GetExtension(content).TrimStart('.'));

            case "basefile":
                return(Path.GetFileNameWithoutExtension(content));

            case "directory":
                try
                {
                    if (content.StartsWith("\"") && content.EndsWith("\""))
                    {
                        return("\"" + Path.GetDirectoryName(content.Trim('"')) + "\"");
                    }
                    else
                    {
                        return(Path.GetDirectoryName(content.Trim('"')));
                    }
                }
                catch
                {
                    return(content);
                }

            case "filename":
                try
                {
                    return(Path.GetFileName(content));
                }
                catch
                {
                    return(content);
                }

            case "filenameWithoutExtension":
                try
                {
                    return(Path.GetFileNameWithoutExtension(content));
                }
                catch
                {
                    return(content);
                }

            case "toupper":
                return(content.ToUpper());

            case "tolower":
                return(content.ToLower());

            case "split":
                if (parts.Length >= 3)
                {
                    string[] contentParts = content.Split(parts[1][0]);
                    int      partNum;
                    if (Int32.TryParse(parts[2], out partNum))
                    {
                        if (partNum < 0)
                        {
                            // Negative number: Count from the end
                            partNum = contentParts.Length + partNum;
                        }
                        if (partNum >= 0 && partNum < contentParts.Length)
                        {
                            return(contentParts[partNum]);
                        }
                    }
                }
                break;

            case "trim":
                if (parts.Length >= 2)
                {
                    return(content.Trim(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.Trim());
                }

            case "padleft":
                if (parts.Length == 3)
                {
                    return(content.PadLeft(Conversion.ToInt(parts[1]), parts[2][0]));
                }
                else if (parts.Length == 2)
                {
                    return(content.PadLeft(Conversion.ToInt(parts[1]), ' '));
                }

                return(content);

            case "padright":
                if (parts.Length == 3)
                {
                    return(content.PadRight(Conversion.ToInt(parts[1]), parts[2][0]));
                }
                else if (parts.Length == 2)
                {
                    return(content.PadRight(Conversion.ToInt(parts[1]), ' '));
                }

                return(content);

            case "trimend":
                if (parts.Length >= 2)
                {
                    return(content.TrimEnd(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.TrimEnd());
                }

            case "trimstart":
                if (parts.Length >= 2)
                {
                    return(content.TrimStart(parts[1].ToCharArray()));
                }
                else
                {
                    return(content.TrimStart());
                }

            case "replace":
                if (parts.Length >= 3)
                {
                    return(content.Replace(parts[1], parts[2]));
                }
                break;

            case "formatfilesize":
                return(FormatFileSize.Format(Conversion.ToInt(content)));

            case "startuppath":
                return(Application.StartupPath);

            case "urldecode":
                return(HttpUtility.UrlDecode(content));

            case "urlencode":
                return(HttpUtility.UrlEncode(content));
            }

            return(content);
        }
示例#16
0
        /// <summary>
        /// Replaces this variable within a given string.
        /// </summary>
        /// <param name="onlyCached">If true, no new content will be downloaded and only chached content will be used.</param>
        /// <param name="fileDate">Current file date, when downloading the modification date of the file being downloaded</param>
        public virtual string ReplaceInString(string value, DateTime fileDate, bool onlyCached)
        {
            if (!IsVariableUsedInString(m_Name, value))
            {
                return(value);
            }

            // Global variable only has static content
            if (onlyCached)
            {
                return((m_CachedContent == null) ? value : Replace(value, m_CachedContent, this.Parent == null ? null : this.Parent.Parent));
            }

            // Ignore missing URLs etc.
            if (IsEmpty)
            {
                return(value);
            }

            // Using textual content?
            if (m_VariableType == Type.Textual)
            {
                m_CachedContent = GetExpandedTextualContent(fileDate);
                LogDialog.Log(this, value, m_CachedContent);
                return(Replace(value, m_CachedContent, this.Parent == null ? null : this.Parent.Parent));
            }

            string page = string.Empty;
            // Get the content we need to put in
            string userAgent = (this.Parent == null) ? null : this.Parent.Parent.Variables.ReplaceAllInString(this.Parent.Parent.UserAgent);

            using (WebClient client = new WebClient(userAgent))
            {
                try
                {
                    string url = ExpandedUrl;
                    client.SetPostData(this);
                    page = client.DownloadString(url);
                }
                catch (ArgumentException)
                {
                    throw new UriFormatException("The URL '" + Url + "' of variable '" + m_Name + "' is not valid.");
                }
                m_DownloadCount++;
            }

            // Normalise line-breaks
            page = page.Replace("\r\n", "\n");
            page = page.Replace("\r", "\n");

            // Using a regular expression?
            if (m_VariableType == Type.RegularExpression)
            {
                Regex regex = CreateRegex();
                if (regex == null)
                {
                    return(value);
                }

                Match match = regex.Match(page);
                if (match.Success)
                {
                    if (match.Groups.Count == 1)
                    {
                        m_CachedContent = match.Value;
                        LogDialog.Log(this, value, m_CachedContent);
                        return(Replace(value, match.Value));
                    }
                    else if (match.Groups.Count >= 2)
                    {
                        // Use first group (except index 0, which is complete match) with a match
                        for (int i = 1; i < match.Groups.Count; i++)
                        {
                            if (match.Groups[i].Success)
                            {
                                m_CachedContent = match.Groups[i].Value;
                                LogDialog.Log(this, value, m_CachedContent);
                                return(Replace(value, match.Groups[i].Value));
                            }
                        }

                        // No group matches, use complete match
                        m_CachedContent = match.Groups[0].Value;
                        LogDialog.Log(this, value, m_CachedContent);
                        return(Replace(value, match.Groups[0].Value));
                    }
                }
                else
                {
                    // No regex match should yield an empty result
                    return(Replace(value, string.Empty));
                }
            }

            // Use whole page if either start or end is missing
            if (string.IsNullOrEmpty(m_StartText) || string.IsNullOrEmpty(m_EndText))
            {
                m_CachedContent = page;
                LogDialog.Log(this, value, m_CachedContent);
                return(Replace(value, page));
            }

            int startPos = page.IndexOf(m_StartText);

            if (startPos < 0)
            {
                return(value);
            }

            int endOfStart = startPos + m_StartText.Length;

            int endPos = page.IndexOf(m_EndText, endOfStart);

            if (endPos < 0)
            {
                return(value);
            }

            string result = page.Substring(endOfStart, endPos - endOfStart);

            m_CachedContent = result;
            LogDialog.Log(this, value, m_CachedContent);
            value = Replace(value, result);

            return(value);
        }
示例#17
0
        /// <summary>
        /// Executes the actual download from an URL. Does not handle exceptions,
        /// but takes care of proper cleanup.
        /// </summary>
        /// <exception cref="NonBinaryFileException">This exception is thrown, if the resulting file is not of a binary type</exception>
        /// <exception cref="TargetPathInvalidException">This exception is thrown, if the resulting target path of an application is not valid</exception>
        /// <param name="job">The job to process</param>
        /// <param name="urlToRequest">URL from which should be downloaded</param>
        /// <returns>true, if a new update has been found and downloaded, false otherwise</returns>
        protected Status DoDownload(ApplicationJob job, Uri urlToRequest)
        {
            // Lower security policies
            try
            {
                ServicePointManager.CheckCertificateRevocationList = false;
            }
            catch (PlatformNotSupportedException)
            {
                // .NET bug under special circumstances
            }

            ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                return(true);
            };

            // If we want to download multiple files simultaneously
            // from the same server, we need to "remove" the connection limit.
            ServicePointManager.DefaultConnectionLimit = 50;

            job.Variables.ResetDownloadCount();

            WebRequest.RegisterPrefix("sf", new ScpWebRequestCreator());
            WebRequest.RegisterPrefix("httpx", new HttpxRequestCreator());

            WebRequest req = WebRequest.CreateDefault(urlToRequest);

            AddRequestToCancel(req);
            req.Timeout = Convert.ToInt32(Settings.GetValue("ConnectionTimeout", 10)) * 1000; // 10 seconds by default

            HttpWebRequest httpRequest = req as HttpWebRequest;

            if (httpRequest != null)
            {
                // Store cookies for future requests. Some sites
                // check for previously stored cookies before allowing to download.
                if (httpRequest.CookieContainer == null)
                {
                    httpRequest.CookieContainer = m_Cookies;
                }
                else
                {
                    httpRequest.CookieContainer.Add(m_Cookies.GetCookies(httpRequest.RequestUri));
                }

                // If we have an HTTP request, some sites may require a correct referer
                // for the download.
                // If there are variables defined (from which most likely the download link
                // or version is being extracted), we'll just use the first variable's URL as referer.
                // The user still has the option to set a custom referer.
                // Note: Some websites don't "like" certain referers
                if (!m_NoAutoReferer.Contains(GetBaseHost(req.RequestUri)))
                {
                    foreach (UrlVariable urlVar in job.Variables.Values)
                    {
                        httpRequest.Referer = urlVar.Url;
                        break;
                    }
                }

                if (!string.IsNullOrEmpty(job.HttpReferer))
                {
                    httpRequest.Referer = job.Variables.ReplaceAllInString(job.HttpReferer);
                }

                LogDialog.Log(job, "Using referer: " + (string.IsNullOrEmpty(httpRequest.Referer) ? "(none)" : httpRequest.Referer));
                httpRequest.UserAgent = (string.IsNullOrEmpty(job.UserAgent) ? WebClient.UserAgent : job.UserAgent);

                // PAD files may be compressed
                httpRequest.AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate);
            }

            using (WebResponse response = WebClient.GetResponse(req))
            {
                LogDialog.Log(job, "Server source file: " + req.RequestUri.AbsolutePath);

                // Occasionally, websites are not available and an error page is encountered
                // For the case that the content type is just plain wrong, ignore it if the size is higher than 500KB
                HttpWebResponse httpResponse = response as HttpWebResponse;
                if (httpResponse != null && response.ContentLength < 500000)
                {
                    if (response.ContentType.StartsWith("text/xml") || response.ContentType.StartsWith("application/xml"))
                    {
                        // If an XML file is served, maybe we have a PAD file
                        ApplicationJob padJob = ApplicationJob.ImportFromPad(httpResponse);
                        if (padJob != null)
                        {
                            job.CachedPadFileVersion = padJob.CachedPadFileVersion;
                            return(DoDownload(job, new Uri(padJob.FixedDownloadUrl)));
                        }
                    }
                    if (response.ContentType.StartsWith("text/html"))
                    {
                        throw NonBinaryFileException.Create(response.ContentType, httpResponse.StatusCode);
                    }
                }

                long fileSize = GetContentLength(response);
                if (fileSize == 0)
                {
                    throw new IOException("Source file on server is empty (ContentLength = 0).");
                }

                string targetFileName = job.GetTargetFile(response, urlToRequest.AbsoluteUri);

                LogDialog.Log(job, "Determined target file name: " + targetFileName);

                // Only download, if the file size or date has changed
                if (!ForceDownload && !job.RequiresDownload(response, targetFileName))
                {
                    // If file already exists (created by user),
                    // the download is not necessary. We still need to
                    // set the file name.
                    // If the file exists, but not at the target location
                    // (after renaming), do not reset the previous location.
                    if (File.Exists(targetFileName))
                    {
                        job.PreviousLocation = targetFileName;
                    }
                    job.Save();
                    return(Status.NoUpdate);
                }

                // Skip downloading!
                // Installing also requires a forced download
                if (!ForceDownload && !m_InstallUpdated && (m_OnlyCheck || (job.CheckForUpdatesOnly && !IgnoreCheckForUpdatesOnly)))
                {
                    LogDialog.Log(job, "Skipped downloading updates");
                    return(Status.UpdateAvailable);
                }

                // Execute: Default pre-update command
                string defaultPreCommand = Settings.GetValue("PreUpdateCommand", "") as string;
                // For starting external download managers: {preupdate-url}
                defaultPreCommand = UrlVariable.Replace(defaultPreCommand, "preupdate-url", urlToRequest.ToString());
                ScriptType defaultPreCommandType = Command.ConvertToScriptType(Settings.GetValue("PreUpdateCommandType", ScriptType.Batch.ToString()) as string);

                int exitCode = new Command(defaultPreCommand, defaultPreCommandType).Execute(job, targetFileName);
                if (exitCode == 1)
                {
                    LogDialog.Log(job, "Default pre-update command returned '1', download aborted");
                    throw new CommandErrorException();
                }
                else if (exitCode == 2)
                {
                    LogDialog.Log(job, "Default pre-update command returned '2', download skipped");
                    return(Status.UpdateAvailable);
                }

                // Execute: Application pre-update command
                exitCode = new Command(UrlVariable.Replace(job.ExecutePreCommand, "preupdate-url", urlToRequest.ToString()), job.ExecutePreCommandType).Execute(job, targetFileName);
                if (exitCode == 1)
                {
                    LogDialog.Log(job, "Pre-update command returned '1', download aborted");
                    throw new CommandErrorException();
                }
                else if (exitCode == 2)
                {
                    LogDialog.Log(job, "Pre-update command returned '2', download skipped");
                    return(Status.UpdateAvailable);
                }
                else if (exitCode == 3)
                {
                    LogDialog.Log(job, "Pre-update command returned '3', external download");
                    job.LastUpdated = DateTime.Now;
                    job.Save();
                    return(Status.UpdateSuccessful);
                }

                // Read all file contents to a temporary location
                string tmpLocation = Path.GetTempFileName();

                // Read contents from the web and put into file
                using (Stream sourceFile = response.GetResponseStream())
                {
                    using (FileStream targetFile = File.Create(tmpLocation))
                    {
                        long byteCount = 0;
                        int  readBytes = 0;
                        m_Size[job] = fileSize;

                        do
                        {
                            if (m_CancelUpdates)
                            {
                                break;
                            }

                            // Some adjustment for SCP download: Read only up to the max known bytes
                            int maxRead = (fileSize > 0) ? (int)Math.Min(fileSize - byteCount, 1024) : 1024;
                            if (maxRead == 0)
                            {
                                break;
                            }

                            byte[] buffer = new byte[maxRead];
                            readBytes = sourceFile.Read(buffer, 0, maxRead);
                            if (readBytes > 0)
                            {
                                targetFile.Write(buffer, 0, readBytes);
                            }
                            byteCount += readBytes;
                            OnProgressChanged(byteCount, fileSize, job);
                        } while (readBytes > 0);
                    }
                }

                if (m_CancelUpdates)
                {
                    m_Progress[job] = 0;
                    OnStatusChanged(job);
                    return(Status.Failure);
                }

                // If each version has a different file name (version number),
                // we might only want to keep one of them. Also, we might
                // want to free some space on the target location.
                if (job.DeletePreviousFile)
                {
                    PathEx.TryDeleteFiles(job.PreviousLocation);
                }

                try
                {
                    File.SetLastWriteTime(tmpLocation, ApplicationJob.GetLastModified(response));
                }
                catch (ArgumentException)
                {
                    // Invalid file date. Ignore and just use DateTime.Now
                }

                try
                {
                    FileInfo downloadedFileInfo = new FileInfo(tmpLocation);
                    job.LastFileSize = downloadedFileInfo.Length;
                    job.LastFileDate = downloadedFileInfo.LastWriteTime;
                }
                catch (Exception ex)
                {
                    LogDialog.Log(job, ex);
                }

                try
                {
                    // Before copying, we might have to create the directory
                    Directory.CreateDirectory(Path.GetDirectoryName(targetFileName));

                    // Copying might fail if variables have been replaced with bad values.
                    // However, we cannot rely on functions to clean up the path, since they
                    // might actually parse the path incorrectly and return an even worse path.
                    File.Copy(tmpLocation, targetFileName, true);
                }
                catch (ArgumentException)
                {
                    throw new TargetPathInvalidException(targetFileName);
                }
                catch (NotSupportedException)
                {
                    throw new TargetPathInvalidException(targetFileName);
                }

                File.Delete(tmpLocation);

                // At this point, the update is complete
                job.LastUpdated      = DateTime.Now;
                job.PreviousLocation = targetFileName;
            }

            job.Save();

            job.ExecutePostUpdateCommands();

            return(Status.UpdateSuccessful);
        }