public void WriterWriteLongCollection() { string outputPath = Guid.NewGuid().ToString() + ".plist"; IDictionary dict = new Dictionary<string, object>(); IDictionary longDict = new Dictionary<string, object>(); List<int> longArray = new List<int>(); for (int i = 0; i < 72; i++) { longDict.Add(i.ToString(CultureInfo.InvariantCulture), i); } for (int i = 0; i < 756; i++) { longArray.Add(i); } dict["Dictionary"] = longDict; dict["Array"] = longArray; BinaryPlistWriter writer = new BinaryPlistWriter(); writer.WriteObject(outputPath, dict); BinaryPlistReader reader = new BinaryPlistReader(); dict = reader.ReadObject(outputPath); Assert.AreEqual(2, dict.Count); Assert.AreEqual(72, ((IDictionary)dict["Dictionary"]).Count); Assert.AreEqual(756, ((object[])dict["Array"]).Length); }
public void WriterWriteLongCollection() { string outputPath = Guid.NewGuid().ToString() + ".plist"; IDictionary dict = new Dictionary <string, object>(); IDictionary longDict = new Dictionary <string, object>(); List <int> longArray = new List <int>(); for (int i = 0; i < 72; i++) { longDict.Add(i.ToString(CultureInfo.InvariantCulture), i); } for (int i = 0; i < 756; i++) { longArray.Add(i); } dict["Dictionary"] = longDict; dict["Array"] = longArray; BinaryPlistWriter writer = new BinaryPlistWriter(); writer.WriteObject(outputPath, dict); BinaryPlistReader reader = new BinaryPlistReader(); dict = reader.ReadObject(outputPath); Assert.AreEqual(2, dict.Count); Assert.AreEqual(72, ((IDictionary)dict["Dictionary"]).Count); Assert.AreEqual(756, ((object[])dict["Array"]).Length); }
public void WriterWriteObjectNested() { string outputPath = Guid.NewGuid().ToString() + ".plist"; BinaryPlistReader reader = new BinaryPlistReader(); IDictionary dictionary; using (Stream stream = File.OpenRead(Paths.NestedPlistPath)) { dictionary = reader.ReadObject(stream); } object[] arr = dictionary["Array"] as object[]; IDictionary dict = dictionary["Dictionary"] as IDictionary; arr[0] = "1st"; arr[1] = "2nd"; arr[2] = "3rd"; dict["Double"] = 2.0000000000000011; arr = dict["Array"] as object[]; arr[0] = "One"; arr[1] = 3; arr[2] = false; arr[3] = "Four"; BinaryPlistWriter writer = new BinaryPlistWriter(); using (Stream stream = File.Create(outputPath)) { writer.WriteObject(stream, dictionary); } using (Stream stream = File.OpenRead(outputPath)) { dictionary = reader.ReadObject(stream); } Assert.AreEqual(2, dictionary.Count); Assert.IsInstanceOfType(dictionary["Array"], typeof(object[])); Assert.IsInstanceOfType(dictionary["Dictionary"], typeof(IDictionary)); arr = dictionary["Array"] as object[]; dict = dictionary["Dictionary"] as IDictionary; Assert.AreEqual(3, arr.Length); Assert.AreEqual(2, dictionary.Count); Assert.AreEqual("1st", arr[0]); Assert.AreEqual("2nd", arr[1]); Assert.AreEqual("3rd", arr[2]); Assert.AreEqual(2.0000000000000011, dict["Double"]); Assert.AreEqual(4, ((object[])dict["Array"]).Length); arr = dict["Array"] as object[]; Assert.AreEqual("One", arr[0]); Assert.AreEqual((short)3, arr[1]); Assert.AreEqual(false, arr[2]); Assert.AreEqual("Four", arr[3]); }
public void WriterWriteEmptyString() { string outputPath = Guid.NewGuid().ToString() + ".plist"; IDictionary dict = new Dictionary<string, object>(); dict["Empty"] = string.Empty; BinaryPlistWriter writer = new BinaryPlistWriter(); writer.WriteObject(outputPath, dict); BinaryPlistReader reader = new BinaryPlistReader(); dict = reader.ReadObject(outputPath); Assert.IsTrue(dict.Contains("Empty")); Assert.AreEqual(string.Empty, dict["Empty"]); }
public void WriterWriteEmptyString() { string outputPath = Guid.NewGuid().ToString() + ".plist"; IDictionary dict = new Dictionary <string, object>(); dict["Empty"] = string.Empty; BinaryPlistWriter writer = new BinaryPlistWriter(); writer.WriteObject(outputPath, dict); BinaryPlistReader reader = new BinaryPlistReader(); dict = reader.ReadObject(outputPath); Assert.IsTrue(dict.Contains("Empty")); Assert.AreEqual(string.Empty, dict["Empty"]); }
public void WriterWriteObjectTypes() { string outputPath = Guid.NewGuid().ToString() + ".plist"; BinaryPlistReader reader = new BinaryPlistReader(); IDictionary dictionary; using (Stream stream = File.OpenRead(Paths.TypesPlistPath)) { dictionary = reader.ReadObject(stream); } dictionary["Unicode"] = "ºª•¶§∞¢£™¬˚∆¨˙©ƒ´ßƒç"; dictionary["False"] = true; dictionary["Data"] = new byte[3]; dictionary["Date"] = new DateTime(2011, 3, 13); dictionary["True"] = false; dictionary["Pi"] = 2.71828; dictionary["Hello"] = "Japan"; BinaryPlistWriter writer = new BinaryPlistWriter(); using (Stream stream = File.Create(outputPath)) { writer.WriteObject(stream, dictionary); } using (Stream stream = File.OpenRead(outputPath)) { dictionary = reader.ReadObject(stream); } Assert.AreEqual(9, dictionary.Count); Assert.AreEqual("ºª•¶§∞¢£™¬˚∆¨˙©ƒ´ßƒç", dictionary["Unicode"]); Assert.AreEqual(true, dictionary["False"]); Assert.IsInstanceOfType(dictionary["Data"], typeof(byte[])); Assert.AreEqual(new DateTime(2011, 3, 13), dictionary["Date"]); Assert.AreEqual(false, dictionary["True"]); Assert.AreEqual(2.71828, dictionary["Pi"]); Assert.AreEqual("Japan", dictionary["Hello"]); }
public void WriterWriteObjectCalculator() { string outputPath = Guid.NewGuid().ToString() + ".plist"; BinaryPlistReader reader = new BinaryPlistReader(); IDictionary dictionary; using (Stream stream = File.OpenRead(Paths.CalculatorPlistPath)) { dictionary = reader.ReadObject(stream); } dictionary["NSWindow Frame Calc_History_Window"] = "620 71 228 289 0 0 1440 870 "; dictionary["NSWindow Frame Calc_Main_Window"] = "668 425 423 283 0 0 1440 870 "; dictionary["Programmer_BinaryIsHidden"] = false; dictionary["Programmer_InputMode"] = 11; dictionary["ViewDefaultsKey"] = "Religious"; BinaryPlistWriter writer = new BinaryPlistWriter(); using (Stream stream = File.Create(outputPath)) { writer.WriteObject(stream, dictionary); } using (Stream stream = File.OpenRead(outputPath)) { dictionary = reader.ReadObject(stream); } Assert.AreEqual(5, dictionary.Count); Assert.AreEqual("620 71 228 289 0 0 1440 870 ", dictionary["NSWindow Frame Calc_History_Window"]); Assert.AreEqual("668 425 423 283 0 0 1440 870 ", dictionary["NSWindow Frame Calc_Main_Window"]); Assert.AreEqual(false, dictionary["Programmer_BinaryIsHidden"]); Assert.AreEqual((short)11, dictionary["Programmer_InputMode"]); Assert.AreEqual("Religious", dictionary["ViewDefaultsKey"]); }
public override async Task OnDataReceivedAsync(Request request, Response response, CancellationToken cancellationToken) { // Get session by active-remote header value var sessionId = request.Headers["Active-Remote"]; var session = await SessionManager.Current.GetSessionAsync(sessionId); if (request.Type == RequestType.GET && "/info".Equals(request.Path, StringComparison.OrdinalIgnoreCase)) { var dict = new Dictionary <string, object>(); dict.Add("features", 61379444727); dict.Add("name", "airserver"); dict.Add("displays", new List <Dictionary <string, object> > { new Dictionary <string, object> { { "primaryInputDevice", 1 }, { "rotation", true }, { "widthPhysical", 0 }, { "edid", "AP///////wAGEBOuhXxiyAoaAQS1PCJ4IA8FrlJDsCYOT1QAAAABAQEBAQEBAQEBAQEBAQEBAAAAEAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAA/ABpTWFjCiAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAqBwE3kDAAMAFIBuAYT/E58AL4AfAD8LUQACAAQAf4EY+hAAAQEAEnYx/Hj7/wIQiGLT+vj4/v//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHkHATeQMAAwFQU+wABP8PnwAvAB8A/whBAAIABABM0AAE/w6fAC8AHwBvCD0AAgAEAMyRAAR/DJ8ALwAfAAcHMwACAAQAVV4ABP8JnwAvAB8AnwUoAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+Q" }, { "widthPixels", 1920.0 }, { "uuid", "061013ae-7b0f-4305-984b-974f677a150b" }, { "heightPhysical", 0 }, { "features", 30 }, { "heightPixels", 1080.0 }, { "overscanned", false } } }); dict.Add("audioFormats", new List <Dictionary <string, object> > { { new Dictionary <string, object> { { "type", 100 }, { "audioInputFormats", 67108860 }, { "audioOutputFormats", 67108860 } } }, { new Dictionary <string, object> { { "type", 101 }, { "audioInputFormats", 67108860 }, { "audioOutputFormats", 67108860 } } } }); dict.Add("vv", 2); dict.Add("statusFlags", 4); dict.Add("keepAliveLowPower", true); dict.Add("sourceVersion", "220.68"); dict.Add("pk", "29fbb183a58b466e05b9ab667b3c429d18a6b785637333d3f0f3a34baa89f45c"); dict.Add("keepAliveSendStatsAsBody", true); dict.Add("deviceID", "78:7B:8A:BD:C9:4D"); dict.Add("model", "AppleTV5,3"); dict.Add("audioLatencies", new List <Dictionary <string, object> > { { new Dictionary <string, object> { { "outputLatencyMicros", 0 }, { "type", 100 }, { "audioType", "default" }, { "inputLatencyMicros", 0 } } }, { new Dictionary <string, object> { { "outputLatencyMicros", 0 }, { "type", 101 }, { "audioType", "default" }, { "inputLatencyMicros", 0 } } } }); dict.Add("macAddress", "78:7B:8A:BD:C9:4D"); var output = default(byte[]); using (var outputStream = new MemoryStream()) { var plistWriter = new BinaryPlistWriter(); plistWriter.WriteObject(outputStream, dict, false); outputStream.Seek(0, SeekOrigin.Begin); output = outputStream.ToArray(); } response.Headers.Add("Content-Type", "application/x-apple-binary-plist"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } if (request.Type == RequestType.POST && "/pair-setup".Equals(request.Path, StringComparison.OrdinalIgnoreCase)) { // Return our 32 bytes public key response.Headers.Add("Content-Type", "application/octet-stream"); await response.WriteAsync(_publicKey, 0, _publicKey.Length).ConfigureAwait(false); } if (request.Type == RequestType.POST && "/pair-verify".Equals(request.Path, StringComparison.OrdinalIgnoreCase)) { using (var mem = new MemoryStream(request.Body)) using (var reader = new BinaryReader(mem)) { // Request: 68 bytes (the first 4 bytes are 01 00 00 00) // Client request packet remaining 64 bytes of content // 01 00 00 00 -> use 01 as flag to check type of verify // If flag is 1: // 32 bytes ecdh_their // 32 bytes ed_their // If flag is 0: // 64 bytes signature var flag = reader.ReadByte(); if (flag > 0) { reader.ReadBytes(3); session.EcdhTheirs = reader.ReadBytes(32); session.EdTheirs = reader.ReadBytes(32); var curve25519 = Curve25519.getInstance(Curve25519.BEST); var curve25519KeyPair = curve25519.generateKeyPair(); session.EcdhOurs = curve25519KeyPair.getPublicKey(); var ecdhPrivateKey = curve25519KeyPair.getPrivateKey(); session.EcdhShared = curve25519.calculateAgreement(ecdhPrivateKey, session.EcdhTheirs); var aesCtr128Encrypt = Utilities.InitializeChiper(session.EcdhShared); byte[] dataToSign = new byte[64]; Array.Copy(session.EcdhOurs, 0, dataToSign, 0, 32); Array.Copy(session.EcdhTheirs, 0, dataToSign, 32, 32); var signature = Chaos.NaCl.Ed25519.Sign(dataToSign, _expandedPrivateKey); byte[] encryptedSignature = aesCtr128Encrypt.DoFinal(signature); byte[] output = new byte[session.EcdhOurs.Length + encryptedSignature.Length]; Array.Copy(session.EcdhOurs, 0, output, 0, session.EcdhOurs.Length); Array.Copy(encryptedSignature, 0, output, session.EcdhOurs.Length, encryptedSignature.Length); response.Headers.Add("Content-Type", "application/octet-stream"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } else { reader.ReadBytes(3); var signature = reader.ReadBytes(64); var aesCtr128Encrypt = Utilities.InitializeChiper(session.EcdhShared); var signatureBuffer = new byte[64]; signatureBuffer = aesCtr128Encrypt.ProcessBytes(signatureBuffer); signatureBuffer = aesCtr128Encrypt.DoFinal(signature); byte[] messageBuffer = new byte[64]; Array.Copy(session.EcdhTheirs, 0, messageBuffer, 0, 32); Array.Copy(session.EcdhOurs, 0, messageBuffer, 32, 32); session.PairVerified = Chaos.NaCl.Ed25519.Verify(signatureBuffer, messageBuffer, session.EdTheirs); Console.WriteLine($"PairVerified: {session.PairVerified}"); } } } if (request.Type == RequestType.POST && "/fp-setup".Equals(request.Path, StringComparison.OrdinalIgnoreCase)) { // If session is not paired, something gone wrong. if (!session.PairCompleted) { response.StatusCode = StatusCode.UNAUTHORIZED; } else { var body = request.Body; if (body.Length == 16) { // Response must be 142 bytes var mode = body[14]; if (body[4] != 0x03) { // Unsupported fairplay version Console.WriteLine($"Unsupported fairplay version: {body[4]}"); return; } // Get mode and send correct reply message mode = body[14]; var output = _replyMessage[mode]; response.Headers.Add("Content-Type", "application/octet-stream"); await response.WriteAsync(output, 0, output.Length); } else if (body.Length == 164) { // Response 32 bytes if (body[4] != 0x03) { // Unsupported fairplay version Console.WriteLine($"Unsupported fairplay version: {body[4]}"); return; } var keyMsg = new byte[164]; Array.Copy(body, 0, keyMsg, 0, 164); session.KeyMsg = keyMsg; var data = body.Skip(144).ToArray(); var output = new byte[32]; Array.Copy(_fpHeader, 0, output, 0, 12); Array.Copy(data, 0, output, 12, 20); response.Headers.Add("Content-Type", "application/octet-stream"); await response.WriteAsync(output, 0, output.Length); } else { // Unsupported fairplay version Console.WriteLine($"Unsupported fairplay version"); return; } } } if (request.Type == RequestType.SETUP) { // If session is not ready, something gone wrong. if (!session.FairPlaySetupCompleted) { Console.WriteLine("FairPlay not ready. Something gone wrong."); response.StatusCode = StatusCode.BADREQUEST; } else { var plistReader = new BinaryPlistReader(); using (var mem = new MemoryStream(request.Body)) { var plist = plistReader.ReadObject(mem); if (plist.Contains("streams")) { // Always one foreach request var stream = (Dictionary <object, object>)((object[])plist["streams"])[0]; var type = (short)stream["type"]; // If screen Mirroring if (type == 110) { session.StreamConnectionId = unchecked ((ulong)(System.Int64)stream["streamConnectionID"]).ToString(); // Set video data port var streams = new Dictionary <string, List <Dictionary <string, int> > >() { { "streams", new List <Dictionary <string, int> > { { new Dictionary <string, int> { { "type", 110 }, { "dataPort", _airPlayPort } } } } } }; byte[] output; using (var outputStream = new MemoryStream()) { var writerRes = new BinaryPlistWriter(); writerRes.WriteObject(outputStream, streams, false); outputStream.Seek(0, SeekOrigin.Begin); output = outputStream.ToArray(); } response.Headers.Add("Content-Type", "application/x-apple-binary-plist"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } // If audio session if (type == 96) { if (stream.ContainsKey("audioFormat")) { var audioFormat = (int)stream["audioFormat"]; session.AudioFormat = (AudioFormat)audioFormat; var description = GetAudioFormatDescription(audioFormat); Console.WriteLine($"Audio type: {description}"); } if (stream.ContainsKey("controlPort")) { // Use this port to request resend lost packet? (remote port) var controlPort = (ushort)((short)stream["controlPort"]); } // Set audio data port var streams = new Dictionary <string, List <Dictionary <string, int> > >() { { "streams", new List <Dictionary <string, int> > { { new Dictionary <string, int> { { "type", 96 }, { "controlPort", 7002 }, { "dataPort", 7003 } } } } } }; byte[] output; using (var outputStream = new MemoryStream()) { var writerRes = new BinaryPlistWriter(); writerRes.WriteObject(outputStream, streams, false); outputStream.Seek(0, SeekOrigin.Begin); output = outputStream.ToArray(); } response.Headers.Add("Content-Type", "application/x-apple-binary-plist"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } } else { // Read ekey and eiv used to decode video and audio data if (plist.Contains("et")) { var et = (short)plist["et"]; Console.WriteLine($"ET: {et}"); } if (plist.Contains("ekey")) { session.AesKey = (byte[])plist["ekey"]; } if (plist.Contains("eiv")) { session.AesIv = (byte[])plist["eiv"]; } if (plist.Contains("isScreenMirroringSession")) { session.MirroringSession = (bool)plist["isScreenMirroringSession"]; } if (plist.Contains("timingPort")) { // Use this port to send heartbeat (remote port) var timingPort = (ushort)((short)plist["timingPort"]); } var dict = new Dictionary <string, int>() { { "timingPort", _airTunesPort }, { "eventPort", _airTunesPort } }; byte[] output; using (var outputStream = new MemoryStream()) { var writerRes = new BinaryPlistWriter(); writerRes.WriteObject(outputStream, dict, false); outputStream.Seek(0, SeekOrigin.Begin); output = outputStream.ToArray(); } response.Headers.Add("Content-Type", "application/x-apple-binary-plist"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } if (session.FairPlayReady && session.MirroringSessionReady && session.MirroringListener == null) { // Start 'MirroringListener' (handle H264 data received from iOS/macOS var mirroring = new MirroringListener(_receiver, session.SessionId, _airPlayPort); await mirroring.StartAsync(cancellationToken).ConfigureAwait(false); session.MirroringListener = mirroring; } if (session.FairPlayReady && (!session.MirroringSession.HasValue || !session.MirroringSession.Value)) { // Start 'StreamingListener' (handle streaming url) var streaming = new StreamingListener(_receiver, session.SessionId, _expandedPrivateKey, _airPlayPort); await streaming.StartAsync(cancellationToken).ConfigureAwait(false); session.StreamingListener = streaming; } if (session.FairPlayReady && session.AudioSessionReady && session.AudioControlListener == null) { // Start 'AudioListener' (handle PCM/AAC/ALAC data received from iOS/macOS var control = new AudioListener(_receiver, session.SessionId, 7002, 7003, _codecConfig, _dumpConfig); await control.StartAsync(cancellationToken).ConfigureAwait(false); session.AudioControlListener = control; } } } } if (request.Type == RequestType.GET_PARAMETER) { var data = Encoding.ASCII.GetString(request.Body); if (data.Equals("volume\r\n")) { var output = Encoding.ASCII.GetBytes("volume: 1.000000\r\n"); response.Headers.Add("Content-Type", "text/parameters"); await response.WriteAsync(output, 0, output.Length).ConfigureAwait(false); } } if (request.Type == RequestType.RECORD) { response.Headers.Add("Audio-Latency", "0"); // 11025 // response.Headers.Add("Audio-Jack-Status", "connected; type=analog"); } if (request.Type == RequestType.SET_PARAMETER) { if (request.Headers.ContainsKey("Content-Type")) { var contentType = request.Headers["Content-Type"]; if (contentType.Equals("text/parameters", StringComparison.OrdinalIgnoreCase)) { var body = Encoding.ASCII.GetString(request.Body); var keyPair = body.Split(":", StringSplitOptions.RemoveEmptyEntries).Select(b => b.Trim(' ', '\r', '\n')).ToArray(); if (keyPair.Length == 2) { var key = keyPair[0]; var val = keyPair[1]; if (key.Equals("volume", StringComparison.OrdinalIgnoreCase)) { // request.Body contains 'volume: N.NNNNNN' _receiver.OnSetVolume(decimal.Parse(val)); } else if (key.Equals("progress", StringComparison.OrdinalIgnoreCase)) { var pVals = val.Split("/", StringSplitOptions.RemoveEmptyEntries); var start = long.Parse(pVals[0]); var current = long.Parse(pVals[1]); var end = long.Parse(pVals[2]); // DO SOMETHING W/ PROGRESS } } } else if (contentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase)) { var image = request.Body; // DO SOMETHING W/ IMAGE } else if (contentType.Equals("application/x-dmap-tagged", StringComparison.OrdinalIgnoreCase)) { var dmap = new DMapTagged(); var output = dmap.Decode(request.Body); } } } if (request.Type == RequestType.OPTIONS) { response.Headers.Add("Public", "SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, ANNOUNCE"); } if (request.Type == RequestType.ANNOUNCE) { } if (request.Type == RequestType.FLUSH) { int next_seq = -1; if (request.Headers.ContainsKey("RTP-Info")) { var rtpinfo = request.Headers["RTP-Info"]; if (!string.IsNullOrWhiteSpace(rtpinfo)) { Console.WriteLine($"Flush with RTP-Info: {rtpinfo}"); var r = new Regex(@"seq\=([^;]*)"); var m = r.Match(rtpinfo); if (m.Success) { next_seq = int.Parse(m.Groups[1].Value); } } } await session.AudioControlListener.FlushAsync(next_seq); } if (request.Type == RequestType.TEARDOWN) { var plistReader = new BinaryPlistReader(); using (var mem = new MemoryStream(request.Body)) { var plist = plistReader.ReadObject(mem); if (plist.Contains("streams")) { // Always one foreach request var stream = (Dictionary <object, object>)((object[])plist["streams"]).Last(); var type = (short)stream["type"]; // If screen Mirroring if (type == 110) { // Stop mirroring session await session.MirroringListener.StopAsync(); } // If audio session if (type == 96) { // Stop audio session await session.AudioControlListener.StopAsync(); } } } } // Request w/ path '/feedback' must return 200 OK w/out response body // So we can do nothing here.. // Save current session await SessionManager.Current.CreateOrUpdateSessionAsync(sessionId, session); }