private static KeyValue GetAppManifestValue(string manifestPath, string key) { var acfKeys = new KeyValue(); var reader = new StreamReader(manifestPath); var acfReader = new KVTextReader(acfKeys, reader.BaseStream); reader.Close(); return acfKeys.Children.FirstOrDefault(k => k.Name == key); }
public void KeyValueIndexerReturnsValidAndInvalid() { KeyValue kv = new KeyValue(); kv.Children.Add( new KeyValue( "exists", "value" ) ); Assert.Equal( kv["exists"].Value, "value" ); Assert.Equal( kv["thiskeydoesntexist"], KeyValue.Invalid ); }
public void KeyValueInitializesCorrectly() { KeyValue kv = new KeyValue( "name", "value" ); Assert.Equal( "name", kv.Name ); Assert.Equal( "value", kv.Value ); Assert.Empty( kv.Children ); }
static string Convert(KeyValue kv, bool compactJSON) { var jo = new JObject(); var rootNode = new JObject(); jo[kv.Name] = ConvertRecursive(kv); StringWriter textWriter = new StringWriter(); JsonWriter jsonWriter = new JsonTextWriter(textWriter); jsonWriter.Formatting = compactJSON ? Formatting.None : Formatting.Indented; jo.WriteTo(jsonWriter); return textWriter.GetStringBuilder().ToString(); }
private static void PrintBinary(CommandArguments command, KeyValue kv, string key) { if (kv[key].Children.Count == 0) { return; } kv = kv[key]; CommandHandler.ReplyToCommand(command, "{0}{1} {2}({3} MB)", CDN, kv["file"].AsString(), Colors.DARKGRAY, (kv["size"].AsLong() / 1048576.0).ToString("0.###")); }
public GCClientNotificationHandler( GCManager manager ) : base(manager) { new GCCallback<CMsgGCClientDisplayNotification>( ClientNotification, OnNotification, manager ); tfEnglish = KeyValue.LoadAsText( Path.Combine( Application.StartupPath, "tf_english.txt" ) ); if ( tfEnglish == null ) { Log.WriteWarn( "GCClientNotificationHandler", "Unable to load tf_english.txt, localizations will be unavailable!" ); } }
public bool GetPackageInfo( uint packageId, out KeyValue packageInfo ) { packageInfo = KeyValue.LoadAsBinary( GetPackageCachePath( packageId ) ); if ( packageInfo == null ) return false; packageInfo = packageInfo.Children .FirstOrDefault(); return packageInfo != null; }
public void KeyValueIndexerUpdatesKey() { KeyValue kv = new KeyValue(); KeyValue subkey = new KeyValue(); Assert.Null( subkey.Name ); kv["subkey"] = subkey; Assert.Equal( subkey.Name, "subkey" ); Assert.Equal( kv["subkey"].Name, "subkey" ); }
public KVTextReader( KeyValue kv, Stream input ) : base( input ) { bool wasQuoted; bool wasConditional; KeyValue currentKey = kv; do { bool bAccepted = true; string s = ReadToken( out wasQuoted, out wasConditional ); if ( string.IsNullOrEmpty( s ) ) break; if ( currentKey == null ) { currentKey = new KeyValue( s ); } else { currentKey.Name = s; } s = ReadToken( out wasQuoted, out wasConditional ); if ( wasConditional ) { bAccepted = ( s == "[$WIN32]" ); // Now get the '{' s = ReadToken( out wasQuoted, out wasConditional ); } if ( s.StartsWith( "{" ) && !wasQuoted ) { // header is valid so load the file currentKey.RecursiveLoadFromBuffer( this ); } else { throw new Exception( "LoadFromBuffer: missing {" ); } currentKey = null; } while ( !EndOfStream ); }
public void KeyValueIndexerDoesntallowDuplicates() { KeyValue kv = new KeyValue(); kv["key"] = new KeyValue(); Assert.Equal( kv.Children.Count, 1 ); kv["key"] = new KeyValue(); Assert.Equal( kv.Children.Count, 1 ); kv["key2"] = new KeyValue(); Assert.Equal( kv.Children.Count, 2 ); }
public static string JsonifyKeyValue(KeyValue keys) { string value = string.Empty; using (var sw = new StringWriter(new StringBuilder())) { using (JsonWriter w = new JsonTextWriter(sw)) { DbWorker.JsonifyKeyValue(w, keys.Children); } value = sw.ToString(); } return value; }
static JToken ConvertRecursive(KeyValue kv) { var jo = new JObject(); if (kv.Children.Count > 0) { foreach (var child in kv.Children) { jo[child.Name] = ConvertRecursive(child); } return jo; } else { return (JToken)kv.Value; } }
public bool GetAppInfo( uint appId, out KeyValue appInfo ) { appInfo = KeyValue.LoadAsText( GetAppCachePath( appId ) ); // cache off the name if ( appInfo != null ) { string name = appInfo[ "common" ][ "name" ].AsString(); string type = appInfo[ "common" ][ "type" ].AsString(); bool isGame = string.Equals( type, "game", StringComparison.OrdinalIgnoreCase ); if ( name != null ) { appNameCache[ name ] = new AppCacheEntry { AppID = appId, IsGame = isGame }; } } return appInfo != null; }
public override void OnCommand(CommandArguments command) { using (var webClient = new WebClient()) { webClient.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) { var kv = new KeyValue(); using (var ms = new MemoryStream(e.Result)) { try { kv.ReadAsText(ms); } catch { CommandHandler.ReplyToCommand(command, "Something went horribly wrong and keyvalue parser died."); return; } } if (kv["bins_osx"].Children.Count == 0) { CommandHandler.ReplyToCommand(command, "Failed to find binaries in parsed response."); return; } kv = kv["bins_osx"]; CommandHandler.ReplyToCommand(command, "You're on your own:{0} {1}{2} {3}({4} MB)", Colors.DARKBLUE, CDN, kv["file"].AsString(), Colors.DARKGRAY, (kv["size"].AsLong() / 1048576.0).ToString("0.###")); }; webClient.DownloadDataAsync(new Uri(string.Format("{0}steam_client_publicbeta_osx?_={1}", CDN, DateTime.UtcNow.Ticks))); } }
/// <summary> /// Initializes a new instance of the <see cref="MessageObject"/> class with an empty inner KeyValues. /// </summary> public MessageObject() { this.KeyValues = new KeyValue( "MessageObject" ); }
/// <summary> /// Initializes a new instance of the <see cref="MessageObject"/> class, using the provided KeyValues object. /// </summary> /// <param name="keyValues">The KeyValue backing store for this message object.</param> public MessageObject( KeyValue keyValues ) { this.KeyValues = keyValues; }
/// <summary> /// Manually calls the specified Web API function with the provided details. /// </summary> /// <param name="func">The function name to call.</param> /// <param name="version">The version of the function to call.</param> /// <param name="args">A dictionary of string key value pairs representing arguments to be passed to the API.</param> /// <param name="method">The http request method. Either "POST" or "GET".</param> /// <param name="secure">if set to <c>true</c> this method will be called through the secure API.</param> /// <returns>A <see cref="Task{T}"/> that contains a <see cref="KeyValue"/> object representing the results of the Web API call.</returns> /// <exception cref="ArgumentNullException">The function name or request method provided were <c>null</c>.</exception> /// <exception cref="WebException">An network error occurred when performing the request.</exception> /// <exception cref="InvalidDataException">An error occured when parsing the response from the WebAPI.</exception> public Task <KeyValue> Call(string func, int version = 1, Dictionary <string, string> args = null, string method = WebRequestMethods.Http.Get, bool secure = false) { if (func == null) { throw new ArgumentNullException("func"); } if (args == null) { args = new Dictionary <string, string>(); } if (method == null) { throw new ArgumentNullException("method"); } StringBuilder urlBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder(); urlBuilder.Append(secure ? "https://" : "http://"); urlBuilder.Append(API_ROOT); urlBuilder.AppendFormat("/{0}/{1}/v{2}", iface, func, version); bool isGet = method.Equals(WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase); if (isGet) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append("/?"); // start our GET params } args.Add("format", "vdf"); if (!string.IsNullOrEmpty(apiKey)) { args.Add("key", apiKey); } // append any args paramBuilder.Append(string.Join("&", args.Select(kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode(kvp.Key); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return(string.Format("{0}={1}", key, value)); }))); var task = Task.Factory.StartNew <KeyValue>(() => { byte[] data = null; if (isGet) { data = webClient.DownloadData(urlBuilder.ToString()); } else { byte[] postData = Encoding.Default.GetBytes(paramBuilder.ToString()); webClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded"); data = webClient.UploadData(urlBuilder.ToString(), postData); } KeyValue kv = new KeyValue(); using (var ms = new MemoryStream(data)) { try { kv.ReadAsText(ms); } catch (Exception ex) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return(kv); }); task.ContinueWith(t => { // we need to observe the exception in this OnlyOnFaulted continuation if our task throws an exception but we're not able to observe it // (such as when waiting for the task times out, and an exception is thrown later) // see: http://msdn.microsoft.com/en-us/library/dd997415.aspx DebugLog.WriteLine("WebAPI", "Threw an unobserved exception: {0}", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted); return(task); }
internal GuestPassListCallback( MsgClientUpdateGuestPassesList msg, Stream payload ) { Result = msg.Result; CountGuestPassesToGive = msg.CountGuestPassesToGive; CountGuestPassesToRedeem = msg.CountGuestPassesToRedeem; GuestPasses = new List<KeyValue>(); for ( int i = 0; i < CountGuestPassesToGive + CountGuestPassesToRedeem; i++ ) { var kv = new KeyValue(); kv.TryReadAsBinary( payload ); GuestPasses.Add( kv ); } }
internal App( CMsgClientAppInfoResponse.App app, AppInfoStatus status ) { Status = status; AppID = app.app_id; ChangeNumber = app.change_number; Sections = new Dictionary<EAppInfoSection, KeyValue>(); foreach ( var section in app.sections ) { KeyValue kv = new KeyValue(); using ( MemoryStream ms = new MemoryStream( section.section_kv ) ) { if ( kv.TryReadAsBinary( ms ) ) { Sections.Add( ( EAppInfoSection )section.section_id, kv ); } } } }
internal Package( CMsgClientPackageInfoResponse.Package pack, Package.PackageStatus status ) { Status = status; PackageID = pack.package_id; ChangeNumber = pack.change_number; Hash = pack.sha; Data = new KeyValue(); using ( var ms = new MemoryStream( pack.buffer ) ) using ( var br = new BinaryReader( ms ) ) { br.ReadUInt32(); // unknown uint at the beginning of the buffer Data.ReadAsBinary( ms ); } if ( Data.Children != null ) { Data = Data.Children.FirstOrDefault() ?? KeyValue.Invalid; } }
public void Process(uint appID, uint changeNumber, KeyValue depots) { var buildID = depots["branches"]["public"]["buildid"].AsInteger(); foreach (KeyValue depot in depots.Children) { // Ignore these for now, parent app should be updated too anyway if (depot["depotfromapp"].Value != null) { ////Log.WriteDebug("Depot Processor", "Ignoring depot {0} with depotfromapp value {1} (parent {2})", depot.Name, depot["depotfromapp"].AsString(), AppID); continue; } uint depotID; if (!uint.TryParse(depot.Name, out depotID)) { // Ignore keys that aren't integers, for example "branches" continue; } // TODO: instead of locking we could wait for current process to finish if (DepotLocks.ContainsKey(depotID)) { continue; } ulong manifestID; var depotName = depot["name"].AsString(); if (depot["manifests"]["public"].Value == null || !ulong.TryParse(depot["manifests"]["public"].Value, out manifestID)) { #if false Log.WriteDebug("Depot Processor", "Failed to public branch for depot {0} (parent {1}) - {2}", DepotID, AppID); // If there is no public manifest for this depot, it still could have some sort of open beta var branch = depot["manifests"].Children.SingleOrDefault(x => x.Name != "local"); if (branch == null || !ulong.TryParse(branch.Value, out ManifestID)) { continue; } #endif DbWorker.ExecuteNonQuery("INSERT INTO `Depots` (`DepotID`, `Name`) VALUES (@DepotID, @Name) ON DUPLICATE KEY UPDATE `LastUpdated` = CURRENT_TIMESTAMP(), `Name` = @Name", new MySqlParameter("@DepotID", depotID), new MySqlParameter("@Name", depotName) ); continue; } var request = new ManifestJob { ChangeNumber = changeNumber, ParentAppID = appID, DepotID = depotID, ManifestID = manifestID, DepotName = depotName }; int currentBuildID = 0; // Check if manifestid in our database is equal using (MySqlDataReader Reader = DbWorker.ExecuteReader("SELECT `Name`, `ManifestID`, `BuildID` FROM `Depots` WHERE `DepotID` = @DepotID LIMIT 1", new MySqlParameter("DepotID", depotID))) { if (Reader.Read()) { currentBuildID = Reader.GetInt32("buildID"); request.PreviousManifestID = Reader.GetUInt64("ManifestID"); if (request.PreviousManifestID == manifestID && Settings.Current.FullRun < 2) { // Update depot name if changed if(!depotName.Equals(Reader.GetString("Name"))) { DbWorker.ExecuteNonQuery("UPDATE `Depots` SET `Name` = @Name WHERE `DepotID` = @DepotID", new MySqlParameter("@DepotID", request.DepotID), new MySqlParameter("@Name", request.DepotName) ); } continue; } if (currentBuildID > buildID) { Log.WriteDebug("Depot Processor", "Skipping depot {0} due to old buildid: {1} > {2}", depotID, currentBuildID, buildID); continue; } } } DepotLocks.TryAdd(depotID, 1); // Update/insert depot information straight away if (currentBuildID != buildID || request.PreviousManifestID != request.ManifestID) { DbWorker.ExecuteNonQuery("INSERT INTO `Depots` (`DepotID`, `Name`, `BuildID`, `ManifestID`) VALUES (@DepotID, @Name, @BuildID, @ManifestID) ON DUPLICATE KEY UPDATE `LastUpdated` = CURRENT_TIMESTAMP(), `Name` = @Name, `BuildID` = @BuildID, `ManifestID` = @ManifestID", new MySqlParameter("@DepotID", request.DepotID), new MySqlParameter("@BuildID", buildID), new MySqlParameter("@ManifestID", request.ManifestID), new MySqlParameter("@Name", request.DepotName) ); MakeHistory(request, string.Empty, "manifest_change", request.PreviousManifestID, request.ManifestID); } request.Server = CDNServers[new Random().Next(CDNServers.Count)]; JobManager.AddJob(() => Steam.Instance.Apps.GetCDNAuthToken(depotID, request.Server), request); } }
/// <summary> /// Manually calls the specified Web API function with the provided details. /// </summary> /// <param name="func">The function name to call.</param> /// <param name="version">The version of the function to call.</param> /// <param name="args">A dictionary of string key value pairs representing arguments to be passed to the API.</param> /// <param name="method">The http request method. Either "POST" or "GET".</param> /// <param name="secure">if set to <c>true</c> this method will be called through the secure API.</param> /// <returns>A <see cref="Task{T}"/> that contains a <see cref="KeyValue"/> object representing the results of the Web API call.</returns> /// <exception cref="ArgumentNullException">The function name or request method provided were <c>null</c>.</exception> /// <exception cref="WebException">An network error occurred when performing the request.</exception> /// <exception cref="InvalidDataException">An error occured when parsing the response from the WebAPI.</exception> public Task<KeyValue> Call( string func, int version = 1, Dictionary<string, string> args = null, string method = WebRequestMethods.Http.Get, bool secure = false ) { if ( func == null ) throw new ArgumentNullException( "func" ); if ( args == null ) args = new Dictionary<string, string>(); if ( method == null ) throw new ArgumentNullException( "method" ); StringBuilder urlBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder(); urlBuilder.Append( secure ? "https://" : "http://" ); urlBuilder.Append( API_ROOT ); urlBuilder.AppendFormat( "/{0}/{1}/v{2}", iface, func, version ); bool isGet = method.Equals( WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase ); if ( isGet ) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append( "/?" ); // start our GET params } args.Add( "format", "vdf" ); if ( !string.IsNullOrEmpty( apiKey ) ) { args.Add( "key", apiKey ); } // append any args paramBuilder.Append( string.Join( "&", args.Select( kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode( kvp.Key ); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return string.Format( "{0}={1}", key, value ); } ) ) ); var task = Task.Factory.StartNew<KeyValue>( () => { byte[] data = null; if ( isGet ) { data = webClient.DownloadData( urlBuilder.ToString() ); } else { byte[] postData = Encoding.Default.GetBytes( paramBuilder.ToString() ); webClient.Headers.Add( HttpRequestHeader.ContentType, "application/x-www-form-urlencoded" ); data = webClient.UploadData( urlBuilder.ToString(), postData ); } KeyValue kv = new KeyValue(); using ( var ms = new MemoryStream( data ) ) { try { kv.ReadAsText( ms ); } catch ( Exception ex ) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return kv; } ); task.ContinueWith( t => { // we need to observe the exception in this OnlyOnFaulted continuation if our task throws an exception but we're not able to observe it // (such as when waiting for the task times out, and an exception is thrown later) // see: http://msdn.microsoft.com/en-us/library/dd997415.aspx DebugLog.WriteLine( "WebAPI", "Threw an unobserved exception: {0}", t.Exception ); }, TaskContinuationOptions.OnlyOnFaulted ); return task; }
public void KeyValuesReadsBinaryWithMultipleChildren() { var hex = "00546573744f626a65637400016b6579310076616c75653100016b6579320076616c756532000808"; var binary = Utils.DecodeHexString( hex ); var kv = new KeyValue(); bool success; using ( var ms = new MemoryStream( binary ) ) { success = kv.TryReadAsBinary( ms ); } Assert.True( success ); Assert.Equal( "TestObject", kv.Name ); Assert.Equal( 2, kv.Children.Count ); Assert.Equal( "key1", kv.Children[ 0 ].Name ); Assert.Equal( "value1", kv.Children[ 0 ].Value ); Assert.Equal( "key2", kv.Children[ 1 ].Name ); Assert.Equal( "value2", kv.Children[ 1 ].Value ); }
public void KeyValuesFailsToReadTruncatedBinary() { // Test every possible truncation boundary we have. for ( int i = 0; i < TestObjectHex.Length; i += 2 ) { var binary = Utils.DecodeHexString( TestObjectHex.Substring( 0, i ) ); var kv = new KeyValue(); bool success; using ( var ms = new MemoryStream( binary ) ) { success = kv.TryReadAsBinary( ms ); Assert.Equal( ms.Length, ms.Position ); } Assert.False( success, "Should not have read test object." ); } }
async Task <KeyValue> CallAsyncCore(HttpMethod method, string func, int version = 1, Dictionary <string, string> args = null, bool secure = false) { if (method == null) { throw new ArgumentNullException(nameof(method)); } if (func == null) { throw new ArgumentNullException(nameof(func)); } if (args == null) { args = new Dictionary <string, string>(); } var urlBuilder = new StringBuilder(); var paramBuilder = new StringBuilder(); urlBuilder.Append(secure ? "https://" : "http://"); urlBuilder.Append(API_ROOT); urlBuilder.AppendFormat("/{0}/{1}/v{2}", iface, func, version); var isGet = HttpMethod.Get.Equals(method); if (isGet) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append("/?"); // start our GET params } args.Add("format", "vdf"); if (!string.IsNullOrEmpty(apiKey)) { args.Add("key", apiKey); } // append any args paramBuilder.Append(string.Join("&", args.Select(kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode(kvp.Key); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return(string.Format("{0}={1}", key, value)); }))); var request = new HttpRequestMessage(method, urlBuilder.ToString()); if (!isGet) { request.Content = new StringContent(paramBuilder.ToString()); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); } var response = await httpClient.SendAsync(request).ConfigureAwait(false); var kv = new KeyValue(); using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { try { kv.ReadAsText(stream); } catch (Exception ex) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return(kv); }
/// <summary> /// Attempts to load the given filename as a binary <see cref="KeyValue"/>. /// </summary> /// <param name="path">The path to the file to load.</param> /// <param name="keyValue">The resulting <see cref="KeyValue"/> object if the load was successful, or <c>null</c> if unsuccessful.</param> /// <returns><c>true</c> if the load was successful, or <c>false</c> on failure.</returns> public static bool TryLoadAsBinary(string path, out KeyValue keyValue) { keyValue = LoadFromFile(path, true); return(keyValue != null); }
/// <summary> /// Fetches a list of content servers. /// </summary> /// <param name="csServer"> /// The optional Steam3 content server to fetch the list from. /// If this parameter is not specified, a random CS server will be selected. /// </param> /// <param name="cellId"> /// The optional CellID used to specify which regional servers should be returned in the list. /// If this parameter is not specified, Steam's GeoIP suggested CellID will be used instead. /// </param> /// <param name="maxServers">The maximum amount of servers to request.</param> /// <returns>A list of servers.</returns> /// <exception cref="System.InvalidOperationException"> /// No Steam CS servers available, or the suggested CellID is unavailable. /// Check that the <see cref="SteamClient"/> associated with this <see cref="CDNClient"/> instance is logged onto Steam. /// </exception> public List <Server> FetchServerList(IPEndPoint csServer = null, uint?cellId = null, int maxServers = 20) { DebugLog.Assert(steamClient.IsConnected, "CDNClient", "CMClient is not connected!"); DebugLog.Assert(steamClient.CellID != null, "CDNClient", "CMClient is not logged on!"); if (csServer == null) { // if we're not specifying what CS server we want to fetch a server list from, randomly select a cached CS server var csServers = steamClient.GetServersOfType(EServerType.CS); if (csServers.Count == 0) { // steamclient doesn't know about any CS servers yet throw new InvalidOperationException("No CS servers available!"); } Random random = new Random(); csServer = csServers[random.Next(csServers.Count)]; } if (cellId == null) { if (steamClient.CellID == null) { throw new InvalidOperationException("Recommended CellID is not available. CMClient not logged on?"); } // fallback to recommended cellid cellId = steamClient.CellID.Value; } KeyValue serverKv = DoCommand(csServer, "serverlist", args: string.Format("{0}/{1}/", cellId, maxServers)); var serverList = new List <Server>(maxServers); if (serverKv["deferred"].AsBoolean()) { return(serverList); } foreach (var server in serverKv.Children) { string type = server["type"].AsString(); string host = server["host"].AsString(); string[] hostSplits = host.Split(':'); int port = 80; if (hostSplits.Length > 1) { int parsedPort; if (int.TryParse(hostSplits[1], out parsedPort)) { port = parsedPort; } } uint serverCell = ( uint )server["cell"].AsInteger(); int load = server["load"].AsInteger(); int weightedLoad = server["weightedload"].AsInteger(); serverList.Add(new Server { Host = host, Port = port, Type = type, CellID = serverCell, Load = load, WeightedLoad = weightedLoad, }); } return(serverList); }
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) { if ((jobID == null) || (msg == null)) { throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg)); } JobID = jobID; PurchaseResult = (EPurchaseResult) msg.purchase_result_details; if (msg.purchase_receipt_info == null) { return; } KeyValue receiptInfo = new KeyValue(); using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) { if (!receiptInfo.TryReadAsBinary(ms)) { Logging.LogNullError(nameof(ms)); return; } } List<KeyValue> lineItems = receiptInfo["lineitems"].Children; if (lineItems.Count == 0) { return; } Items = new Dictionary<uint, string>(lineItems.Count); foreach (KeyValue lineItem in lineItems) { uint packageID = lineItem["PackageID"].AsUnsignedInteger(); if (packageID == 0) { // Valid, coupons have PackageID of -1 (don't ask me why) packageID = lineItem["ItemAppID"].AsUnsignedInteger(); if (packageID == 0) { Logging.LogNullError(nameof(packageID)); return; } } string gameName = lineItem["ItemDescription"].Value; if (string.IsNullOrEmpty(gameName)) { Logging.LogNullError(nameof(gameName)); return; } gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML Items[packageID] = WebUtility.HtmlDecode(gameName); } }
public static void Process(uint appID, uint changeNumber, KeyValue depots) { foreach (KeyValue depot in depots.Children) { // Ignore these for now, parent app should be updated too anyway if (depot["depotfromapp"].Value != null) { ////Log.WriteDebug("Depot Processor", "Ignoring depot {0} with depotfromapp value {1} (parent {2})", depot.Name, depot["depotfromapp"].AsString(), AppID); continue; } uint depotID; if (!uint.TryParse(depot.Name, out depotID)) { // Ignore keys that aren't integers, for example "branches" continue; } lock (ManifestJobs) { if (ManifestJobs.Find(r => r.DepotID == depotID) != null) { // If we already have this depot in our job list, ignore it continue; } } ulong manifestID; if (depot["manifests"]["public"].Value == null || !ulong.TryParse(depot["manifests"]["public"].Value, out manifestID)) { #if false Log.WriteDebug("Depot Processor", "Failed to public branch for depot {0} (parent {1}) - {2}", DepotID, AppID); // If there is no public manifest for this depot, it still could have some sort of open beta var branch = depot["manifests"].Children.SingleOrDefault(x => x.Name != "local"); if (branch == null || !ulong.TryParse(branch.Value, out ManifestID)) { continue; } #endif continue; } var request = new ManifestJob { ChangeNumber = changeNumber, ParentAppID = appID, DepotID = depotID, ManifestID = manifestID, DepotName = depot["name"].AsString() }; // Check if manifestid in our database is equal using (MySqlDataReader Reader = DbWorker.ExecuteReader("SELECT `ManifestID` FROM `Depots` WHERE `DepotID` = @DepotID AND `Files` != '' LIMIT 1", new MySqlParameter("DepotID", depotID))) { if (Reader.Read()) { request.PreviousManifestID = Reader.GetUInt64("ManifestID"); if (Settings.Current.FullRun != 3 && request.PreviousManifestID == manifestID) { continue; } } } Log.WriteInfo("Depot Processor", "DepotID: {0}", depotID); lock (ManifestJobs) { ManifestJobs.Add(request); } request.JobID = Steam.Instance.Apps.GetAppOwnershipTicket(depotID); } }
/// <summary> /// Manually calls the specified Web API function with the provided details. /// </summary> /// <param name="func">The function name to call.</param> /// <param name="version">The version of the function to call.</param> /// <param name="args">A dictionary of string key value pairs representing arguments to be passed to the API.</param> /// <param name="method">The http request method. Either "POST" or "GET".</param> /// <param name="secure">if set to <c>true</c> this method will be called through the secure API.</param> /// <returns>A <see cref="KeyValue"/> object representing the results of the Web API call.</returns> /// <exception cref="ArgumentNullException">The function name or request method provided were <c>null</c>.</exception> /// <exception cref="WebException">An network error occurred when performing the request.</exception> /// <exception cref="InvalidDataException">An error occured when parsing the response from the WebAPI.</exception> public KeyValue Call(string func, int version = 1, Dictionary <string, string> args = null, string method = WebRequestMethods.Http.Get, bool secure = false) { if (func == null) { throw new ArgumentNullException("func"); } if (args == null) { args = new Dictionary <string, string>(); } if (method == null) { throw new ArgumentNullException("method"); } StringBuilder urlBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder(); urlBuilder.Append(secure ? "https://" : "http://"); urlBuilder.Append(API_ROOT); urlBuilder.AppendFormat("/{0}/{1}/v{2}", iface, func, version); bool isGet = method.Equals(WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase); if (isGet) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append("/?"); // start our GET params } args.Add("format", "vdf"); if (!string.IsNullOrEmpty(apiKey)) { args.Add("key", apiKey); } // append any args paramBuilder.Append(string.Join("&", args.Select(kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode(kvp.Key); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return(string.Format("{0}={1}", key, value)); }))); byte[] data = null; if (isGet) { data = webClient.DownloadData(urlBuilder.ToString()); } else { byte[] postData = Encoding.Default.GetBytes(paramBuilder.ToString()); webClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded"); data = webClient.UploadData(urlBuilder.ToString(), postData); } KeyValue kv = new KeyValue(); using (var ms = new MemoryStream(data)) { try { kv.ReadAsText(ms); } catch (Exception ex) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return(kv); }
internal App( CMsgClientAppInfoResponse.App app, AppInfoStatus status ) { Status = status; AppID = app.app_id; ChangeNumber = app.change_number; Sections = new Dictionary<EAppInfoSection, KeyValue>(); foreach ( var section in app.sections ) { KeyValue kv = new KeyValue(); using ( MemoryStream ms = new MemoryStream( section.section_kv ) ) kv.ReadAsBinary( ms ); if ( kv.Children != null ) { Sections.Add( ( EAppInfoSection )section.section_id, kv.Children.FirstOrDefault() ?? KeyValue.Invalid ); } } }
/// <summary> /// Populate this instance from the given <see cref="Stream"/> as a binary <see cref="KeyValue"/>. /// </summary> /// <param name="input">The input <see cref="Stream"/> to read from.</param> /// <returns><c>true</c> if the read was successful; otherwise, <c>false</c>.</returns> public bool ReadAsBinary(Stream input) { this.Children = new List <KeyValue>(); while (true) { var type = ( Type )input.ReadByte(); if (type == Type.End) { break; } var current = new KeyValue(); current.Name = input.ReadNullTermString(Encoding.UTF8); try { switch (type) { case Type.None: { current.ReadAsBinary(input); break; } case Type.String: { current.Value = input.ReadNullTermString(Encoding.UTF8); break; } case Type.WideString: { throw new InvalidDataException("wstring is unsupported"); } case Type.Int32: case Type.Color: case Type.Pointer: { current.Value = Convert.ToString(input.ReadInt32()); break; } case Type.UInt64: { current.Value = Convert.ToString(input.ReadUInt64()); break; } case Type.Float32: { current.Value = Convert.ToString(input.ReadFloat()); break; } default: { throw new InvalidDataException("Unknown KV type encountered."); } } } catch (InvalidDataException ex) { throw new InvalidDataException(string.Format("An exception ocurred while reading KV '{0}'", current.Name), ex); } this.Children.Add(current); } return(input.Position == input.Length); }
/// <summary> /// Manually calls the specified Web API function with the provided details. /// </summary> /// <param name="func">The function name to call.</param> /// <param name="version">The version of the function to call.</param> /// <param name="args">A dictionary of string key value pairs representing arguments to be passed to the API.</param> /// <param name="method">The http request method. Either "POST" or "GET".</param> /// <param name="secure">if set to <c>true</c> this method will be called through the secure API.</param> /// <returns>A <see cref="KeyValue"/> object representing the results of the Web API call.</returns> /// <exception cref="ArgumentNullException">The function name or request method provided were <c>null</c>.</exception> /// <exception cref="WebException">An network error occurred when performing the request.</exception> /// <exception cref="InvalidDataException">An error occured when parsing the response from the WebAPI.</exception> public KeyValue Call( string func, int version = 1, Dictionary<string, string> args = null, string method = WebRequestMethods.Http.Get, bool secure = false ) { if ( func == null ) throw new ArgumentNullException( "func" ); if ( args == null ) args = new Dictionary<string, string>(); if ( method == null ) throw new ArgumentNullException( "method" ); StringBuilder urlBuilder = new StringBuilder(); StringBuilder paramBuilder = new StringBuilder(); urlBuilder.Append( secure ? "https://" : "http://" ); urlBuilder.Append( API_ROOT ); urlBuilder.AppendFormat( "/{0}/{1}/v{2}", iface, func, version ); bool isGet = method.Equals( WebRequestMethods.Http.Get, StringComparison.OrdinalIgnoreCase ); if ( isGet ) { // if we're doing a GET request, we'll build the params onto the url paramBuilder = urlBuilder; paramBuilder.Append( "/?" ); // start our GET params } args.Add( "format", "vdf" ); if ( !string.IsNullOrEmpty( apiKey ) ) { args.Add( "key", apiKey ); } // append any args paramBuilder.Append( string.Join( "&", args.Select( kvp => { // TODO: the WebAPI is a special snowflake that needs to appropriately handle url encoding // this is in contrast to the steam3 content server APIs which use an entirely different scheme of encoding string key = WebHelpers.UrlEncode( kvp.Key ); string value = kvp.Value; // WebHelpers.UrlEncode( kvp.Value ); return string.Format( "{0}={1}", key, value ); } ) ) ); byte[] data = null; if ( isGet ) { data = webClient.DownloadData( urlBuilder.ToString() ); } else { byte[] postData = Encoding.Default.GetBytes( paramBuilder.ToString() ); webClient.Headers.Add( HttpRequestHeader.ContentType, "application/x-www-form-urlencoded" ); data = webClient.UploadData( urlBuilder.ToString(), postData ); } KeyValue kv = new KeyValue(); using ( var ms = new MemoryStream( data ) ) { try { kv.ReadAsText( ms ); } catch ( Exception ex ) { throw new InvalidDataException( "An internal error occurred when attempting to parse the response from the WebAPI server. This can indicate a change in the VDF format.", ex ); } } return kv; }
internal void RecursiveLoadFromBuffer(KVTextReader kvr) { bool wasQuoted; bool wasConditional; while (true) { // bool bAccepted = true; // get the key name var name = kvr.ReadToken(out wasQuoted, out wasConditional); if (name is null || name.Length == 0) { throw new InvalidDataException("RecursiveLoadFromBuffer: got EOF or empty keyname"); } if (name.StartsWith("}") && !wasQuoted) // top level closed, stop reading { break; } KeyValue dat = new KeyValue(name); dat.Children = new List <KeyValue>(); this.Children.Add(dat); // get the value string?value = kvr.ReadToken(out wasQuoted, out wasConditional); if (wasConditional && value != null) { // bAccepted = ( value == "[$WIN32]" ); value = kvr.ReadToken(out wasQuoted, out wasConditional); } if (value == null) { throw new Exception("RecursiveLoadFromBuffer: got NULL key"); } if (value.StartsWith("}") && !wasQuoted) { throw new Exception("RecursiveLoadFromBuffer: got } in key"); } if (value.StartsWith("{") && !wasQuoted) { dat.RecursiveLoadFromBuffer(kvr); } else { if (wasConditional) { throw new Exception("RecursiveLoadFromBuffer: got conditional between key and value"); } dat.Value = value; // blahconditionalsdontcare } } }
internal Package( CMsgClientPackageInfoResponse.Package pack, Package.PackageStatus status ) { Status = status; PackageID = pack.package_id; ChangeNumber = pack.change_number; Hash = pack.sha; Data = new KeyValue(); using ( var ms = new MemoryStream( pack.buffer ) ) using ( var br = new BinaryReader( ms ) ) { // steamclient checks this value == 1 before it attempts to read the KV from the buffer // see: CPackageInfo::UpdateFromBuffer(CSHA const&,uint,CUtlBuffer &) // todo: we've apparently ignored this with zero ill effects, but perhaps we want to respect it? br.ReadUInt32(); Data.TryReadAsBinary( ms ); } }
static bool TryReadAsBinaryCore(Stream input, KeyValue current, KeyValue?parent) { current.Children = new List <KeyValue>(); while (true) { var type = ( Type )input.ReadByte(); if (type == Type.End) { break; } current.Name = input.ReadNullTermString(Encoding.UTF8); switch (type) { case Type.None: { var child = new KeyValue(); var didReadChild = TryReadAsBinaryCore(input, child, current); if (!didReadChild) { return(false); } break; } case Type.String: { current.Value = input.ReadNullTermString(Encoding.UTF8); break; } case Type.WideString: { DebugLog.WriteLine("KeyValue", "Encountered WideString type when parsing binary KeyValue, which is unsupported. Returning false."); return(false); } case Type.Int32: case Type.Color: case Type.Pointer: { current.Value = Convert.ToString(input.ReadInt32()); break; } case Type.UInt64: { current.Value = Convert.ToString(input.ReadUInt64()); break; } case Type.Float32: { current.Value = Convert.ToString(input.ReadFloat()); break; } case Type.Int64: { current.Value = Convert.ToString(input.ReadInt64()); break; } default: { return(false); } } if (parent != null) { parent.Children.Add(current); } current = new KeyValue(); } return(true); }
public void KeyValuesMissingKeysGiveInvalid() { KeyValue kv = new KeyValue(); Assert.Same( KeyValue.Invalid, kv[ "missingkey" ] ); }
/// <summary> /// Initializes a new instance of the <see cref="ChatMemberInfo"/> class. /// </summary> /// <param name="keyValues">The KeyValue backing store for this member info.</param> public ChatMemberInfo(KeyValue keyValues) : base(keyValues) { }
KeyValue DoCommand( Server server, string command, string data = null, string method = WebRequestMethods.Http.Get, bool doAuth = false, string args = "" ) { byte[] resultData = DoRawCommand( server, command, data, method, doAuth, args ); var dataKv = new KeyValue(); using ( MemoryStream ms = new MemoryStream( resultData ) ) { try { dataKv.ReadAsText( ms ); } catch ( Exception ex ) { throw new InvalidDataException( "An internal error occurred while attempting to parse the response from the CS server.", ex ); } } return dataKv; }
/// <summary> /// Initializes a new instance of the <see cref="MessageObject"/> class with an empty inner KeyValues. /// </summary> public MessageObject() { this.KeyValues = new KeyValue("MessageObject"); }