/// <summary> /// Returns a sorted list of all existing application jobs. /// </summary> public static ApplicationJob[] GetJobs() { Dictionary <Guid, List <UrlVariable> > allVariables = new Dictionary <Guid, List <UrlVariable> >(); using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM variables"; using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { UrlVariable variable = new UrlVariable(); variable.Hydrate(reader); Guid jobGuid = new Guid(reader["JobGuid"] as string); if (!allVariables.ContainsKey(jobGuid)) { allVariables[jobGuid] = new List <UrlVariable>(); } allVariables[jobGuid].Add(variable); } } } using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM jobs ORDER BY ApplicationName"; List <ApplicationJob> result = new List <ApplicationJob>(); using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { ApplicationJob job = new ApplicationJob(); job.Hydrate(reader); if (allVariables.ContainsKey(job.Guid)) { foreach (UrlVariable var in allVariables[job.Guid]) { job.Variables[var.Name] = var; } } result.Add(job); } } return(result.ToArray()); } }
/// <summary> /// Sets the POST data which is sent /// along with the request. Replaces variables. /// </summary> /// <param name="variable">The variable, which sends the request</param> public void SetPostData(UrlVariable variable) { string[][] pairs = GetKeyValuePairs(variable.PostData); if (variable.Parent != null) { foreach (string[] keyValue in pairs) { keyValue[0] = variable.Parent.ReplaceAllInString(keyValue[0]); keyValue[1] = variable.Parent.ReplaceAllInString(keyValue[1]); } } StringBuilder sb = new StringBuilder(); foreach (string[] keyValue in pairs) { sb.Append(HttpUtility.UrlEncode(keyValue[0]) + "=" + HttpUtility.UrlEncode(keyValue[1]) + "&"); } m_PostData = sb.ToString().TrimEnd('&'); }
/// <summary> /// Returns an application from the database which has the specified GUID. /// </summary> public static ApplicationJob GetJob(Guid appGuid) { ApplicationJob job = null; using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM jobs WHERE JobGuid = @JobGuid"; command.Parameters.Add(new SQLiteParameter("@JobGuid", FormatGuid(appGuid))); using (IDataReader reader = command.ExecuteReader()) { if (reader.Read()) { job = new ApplicationJob(); job.Hydrate(reader); } } } if (job != null) { using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = @"SELECT * FROM variables WHERE JobGuid = @JobGuid"; command.Parameters.Add(new SQLiteParameter("@JobGuid", FormatGuid(appGuid))); using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { UrlVariable variable = new UrlVariable(job.Variables); variable.Hydrate(reader); job.Variables.Add(variable.Name, variable); } } } } return(job); }
/// <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); }
/// <summary> /// Returns a sorted list of all existing application jobs. /// </summary> public static ApplicationJob[] GetJobs() { Dictionary<Guid, List<UrlVariable>> allVariables = new Dictionary<Guid, List<UrlVariable>>(); using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM variables"; using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { UrlVariable variable = new UrlVariable(); variable.Hydrate(reader); Guid jobGuid = new Guid(reader["JobGuid"] as string); if (!allVariables.ContainsKey(jobGuid)) { allVariables[jobGuid] = new List<UrlVariable>(); } allVariables[jobGuid].Add(variable); } } } using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM jobs ORDER BY ApplicationName"; List<ApplicationJob> result = new List<ApplicationJob>(); using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { ApplicationJob job = new ApplicationJob(); job.Hydrate(reader); if (allVariables.ContainsKey(job.Guid)) { foreach (UrlVariable var in allVariables[job.Guid]) { job.Variables[var.Name] = var; } } result.Add(job); } } return result.ToArray(); } }
/// <summary> /// Returns an application from the database which has the specified GUID. /// </summary> public static ApplicationJob GetJob(Guid appGuid) { ApplicationJob job = null; using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = "SELECT * FROM jobs WHERE JobGuid = @JobGuid"; command.Parameters.Add(new SQLiteParameter("@JobGuid", FormatGuid(appGuid))); using (IDataReader reader = command.ExecuteReader()) { if (reader.Read()) { job = new ApplicationJob(); job.Hydrate(reader); } } } if (job != null) { using (IDbCommand command = Connection.CreateCommand()) { command.CommandText = @"SELECT * FROM variables WHERE JobGuid = @JobGuid"; command.Parameters.Add(new SQLiteParameter("@JobGuid", DbManager.FormatGuid(appGuid))); using (IDataReader reader = command.ExecuteReader()) { while (reader.Read()) { UrlVariable variable = new UrlVariable(job.Variables); variable.Hydrate(reader); job.Variables.Add(variable.Name, variable); } } } } return job; }
public static void ImportFromFile(string filename) { XmlDocument doc = new XmlDocument(); doc.Load(filename); // Import settings from file as dictionary XmlElement settingsElem = doc.SelectSingleNode("//Settings/dictionary") as XmlElement; if (settingsElem == null) { // Backward compatibility settingsElem = doc.SelectSingleNode("//dictionary") as XmlElement; } if (settingsElem != null) { XmlSerializer serializer = new XmlSerializer(typeof(SerializableDictionary<string, string>)); using (StringReader textReader = new StringReader(settingsElem.OuterXml)) { DbManager.SetSettings(serializer.Deserialize(textReader) as Dictionary<string, string>); } } // Import global variables XmlElement varNodes = doc.SelectSingleNode("//GlobalVariables") as XmlElement; if (varNodes != null) { UrlVariable.GlobalVariables.Clear(); foreach (XmlElement varElem in doc.SelectNodes("//GlobalVariables/Variable")) { UrlVariable newVar = new UrlVariable(); newVar.Name = varElem.GetAttribute("Name"); newVar.CachedContent = varElem.GetAttribute("Content"); UrlVariable.GlobalVariables[newVar.Name] = newVar; } UrlVariable.GlobalVariables.Save(); } // Import code snippets XmlElement snippetNodes = doc.SelectSingleNode("//CodeSnippets") as XmlElement; if (snippetNodes != null) { using (SQLiteCommand comm = DbManager.Connection.CreateCommand()) { comm.CommandText = "DELETE FROM snippets"; comm.ExecuteNonQuery(); } foreach (XmlElement snippetElem in doc.SelectNodes("//CodeSnippets/Snippet")) { Snippet snippet = new Snippet(); snippet.Guid = new Guid(snippetElem.GetAttribute("Guid")); snippet.Name = snippetElem.GetAttribute("Name"); snippet.Type = (ScriptType)Convert.ToInt32(snippetElem.GetAttribute("Type")); snippet.Text = snippetElem.InnerText; snippet.Save(); } } XmlElement setupNodes = doc.SelectSingleNode("//SetupLists") as XmlElement; if (setupNodes != null) { ApplicationJob[] apps = DbManager.GetJobs(); using (IDbCommand command = DbManager.Connection.CreateCommand()) { command.CommandText = @"DELETE FROM setuplists_applications"; command.ExecuteNonQuery(); } using (IDbCommand command = DbManager.Connection.CreateCommand()) { command.CommandText = @"DELETE FROM setuplists"; command.ExecuteNonQuery(); } foreach (XmlElement listElem in doc.SelectNodes("//SetupLists/List")) { ApplicationList list = new ApplicationList(); list.Name = listElem.GetAttribute("Name"); list.Guid = new Guid(listElem.GetAttribute("Guid")); foreach (XmlElement appListElem in listElem.SelectNodes("Applications/Application")) { Guid guid = new Guid(appListElem.GetAttribute("Guid")); ApplicationJob job = DbManager.GetJob(guid); if (job != null) { list.Applications.Add(job); } } list.Save(); } } }
/// <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 = KetarinProtocolProvider.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 KetarinProtocolProvider(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); }
public static void ImportFromFile(string filename) { XmlDocument doc = new XmlDocument(); doc.Load(filename); // Import settings from file as dictionary XmlElement settingsElem = doc.SelectSingleNode("//Settings/dictionary") as XmlElement ?? doc.SelectSingleNode("//dictionary") as XmlElement; if (settingsElem != null) { XmlSerializer serializer = new XmlSerializer(typeof(SerializableDictionary <string, string>)); using (StringReader textReader = new StringReader(settingsElem.OuterXml)) { DbManager.SetSettings(serializer.Deserialize(textReader) as Dictionary <string, string>); } } // Import global variables XmlElement varNodes = doc.SelectSingleNode("//GlobalVariables") as XmlElement; if (varNodes != null) { UrlVariable.GlobalVariables.Clear(); foreach (XmlElement varElem in doc.SelectNodes("//GlobalVariables/Variable")) { UrlVariable newVar = new UrlVariable { Name = varElem.GetAttribute("Name"), CachedContent = varElem.GetAttribute("Content") }; UrlVariable.GlobalVariables[newVar.Name] = newVar; } UrlVariable.GlobalVariables.Save(); } // Import code snippets XmlElement snippetNodes = doc.SelectSingleNode("//CodeSnippets") as XmlElement; if (snippetNodes != null) { using (SQLiteCommand comm = DbManager.Connection.CreateCommand()) { comm.CommandText = "DELETE FROM snippets"; comm.ExecuteNonQuery(); } foreach (XmlElement snippetElem in doc.SelectNodes("//CodeSnippets/Snippet")) { Snippet snippet = new Snippet { Guid = new Guid(snippetElem.GetAttribute("Guid")), Name = snippetElem.GetAttribute("Name"), Type = (ScriptType)Convert.ToInt32(snippetElem.GetAttribute("Type")), Text = snippetElem.InnerText }; snippet.Save(); } } XmlElement setupNodes = doc.SelectSingleNode("//SetupLists") as XmlElement; if (setupNodes != null) { using (IDbCommand command = DbManager.Connection.CreateCommand()) { command.CommandText = @"DELETE FROM setuplists_applications"; command.ExecuteNonQuery(); } using (IDbCommand command = DbManager.Connection.CreateCommand()) { command.CommandText = @"DELETE FROM setuplists"; command.ExecuteNonQuery(); } foreach (XmlElement listElem in doc.SelectNodes("//SetupLists/List")) { ApplicationList list = new ApplicationList { Name = listElem.GetAttribute("Name"), Guid = new Guid(listElem.GetAttribute("Guid")) }; foreach (XmlElement appListElem in listElem.SelectNodes("Applications/Application")) { Guid guid = new Guid(appListElem.GetAttribute("Guid")); ApplicationJob job = DbManager.GetJob(guid); if (job != null) { list.Applications.Add(job); } } list.Save(); } } }
/// <summary> /// Sets the POST data which is sent /// along with the request. Replaces variables. /// </summary> /// <param name="variable">The variable, which sends the request</param> public void SetPostData(UrlVariable variable) { string[][] pairs = GetKeyValuePairs(variable.PostData); if (variable.Parent != null) { foreach (string[] keyValue in pairs) { keyValue[0] = variable.Parent.ReplaceAllInString(keyValue[0]); keyValue[1] = variable.Parent.ReplaceAllInString(keyValue[1]); } } StringBuilder sb = new StringBuilder(); foreach (string[] keyValue in pairs) { sb.Append(HttpUtility.UrlEncode(keyValue[0]) + "=" + HttpUtility.UrlEncode(keyValue[1]) + "&"); } m_PostData = sb.ToString().TrimEnd('&'); }
/// <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); }