private void UpdateMachineAuthCallback(SteamUser.UpdateMachineAuthCallback machineAuth)
        {
            byte[] hash = Util.SHAHash(machineAuth.Data);
            Console.WriteLine("Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash);

            ConfigStore.TheConfig.SentryData[logonDetails.Username] = machineAuth.Data;
            ConfigStore.Save();

            var authResponse = new SteamUser.MachineAuthDetails
            {
                BytesWritten = machineAuth.BytesToWrite,
                FileName     = machineAuth.FileName,
                FileSize     = machineAuth.BytesToWrite,
                Offset       = machineAuth.Offset,

                SentryFileHash = hash,                         // should be the sha1 hash of the sentry file we just wrote

                OneTimePassword = machineAuth.OneTimePassword, // not sure on this one yet, since we've had no examples of steam using OTPs

                LastError = 0,                                 // result from win32 GetLastError
                Result    = EResult.OK,                        // if everything went okay, otherwise ~who knows~

                JobID = machineAuth.JobID,                     // so we respond to the correct server job
            };

            // send off our response
            steamUser.SendMachineAuthResponse(authResponse);
        }
        public Steam3Session(SteamUser.LogOnDetails details)
        {
            this.logonDetails = details;

            this.authenticatedUser = details.Username != null;
            this.credentials       = new Credentials();
            this.bConnected        = false;
            this.bConnecting       = false;
            this.bAborted          = false;
            this.seq = 0;

            this.AppTickets       = new Dictionary <uint, byte[]>();
            this.AppTokens        = new Dictionary <uint, ulong>();
            this.DepotKeys        = new Dictionary <uint, byte[]>();
            this.CDNAuthTokens    = new ConcurrentDictionary <string, SteamApps.CDNAuthTokenCallback>();
            this.AppInfo          = new Dictionary <uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
            this.PackageInfo      = new Dictionary <uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
            this.AppBetaPasswords = new Dictionary <string, byte[]>();

            this.steamClient = new SteamClient();

            this.steamUser = this.steamClient.GetHandler <SteamUser>();
            this.steamApps = this.steamClient.GetHandler <SteamApps>();
            var steamUnifiedMessages = this.steamClient.GetHandler <SteamUnifiedMessages>();

            this.steamPublishedFile = steamUnifiedMessages.CreateService <IPublishedFile>();

            this.callbacks = new CallbackManager(this.steamClient);

            this.callbacks.Subscribe <SteamClient.ConnectedCallback>(ConnectedCallback);
            this.callbacks.Subscribe <SteamClient.DisconnectedCallback>(DisconnectedCallback);
            this.callbacks.Subscribe <SteamUser.LoggedOnCallback>(LogOnCallback);
            this.callbacks.Subscribe <SteamUser.SessionTokenCallback>(SessionTokenCallback);
            this.callbacks.Subscribe <SteamApps.LicenseListCallback>(LicenseListCallback);
            this.callbacks.Subscribe <SteamUser.UpdateMachineAuthCallback>(UpdateMachineAuthCallback);
            this.callbacks.Subscribe <SteamUser.LoginKeyCallback>(LoginKeyCallback);

            Console.Write("Connecting to Steam3...");

            if (authenticatedUser)
            {
                FileInfo fi = new FileInfo(String.Format("{0}.sentryFile", logonDetails.Username));
                if (ConfigStore.TheConfig.SentryData != null && ConfigStore.TheConfig.SentryData.ContainsKey(logonDetails.Username))
                {
                    logonDetails.SentryFileHash = Util.SHAHash(ConfigStore.TheConfig.SentryData[logonDetails.Username]);
                }
                else if (fi.Exists && fi.Length > 0)
                {
                    var sentryData = File.ReadAllBytes(fi.FullName);
                    logonDetails.SentryFileHash = Util.SHAHash(sentryData);
                    ConfigStore.TheConfig.SentryData[logonDetails.Username] = sentryData;
                    ConfigStore.Save();
                }
            }

            Connect();
        }
        private void LoginKeyCallback(SteamUser.LoginKeyCallback loginKey)
        {
            Console.WriteLine("Accepted new login key for account {0}", logonDetails.Username);

            ConfigStore.TheConfig.LoginKeys[logonDetails.Username] = loginKey.LoginKey;
            ConfigStore.Save();

            steamUser.AcceptNewLoginKey(loginKey);
        }
Beispiel #4
0
        private void LoginKeyCallback(SteamUser.LoginKeyCallback loginKey)
        {
            DebugLog.WriteLine("Steam3Session", "Accepted new login key for account " + logonDetails.Username);

            ConfigStore.TheConfig.LoginKeys[logonDetails.Username] = loginKey.LoginKey;
            ConfigStore.Save();

            steamUser.AcceptNewLoginKey(loginKey);
        }
Beispiel #5
0
        private static void DownloadSteam3(uint appId, List <DepotDownloadInfo> depots)
        {
            ulong TotalBytesCompressed   = 0;
            ulong TotalBytesUncompressed = 0;

            foreach (var depot in depots)
            {
                ulong DepotBytesCompressed   = 0;
                ulong DepotBytesUncompressed = 0;

                Console.WriteLine("Downloading depot {0} - {1}", depot.id, depot.contentName);

                CancellationTokenSource cts = new CancellationTokenSource();

                ProtoManifest oldProtoManifest = null;
                ProtoManifest newProtoManifest = null;
                string        configDir        = Path.Combine(depot.installDir, CONFIG_DIR);

                ulong lastManifestId = INVALID_MANIFEST_ID;
                ConfigStore.TheConfig.LastManifests.TryGetValue(depot.id, out lastManifestId);

                // In case we have an early exit, this will force equiv of verifyall next run.
                ConfigStore.TheConfig.LastManifests[depot.id] = INVALID_MANIFEST_ID;
                ConfigStore.Save();

                if (lastManifestId != INVALID_MANIFEST_ID)
                {
                    var oldManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", lastManifestId));
                    if (File.Exists(oldManifestFileName))
                    {
                        oldProtoManifest = ProtoManifest.LoadFromFile(oldManifestFileName);
                    }
                }

                if (lastManifestId == depot.manifestId && oldProtoManifest != null)
                {
                    newProtoManifest = oldProtoManifest;
                    Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id);
                }
                else
                {
                    var newManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", depot.manifestId));
                    if (newManifestFileName != null)
                    {
                        newProtoManifest = ProtoManifest.LoadFromFile(newManifestFileName);
                    }

                    if (newProtoManifest != null)
                    {
                        Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id);
                    }
                    else
                    {
                        Console.Write("Downloading depot manifest...");

                        DepotManifest depotManifest = null;

                        while (depotManifest == null)
                        {
                            CDNClient client = null;
                            try
                            {
                                client = cdnPool.GetConnectionForDepot(appId, depot.id, depot.depotKey, CancellationToken.None);

                                depotManifest = client.DownloadManifest(depot.id, depot.manifestId);

                                cdnPool.ReturnConnection(client);
                            }
                            catch (WebException e)
                            {
                                cdnPool.ReturnBrokenConnection(client);

                                if (e.Status == WebExceptionStatus.ProtocolError)
                                {
                                    var response = e.Response as HttpWebResponse;
                                    if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
                                    {
                                        Console.WriteLine("Encountered 401 for depot manifest {0} {1}. Aborting.", depot.id, depot.manifestId);
                                        break;
                                    }
                                    else
                                    {
                                        Console.WriteLine("Encountered error downloading depot manifest {0} {1}: {2}", depot.id, depot.manifestId, response.StatusCode);
                                    }
                                }
                                else
                                {
                                    Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.id, depot.manifestId, e.Status);
                                }
                            }
                            catch (Exception e)
                            {
                                cdnPool.ReturnBrokenConnection(client);
                                Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.id, depot.manifestId, e.Message);
                            }
                        }

                        if (depotManifest == null)
                        {
                            Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.manifestId, depot.id);
                            return;
                        }

                        newProtoManifest = new ProtoManifest(depotManifest, depot.manifestId);
                        newProtoManifest.SaveToFile(newManifestFileName);

                        Console.WriteLine("Successfully downloaded manifest {0} for depot {1}", depot.manifestId, depot.id);

                        Console.WriteLine(" Done!");
                    }
                }

                newProtoManifest.Files.Sort((x, y) => { return(x.FileName.CompareTo(y.FileName)); });

                if (Config.DownloadManifestOnly)
                {
                    StringBuilder manifestBuilder = new StringBuilder();
                    string        txtManifest     = Path.Combine(depot.installDir, string.Format("manifest_{0}.txt", depot.id));

                    foreach (var file in newProtoManifest.Files)
                    {
                        if (file.Flags.HasFlag(EDepotFileFlag.Directory))
                        {
                            continue;
                        }

                        manifestBuilder.Append(string.Format("{0}\n", file.FileName));
                    }

                    File.WriteAllText(txtManifest, manifestBuilder.ToString());
                    continue;
                }

                ulong  complete_download_size = 0;
                ulong  size_downloaded        = 0;
                string stagingDir             = Path.Combine(depot.installDir, STAGING_DIR);

                var filesAfterExclusions = newProtoManifest.Files.AsParallel().Where(f => TestIsFileIncluded(f.FileName)).ToList();

                // Pre-process
                filesAfterExclusions.ForEach(file =>
                {
                    var fileFinalPath   = Path.Combine(depot.installDir, file.FileName);
                    var fileStagingPath = Path.Combine(stagingDir, file.FileName);

                    if (file.Flags.HasFlag(EDepotFileFlag.Directory))
                    {
                        Directory.CreateDirectory(fileFinalPath);
                        Directory.CreateDirectory(fileStagingPath);
                    }
                    else
                    {
                        // Some manifests don't explicitly include all necessary directories
                        Directory.CreateDirectory(Path.GetDirectoryName(fileFinalPath));
                        Directory.CreateDirectory(Path.GetDirectoryName(fileStagingPath));

                        complete_download_size += file.TotalSize;
                    }
                });

                filesAfterExclusions.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory))
                .AsParallel().WithCancellation(cts.Token).WithDegreeOfParallelism(Config.MaxDownloads)
                .ForAll(file =>
                {
                    string fileFinalPath   = Path.Combine(depot.installDir, file.FileName);
                    string fileStagingPath = Path.Combine(stagingDir, file.FileName);

                    // This may still exist if the previous run exited before cleanup
                    if (File.Exists(fileStagingPath))
                    {
                        File.Delete(fileStagingPath);
                    }

                    FileStream fs = null;
                    List <ProtoManifest.ChunkData> neededChunks;
                    FileInfo fi = new FileInfo(fileFinalPath);
                    if (!fi.Exists)
                    {
                        // create new file. need all chunks
                        fs = File.Create(fileFinalPath);
                        fs.SetLength(( long )file.TotalSize);
                        neededChunks = new List <ProtoManifest.ChunkData>(file.Chunks);
                    }
                    else
                    {
                        // open existing
                        ProtoManifest.FileData oldManifestFile = null;
                        if (oldProtoManifest != null)
                        {
                            oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName);
                        }

                        if (oldManifestFile != null)
                        {
                            neededChunks = new List <ProtoManifest.ChunkData>();

                            if (Config.VerifyAll || !oldManifestFile.FileHash.SequenceEqual(file.FileHash))
                            {
                                // we have a version of this file, but it doesn't fully match what we want

                                var matchingChunks = new List <ChunkMatch>();

                                foreach (var chunk in file.Chunks)
                                {
                                    var oldChunk = oldManifestFile.Chunks.FirstOrDefault(c => c.ChunkID.SequenceEqual(chunk.ChunkID));
                                    if (oldChunk != null)
                                    {
                                        matchingChunks.Add(new ChunkMatch(oldChunk, chunk));
                                    }
                                    else
                                    {
                                        neededChunks.Add(chunk);
                                    }
                                }

                                File.Move(fileFinalPath, fileStagingPath);

                                fs = File.Open(fileFinalPath, FileMode.Create);
                                fs.SetLength(( long )file.TotalSize);

                                using (var fsOld = File.Open(fileStagingPath, FileMode.Open))
                                {
                                    foreach (var match in matchingChunks)
                                    {
                                        fsOld.Seek(( long )match.OldChunk.Offset, SeekOrigin.Begin);

                                        byte[] tmp = new byte[match.OldChunk.UncompressedLength];
                                        fsOld.Read(tmp, 0, tmp.Length);

                                        byte[] adler = Util.AdlerHash(tmp);
                                        if (!adler.SequenceEqual(match.OldChunk.Checksum))
                                        {
                                            neededChunks.Add(match.NewChunk);
                                        }
                                        else
                                        {
                                            fs.Seek(( long )match.NewChunk.Offset, SeekOrigin.Begin);
                                            fs.Write(tmp, 0, tmp.Length);
                                        }
                                    }
                                }

                                File.Delete(fileStagingPath);
                            }
                        }
                        else
                        {
                            // No old manifest or file not in old manifest. We must validate.

                            fs = File.Open(fileFinalPath, FileMode.Open);
                            if (( ulong )fi.Length != file.TotalSize)
                            {
                                fs.SetLength(( long )file.TotalSize);
                            }

                            neededChunks = Util.ValidateSteam3FileChecksums(fs, file.Chunks.OrderBy(x => x.Offset).ToArray());
                        }

                        if (neededChunks.Count() == 0)
                        {
                            size_downloaded += file.TotalSize;
                            Console.WriteLine("{0,6:#00.00}% {1}", (( float )size_downloaded / ( float )complete_download_size) * 100.0f, fileFinalPath);
                            if (fs != null)
                            {
                                fs.Close();
                            }
                            return;
                        }
                        else
                        {
                            size_downloaded += (file.TotalSize - ( ulong )neededChunks.Select(x => ( long )x.UncompressedLength).Sum());
                        }
                    }

                    foreach (var chunk in neededChunks)
                    {
                        if (cts.IsCancellationRequested)
                        {
                            break;
                        }

                        string chunkID = Util.EncodeHexString(chunk.ChunkID);
                        CDNClient.DepotChunk chunkData = null;

                        while (!cts.IsCancellationRequested)
                        {
                            CDNClient client;
                            try
                            {
                                client = cdnPool.GetConnectionForDepot(appId, depot.id, depot.depotKey, cts.Token);
                            }
                            catch (OperationCanceledException)
                            {
                                break;
                            }

                            DepotManifest.ChunkData data = new DepotManifest.ChunkData();
                            data.ChunkID            = chunk.ChunkID;
                            data.Checksum           = chunk.Checksum;
                            data.Offset             = chunk.Offset;
                            data.CompressedLength   = chunk.CompressedLength;
                            data.UncompressedLength = chunk.UncompressedLength;

                            try
                            {
                                chunkData = client.DownloadDepotChunk(depot.id, data);
                                cdnPool.ReturnConnection(client);
                                break;
                            }
                            catch (WebException e)
                            {
                                cdnPool.ReturnBrokenConnection(client);

                                if (e.Status == WebExceptionStatus.ProtocolError)
                                {
                                    var response = e.Response as HttpWebResponse;
                                    if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
                                    {
                                        Console.WriteLine("Encountered 401 for chunk {0}. Aborting.", chunkID);
                                        cts.Cancel();
                                        break;
                                    }
                                    else
                                    {
                                        Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, response.StatusCode);
                                    }
                                }
                                else
                                {
                                    Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, e.Status);
                                }
                            }
                            catch (Exception e)
                            {
                                cdnPool.ReturnBrokenConnection(client);
                                Console.WriteLine("Encountered unexpected error downloading chunk {0}: {1}", chunkID, e.Message);
                            }
                        }

                        if (chunkData == null)
                        {
                            Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.id);
                            return;
                        }

                        TotalBytesCompressed   += chunk.CompressedLength;
                        DepotBytesCompressed   += chunk.CompressedLength;
                        TotalBytesUncompressed += chunk.UncompressedLength;
                        DepotBytesUncompressed += chunk.UncompressedLength;

                        fs.Seek(( long )chunk.Offset, SeekOrigin.Begin);
                        fs.Write(chunkData.Data, 0, chunkData.Data.Length);

                        size_downloaded += chunk.UncompressedLength;
                    }

                    fs.Close();

                    Console.WriteLine("{0,6:#00.00}% {1}", (( float )size_downloaded / ( float )complete_download_size) * 100.0f, fileFinalPath);
                });

                ConfigStore.TheConfig.LastManifests[depot.id] = depot.manifestId;
                ConfigStore.Save();

                Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, DepotBytesCompressed, DepotBytesUncompressed);
            }

            Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", TotalBytesCompressed, TotalBytesUncompressed, depots.Count);
        }
        private void LogOnCallback(SteamUser.LoggedOnCallback loggedOn)
        {
            bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
            bool is2FA        = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
            bool isLoginKey   = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;

            if (isSteamGuard || is2FA || isLoginKey)
            {
                bExpectingDisconnectRemote = true;
                Abort(false);

                if (!isLoginKey)
                {
                    Console.WriteLine("This account is protected by Steam Guard.");
                }

                if (is2FA)
                {
                    Console.Write("Please enter your 2 factor auth code from your authenticator app: ");
                    logonDetails.TwoFactorCode = Console.ReadLine();
                }
                else if (isLoginKey)
                {
                    ConfigStore.TheConfig.LoginKeys.Remove(logonDetails.Username);
                    ConfigStore.Save();

                    logonDetails.LoginKey = null;

                    if (ContentDownloader.Config.SuppliedPassword != null)
                    {
                        Console.WriteLine("Login key was expired. Connecting with supplied password.");
                        logonDetails.Password = ContentDownloader.Config.SuppliedPassword;
                    }
                    else
                    {
                        Console.WriteLine("Login key was expired. Please enter your password: "******"Please enter the authentication code sent to your email address: ");
                    logonDetails.AuthCode = Console.ReadLine();
                }

                Console.Write("Retrying Steam3 connection...");
                Connect();

                return;
            }
            else if (loggedOn.Result == EResult.ServiceUnavailable)
            {
                Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result);
                Abort(false);

                return;
            }
            else if (loggedOn.Result != EResult.OK)
            {
                Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result);
                Abort();

                return;
            }

            Console.WriteLine(" Done!");

            this.seq++;
            credentials.LoggedOn = true;

            if (ContentDownloader.Config.CellID == 0)
            {
                Console.WriteLine("Using Steam3 suggested CellID: " + loggedOn.CellID);
                ContentDownloader.Config.CellID = ( int )loggedOn.CellID;
            }
        }
        private static async Task DownloadSteam3Async(uint appId, List <DepotDownloadInfo> depots, Action downloadCompleteAction = null)
        {
            ulong TotalBytesCompressed   = 0;
            ulong TotalBytesUncompressed = 0;

            foreach (var depot in depots)
            {
                ulong DepotBytesCompressed   = 0;
                ulong DepotBytesUncompressed = 0;

                DebugLog.WriteLine("ContentDownloader", "Downloading depot " + depot.id + " - " + depot.contentName);

                CancellationTokenSource cts = new CancellationTokenSource();
                cdnPool.ExhaustedToken = cts;

                ProtoManifest oldProtoManifest = null;
                ProtoManifest downloadManifest = null;
                string        configDir        = Path.Combine(depot.installDir, CONFIG_DIR);

                ulong lastManifestId = INVALID_MANIFEST_ID;
                ConfigStore.TheConfig.LastManifests.TryGetValue(depot.id, out lastManifestId);

                // In case we have an early exit, this will force equiv of verifyall next run.
                ConfigStore.TheConfig.LastManifests[depot.id] = INVALID_MANIFEST_ID;
                ConfigStore.Save();

                if (lastManifestId != INVALID_MANIFEST_ID)
                {
                    var oldManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", lastManifestId));
                    DebugLog.WriteLine(DEBUG_NAME_FILES, "Checking if " + oldManifestFileName + " exists");
                    if (File.Exists(oldManifestFileName))
                    {
                        DebugLog.WriteLine(DEBUG_NAME_FILES, oldManifestFileName + " exists, reading!");
                        oldProtoManifest = ProtoManifest.LoadFromFile(oldManifestFileName);
                    }
                }

                if (lastManifestId == depot.manifestId && oldProtoManifest != null)
                {
                    downloadManifest = oldProtoManifest;
                    DebugLog.WriteLine("ContentDownloader", "Already have manifest " + depot.manifestId + " for depot " + depot.id + ".");
                }
                else
                {
                    var newManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", depot.manifestId));
                    if (newManifestFileName != null)
                    {
                        downloadManifest = ProtoManifest.LoadFromFile(newManifestFileName);
                    }

                    if (downloadManifest != null)
                    {
                        DebugLog.WriteLine("ContentDownloader", "Already have manifest " + depot.manifestId + " for depot " + depot.id + ".");
                    }
                    else
                    {
                        DebugLog.WriteLine("ContentDownloader", "Downloading depot manifest...");

                        DepotManifest depotManifest = null;

                        while (depotManifest == null)
                        {
                            CDNClient client = null;
                            try
                            {
                                client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, CancellationToken.None).ConfigureAwait(false);

                                //client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, CancellationToken.None);

                                depotManifest = await client.DownloadManifestAsync(depot.id, depot.manifestId).ConfigureAwait(false);

                                //depotManifest = await client.DownloadManifestAsync(depot.id, depot.manifestId);

                                cdnPool.ReturnConnection(client);
                            }
                            catch (WebException e)
                            {
                                cdnPool.ReturnBrokenConnection(client);

                                if (e.Status == WebExceptionStatus.ProtocolError)
                                {
                                    var response = e.Response as HttpWebResponse;
                                    if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
                                    {
                                        DebugLog.WriteLine("ContentDownloader", "Encountered 401 for depot manifest " + depot.id + " " + depot.manifestId + ". Aborting.");
                                        break;
                                    }
                                    else
                                    {
                                        DebugLog.WriteLine("ContentDownloader", "Encountered error downloading depot manifest " + depot.id + " " + depot.manifestId + ": " + response.StatusCode);
                                    }
                                }
                                else
                                {
                                    DebugLog.WriteLine("ContentDownloader", "Encountered error downloading manifest for depot " + depot.id + " " + depot.manifestId + ": " + e.Status);
                                }
                            }
                            catch (Exception e)
                            {
                                cdnPool.ReturnBrokenConnection(client);
                                DebugLog.WriteLine("ContentDownloader", "Encountered error downloading manifest for depot " + depot.id + " " + depot.manifestId + ": " + e.Message);
                            }
                        }

                        if (depotManifest == null)
                        {
                            DebugLog.WriteLine("ContentDownloader", "\nUnable to download manifest " + depot.manifestId + " for depot " + depot.id);
                            return;
                        }

                        downloadManifest = new ProtoManifest(depotManifest, depot.manifestId);
                        downloadManifest.SaveToFile(newManifestFileName);

                        DebugLog.WriteLine("ContentDownloader", "Done!");
                    }
                }

                downloadManifest.Files.Sort((x, y) => { return(x.FileName.CompareTo(y.FileName)); });
                if (downloadManifest != null)
                {
                    onManifestReceived?.Invoke(appId, depot.id, depot.contentName, downloadManifest);
                }

                if (Config.DownloadManifestOnly)
                {
                    continue;
                }

                complete_download_size = 0;
                size_downloaded        = 0;
                string stagingDir = Path.Combine(depot.installDir, STAGING_DIR);

                var filesAfterExclusions = downloadManifest.Files.Where(f => TestIsFileIncluded(f.FileName)).ToList();

                // Pre-process
                filesAfterExclusions.ForEach(file =>
                {
                    var fileFinalPath   = Path.Combine(depot.installDir, file.FileName);
                    var fileStagingPath = Path.Combine(stagingDir, file.FileName);

                    if (file.Flags.HasFlag(EDepotFileFlag.Directory))
                    {
                        Directory.CreateDirectory(fileFinalPath);
                        Directory.CreateDirectory(fileStagingPath);
                    }
                    else
                    {
                        // Some manifests don't explicitly include all necessary directories
                        Directory.CreateDirectory(Path.GetDirectoryName(fileFinalPath));
                        Directory.CreateDirectory(Path.GetDirectoryName(fileStagingPath));

                        complete_download_size += file.TotalSize;
                    }
                });

                var semaphore = new SemaphoreSlim(Config.MaxDownloads);
                var files     = filesAfterExclusions.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)).ToArray();
                var tasks     = new Task[files.Length];
                for (var i = 0; i < files.Length; i++)
                {
                    var file = files[i];
                    var task = Task.Run(async() =>
                    {
                        cts.Token.ThrowIfCancellationRequested();

                        try
                        {
                            await semaphore.WaitAsync().ConfigureAwait(false);
                            //await semaphore.WaitAsync();
                            cts.Token.ThrowIfCancellationRequested();

                            string fileFinalPath   = Path.Combine(depot.installDir, file.FileName);
                            string fileStagingPath = Path.Combine(stagingDir, file.FileName);

                            // This may still exist if the previous run exited before cleanup
                            DebugLog.WriteLine(DEBUG_NAME_FILES, "Checking if " + fileStagingPath + " exists");
                            if (File.Exists(fileStagingPath))
                            {
                                DebugLog.WriteLine(DEBUG_NAME_FILES, fileStagingPath + " exists, deleting!");
                                File.Delete(fileStagingPath);
                            }

                            List <ProtoManifest.ChunkData> neededChunks;
                            FileInfo fi = new FileInfo(fileFinalPath);
                            DebugLog.WriteLine(DEBUG_NAME_FILES, "Checking if " + fileFinalPath + " exists");
                            if (!fi.Exists)
                            {
                                // create new file. need all chunks
                                DebugLog.WriteLine(DEBUG_NAME_FILES, fileFinalPath + " does not exist, creating!");
                                using (FileStream fs = File.Create(fileFinalPath))
                                {
                                    fs.SetLength((long)file.TotalSize);
                                    neededChunks = new List <ProtoManifest.ChunkData>(file.Chunks);
                                }
                            }
                            else
                            {
                                // open existing
                                ProtoManifest.FileData oldManifestFile = null;
                                if (oldProtoManifest != null)
                                {
                                    oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName);
                                }

                                if (oldManifestFile != null)
                                {
                                    neededChunks = new List <ProtoManifest.ChunkData>();

                                    if (Config.VerifyAll || !oldManifestFile.FileHash.SequenceEqual(file.FileHash))
                                    {
                                        // we have a version of this file, but it doesn't fully match what we want

                                        var matchingChunks = new List <ChunkMatch>();

                                        foreach (var chunk in file.Chunks)
                                        {
                                            var oldChunk = oldManifestFile.Chunks.FirstOrDefault(c => c.ChunkID.SequenceEqual(chunk.ChunkID));
                                            if (oldChunk != null)
                                            {
                                                matchingChunks.Add(new ChunkMatch(oldChunk, chunk));
                                            }
                                            else
                                            {
                                                neededChunks.Add(chunk);
                                            }
                                        }

                                        DebugLog.WriteLine(DEBUG_NAME_FILES, "Moving file " + fileFinalPath + " to " + fileStagingPath);
                                        File.Move(fileFinalPath, fileStagingPath);

                                        DebugLog.WriteLine(DEBUG_NAME_FILES, "Creating file " + fileFinalPath);
                                        using (FileStream fs = File.Open(fileFinalPath, FileMode.Create))
                                        {
                                            fs.SetLength((long)file.TotalSize);

                                            DebugLog.WriteLine(DEBUG_NAME_FILES, "Opening file " + fileStagingPath);
                                            using (var fsOld = File.Open(fileStagingPath, FileMode.Open))
                                            {
                                                foreach (var match in matchingChunks)
                                                {
                                                    fsOld.Seek((long)match.OldChunk.Offset, SeekOrigin.Begin);

                                                    byte[] tmp = new byte[match.OldChunk.UncompressedLength];
                                                    fsOld.Read(tmp, 0, tmp.Length);

                                                    byte[] adler = Util.AdlerHash(tmp);
                                                    if (!adler.SequenceEqual(match.OldChunk.Checksum))
                                                    {
                                                        neededChunks.Add(match.NewChunk);
                                                    }
                                                    else
                                                    {
                                                        fs.Seek((long)match.NewChunk.Offset, SeekOrigin.Begin);
                                                        fs.Write(tmp, 0, tmp.Length);
                                                    }
                                                }
                                            }
                                        }
                                        DebugLog.WriteLine(DEBUG_NAME_FILES, "Deleting file " + fileStagingPath);
                                        File.Delete(fileStagingPath);
                                    }
                                }
                                else
                                {
                                    // No old manifest or file not in old manifest. We must validate.

                                    DebugLog.WriteLine(DEBUG_NAME_FILES, "Opening file " + fileFinalPath);
                                    using (FileStream fs = File.Open(fileFinalPath, FileMode.Open))
                                    {
                                        if ((ulong)fi.Length != file.TotalSize)
                                        {
                                            fs.SetLength((long)file.TotalSize);
                                        }

                                        neededChunks = Util.ValidateSteam3FileChecksums(fs, file.Chunks.OrderBy(x => x.Offset).ToArray());
                                    }
                                }

                                if (neededChunks.Count() == 0)
                                {
                                    size_downloaded += file.TotalSize;
                                    DebugLog.WriteLine("ContentDownloader", DownloadPercent * 100.0f + "% " + fileFinalPath);
                                    return;
                                }
                                else
                                {
                                    size_downloaded += (file.TotalSize - ( ulong )neededChunks.Select(x => ( long )x.UncompressedLength).Sum());
                                }
                            }

                            DebugLog.WriteLine(DEBUG_NAME_FILES, "Opening file " + fileFinalPath);
                            using (FileStream fs = File.Open(fileFinalPath, FileMode.Open))
                            {
                                foreach (var chunk in neededChunks)
                                {
                                    if (cts.IsCancellationRequested)
                                    {
                                        break;
                                    }

                                    string chunkID = Util.EncodeHexString(chunk.ChunkID);
                                    CDNClient.DepotChunk chunkData = null;

                                    while (!cts.IsCancellationRequested)
                                    {
                                        CDNClient client;
                                        try
                                        {
                                            client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, cts.Token).ConfigureAwait(false);
                                            //client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, cts.Token);
                                        }
                                        catch (OperationCanceledException)
                                        {
                                            break;
                                        }

                                        DepotManifest.ChunkData data = new DepotManifest.ChunkData();
                                        data.ChunkID            = chunk.ChunkID;
                                        data.Checksum           = chunk.Checksum;
                                        data.Offset             = chunk.Offset;
                                        data.CompressedLength   = chunk.CompressedLength;
                                        data.UncompressedLength = chunk.UncompressedLength;

                                        try
                                        {
                                            //chunkData = await client.DownloadDepotChunkAsStreamAsync(depot.id, data).ConfigureAwait(false);
                                            chunkData = client.DownloadDepotChunkAsStreamAsync(depot.id, data).Result;
                                            //chunkData = await client.DownloadDepotChunkAsync(depot.id, data);
                                            cdnPool.ReturnConnection(client);
                                            break;
                                        }
                                        catch (WebException e)
                                        {
                                            cdnPool.ReturnBrokenConnection(client);

                                            if (e.Status == WebExceptionStatus.ProtocolError)
                                            {
                                                var response = e.Response as HttpWebResponse;
                                                if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
                                                {
                                                    DebugLog.WriteLine("ContentDownloader", "Encountered 401 for chunk " + chunkID + ". Aborting.");
                                                    cts.Cancel();
                                                    break;
                                                }
                                                else
                                                {
                                                    DebugLog.WriteLine("ContentDownloader", "Encountered error downloading chunk " + chunkID + ": " + response.StatusCode);
                                                }
                                            }
                                            else
                                            {
                                                DebugLog.WriteLine("ContentDownloader", "Encountered error downloading chunk " + chunkID + ": " + e.Status);
                                            }
                                        }
                                        catch (Exception e)
                                        {
                                            cdnPool.ReturnBrokenConnection(client);
                                            DebugLog.WriteLine("ContentDownloader", "Encountered unexpected error downloading chunk " + chunkID + ": " + e.Message);
                                        }
                                    }

                                    if (chunkData == null)
                                    {
                                        DebugLog.WriteLine("ContentDownloader", "Failed to find any server with chunk " + chunkID + " for depot " + depot.id + ". Aborting.");
                                        cts.Cancel();
                                        return;
                                    }

                                    TotalBytesCompressed   += chunk.CompressedLength;
                                    DepotBytesCompressed   += chunk.CompressedLength;
                                    TotalBytesUncompressed += chunk.UncompressedLength;
                                    DepotBytesUncompressed += chunk.UncompressedLength;

                                    using (chunkData.DataStream)
                                    {
                                        fs.Seek((long)chunk.Offset, SeekOrigin.Begin);
                                        chunkData.DataStream.CopyTo(fs);
                                        //fs.Write(chunkData.Data, 0, chunkData.Data.Length);
                                    }

                                    size_downloaded += chunk.UncompressedLength;
                                }
                            }
                            DebugLog.WriteLine("ContentDownloader", DownloadPercent * 100.0f + "% " + fileFinalPath);
                        }
                        finally
                        {
                            semaphore.Release();
                        }
                    });

                    tasks[i] = task;
                }

                Task.WaitAll(tasks);

                ConfigStore.TheConfig.LastManifests[depot.id] = depot.manifestId;
                ConfigStore.Save();

                DebugLog.WriteLine("ContentDownloader", "Depot " + depot.id + " - Downloaded " + DepotBytesCompressed + " bytes (" + DepotBytesUncompressed + " bytes uncompressed)");
            }

            IsDownloading = false;
            downloadCompleteAction?.Invoke();
            onDownloadCompleted?.Invoke();
            DebugLog.WriteLine("ContentDownloader", "Total downloaded: " + TotalBytesCompressed + " bytes (" + TotalBytesUncompressed + " bytes uncompressed) from " + depots.Count + " depots");
        }
Beispiel #8
0
        private void LogOnCallback(SteamUser.LoggedOnCallback loggedOn)
        {
            isLoggingIn = false;

            if (logonDetails != null)
            {
                bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
                bool is2FA        = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
                bool isLoginKey   = logonDetails.ShouldRememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;
                if (isSteamGuard || is2FA || isLoginKey)
                {
                    bExpectingDisconnectRemote = true;
                    Abort(false);

                    if (!isLoginKey)
                    {
                        DebugLog.WriteLine("Steam3Session", "This account is protected by Steam Guard.");
                    }

                    if (is2FA)
                    {
                        DebugLog.WriteLine("Steam3Session", "Please enter your 2 factor auth code from your authenticator app: ");
                        on2faRequired?.Invoke(loggedOn.Result);
                        //logonDetails.TwoFactorCode = Console.ReadLine();
                    }
                    else if (isLoginKey)
                    {
                        ConfigStore.TheConfig.LoginKeys.Remove(logonDetails.Username);
                        ConfigStore.Save();

                        logonDetails.LoginKey = null;

                        DebugLog.WriteLine("Steam3Session", "Login key was expired. Please enter your password: "******"Steam3Session", "Please enter the authentication code sent to your email address: ");
                        onAuthRequired?.Invoke(loggedOn.Result);
                        //logonDetails.AuthCode = Console.ReadLine();
                    }

                    //DebugLog.WriteLine("Steam3Session",  "Retrying Steam3 connection..." );
                    //Connect();
                    onLogonFailed?.Invoke(loggedOn.Result);

                    return;
                }
                else if (loggedOn.Result == EResult.ServiceUnavailable)
                {
                    DebugLog.WriteLine("Steam3Session", "Unable to login to Steam3: " + loggedOn.Result);
                    Abort(false);

                    onLogonFailed?.Invoke(loggedOn.Result);
                    return;
                }
                else if (loggedOn.Result != EResult.OK)
                {
                    DebugLog.WriteLine("Steam3Session", "Unable to login to Steam3: " + loggedOn.Result);
                    Abort();

                    onLogonFailed?.Invoke(loggedOn.Result);
                    return;
                }
            }

            DebugLog.WriteLine("Steam3Session", " Done!");
            DebugLog.WriteLine("Steam3Session", "Using Steam3 suggested CellID: " + loggedOn.CellID);
            ContentDownloader.Config.CellID = (int)loggedOn.CellID;

            onLoggedOn?.Invoke();
        }