diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj
index 3e3f968ce..3c69c965a 100644
--- a/ArchiSteamFarm/ArchiSteamFarm.csproj
+++ b/ArchiSteamFarm/ArchiSteamFarm.csproj
@@ -12,6 +12,7 @@
v4.5.1
512
false
+
publish\
true
Disk
@@ -26,7 +27,6 @@
1.0.0.%2a
false
true
-
AnyCPU
@@ -108,6 +108,7 @@
+
diff --git a/ArchiSteamFarm/JSON/GitHub.cs b/ArchiSteamFarm/JSON/GitHub.cs
new file mode 100644
index 000000000..e77d78c80
--- /dev/null
+++ b/ArchiSteamFarm/JSON/GitHub.cs
@@ -0,0 +1,46 @@
+/*
+ _ _ _ ____ _ _____
+ / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
+ / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
+ / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
+/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
+
+ Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
+ Contact: JustArchi@JustArchi.net
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+using Newtonsoft.Json;
+using System.Collections.Generic;
+
+namespace ArchiSteamFarm {
+ internal static class GitHub {
+ internal sealed class Asset {
+ [JsonProperty(PropertyName = "name", Required = Required.Always)]
+ internal string Name { get; private set; }
+
+ [JsonProperty(PropertyName = "browser_download_url", Required = Required.Always)]
+ internal string DownloadURL { get; private set; }
+ }
+
+ internal sealed class ReleaseResponse {
+ [JsonProperty(PropertyName = "tag_name", Required = Required.Always)]
+ internal string Tag { get; private set; }
+
+ [JsonProperty(PropertyName = "assets", Required = Required.Always)]
+ internal List Assets { get; private set; }
+ }
+ }
+}
diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs
index 5c81c5e37..0342d5fef 100644
--- a/ArchiSteamFarm/Program.cs
+++ b/ArchiSteamFarm/Program.cs
@@ -22,8 +22,10 @@
*/
-using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
@@ -48,7 +50,7 @@ namespace ArchiSteamFarm {
Server // Normal + WCF server
}
- private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
+ private const string GithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases";
internal const string ASF = "ASF";
internal const string ConfigDirectory = "config";
@@ -62,6 +64,7 @@ namespace ArchiSteamFarm {
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string ExecutableFile = Assembly.Location;
+ private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
private static readonly WCF WCF = new WCF();
@@ -74,29 +77,161 @@ namespace ArchiSteamFarm {
private static EMode Mode = EMode.Normal;
private static async Task CheckForUpdate() {
- JObject response = await WebBrowser.UrlGetToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
- if (response == null) {
+ string oldExeFile = ExecutableFile + ".old";
+
+ // We booted successfully so we can now remove old exe file
+ if (File.Exists(oldExeFile)) {
+ try {
+ File.Delete(oldExeFile);
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ return;
+ }
+ }
+
+ if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Unknown) {
return;
}
- string remoteVersion = response["tag_name"].ToString();
- if (string.IsNullOrEmpty(remoteVersion)) {
+ string releaseURL = GithubReleaseURL;
+ if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
+ releaseURL += "/latest";
+ }
+
+ string response = null;
+ Logging.LogGenericInfo("Checking new version...");
+ for (byte i = 0; i < WebBrowser.MaxRetries && string.IsNullOrEmpty(response); i++) {
+ response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false);
+ }
+
+ if (string.IsNullOrEmpty(response)) {
+ Logging.LogGenericWarning("Could not check latest version!");
return;
}
- string localVersion = Version;
+ GitHub.ReleaseResponse releaseResponse = null;
+ if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
+ try {
+ releaseResponse = JsonConvert.DeserializeObject(response);
+ } catch (JsonException e) {
+ Logging.LogGenericException(e);
+ return;
+ }
+ } else {
+ List releases;
+ try {
+ releases = JsonConvert.DeserializeObject>(response);
+ } catch (JsonException e) {
+ Logging.LogGenericException(e);
+ return;
+ }
- Logging.LogGenericInfo("Local version: " + localVersion);
- Logging.LogGenericInfo("Remote version: " + remoteVersion);
+ if (releases == null || releases.Count == 0) {
+ Logging.LogGenericWarning("Could not check latest version!");
+ return;
+ }
- int comparisonResult = localVersion.CompareTo(remoteVersion);
- if (comparisonResult < 0) {
+ releaseResponse = releases[0];
+ }
+
+ if (string.IsNullOrEmpty(releaseResponse.Tag)) {
+ Logging.LogGenericWarning("Could not check latest version!");
+ return;
+ }
+
+ Logging.LogGenericInfo("Local version: " + Version);
+ Logging.LogGenericInfo("Remote version: " + releaseResponse.Tag);
+
+ int comparisonResult = Version.CompareTo(releaseResponse.Tag);
+ if (comparisonResult >= 0) {
+ return;
+ }
+
+ if (!GlobalConfig.AutoUpdates) {
Logging.LogGenericInfo("New version is available!");
Logging.LogGenericInfo("Consider updating yourself!");
await Utilities.SleepAsync(5000).ConfigureAwait(false);
- } else if (comparisonResult > 0) {
- Logging.LogGenericInfo("You're currently using pre-release version!");
- Logging.LogGenericInfo("Be careful!");
+ return;
+ }
+
+ // Auto update logic starts here
+ if (releaseResponse.Assets == null) {
+ Logging.LogGenericWarning("Could not proceed with update because that version doesn't include assets!");
+ return;
+ }
+
+ GitHub.Asset binaryAsset = null;
+ foreach (var asset in releaseResponse.Assets) {
+ if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName)) {
+ continue;
+ }
+
+ binaryAsset = asset;
+ break;
+ }
+
+ if (binaryAsset == null) {
+ Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) {
+ Logging.LogGenericWarning("Could not proceed with update because download URL is empty!");
+ return;
+ }
+
+ Logging.LogGenericInfo("Downloading new version...");
+ Stream newExe = await WebBrowser.UrlGetToStream(binaryAsset.DownloadURL).ConfigureAwait(false);
+ if (newExe == null) {
+ Logging.LogGenericWarning("Could not download new version!");
+ return;
+ }
+
+ // We start deep update logic here
+ string newExeFile = ExecutableFile + ".new";
+
+ // Firstly we create new exec
+ try {
+ using (FileStream fileStream = File.Open(newExeFile, FileMode.Create)) {
+ await newExe.CopyToAsync(fileStream).ConfigureAwait(false);
+ }
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ return;
+ }
+
+ // Now we move current -> old
+ try {
+ File.Move(ExecutableFile, oldExeFile);
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ try {
+ // Cleanup
+ File.Delete(newExeFile);
+ } catch { }
+ return;
+ }
+
+ // Now we move new -> current
+ try {
+ File.Move(newExeFile, ExecutableFile);
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ try {
+ // Cleanup
+ File.Move(oldExeFile, ExecutableFile);
+ File.Delete(newExeFile);
+ } catch { }
+ return;
+ }
+
+ Logging.LogGenericInfo("Update process is finished! ASF will now restart itself...");
+ await Utilities.SleepAsync(5000);
+
+ if (!Restart()) {
+ // Shit happens
+ Logging.LogGenericWarning("ASF could not restart itself, you may need to restart it manually!");
+ await Utilities.SleepAsync(5000);
}
}
@@ -104,9 +239,19 @@ namespace ArchiSteamFarm {
Environment.Exit(exitCode);
}
- internal static void Restart() {
- System.Diagnostics.Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs()));
- Exit();
+ internal static bool Restart() {
+ try {
+ // TODO: This probably won't work on Mono, I wonder if I can make it work at some point
+ if (Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs())) != null) {
+ Exit();
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ Logging.LogGenericException(e);
+ return false;
+ }
}
internal static async Task LimitSteamRequestsAsync() {
@@ -283,7 +428,7 @@ namespace ArchiSteamFarm {
Exit(1);
}
- Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
+ CheckForUpdate().Wait();
// Before attempting to connect, initialize our list of CMs
Bot.RefreshCMs(GlobalDatabase.CellID).Wait();
diff --git a/ArchiSteamFarm/Properties/AssemblyInfo.cs b/ArchiSteamFarm/Properties/AssemblyInfo.cs
index fc1f591cf..44518182a 100644
--- a/ArchiSteamFarm/Properties/AssemblyInfo.cs
+++ b/ArchiSteamFarm/Properties/AssemblyInfo.cs
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ArchiSteamFarm")]
-[assembly: AssemblyCopyright("Copyright © Łukasz Domeradzki 2015")]
+[assembly: AssemblyCopyright("Copyright © Łukasz Domeradzki 2015-2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.0.0")]
-[assembly: AssemblyFileVersion("2.0.0.0")]
+[assembly: AssemblyVersion("2.0.0.1")]
+[assembly: AssemblyFileVersion("2.0.0.1")]
diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs
index 3fcf52841..5e1d60170 100644
--- a/ArchiSteamFarm/WebBrowser.cs
+++ b/ArchiSteamFarm/WebBrowser.cs
@@ -26,6 +26,7 @@ using HtmlAgilityPack;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
@@ -81,7 +82,7 @@ namespace ArchiSteamFarm {
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer).ConfigureAwait(false);
}
- internal static async Task UrlGetToContent(string request, Dictionary cookies, string referer = null) {
+ internal static async Task UrlGetToContent(string request, Dictionary cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
@@ -91,12 +92,28 @@ namespace ArchiSteamFarm {
return null;
}
- HttpContent httpContent = httpResponse.Content;
- if (httpContent == null) {
+ if (httpResponse.Content == null) {
return null;
}
- return await httpContent.ReadAsStringAsync().ConfigureAwait(false);
+ return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
+ }
+
+ internal static async Task UrlGetToStream(string request, Dictionary cookies = null, string referer = null) {
+ if (string.IsNullOrEmpty(request)) {
+ return null;
+ }
+
+ HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
+ if (httpResponse == null) {
+ return null;
+ }
+
+ if (httpResponse.Content == null) {
+ return null;
+ }
+
+ return await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
internal static async Task UrlGetToHtmlDocument(string request, Dictionary cookies = null, string referer = null) {
@@ -145,7 +162,7 @@ namespace ArchiSteamFarm {
HttpResponseMessage responseMessage;
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
- if (data != null) {
+ if (data != null && data.Count > 0) {
try {
requestMessage.Content = new FormUrlEncodedContent(data);
} catch (UriFormatException e) {
@@ -162,7 +179,7 @@ namespace ArchiSteamFarm {
requestMessage.Headers.Add("Cookie", cookieHeader.ToString());
}
- if (referer != null) {
+ if (!string.IsNullOrEmpty(referer)) {
requestMessage.Headers.Referrer = new Uri(referer);
}