Represents a recursive string key to arbitrary value container.
예제 #1
0
 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);
 }
예제 #2
0
        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 );
        }
예제 #3
0
        public void KeyValueInitializesCorrectly()
        {
            KeyValue kv = new KeyValue( "name", "value" );

            Assert.Equal( "name", kv.Name );
            Assert.Equal( "value", kv.Value );

            Assert.Empty( kv.Children );
        }
예제 #4
0
 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();
 }
예제 #5
0
        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.###"));
        }
예제 #6
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;
        }
예제 #8
0
        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" );
        }
예제 #9
0
        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 );
        }
예제 #10
0
        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 );
        }
예제 #11
0
        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;
        }
예제 #12
0
        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;
        }
예제 #14
0
        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)));
            }
        }
예제 #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MessageObject"/> class with an empty inner KeyValues.
 /// </summary>
 public MessageObject()
 {
     this.KeyValues = new KeyValue( "MessageObject" );
 }
예제 #16
0
 /// <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;
 }
예제 #17
0
            /// <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);
            }
예제 #18
0
            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 );
                }
            }
예제 #19
0
                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 );
                            }
                        }
                    }
                }
예제 #20
0
                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);
            }
        }
예제 #22
0
파일: WebAPI.cs 프로젝트: JustHev/SteamKit
            /// <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;
            }
예제 #23
0
        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 );
        }
예제 #24
0
        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." );
            }
        }
예제 #25
0
            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);
            }
예제 #26
0
 /// <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);
 }
예제 #27
0
        /// <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);
        }
예제 #28
0
			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);
				}
			}
예제 #29
0
        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);
            }
        }
예제 #30
0
            /// <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);
            }
예제 #31
0
                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);
        }
예제 #33
0
            /// <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;
            }
예제 #34
0
        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
                }
            }
        }
예제 #35
0
                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 );
                    }
                }
예제 #36
0
        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);
        }
예제 #37
0
        public void KeyValuesMissingKeysGiveInvalid()
        {
            KeyValue kv = new KeyValue();

            Assert.Same( KeyValue.Invalid, kv[ "missingkey" ] );
        }
예제 #38
0
 /// <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)
 {
 }
예제 #39
0
        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;
        }
예제 #40
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MessageObject"/> class with an empty inner KeyValues.
 /// </summary>
 public MessageObject()
 {
     this.KeyValues = new KeyValue("MessageObject");
 }