From 1431f5bd50d1d6107f57e7f80a4d151eab096cbf Mon Sep 17 00:00:00 2001 From: Mitchell Trent Date: Sun, 3 Sep 2017 02:01:25 +1000 Subject: [PATCH 1/5] Ported across and created package called PushSharp.Core --- .gitmodules | 3 - HttpTwo | 1 - PushSharp.Amazon/AdmConfiguration.cs | 4 +- PushSharp.Amazon/AdmConnection.cs | 11 +- PushSharp.Amazon/AdmNotification.cs | 5 +- PushSharp.Amazon/Exceptions.cs | 3 +- PushSharp.Amazon/Properties/AssemblyInfo.cs | 27 -- PushSharp.Amazon/PushSharp.Amazon.csproj | 61 +--- PushSharp.Amazon/packages.config | 4 - PushSharp.Apple/ApnsConnection.cs | 2 +- PushSharp.Apple/ApnsHttp2Configuration.cs | 156 --------- PushSharp.Apple/ApnsHttp2Connection.cs | 135 -------- PushSharp.Apple/ApnsHttp2Notification.cs | 89 ----- PushSharp.Apple/ApnsHttp2ServiceConnection.cs | 43 --- PushSharp.Apple/ApnsNotification.cs | 2 +- PushSharp.Apple/ApnsServiceConnection.cs | 2 +- PushSharp.Apple/Exceptions.cs | 2 +- PushSharp.Apple/Properties/AssemblyInfo.cs | 27 -- PushSharp.Apple/PushSharp.Apple.csproj | 63 +--- PushSharp.Apple/packages.config | 4 - PushSharp.Blackberry/BlackberryConnection.cs | 2 +- .../BlackberryNotification.cs | 5 +- PushSharp.Blackberry/Exceptions.cs | 2 +- .../Properties/AssemblyInfo.cs | 27 -- .../PushSharp.Blackberry.csproj | 61 +--- .../Exceptions.cs | 15 +- .../INotification.cs | 4 +- .../IServiceBroker.cs | 2 +- .../IServiceConnection.cs | 2 +- .../IServiceConnectionFactory.cs | 4 +- {PushSharp.Core => PushSharp.Common}/Log.cs | 2 +- .../NotificationBlockingCollection.cs | 4 +- .../PublishProfiles/FolderProfile.pubxml | 13 + .../PushHttpClient.cs | 4 +- PushSharp.Common/PushSharp.Common.csproj | 7 + .../ServiceBroker.cs | 8 +- .../ServiceBrokerConfiguration.cs | 4 +- PushSharp.Core.1.0.0.nupkg | Bin 0 -> 126202 bytes PushSharp.Core.sln | 79 +++++ PushSharp.Core/Properties/AssemblyInfo.cs | 27 -- PushSharp.Core/PushSharp.Core.csproj | 49 --- PushSharp.Firefox/Exceptions.cs | 2 +- PushSharp.Firefox/FirefoxConnection.cs | 2 +- PushSharp.Firefox/FirefoxNotification.cs | 2 +- PushSharp.Firefox/Properties/AssemblyInfo.cs | 27 -- PushSharp.Firefox/PushSharp.Firefox.csproj | 55 +-- PushSharp.Google/Exceptions.cs | 2 +- PushSharp.Google/GcmNotification.cs | 10 +- PushSharp.Google/GcmServiceConnection.cs | 2 +- PushSharp.Google/Properties/AssemblyInfo.cs | 27 -- PushSharp.Google/PushSharp.Google.csproj | 66 +--- PushSharp.Google/Xmpp/GcmXmppConfiguration.cs | 60 ---- PushSharp.Google/Xmpp/GcmXmppConnection.cs | 323 ------------------ PushSharp.Google/Xmpp/GcmXmppNotification.cs | 119 ------- PushSharp.Google/Xmpp/GcmXmppResponse.cs | 93 ----- PushSharp.Google/Xmpp/GcmXmppService.cs | 50 --- PushSharp.Google/packages.config | 4 - PushSharp.Tests/AdmRealTests.cs | 2 +- PushSharp.Tests/ApnsHttp2RealTests.cs | 47 --- PushSharp.Tests/ApnsTests.cs | 3 +- PushSharp.Tests/BrokerTests.cs | 2 +- PushSharp.Tests/GcmXmppTests.cs | 45 --- PushSharp.Tests/PushSharp.Tests.csproj | 94 +---- PushSharp.Tests/Servers/TestApnsServer.cs | 8 +- PushSharp.Tests/TestServiceConnection.cs | 2 +- PushSharp.Tests/apns-com.pushsharp.sample.p12 | Bin 3319 -> 0 bytes PushSharp.Tests/packages.config | 5 - PushSharp.Tests/settings.sample.json | 22 -- PushSharp.Windows/Exceptions.cs | 2 +- PushSharp.Windows/Properties/AssemblyInfo.cs | 27 -- PushSharp.Windows/PushSharp.Windows.csproj | 65 +--- PushSharp.Windows/WnsConnection.cs | 2 +- PushSharp.Windows/WnsNotification.cs | 4 +- PushSharp.Windows/WnsTokenAccessManager.cs | 2 +- PushSharp.Windows/packages.config | 4 - PushSharp.nuspec | 44 +-- PushSharp.sln | 71 ---- 77 files changed, 268 insertions(+), 1992 deletions(-) delete mode 160000 HttpTwo delete mode 100644 PushSharp.Amazon/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Amazon/packages.config delete mode 100644 PushSharp.Apple/ApnsHttp2Configuration.cs delete mode 100644 PushSharp.Apple/ApnsHttp2Connection.cs delete mode 100644 PushSharp.Apple/ApnsHttp2Notification.cs delete mode 100644 PushSharp.Apple/ApnsHttp2ServiceConnection.cs delete mode 100644 PushSharp.Apple/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Apple/packages.config delete mode 100644 PushSharp.Blackberry/Properties/AssemblyInfo.cs rename {PushSharp.Core => PushSharp.Common}/Exceptions.cs (67%) rename {PushSharp.Core => PushSharp.Common}/INotification.cs (75%) rename {PushSharp.Core => PushSharp.Common}/IServiceBroker.cs (95%) rename {PushSharp.Core => PushSharp.Common}/IServiceConnection.cs (94%) rename {PushSharp.Core => PushSharp.Common}/IServiceConnectionFactory.cs (79%) rename {PushSharp.Core => PushSharp.Common}/Log.cs (99%) rename {PushSharp.Core => PushSharp.Common}/NotificationBlockingCollection.cs (71%) create mode 100644 PushSharp.Common/Properties/PublishProfiles/FolderProfile.pubxml rename {PushSharp.Core => PushSharp.Common}/PushHttpClient.cs (99%) create mode 100644 PushSharp.Common/PushSharp.Common.csproj rename {PushSharp.Core => PushSharp.Common}/ServiceBroker.cs (99%) rename {PushSharp.Core => PushSharp.Common}/ServiceBrokerConfiguration.cs (94%) create mode 100644 PushSharp.Core.1.0.0.nupkg create mode 100644 PushSharp.Core.sln delete mode 100644 PushSharp.Core/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Core/PushSharp.Core.csproj delete mode 100644 PushSharp.Firefox/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Google/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Google/Xmpp/GcmXmppConfiguration.cs delete mode 100644 PushSharp.Google/Xmpp/GcmXmppConnection.cs delete mode 100644 PushSharp.Google/Xmpp/GcmXmppNotification.cs delete mode 100644 PushSharp.Google/Xmpp/GcmXmppResponse.cs delete mode 100644 PushSharp.Google/Xmpp/GcmXmppService.cs delete mode 100644 PushSharp.Google/packages.config delete mode 100644 PushSharp.Tests/ApnsHttp2RealTests.cs delete mode 100644 PushSharp.Tests/GcmXmppTests.cs delete mode 100644 PushSharp.Tests/apns-com.pushsharp.sample.p12 delete mode 100644 PushSharp.Tests/packages.config delete mode 100644 PushSharp.Tests/settings.sample.json delete mode 100644 PushSharp.Windows/Properties/AssemblyInfo.cs delete mode 100644 PushSharp.Windows/packages.config delete mode 100644 PushSharp.sln diff --git a/.gitmodules b/.gitmodules index 5e19cd95..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "HttpTwo"] - path = HttpTwo - url = git@github.com:Redth/HttpTwo.git diff --git a/HttpTwo b/HttpTwo deleted file mode 160000 index 7ba7b0e8..00000000 --- a/HttpTwo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7ba7b0e814d326b063f45e95376b264bfd288980 diff --git a/PushSharp.Amazon/AdmConfiguration.cs b/PushSharp.Amazon/AdmConfiguration.cs index 529fbbc1..1c39572b 100644 --- a/PushSharp.Amazon/AdmConfiguration.cs +++ b/PushSharp.Amazon/AdmConfiguration.cs @@ -1,6 +1,4 @@ -using System; - -namespace PushSharp.Amazon +namespace PushSharp.Amazon { public class AdmConfiguration { diff --git a/PushSharp.Amazon/AdmConnection.cs b/PushSharp.Amazon/AdmConnection.cs index 3ce9d346..00c9de5a 100644 --- a/PushSharp.Amazon/AdmConnection.cs +++ b/PushSharp.Amazon/AdmConnection.cs @@ -1,14 +1,11 @@ using System; -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using Newtonsoft.Json.Linq; -using System.Net; -using PushSharp.Core; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Threading; +using PushSharp.Common; namespace PushSharp.Amazon { diff --git a/PushSharp.Amazon/AdmNotification.cs b/PushSharp.Amazon/AdmNotification.cs index 74dad5b4..1b6c64e6 100644 --- a/PushSharp.Amazon/AdmNotification.cs +++ b/PushSharp.Amazon/AdmNotification.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; -using PushSharp.Core; +using System.Collections.Generic; using Newtonsoft.Json.Linq; +using PushSharp.Common; namespace PushSharp.Amazon { diff --git a/PushSharp.Amazon/Exceptions.cs b/PushSharp.Amazon/Exceptions.cs index b3f145b9..a0aea291 100644 --- a/PushSharp.Amazon/Exceptions.cs +++ b/PushSharp.Amazon/Exceptions.cs @@ -1,5 +1,4 @@ -using System; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Amazon { diff --git a/PushSharp.Amazon/Properties/AssemblyInfo.cs b/PushSharp.Amazon/Properties/AssemblyInfo.cs deleted file mode 100644 index a2f8a614..00000000 --- a/PushSharp.Amazon/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Amazon")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Amazon/PushSharp.Amazon.csproj b/PushSharp.Amazon/PushSharp.Amazon.csproj index 594d22c3..65fce3bc 100644 --- a/PushSharp.Amazon/PushSharp.Amazon.csproj +++ b/PushSharp.Amazon/PushSharp.Amazon.csproj @@ -1,56 +1,21 @@ - - + + - Debug - AnyCPU - {2468C63B-C964-4FC3-9B16-13DC17CF7D11} - Library - PushSharp.Amazon + netstandard2.0;net45; PushSharp.Amazon - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + PushSharp.Amazon + - - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - + - - - - - - - - - - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + + + + - + - \ No newline at end of file + + diff --git a/PushSharp.Amazon/packages.config b/PushSharp.Amazon/packages.config deleted file mode 100644 index 505e5883..00000000 --- a/PushSharp.Amazon/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 0908b166..d516e65b 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading; using System.Net; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Apple { diff --git a/PushSharp.Apple/ApnsHttp2Configuration.cs b/PushSharp.Apple/ApnsHttp2Configuration.cs deleted file mode 100644 index d3ba0e76..00000000 --- a/PushSharp.Apple/ApnsHttp2Configuration.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; - -namespace PushSharp.Apple -{ - public class ApnsHttp2Configuration - { - #region Constants - const string APNS_SANDBOX_HOST = "api.development.push.apple.com"; - const string APNS_PRODUCTION_HOST = "api.push.apple.com"; - - const uint APNS_SANDBOX_PORT = 443; - const uint APNS_PRODUCTION_PORT = 443; - #endregion - - public ApnsHttp2Configuration (ApnsServerEnvironment serverEnvironment, string certificateFile, string certificateFilePwd) - : this (serverEnvironment, System.IO.File.ReadAllBytes (certificateFile), certificateFilePwd) - { - } - - public ApnsHttp2Configuration (ApnsServerEnvironment serverEnvironment, byte[] certificateData, string certificateFilePwd) - : this (serverEnvironment, new X509Certificate2 (certificateData, certificateFilePwd, - X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable)) - { - } - - public ApnsHttp2Configuration (string overrideHost, uint overridePort, bool skipSsl = true) - { - SkipSsl = skipSsl; - - Initialize (ApnsServerEnvironment.Sandbox, null); - - OverrideServer (overrideHost, overridePort); - } - - public ApnsHttp2Configuration (ApnsServerEnvironment serverEnvironment, X509Certificate2 certificate) - { - Initialize (serverEnvironment, certificate); - } - - void Initialize (ApnsServerEnvironment serverEnvironment, X509Certificate2 certificate) - { - var production = serverEnvironment == ApnsServerEnvironment.Production; - - Host = production ? APNS_PRODUCTION_HOST : APNS_SANDBOX_HOST; - Port = production ? APNS_PRODUCTION_PORT : APNS_SANDBOX_PORT; - - Certificate = certificate; - - MillisecondsToWaitBeforeMessageDeclaredSuccess = 3000; - ConnectionTimeout = 10000; - MaxConnectionAttempts = 3; - - FeedbackIntervalMinutes = 10; - FeedbackTimeIsUTC = false; - - AdditionalCertificates = new List (); - AddLocalAndMachineCertificateStores = false; - - CheckIsApnsCertificate (); - - ValidateServerCertificate = false; - - KeepAlivePeriod = TimeSpan.FromMinutes (20); - KeepAliveRetryPeriod = TimeSpan.FromSeconds (30); - - InternalBatchSize = 1000; - InternalBatchingWaitPeriod = TimeSpan.FromMilliseconds (750); - - InternalBatchFailureRetryCount = 1; - } - - - void CheckIsApnsCertificate () - { - if (Certificate != null) { - var issuerName = Certificate.IssuerName.Name; - var commonName = Certificate.SubjectName.Name; - - if (!issuerName.Contains ("Apple")) - throw new ApnsConnectionException ("Your Certificate does not appear to be issued by Apple! Please check to ensure you have the correct certificate!"); - if (!commonName.Contains ("Apple Push Services:")) - throw new ApnsConnectionException ("Your Certificate is not in the new combined Sandbox/Production APNS certificate format, please create a new single certificate to use"); - - } else { - throw new ApnsConnectionException ("You must provide a Certificate to connect to APNS with!"); - } - } - - public void OverrideServer (string host, uint port) - { - Host = host; - Port = port; - } - - public string Host { get; private set; } - - public uint Port { get; private set; } - - public X509Certificate2 Certificate { get; private set; } - - public List AdditionalCertificates { get; private set; } - - public bool AddLocalAndMachineCertificateStores { get; set; } - - public bool SkipSsl { get; set; } - - public int MillisecondsToWaitBeforeMessageDeclaredSuccess { get; set; } - - public int FeedbackIntervalMinutes { get; set; } - - public bool FeedbackTimeIsUTC { get; set; } - - public bool ValidateServerCertificate { get; set; } - - public int ConnectionTimeout { get; set; } - - public int MaxConnectionAttempts { get; set; } - - /// - /// The internal connection to APNS servers batches notifications to send before waiting for errors for a short time. - /// This value will set a maximum size per batch. The default value is 1000. You probably do not want this higher than 7500. - /// - /// The size of the internal batch. - public int InternalBatchSize { get; set; } - - /// - /// How long the internal connection to APNS servers should idle while collecting notifications in a batch to send. - /// Setting this value too low might result in many smaller batches being used. - /// - /// The internal batching wait period. - public TimeSpan InternalBatchingWaitPeriod { get; set; } - - /// - /// How many times the internal batch will retry to send in case of network failure. The default value is 1. - /// - /// The internal batch failure retry count. - public int InternalBatchFailureRetryCount { get; set; } - - /// - /// Gets or sets the keep alive period to set on the APNS socket - /// - public TimeSpan KeepAlivePeriod { get; set; } - - /// - /// Gets or sets the keep alive retry period to set on the APNS socket - /// - public TimeSpan KeepAliveRetryPeriod { get; set; } - - public enum ApnsServerEnvironment { - Sandbox, - Production - } - } -} \ No newline at end of file diff --git a/PushSharp.Apple/ApnsHttp2Connection.cs b/PushSharp.Apple/ApnsHttp2Connection.cs deleted file mode 100644 index 543ec43c..00000000 --- a/PushSharp.Apple/ApnsHttp2Connection.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; -using System.Collections.Specialized; -using System.Collections.Generic; -using System.Text; -using System.Linq; -using Newtonsoft.Json.Linq; -using System.Net; - -namespace PushSharp.Apple -{ - public class ApnsHttp2Connection - { - static int ID = 0; - - public ApnsHttp2Connection (ApnsHttp2Configuration configuration) - { - id = ++ID; - if (id >= int.MaxValue) - ID = 0; - - Configuration = configuration; - - certificate = Configuration.Certificate; - - certificates = new X509CertificateCollection (); - - // Add local/machine certificate stores to our collection if requested - if (Configuration.AddLocalAndMachineCertificateStores) { - var store = new X509Store (StoreLocation.LocalMachine); - certificates.AddRange (store.Certificates); - - store = new X509Store (StoreLocation.CurrentUser); - certificates.AddRange (store.Certificates); - } - - // Add optionally specified additional certs into our collection - if (Configuration.AdditionalCertificates != null) { - foreach (var addlCert in Configuration.AdditionalCertificates) - certificates.Add (addlCert); - } - - // Finally, add the main private cert for authenticating to our collection - if (certificate != null) - certificates.Add (certificate); - - var http2Settings = new HttpTwo.Http2ConnectionSettings ( - Configuration.Host, - (uint)Configuration.Port, - true, - certificates); - - http2 = new HttpTwo.Http2Client (http2Settings); - } - - public ApnsHttp2Configuration Configuration { get; private set; } - - X509CertificateCollection certificates; - X509Certificate2 certificate; - int id = 0; - HttpTwo.Http2Client http2; - - public async Task Send (ApnsHttp2Notification notification) - { - var url = string.Format ("https://{0}:{1}/3/device/{2}", - Configuration.Host, - Configuration.Port, - notification.DeviceToken); - var uri = new Uri (url); - - var payload = notification.Payload.ToString (); - - var data = Encoding.ASCII.GetBytes (payload); - - var headers = new NameValueCollection (); - headers.Add ("apns-id", notification.Uuid); // UUID - - if (notification.Expiration.HasValue) { - var sinceEpoch = notification.Expiration.Value.ToUniversalTime () - new DateTime (1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var secondsSinceEpoch = (long)sinceEpoch.TotalSeconds; - headers.Add ("apns-expiration", secondsSinceEpoch.ToString ()); //Epoch in seconds - } - - if (notification.Priority.HasValue) - headers.Add ("apns-priority", notification.Priority == ApnsPriority.Low ? "5" : "10"); // 5 or 10 - - headers.Add ("content-length", data.Length.ToString ()); - - if (!string.IsNullOrEmpty (notification.Topic)) - headers.Add ("apns-topic", notification.Topic); // string topic - - var response = await http2.Post (uri, headers, data); - - if (response.Status == HttpStatusCode.OK) { - // Check for matching uuid's - var responseUuid = response.Headers ["apns-id"]; - if (responseUuid != notification.Uuid) - throw new Exception ("Mismatched APNS-ID header values"); - } else { - // Try parsing json body - var json = new JObject (); - - if (response.Body != null && response.Body.Length > 0) { - var body = Encoding.ASCII.GetString (response.Body); - json = JObject.Parse (body); - } - - if (response.Status == HttpStatusCode.Gone) { - - var timestamp = DateTime.UtcNow; - if (json != null && json["timestamp"] != null) { - var sinceEpoch = json.Value ("timestamp"); - timestamp = new DateTime (1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds (sinceEpoch); - } - - // Expired - throw new PushSharp.Core.DeviceSubscriptonExpiredException { - OldSubscriptionId = notification.DeviceToken, - NewSubscriptionId = null, - ExpiredAt = timestamp - }; - } - - // Get the reason - var reasonStr = json.Value ("reason"); - - var reason = (ApnsHttp2FailureReason)Enum.Parse (typeof (ApnsHttp2FailureReason), reasonStr, true); - - throw new ApnsHttp2NotificationException (reason, notification); - } - } - } -} - diff --git a/PushSharp.Apple/ApnsHttp2Notification.cs b/PushSharp.Apple/ApnsHttp2Notification.cs deleted file mode 100644 index 5a390d31..00000000 --- a/PushSharp.Apple/ApnsHttp2Notification.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using PushSharp.Core; -using Newtonsoft.Json.Linq; -using System.Net; -using System.Text; -using System.Collections.Generic; - -namespace PushSharp.Apple -{ - public class ApnsHttp2Notification : INotification - { - /// - /// Store whatever associated information you'd like here - /// - /// The tag. - public object Tag { get; set; } - - /// - /// Unique Identifier to match server responses with - /// - /// The UUID. - public string Uuid { get; set; } - - /// - /// Device Token to send notifications to - /// - /// The device token. - public string DeviceToken { get; set; } - - /// - /// JSON APNS Payload - /// - /// The payload. - public JObject Payload { get; set; } - - /// - /// The expiration date after which Apple will no longer store and forward this push notification. - /// If no value is provided, an assumed value of one year from now is used. If you do not wish - /// for Apple to store and forward, set this value to Notification.DoNotStore. - /// - public DateTime? Expiration { get; set; } - - public ApnsPriority? Priority { get; set; } - - public string Topic { get;set; } - - public const int MAX_PAYLOAD_SIZE = 4096; - public static readonly DateTime DoNotStore = DateTime.MinValue; - - public ApnsHttp2Notification () : this (string.Empty, new JObject ()) - { - } - - public ApnsHttp2Notification (string deviceToken) : this (deviceToken, new JObject ()) - { - } - - public ApnsHttp2Notification (string deviceToken, JObject payload) - { - DeviceToken = deviceToken; - Payload = payload; - - Uuid = Guid.NewGuid ().ToString ("D"); - } - - public bool IsDeviceRegistrationIdValid () - { - var r = new System.Text.RegularExpressions.Regex (@"^[0-9A-F]+$", System.Text.RegularExpressions.RegexOptions.IgnoreCase); - return r.Match (DeviceToken).Success; - } - - public override string ToString () - { - try { - if (Payload != null) - return Payload.ToString (); - } catch { - } - - return "{}"; - } - } - - public enum ApnsPriority - { - Low = 5, - High = 10 - } -} diff --git a/PushSharp.Apple/ApnsHttp2ServiceConnection.cs b/PushSharp.Apple/ApnsHttp2ServiceConnection.cs deleted file mode 100644 index 39d8587c..00000000 --- a/PushSharp.Apple/ApnsHttp2ServiceConnection.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using PushSharp.Core; -using System.Threading.Tasks; - -namespace PushSharp.Apple -{ - public class ApnsHttp2ServiceConnectionFactory : IServiceConnectionFactory - { - public ApnsHttp2ServiceConnectionFactory (ApnsHttp2Configuration configuration) - { - Configuration = configuration; - } - - public ApnsHttp2Configuration Configuration { get; private set; } - - public IServiceConnection Create() - { - return new ApnsHttp2ServiceConnection (Configuration); - } - } - - public class ApnsHttp2ServiceBroker : ServiceBroker - { - public ApnsHttp2ServiceBroker (ApnsHttp2Configuration configuration) : base (new ApnsHttp2ServiceConnectionFactory (configuration)) - { - } - } - - public class ApnsHttp2ServiceConnection : IServiceConnection - { - readonly ApnsHttp2Connection connection; - - public ApnsHttp2ServiceConnection (ApnsHttp2Configuration configuration) - { - connection = new ApnsHttp2Connection (configuration); - } - - public async Task Send (ApnsHttp2Notification notification) - { - await connection.Send (notification); - } - } -} diff --git a/PushSharp.Apple/ApnsNotification.cs b/PushSharp.Apple/ApnsNotification.cs index b9a4ea9e..6044f636 100644 --- a/PushSharp.Apple/ApnsNotification.cs +++ b/PushSharp.Apple/ApnsNotification.cs @@ -1,9 +1,9 @@ using System; -using PushSharp.Core; using Newtonsoft.Json.Linq; using System.Net; using System.Text; using System.Collections.Generic; +using PushSharp.Common; namespace PushSharp.Apple { diff --git a/PushSharp.Apple/ApnsServiceConnection.cs b/PushSharp.Apple/ApnsServiceConnection.cs index 2dc760cb..21234223 100644 --- a/PushSharp.Apple/ApnsServiceConnection.cs +++ b/PushSharp.Apple/ApnsServiceConnection.cs @@ -1,6 +1,6 @@ using System; -using PushSharp.Core; using System.Threading.Tasks; +using PushSharp.Common; namespace PushSharp.Apple { diff --git a/PushSharp.Apple/Exceptions.cs b/PushSharp.Apple/Exceptions.cs index 33d28192..b559f52e 100644 --- a/PushSharp.Apple/Exceptions.cs +++ b/PushSharp.Apple/Exceptions.cs @@ -1,5 +1,5 @@ using System; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Apple { diff --git a/PushSharp.Apple/Properties/AssemblyInfo.cs b/PushSharp.Apple/Properties/AssemblyInfo.cs deleted file mode 100644 index 3b074103..00000000 --- a/PushSharp.Apple/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Apple")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Apple/PushSharp.Apple.csproj b/PushSharp.Apple/PushSharp.Apple.csproj index 3c9344a0..7ab6804d 100644 --- a/PushSharp.Apple/PushSharp.Apple.csproj +++ b/PushSharp.Apple/PushSharp.Apple.csproj @@ -1,58 +1,21 @@ - - + + - Debug - AnyCPU - {A9D99F80-FEEB-4E74-96C5-66F17103C773} - Library - PushSharp.Apple + netstandard2.0;net45; PushSharp.Apple - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + PushSharp.Apple + - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - - + - - - - - - - - - - - - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + + + + - + - \ No newline at end of file + + diff --git a/PushSharp.Apple/packages.config b/PushSharp.Apple/packages.config deleted file mode 100644 index 505e5883..00000000 --- a/PushSharp.Apple/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/PushSharp.Blackberry/BlackberryConnection.cs b/PushSharp.Blackberry/BlackberryConnection.cs index 14f17573..6ec825fa 100644 --- a/PushSharp.Blackberry/BlackberryConnection.cs +++ b/PushSharp.Blackberry/BlackberryConnection.cs @@ -3,7 +3,7 @@ using System.Net; using System.Xml.Linq; using System.Linq; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Blackberry { diff --git a/PushSharp.Blackberry/BlackberryNotification.cs b/PushSharp.Blackberry/BlackberryNotification.cs index 64b1fa17..b778eb9c 100644 --- a/PushSharp.Blackberry/BlackberryNotification.cs +++ b/PushSharp.Blackberry/BlackberryNotification.cs @@ -1,9 +1,10 @@ using System; -using PushSharp.Core; using System.Collections.Generic; using System.Xml.Linq; using System.Text; using System.Globalization; +using System.Web; +using PushSharp.Common; namespace PushSharp.Blackberry { @@ -92,7 +93,7 @@ public string ToPapXml() if (!string.IsNullOrEmpty(r.RecipientType)) { - addrValue = string.Format("WAPPUSH={0}%3A{1}/TYPE={2}", System.Web.HttpUtility.UrlEncode(r.Recipient), + addrValue = string.Format("WAPPUSH={0}%3A{1}/TYPE={2}", HttpUtility.UrlEncode(r.Recipient), r.Port, r.RecipientType); } diff --git a/PushSharp.Blackberry/Exceptions.cs b/PushSharp.Blackberry/Exceptions.cs index be990fce..b9e86497 100644 --- a/PushSharp.Blackberry/Exceptions.cs +++ b/PushSharp.Blackberry/Exceptions.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Blackberry { diff --git a/PushSharp.Blackberry/Properties/AssemblyInfo.cs b/PushSharp.Blackberry/Properties/AssemblyInfo.cs deleted file mode 100644 index ba2f5b78..00000000 --- a/PushSharp.Blackberry/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Blackberry")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Blackberry/PushSharp.Blackberry.csproj b/PushSharp.Blackberry/PushSharp.Blackberry.csproj index 4e0e54c3..303ee08f 100644 --- a/PushSharp.Blackberry/PushSharp.Blackberry.csproj +++ b/PushSharp.Blackberry/PushSharp.Blackberry.csproj @@ -1,55 +1,22 @@ - - + + - Debug - AnyCPU - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12} - Library - PushSharp.Blackberry + netstandard2.0;net45; PushSharp.Blackberry - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + PushSharp.Blackberry + - - - - - + - - - - - - - - + + + + - + - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + - \ No newline at end of file + + diff --git a/PushSharp.Core/Exceptions.cs b/PushSharp.Common/Exceptions.cs similarity index 67% rename from PushSharp.Core/Exceptions.cs rename to PushSharp.Common/Exceptions.cs index 4cfc29fb..d752880d 100644 --- a/PushSharp.Core/Exceptions.cs +++ b/PushSharp.Common/Exceptions.cs @@ -1,19 +1,10 @@ using System; -using System.Collections.Generic; -namespace PushSharp.Core +namespace PushSharp.Common { - public class DeviceSubscriptionExpiredException : DeviceSubscriptonExpiredException + public class DeviceSubscriptionExpiredException : NotificationException { - public DeviceSubscriptionExpiredException (INotification notification) : base (notification) - { - } - } - - [Obsolete ("Do not use this class directly, it has a typo in it, instead use DeviceSubscriptionExpiredException")] - public class DeviceSubscriptonExpiredException : NotificationException - { - public DeviceSubscriptonExpiredException (INotification notification) : base ("Device Subscription has Expired", notification) + public DeviceSubscriptionExpiredException(INotification notification) : base ("Device Subscription has Expired", notification) { ExpiredAt = DateTime.UtcNow; } diff --git a/PushSharp.Core/INotification.cs b/PushSharp.Common/INotification.cs similarity index 75% rename from PushSharp.Core/INotification.cs rename to PushSharp.Common/INotification.cs index 23e26d0f..72777f56 100644 --- a/PushSharp.Core/INotification.cs +++ b/PushSharp.Common/INotification.cs @@ -1,6 +1,4 @@ -using System; - -namespace PushSharp.Core +namespace PushSharp.Common { public interface INotification { diff --git a/PushSharp.Core/IServiceBroker.cs b/PushSharp.Common/IServiceBroker.cs similarity index 95% rename from PushSharp.Core/IServiceBroker.cs rename to PushSharp.Common/IServiceBroker.cs index 0aa82a75..5924e758 100644 --- a/PushSharp.Core/IServiceBroker.cs +++ b/PushSharp.Common/IServiceBroker.cs @@ -1,6 +1,6 @@ using System; -namespace PushSharp.Core +namespace PushSharp.Common { public interface IServiceBroker where TNotification : INotification { diff --git a/PushSharp.Core/IServiceConnection.cs b/PushSharp.Common/IServiceConnection.cs similarity index 94% rename from PushSharp.Core/IServiceConnection.cs rename to PushSharp.Common/IServiceConnection.cs index cfc1812c..8344e3b4 100644 --- a/PushSharp.Core/IServiceConnection.cs +++ b/PushSharp.Common/IServiceConnection.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace PushSharp.Core +namespace PushSharp.Common { public delegate void NotificationSuccessDelegate (TNotification notification) where TNotification : INotification; public delegate void NotificationFailureDelegate (TNotification notification, AggregateException exception) where TNotification : INotification; diff --git a/PushSharp.Core/IServiceConnectionFactory.cs b/PushSharp.Common/IServiceConnectionFactory.cs similarity index 79% rename from PushSharp.Core/IServiceConnectionFactory.cs rename to PushSharp.Common/IServiceConnectionFactory.cs index bb30597a..036aec70 100644 --- a/PushSharp.Core/IServiceConnectionFactory.cs +++ b/PushSharp.Common/IServiceConnectionFactory.cs @@ -1,6 +1,4 @@ -using System; - -namespace PushSharp.Core +namespace PushSharp.Common { public interface IServiceConnectionFactory where TNotification : INotification { diff --git a/PushSharp.Core/Log.cs b/PushSharp.Common/Log.cs similarity index 99% rename from PushSharp.Core/Log.cs rename to PushSharp.Common/Log.cs index fd749d2d..11a0b4c2 100644 --- a/PushSharp.Core/Log.cs +++ b/PushSharp.Common/Log.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace PushSharp.Core +namespace PushSharp.Common { [Flags] public enum LogLevel diff --git a/PushSharp.Core/NotificationBlockingCollection.cs b/PushSharp.Common/NotificationBlockingCollection.cs similarity index 71% rename from PushSharp.Core/NotificationBlockingCollection.cs rename to PushSharp.Common/NotificationBlockingCollection.cs index 7cd4352b..00d8fa3a 100644 --- a/PushSharp.Core/NotificationBlockingCollection.cs +++ b/PushSharp.Common/NotificationBlockingCollection.cs @@ -1,6 +1,4 @@ -using System; - -namespace PushSharp.Core +namespace PushSharp.Common { public class NotificationBlockingCollection { diff --git a/PushSharp.Common/Properties/PublishProfiles/FolderProfile.pubxml b/PushSharp.Common/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 00000000..296ffbdb --- /dev/null +++ b/PushSharp.Common/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,13 @@ + + + + + FileSystem + Release + netstandard2.0 + bin\Release\PublishOutput + + \ No newline at end of file diff --git a/PushSharp.Core/PushHttpClient.cs b/PushSharp.Common/PushHttpClient.cs similarity index 99% rename from PushSharp.Core/PushHttpClient.cs rename to PushSharp.Common/PushHttpClient.cs index cbc6fcfd..26f90b9a 100644 --- a/PushSharp.Core/PushHttpClient.cs +++ b/PushSharp.Common/PushHttpClient.cs @@ -1,10 +1,10 @@ using System; +using System.IO; using System.Net; using System.Text; -using System.IO; using System.Threading.Tasks; -namespace PushSharp.Core +namespace PushSharp.Common { public static class PushHttpClient { diff --git a/PushSharp.Common/PushSharp.Common.csproj b/PushSharp.Common/PushSharp.Common.csproj new file mode 100644 index 00000000..284058f9 --- /dev/null +++ b/PushSharp.Common/PushSharp.Common.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0;net45; + + + diff --git a/PushSharp.Core/ServiceBroker.cs b/PushSharp.Common/ServiceBroker.cs similarity index 99% rename from PushSharp.Core/ServiceBroker.cs rename to PushSharp.Common/ServiceBroker.cs index 4b87f7b1..4b93a587 100644 --- a/PushSharp.Core/ServiceBroker.cs +++ b/PushSharp.Common/ServiceBroker.cs @@ -1,12 +1,12 @@ using System; -using System.Linq; using System.Collections.Concurrent; -using System.Threading.Tasks; -using System.Threading; using System.Collections.Generic; +using System.Linq; using System.Net; +using System.Threading; +using System.Threading.Tasks; -namespace PushSharp.Core +namespace PushSharp.Common { public class ServiceBroker : IServiceBroker where TNotification : INotification { diff --git a/PushSharp.Core/ServiceBrokerConfiguration.cs b/PushSharp.Common/ServiceBrokerConfiguration.cs similarity index 94% rename from PushSharp.Core/ServiceBrokerConfiguration.cs rename to PushSharp.Common/ServiceBrokerConfiguration.cs index 5db03aab..b7b58a2a 100644 --- a/PushSharp.Core/ServiceBrokerConfiguration.cs +++ b/PushSharp.Common/ServiceBrokerConfiguration.cs @@ -1,6 +1,4 @@ -using System; - -namespace PushSharp.Core +namespace PushSharp.Common { public class ServiceBrokerConfiguration // : IPushServiceSettings { diff --git a/PushSharp.Core.1.0.0.nupkg b/PushSharp.Core.1.0.0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..4445344618ad36e819321ea058cf051ce9b887db GIT binary patch literal 126202 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

#-KV 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%0As`gM=Wh%4S?HMwgy#dAJ@Cgev2WuP=Kso( z#u1=( zW?)+=yI$tfrk+i?BXbxjpnwVj{aq)E#8;k1slz+|v@BIUo}%#~PMVBm%HrdI;j~%Q za7BP1EgY0-80DGrB@TM9=rMC~+omKdJrCZR>l>4bV|>2dwDj9(_W z+jD*JzGX_Kjf(PCZS-Y&C<^FTU;&xAT?BHm$O(3~-;EE8UqL?cFl>l&y=M(!D;o7O zvn!~%O4`G!Rj%yCAO?(LUz<+oRKTewe`bK|K_t{#rI1wH8eM!%e1_^d=X;Xm6gXZ9 ztbN3Bt!@<}s||`cXVHJP%at*Ct#p5alb3>o!i2yCixwW@Gb*_h7GAA0Cb%0vffq#Z zvwv?o_C}Ttj0}I=|GwjSRVdpwxDULAg@C~N+g|)&l)RIJ=~q)dds~Lj;2ywW?c`u< zWbivegng - - - Debug - AnyCPU - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - Library - PushSharp.Core - PushSharp.Core - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/PushSharp.Firefox/Exceptions.cs b/PushSharp.Firefox/Exceptions.cs index 406fb1fd..be14b270 100644 --- a/PushSharp.Firefox/Exceptions.cs +++ b/PushSharp.Firefox/Exceptions.cs @@ -1,5 +1,5 @@ using System; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Firefox { diff --git a/PushSharp.Firefox/FirefoxConnection.cs b/PushSharp.Firefox/FirefoxConnection.cs index acdd8afe..3944b0ff 100644 --- a/PushSharp.Firefox/FirefoxConnection.cs +++ b/PushSharp.Firefox/FirefoxConnection.cs @@ -1,9 +1,9 @@ using System; -using PushSharp.Core; using System.Threading.Tasks; using System.Net.Http; using System.Net; using System.Net.Http.Headers; +using PushSharp.Common; namespace PushSharp.Firefox { diff --git a/PushSharp.Firefox/FirefoxNotification.cs b/PushSharp.Firefox/FirefoxNotification.cs index cbf1fd0c..7b5ee1e9 100644 --- a/PushSharp.Firefox/FirefoxNotification.cs +++ b/PushSharp.Firefox/FirefoxNotification.cs @@ -1,5 +1,5 @@ using System; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Firefox { diff --git a/PushSharp.Firefox/Properties/AssemblyInfo.cs b/PushSharp.Firefox/Properties/AssemblyInfo.cs deleted file mode 100644 index ccdecdce..00000000 --- a/PushSharp.Firefox/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Firefox")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Firefox/PushSharp.Firefox.csproj b/PushSharp.Firefox/PushSharp.Firefox.csproj index 4fb18fb7..423e97be 100644 --- a/PushSharp.Firefox/PushSharp.Firefox.csproj +++ b/PushSharp.Firefox/PushSharp.Firefox.csproj @@ -1,50 +1,19 @@ - - + + - Debug - AnyCPU - {54A4C1F9-6571-4086-BB4B-EC202138AF00} - Library - PushSharp.Firefox - PushSharp.Firefox - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + netstandard2.0;net45; + - - + - - - - - - + + + - + - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + - \ No newline at end of file + + diff --git a/PushSharp.Google/Exceptions.cs b/PushSharp.Google/Exceptions.cs index b27da6ec..cd059c7a 100644 --- a/PushSharp.Google/Exceptions.cs +++ b/PushSharp.Google/Exceptions.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Google { diff --git a/PushSharp.Google/GcmNotification.cs b/PushSharp.Google/GcmNotification.cs index 28ba0599..4b45f1c9 100644 --- a/PushSharp.Google/GcmNotification.cs +++ b/PushSharp.Google/GcmNotification.cs @@ -2,9 +2,9 @@ using System.Runtime.Serialization; using Newtonsoft.Json.Linq; using System.Collections.Generic; -using PushSharp.Core; using System.Linq; using Newtonsoft.Json; +using PushSharp.Common; namespace PushSharp.Google { @@ -26,7 +26,6 @@ public static GcmNotification ForSingleResult (GcmResponse response, int resultI result.DryRun = response.OriginalNotification.DryRun; result.Priority = response.OriginalNotification.Priority; result.To = response.OriginalNotification.To; - result.NotificationKey = response.OriginalNotification.NotificationKey; return result; } @@ -44,7 +43,6 @@ public static GcmNotification ForSingleRegistrationId (GcmNotification msg, stri result.ContentAvailable = msg.ContentAvailable; result.DryRun = msg.DryRun; result.Priority = msg.Priority; - result.NotificationKey = msg.NotificationKey; return result; } @@ -118,12 +116,6 @@ public bool IsDeviceRegistrationIdValid () [JsonProperty ("dry_run")] public bool? DryRun { get; set; } - ///

- /// A string that maps a single user to multiple registration IDs associated with that user. This allows a 3rd-party server to send a single message to multiple app instances (typically on multiple devices) owned by a single user. - /// - [Obsolete ("Deprecated on GCM Server API. Use Device Group Messaging to send to multiple devices.")] - public string NotificationKey { get; set; } - /// /// A string containing the package name of your application. When set, messages will only be sent to registration IDs that match the package name /// diff --git a/PushSharp.Google/GcmServiceConnection.cs b/PushSharp.Google/GcmServiceConnection.cs index b3b539c5..78ce77e8 100644 --- a/PushSharp.Google/GcmServiceConnection.cs +++ b/PushSharp.Google/GcmServiceConnection.cs @@ -4,10 +4,10 @@ using System.Net.Http.Headers; using Newtonsoft.Json.Linq; using System.Net; -using PushSharp.Core; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using PushSharp.Common; namespace PushSharp.Google { diff --git a/PushSharp.Google/Properties/AssemblyInfo.cs b/PushSharp.Google/Properties/AssemblyInfo.cs deleted file mode 100644 index 2afce1ae..00000000 --- a/PushSharp.Google/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Google")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Google/PushSharp.Google.csproj b/PushSharp.Google/PushSharp.Google.csproj index 92d72f31..423e97be 100644 --- a/PushSharp.Google/PushSharp.Google.csproj +++ b/PushSharp.Google/PushSharp.Google.csproj @@ -1,61 +1,19 @@ - - + + - Debug - AnyCPU - {94F16497-471F-433F-A99E-C455FB2D7724} - Library - PushSharp.Google - PushSharp.Google - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + netstandard2.0;net45; + - - - - - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - + - - - - - - - - - - - - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + + + + - + - \ No newline at end of file + + diff --git a/PushSharp.Google/Xmpp/GcmXmppConfiguration.cs b/PushSharp.Google/Xmpp/GcmXmppConfiguration.cs deleted file mode 100644 index fa186494..00000000 --- a/PushSharp.Google/Xmpp/GcmXmppConfiguration.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace PushSharp.Google -{ - public class GcmXmppConfiguration - { - public GcmXmppConfiguration () - { - Production = true; - SenderIDs = new List (); - } - - public const string GCM_XMPP_SERVER = "gcm.googleapis.com"; - public const string GCM_XMPP_PREPROD_SERVER = "gcm-preprod.googleapis.com"; - public const int GCM_XMPP_PORT = 5235; - public const int GCM_XMPP_PREPROD_PORT = 5236; - - public List SenderIDs { get; set; } - public string AuthenticationToken { get; set; } - - public string SaslAuthToken { - get { - var s = new StringBuilder (); - - foreach (var sender in SenderIDs) - s.Append ("\0" + sender + "@gcm.googleapis.com"); - - s.Append ("\0" + AuthenticationToken); - - return Convert.ToBase64String (Encoding.UTF8.GetBytes (s.ToString ())); - } - } - - public bool Production { get;set; } - - string overrideHost = string.Empty; - int? overridePort = null; - - public string Host { - get { - if (!string.IsNullOrEmpty (overrideHost)) - return overrideHost; - - return Production ? GCM_XMPP_SERVER : GCM_XMPP_PREPROD_SERVER; - } - } - - public int Port { - get { - if (overridePort.HasValue) - return overridePort.Value; - - return Production ? GCM_XMPP_PORT : GCM_XMPP_PREPROD_PORT; - } - } - } -} - diff --git a/PushSharp.Google/Xmpp/GcmXmppConnection.cs b/PushSharp.Google/Xmpp/GcmXmppConnection.cs deleted file mode 100644 index 5c9b0be3..00000000 --- a/PushSharp.Google/Xmpp/GcmXmppConnection.cs +++ /dev/null @@ -1,323 +0,0 @@ -using System; -using System.Net.Sockets; -using System.Threading.Tasks; -using System.Net.Security; -using System.Xml; -using System.Text; -using System.IO; -using System.Xml.Linq; -using PushSharp.Core; -using System.Security.Cryptography.X509Certificates; -using System.Collections.Generic; - -namespace PushSharp.Google -{ - public class GcmXmppConnection - { - public const string STREAM_ELEMENT_NAME = "stream"; - public const string SASL_AUTH_ELEMENT_NAME = "auth"; - public const string BIND_ELEMENT_NAME = "bind"; - public const string IQ_ELEMENT_NAME = "iq"; - public const string MESSAGE_ELEMENT_NAME = "message"; - public const string RESOURCE_ELEMENT_NAME = "resource"; - public const string MECHANISMS_ELEMENT_NAME = "mechanisms"; - public const string STREAM_PREFIX = "stream"; - public const string STREAM_NAMESPACE = "http://etherx.jabber.org/streams"; - public const string STREAM_PARAMS_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams"; - public const string JABBER_NAMESPACE = "jabber:client"; - public const string GCM_MSG_NAMESPACE = "google:mobile:data"; - public const string SASL_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl"; - public const string BIND_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-bind"; - public const int MAJOR_VERSION = 1; - public const int MINOR_VERSION = 0; - - readonly XNamespace GCM_NS = GCM_MSG_NAMESPACE; - - X509CertificateCollection certificates; - - TaskCompletionSource authCompletion; - - public Dictionary notifications; - - public GcmXmppConnection (GcmXmppConfiguration configuration) - { - authCompletion = new TaskCompletionSource (); - - notifications = new Dictionary (); - Configuration = configuration; - - certificates = new X509CertificateCollection (); - - // Add local/machine certificate stores to our collection if requested - //if (Configuration.AddLocalAndMachineCertificateStores) { - var store = new X509Store (StoreLocation.LocalMachine); - certificates.AddRange (store.Certificates); - - store = new X509Store (StoreLocation.CurrentUser); - certificates.AddRange (store.Certificates); - //} - - // Add optionally specified additional certs into our collection -// if (Configuration.AdditionalCertificates != null) { -// foreach (var addlCert in Configuration.AdditionalCertificates) -// certificates.Add (addlCert); -// } - - // Finally, add the main private cert for authenticating to our collection -// if (certificate != null) -// certificates.Add (certificate); - } - - public delegate void ReceiveMessageDelegate (); - public event ReceiveMessageDelegate ReceiveMessage; - - public GcmXmppConfiguration Configuration { get; private set; } - - TcpClient client; - SslStream sslStream; - Stream stream; - - XmlWriter xml; - bool exit = false; - readonly object writeLock = new object (); - - public Task Connect () - { - lock (writeLock) { - client = new TcpClient (); - - Log.Debug ("GCM-XMPP: Connecting..."); - - //await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); - - client.Connect (Configuration.Host, Configuration.Port); - - Log.Debug ("GCM-XMPP: Connected. Creating Secure Channel..."); - - sslStream = new SslStream (client.GetStream (), true, (sender, certificate, chain, sslPolicyErrors) => true); - - Log.Debug ("GCM-XMPP: Authenticating Tls..."); - - sslStream.AuthenticateAsClient (Configuration.Host, certificates, System.Security.Authentication.SslProtocols.Tls, false); - stream = sslStream; - - Log.Debug ("GCM-XMPP: Authenticated Tls."); - - var xws = new XmlWriterSettings { - Async = true, - OmitXmlDeclaration = true, - Indent = true, - NewLineHandling = NewLineHandling.None, - Encoding = new UTF8Encoding (false), - CloseOutput = false, - }; - - xml = XmlWriter.Create (stream, xws); - - Log.Debug ("GCM-XMPP: Writing opening element..."); - - //Write initial stream:stream element - xml.WriteStartElement (STREAM_PREFIX, STREAM_ELEMENT_NAME, STREAM_NAMESPACE); - xml.WriteAttributeString (string.Empty, "to", string.Empty, "gcm.googleapis.com"); - xml.WriteAttributeString ("version", string.Format ("{0}.{1}", MAJOR_VERSION, MINOR_VERSION)); - xml.WriteAttributeString ("xmlns", JABBER_NAMESPACE); - xml.WriteWhitespace ("\n"); - xml.Flush (); - - Log.Debug ("GCM-XMPP: Starting Listening..."); - } - - Task.Factory.StartNew (Listen); - - Log.Debug ("GCM-XMPP: Waiting for Authentication..."); - return authCompletion.Task; - } - - public void Authenticate () - { -// Auth Token - Log.Debug ("GCM-XMPP: Authenticating..."); - - XNamespace ns = SASL_NAMESPACE; - var el = new XElement (ns + SASL_AUTH_ELEMENT_NAME, new XAttribute ("mechanism", "PLAIN"), Configuration.SaslAuthToken); - WriteElement (el); - } - - public void Send (CompletableNotification notification) - { - - XNamespace ns = GCM_MSG_NAMESPACE; - var gcm = new XElement (ns + "gcm", notification.Notification.ToJson ()); - var msg = new XElement ("message", new XAttribute ("id", string.Empty), gcm); - - Log.Debug ("GCM-XMPP: Sending: " + msg); - - try { - WriteElement (msg); - - notifications.Add (notification.Notification.MessageId, notification); - } catch (Exception ex) { - notification.CompleteFailed (ex); - } - } - - - void Listen() - { - try { - var xrs = new XmlReaderSettings { - //Async = true, - CloseInput = false, - ConformanceLevel = ConformanceLevel.Fragment, - - //IgnoreComments = true, - //IgnoreWhitespace = true, - // XmlResolver = null, - }; - - Log.Debug ("GCM-XMPP: Listening..."); - - using (var xmlReader = XmlReader.Create (stream, xrs)) - { - while (!exit && xmlReader.Read ()) //await xmlReader.ReadAsync ().ConfigureAwait (false)) - { - Log.Debug ("GCM-XMPP: Read: " + xmlReader.NodeType + " - " + xmlReader.Name); - - if (xmlReader.NodeType == XmlNodeType.Element) - { - if (xmlReader.Name == STREAM_PREFIX + ":" + STREAM_ELEMENT_NAME) { - - Log.Debug ("GCM-XMPP: Stream initialization node received"); - - } else { - Log.Debug ("GCM-XMPP: Reading subtree..."); - - var elem = XElement.Load (xmlReader.ReadSubtree ()); - - Log.Debug ("GCM-XMPP: Loaded Subtree: " + elem); - - switch (elem.Name.LocalName) - { - case "success": - authCompletion.TrySetResult (true); - break; - case "failed": - authCompletion.TrySetResult (false); - break; - case "error": - - //TODO: Check for stanza error GCM - Log.Debug ("XMPPStream error: " + elem.Value); - break; - case "features": - Log.Debug ("Got Features"); - Authenticate (); - break; - case "message": - - //TODO: Received a message, let's parse it! - - var gcm = elem.Element (GCM_NS + "gcm"); - - var err = elem.Element ("error"); - - if (err != null) { - //TODO: Handle stanza error - } else { - HandleMessage (gcm.Value); - } - break; - default: - break; - } - - Log.Debug ("GCM-XMPP: Received: " + elem); - } - - } - else if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == STREAM_PREFIX + ":" + STREAM_ELEMENT_NAME) - { - Log.Debug ("GCM-XMPP: Server closed the stream"); - Close(); - break; - } else { - Log.Debug ("GCM-XMPP: WHAT?"); - } - - - - - - } - - - } - } catch (Exception ex) { - - Log.Error ("GCM-XMPP: Listener Error {0}", ex); - authCompletion.TrySetResult (false); - } - - // If there are any notifications we're waiting on, they need to be failed - foreach (var n in notifications.Values) - n.CompleteFailed (new Exception ("Connection Closed before response was received")); - - Log.Debug ("GCM-XMPP: Closed Listener"); - } - - public void Close () - { - // exit = true; - - Log.Debug ("GCM-XMPP: Closing XMPP Stream"); - xml.WriteEndDocument (); - xml.Flush (); - } - - void WriteElement(XElement element) - { - lock (writeLock) { - Log.Debug ("GCM-XMPP: Sending: " + element); - - element.WriteTo (xml); - xml.Flush (); - } - } - - - void HandleMessage (string json) - { - Log.Debug ("Incoming Message: " + json); - } - - public class CompletableNotification - { - public CompletableNotification (GcmXmppNotification notification) - { - Notification = notification; - completionSource = new TaskCompletionSource (); - } - - public GcmXmppNotification Notification { get; private set; } - - readonly TaskCompletionSource completionSource; - - public async Task WaitForComplete () - { - return await completionSource.Task.ConfigureAwait (false); - } - - public void CompleteSuccessfully () - { - completionSource.SetResult (null); - } - - public void CompleteFailed (Exception ex) - { - completionSource.SetResult (ex); - } - } - - } -} - diff --git a/PushSharp.Google/Xmpp/GcmXmppNotification.cs b/PushSharp.Google/Xmpp/GcmXmppNotification.cs deleted file mode 100644 index 283fac3e..00000000 --- a/PushSharp.Google/Xmpp/GcmXmppNotification.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using PushSharp.Core; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json; - -namespace PushSharp.Google -{ - public class GcmXmppNotification : INotification - { - public GcmXmppNotification () - { - MessageId = "m-" + Guid.NewGuid ().ToString ("N"); - CollapseKey = string.Empty; - Data = null; - DelayWhileIdle = null; - } - - public bool IsDeviceRegistrationIdValid () - { - return !string.IsNullOrEmpty (To); - } - - [JsonIgnore] - public object Tag { get;set; } - - [JsonProperty ("message_id")] - public string MessageId { get;set; } - - /// - /// Registration ID or Group/Topic to send notification to. Overrides RegsitrationIds. - /// - /// To. - [JsonProperty ("to")] - public string To { get;set; } - - /// - /// Only the latest message with the same collapse key will be delivered - /// - [JsonProperty ("collapse_key")] - public string CollapseKey { get; set; } - - /// - /// JSON Payload to be sent in the message - /// - [JsonProperty ("data")] - public JObject Data { get; set; } - - /// - /// Notification JSON payload - /// - /// The notification payload. - [JsonProperty ("notification")] - public JObject Notification { get; set; } - - [JsonProperty ("delivery_receipt_requested")] - public bool? DeliveryReceiptRequested { get; set; } - - /// - /// If true, GCM will only be delivered once the device's screen is on - /// - [JsonProperty ("delay_while_idle")] - public bool? DelayWhileIdle { get; set; } - - /// - /// Time in seconds that a message should be kept on the server if the device is offline. Default (and maximum) is 4 weeks. - /// - [JsonProperty ("time_to_live")] - public int? TimeToLive { get; set; } - - /// - /// If true, dry_run attribute will be sent in payload causing the notification not to be actually sent, but the result returned simulating the message - /// - [JsonProperty ("dry_run")] - public bool? DryRun { get; set; } - - /// - /// A string that maps a single user to multiple registration IDs associated with that user. This allows a 3rd-party server to send a single message to multiple app instances (typically on multiple devices) owned by a single user. - /// - [Obsolete ("Deprecated on GCM Server API. Use Device Group Messaging to send to multiple devices.")] - public string NotificationKey { get; set; } - - /// - /// A string containing the package name of your application. When set, messages will only be sent to registration IDs that match the package name - /// - [JsonProperty ("restricted_package_name")] - public string RestrictedPackageName { get; set; } - - /// - /// On iOS, use this field to represent content-available in the APNS payload. When a notification or message is sent and this is set to true, an inactive client app is awoken. On Android, data messages wake the app by default. On Chrome, currently not supported. - /// - /// The content available. - [JsonProperty ("content_available")] - public bool? ContentAvailable { get; set; } - - /// - /// Corresponds to iOS APNS priorities (Normal is 5 and high is 10). Default is Normal. - /// - /// The priority. - [JsonProperty ("priority")] - public GcmNotificationPriority? Priority { get; set; } - - internal string GetJson () - { - return JsonConvert.SerializeObject (this); - } - - public override string ToString () - { - return GetJson (); - } - - - public string ToJson () - { - return JsonConvert.SerializeObject (this); - } - } -} - diff --git a/PushSharp.Google/Xmpp/GcmXmppResponse.cs b/PushSharp.Google/Xmpp/GcmXmppResponse.cs deleted file mode 100644 index d0d2b6a3..00000000 --- a/PushSharp.Google/Xmpp/GcmXmppResponse.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System.Runtime.Serialization; - -namespace PushSharp.Google -{ - public class GcmXmppResponse - { - public GcmXmppResponse () - { - } - - [JsonProperty ("message_type", ItemConverterType = typeof (StringEnumConverter))] - public ResponseMessageType MessageType { get;set; } - - [JsonProperty ("message_id")] - public string MessageId { get;set; } - - [JsonProperty ("from")] - public string From { get;set; } - - [JsonProperty ("error", ItemConverterType = typeof (StringEnumConverter))] - public ResponseErrorType Error { get; set; } - - [JsonProperty ("error_description")] - public string ErrorDescription { get; set; } - - } - - public enum ResponseMessageType - { - [EnumMember (Value="ack")] - Ack, - - [EnumMember (Value="nack")] - Nack, - - [EnumMember (Value="control")] - Control - } - - public enum ResponseErrorType - { - /// - /// The ACK message is improperly formed. - /// - [EnumMember (Value="BAD_ACK")] - BadAck, - - /// - /// The device has a registration ID, but it's invalid or expired. - /// - [EnumMember (Value="BAD_REGISTRATION")] - BadRegistration, - - /// - /// The message couldn't be processed because the connection is draining. The message should be immediately retried over another connection. - /// - [EnumMember (Value="CONNECTION_DRAINING")] - ConnectionDraining, - - /// - /// The device is not registered. - /// - [EnumMember (Value="DEVICE_UNREGISTERED")] - DeviceUnregistered, - - /// - /// The server encountered an error while trying to process the request. - /// - [EnumMember (Value="INTERNAL_SERVER_ERROR")] - InternalServerError, - - /// - /// The JSON message payload is not valid. - /// - [EnumMember (Value="INVALID_JSON")] - InvalidJson, - - /// - /// The rate of messages to a particular registration ID (in other words, to a sender/device pair) is too high. If you want to retry the message, try using a slower rate. - /// - [EnumMember (Value="QUOTA_EXCEEDED")] - QuotaExceeded, - - /// - /// CCS is not currently able to process the message. The message should be r - /// - [EnumMember (Value="SERVICE_UNAVAILABLE")] - ServiceUnavailable - } -} diff --git a/PushSharp.Google/Xmpp/GcmXmppService.cs b/PushSharp.Google/Xmpp/GcmXmppService.cs deleted file mode 100644 index 0e545ca8..00000000 --- a/PushSharp.Google/Xmpp/GcmXmppService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using PushSharp.Core; -using System.Threading.Tasks; - -namespace PushSharp.Google -{ - public class GcmXmppServiceConnectionFactory : IServiceConnectionFactory - { - public GcmXmppServiceConnectionFactory (GcmXmppConfiguration configuration) - { - Configuration = configuration; - } - - public GcmXmppConfiguration Configuration { get; private set; } - - public IServiceConnection Create() - { - return new GcmXmppServiceConnection (Configuration); - } - } - - public class GcmXmppServiceBroker : ServiceBroker - { - public GcmXmppServiceBroker (GcmXmppConfiguration configuration) : base (new GcmXmppServiceConnectionFactory (configuration)) - { - } - } - - public class GcmXmppServiceConnection : IServiceConnection - { - readonly GcmXmppConnection connection; - - public GcmXmppServiceConnection (GcmXmppConfiguration configuration) - { - connection = new GcmXmppConnection (configuration); - } - - public async Task Send (GcmXmppNotification notification) - { - var completableNotification = new GcmXmppConnection.CompletableNotification (notification); - - connection.Send (completableNotification); - - var ex = await completableNotification.WaitForComplete ().ConfigureAwait (false); - - if (ex != null) - throw ex; - } - } -} diff --git a/PushSharp.Google/packages.config b/PushSharp.Google/packages.config deleted file mode 100644 index 505e5883..00000000 --- a/PushSharp.Google/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/PushSharp.Tests/AdmRealTests.cs b/PushSharp.Tests/AdmRealTests.cs index 4c17e4c8..87fa0ec3 100644 --- a/PushSharp.Tests/AdmRealTests.cs +++ b/PushSharp.Tests/AdmRealTests.cs @@ -1,7 +1,7 @@ using System; using NUnit.Framework; -using PushSharp.Amazon; using System.Collections.Generic; +using PushSharp.Amazon; namespace PushSharp.Tests { diff --git a/PushSharp.Tests/ApnsHttp2RealTests.cs b/PushSharp.Tests/ApnsHttp2RealTests.cs deleted file mode 100644 index 80b249ac..00000000 --- a/PushSharp.Tests/ApnsHttp2RealTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using NUnit.Framework; -using PushSharp.Apple; -using Newtonsoft.Json.Linq; - -namespace PushSharp.Tests -{ - [TestFixture] - [Category("Disabled")] - public class ApnsHttp2RealTests - { - [Test] - public void APNSHTTP2_Send_Single() - { - var succeeded = 0; - var failed = 0; - var attempted = 0; - - var config = new ApnsHttp2Configuration(ApnsHttp2Configuration.ApnsServerEnvironment.Sandbox, Settings.Instance.ApnsCertificateFile, Settings.Instance.ApnsCertificatePassword); - var broker = new ApnsHttp2ServiceBroker(config); - broker.OnNotificationFailed += (notification, exception) => { - failed++; - }; - broker.OnNotificationSucceeded += (notification) => { - succeeded++; - }; - broker.Start(); - - foreach (var dt in Settings.Instance.ApnsDeviceTokens) - { - attempted++; - broker.QueueNotification(new ApnsHttp2Notification - { - DeviceToken = dt, - Topic = "com.pushsharp.sample", - Payload = JObject.Parse("{ \"aps\" : { \"alert\" : \"Hello PushSharp!\", \"badge\" : 5, \"sound\" : \"blank.aiff\" } }") - }); - } - - broker.Stop(); - - Assert.AreEqual(attempted, succeeded); - Assert.AreEqual(0, failed); - } - } -} - diff --git a/PushSharp.Tests/ApnsTests.cs b/PushSharp.Tests/ApnsTests.cs index b7e5ab3f..0ab37046 100644 --- a/PushSharp.Tests/ApnsTests.cs +++ b/PushSharp.Tests/ApnsTests.cs @@ -5,7 +5,8 @@ using System.Threading; using Newtonsoft.Json.Linq; using NUnit.Framework; -using PushSharp.Core; +using PushSharp.Common; +using PushSharp.Tests.Servers; namespace PushSharp.Tests { diff --git a/PushSharp.Tests/BrokerTests.cs b/PushSharp.Tests/BrokerTests.cs index 92308707..ed3f6fef 100644 --- a/PushSharp.Tests/BrokerTests.cs +++ b/PushSharp.Tests/BrokerTests.cs @@ -1,13 +1,13 @@ using NUnit.Framework; using System; -using PushSharp.Core; using System.Threading.Tasks; using System.Linq; using System.Collections.Generic; using PushSharp.Apple; using Newtonsoft.Json.Linq; using System.Threading; +using PushSharp.Common; namespace PushSharp.Tests diff --git a/PushSharp.Tests/GcmXmppTests.cs b/PushSharp.Tests/GcmXmppTests.cs deleted file mode 100644 index 4adf6795..00000000 --- a/PushSharp.Tests/GcmXmppTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using NUnit.Framework; -using System.Threading.Tasks; -using PushSharp.Google; -using PushSharp.Core; -using System.Collections.Generic; -using Newtonsoft.Json.Linq; - -namespace PushSharp.Tests -{ - [TestFixture] - [Category ("Disabled")] - public class GcmXmppTests - { - [Test] - public async Task GCMXMPP_Connect () - { - var succeeded = 0; - var failed = 0; - var attempted = 0; - - var c = new GcmXmppConfiguration { - Production = false, - AuthenticationToken = Settings.Instance.GcmAuthToken, - SenderIDs = new List { Settings.Instance.GcmSenderId } - }; - - var gcm = new GcmXmppConnection (c); - await gcm.Connect (); - - foreach (var regId in Settings.Instance.GcmRegistrationIds) { - gcm.Send (new GcmXmppConnection.CompletableNotification (new GcmXmppNotification { - To = regId, - Data = JObject.Parse ("{ \"somekey\" : \"somevalue\" }") - })); - } - - gcm.Close (); - - Assert.AreEqual (attempted, succeeded); - Assert.AreEqual (0, failed); - } - } -} - diff --git a/PushSharp.Tests/PushSharp.Tests.csproj b/PushSharp.Tests/PushSharp.Tests.csproj index 6cdef5f4..e958cf0f 100644 --- a/PushSharp.Tests/PushSharp.Tests.csproj +++ b/PushSharp.Tests/PushSharp.Tests.csproj @@ -1,84 +1,22 @@ - - + + - Debug - AnyCPU - {989B7357-800E-46B9-91AF-A4CE8A55F389} - Library - PushSharp.Tests - PushSharp.Tests - v4.5 + netstandard2.0;net45; - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false - - - - - ..\packages\NUnit.2.6.4\lib\nunit.framework.dll - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - - - {A9D99F80-FEEB-4E74-96C5-66F17103C773} - PushSharp.Apple - - - {2468C63B-C964-4FC3-9B16-13DC17CF7D11} - PushSharp.Amazon - - - {94F16497-471F-433F-A99E-C455FB2D7724} - PushSharp.Google - - - {DC80552B-6730-44AA-9B74-1E036BD909C3} - PushSharp.Windows - - + - - + + - + - + + + + + + + - \ No newline at end of file + + diff --git a/PushSharp.Tests/Servers/TestApnsServer.cs b/PushSharp.Tests/Servers/TestApnsServer.cs index 1be38243..8c086d54 100644 --- a/PushSharp.Tests/Servers/TestApnsServer.cs +++ b/PushSharp.Tests/Servers/TestApnsServer.cs @@ -1,11 +1,11 @@ using System; -using System.Threading.Tasks; -using System.Net.Sockets; -using System.Net; using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; -namespace PushSharp.Tests +namespace PushSharp.Tests.Servers { public class TestApnsServer { diff --git a/PushSharp.Tests/TestServiceConnection.cs b/PushSharp.Tests/TestServiceConnection.cs index 628dac9c..4ac3a0fd 100644 --- a/PushSharp.Tests/TestServiceConnection.cs +++ b/PushSharp.Tests/TestServiceConnection.cs @@ -1,7 +1,7 @@ using System; using System.Linq; -using PushSharp.Core; using System.Threading.Tasks; +using PushSharp.Common; namespace PushSharp.Tests { diff --git a/PushSharp.Tests/apns-com.pushsharp.sample.p12 b/PushSharp.Tests/apns-com.pushsharp.sample.p12 deleted file mode 100644 index 49e2c9b345857126b2cb0ae5d7069afd3a19b6cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3319 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 diff --git a/PushSharp.Tests/packages.config b/PushSharp.Tests/packages.config deleted file mode 100644 index 3b829b9f..00000000 --- a/PushSharp.Tests/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/PushSharp.Tests/settings.sample.json b/PushSharp.Tests/settings.sample.json deleted file mode 100644 index b4994bbe..00000000 --- a/PushSharp.Tests/settings.sample.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "apns_cert_file" : "", - "apns_cert_pwd" : "", - "apns_device_tokens" : - [ - "", - ], - - "gcm_auth_token" : "", - "gcm_sender_id" : "", - "gcm_registration_ids" : - [ - "", - ], - - "adm_client_id" : "", - "adm_client_secret" : "", - "adm_registration_ids" : - [ - "", - ], -} diff --git a/PushSharp.Windows/Exceptions.cs b/PushSharp.Windows/Exceptions.cs index d3d39ded..284984ae 100644 --- a/PushSharp.Windows/Exceptions.cs +++ b/PushSharp.Windows/Exceptions.cs @@ -1,5 +1,5 @@ using System; -using PushSharp.Core; +using PushSharp.Common; namespace PushSharp.Windows { diff --git a/PushSharp.Windows/Properties/AssemblyInfo.cs b/PushSharp.Windows/Properties/AssemblyInfo.cs deleted file mode 100644 index 61efcba9..00000000 --- a/PushSharp.Windows/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("PushSharp.Windows")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/PushSharp.Windows/PushSharp.Windows.csproj b/PushSharp.Windows/PushSharp.Windows.csproj index e85dc90b..423e97be 100644 --- a/PushSharp.Windows/PushSharp.Windows.csproj +++ b/PushSharp.Windows/PushSharp.Windows.csproj @@ -1,60 +1,19 @@ - - + + - Debug - AnyCPU - {DC80552B-6730-44AA-9B74-1E036BD909C3} - Library - PushSharp.Windows - PushSharp.Windows - v4.5 - true - ..\PushSharp-Signing.snk - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false + netstandard2.0;net45; + - - - - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - + - - - - - - - - - - - - - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E} - PushSharp.Core - + + + + - + - \ No newline at end of file + + diff --git a/PushSharp.Windows/WnsConnection.cs b/PushSharp.Windows/WnsConnection.cs index 6ab72c43..b4328b5c 100644 --- a/PushSharp.Windows/WnsConnection.cs +++ b/PushSharp.Windows/WnsConnection.cs @@ -1,5 +1,4 @@ using System; -using PushSharp.Core; using System.Threading.Tasks; using System.Net.Http; using System.Net.Http.Headers; @@ -10,6 +9,7 @@ using System.Net; using System.Text; using System.IO; +using PushSharp.Common; namespace PushSharp.Windows { diff --git a/PushSharp.Windows/WnsNotification.cs b/PushSharp.Windows/WnsNotification.cs index a8a16dca..9b6e1bf9 100644 --- a/PushSharp.Windows/WnsNotification.cs +++ b/PushSharp.Windows/WnsNotification.cs @@ -1,5 +1,5 @@ -using PushSharp.Core; -using System.Xml.Linq; +using System.Xml.Linq; +using PushSharp.Common; namespace PushSharp.Windows { diff --git a/PushSharp.Windows/WnsTokenAccessManager.cs b/PushSharp.Windows/WnsTokenAccessManager.cs index 8e2c9908..92d4346e 100644 --- a/PushSharp.Windows/WnsTokenAccessManager.cs +++ b/PushSharp.Windows/WnsTokenAccessManager.cs @@ -1,9 +1,9 @@ using System; using System.Threading.Tasks; using System.Net.Http; -using PushSharp.Core; using System.Collections.Generic; using Newtonsoft.Json.Linq; +using PushSharp.Common; namespace PushSharp.Windows { diff --git a/PushSharp.Windows/packages.config b/PushSharp.Windows/packages.config deleted file mode 100644 index 505e5883..00000000 --- a/PushSharp.Windows/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/PushSharp.nuspec b/PushSharp.nuspec index ab2b8a80..28f3c692 100644 --- a/PushSharp.nuspec +++ b/PushSharp.nuspec @@ -1,37 +1,41 @@ - PushSharp - $version$ - PushSharp - Redth - Redth + PushSharp.Core + 1.0.0 + PushSharp.Core + Redth, mitch-tofi + Redth, mitch-tofi http://www.apache.org/licenses/LICENSE-2.0.txt - https://github.com/Redth/PushSharp/ - https://github.com/Redth/PushSharp/raw/master/Resources/PushSharp-Icon-NuGet-Small.png + https://github.com/mitch-tofi/PushSharp.Core + https://github.com/mitch-tofi/PushSharp.Core/raw/master/Resources/PushSharp-Icon-NuGet-Small.png false A server-side library for sending Push Notifications to iOS/OSX (APNS), Android/Chrome (GCM), Windows Phone/Windows (WNS), Amazon (ADM), Blackberry and Firefox OS A server-side library for sending Push Notifications to many platforms (APNS, GCM, WNS, ADM) - 3.0.0 - Complete Rewrite, New API, see project site for more info + 1.0.0 - Forked from PushSharp and added support NetStandard 2.0 - 2012-2016 Redth + 2012-2017 Redth, mitch-tofi en-US APN, APNS, GCM, XMPP, ADM, GCMXMPP, C2DM, PAP, BIS, BEM, Blackberry, iOS, Android, iPad, iPhone, PushSharp, MDM, UWP, WNS, Windows Notification, Amazon, Amazon Device, Push, Push Notifications, Google Cloud, Cloud Messaging, Google Cloud Messaging, OSX, Mac, WindowsPhone, PassKit - + - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PushSharp.sln b/PushSharp.sln deleted file mode 100644 index d96c467d..00000000 --- a/PushSharp.sln +++ /dev/null @@ -1,71 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Core", "PushSharp.Core\PushSharp.Core.csproj", "{2B44A8DA-60BC-4577-A2D7-D9D53F164B2E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Tests", "PushSharp.Tests\PushSharp.Tests.csproj", "{989B7357-800E-46B9-91AF-A4CE8A55F389}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platforms", "Platforms", "{2B7243CB-60A5-4682-802B-B7EC3DEBCF9A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Apple", "PushSharp.Apple\PushSharp.Apple.csproj", "{A9D99F80-FEEB-4E74-96C5-66F17103C773}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Windows", "PushSharp.Windows\PushSharp.Windows.csproj", "{DC80552B-6730-44AA-9B74-1E036BD909C3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Firefox", "PushSharp.Firefox\PushSharp.Firefox.csproj", "{54A4C1F9-6571-4086-BB4B-EC202138AF00}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Amazon", "PushSharp.Amazon\PushSharp.Amazon.csproj", "{2468C63B-C964-4FC3-9B16-13DC17CF7D11}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Google", "PushSharp.Google\PushSharp.Google.csproj", "{94F16497-471F-433F-A99E-C455FB2D7724}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PushSharp.Blackberry", "PushSharp.Blackberry\PushSharp.Blackberry.csproj", "{9F972AE9-DE47-4C26-AEE0-97E8A14F2E12}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Support Libraries", "Support Libraries", "{6449DAB1-E76A-4354-B633-CC6AC53BB757}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2468C63B-C964-4FC3-9B16-13DC17CF7D11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2468C63B-C964-4FC3-9B16-13DC17CF7D11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2468C63B-C964-4FC3-9B16-13DC17CF7D11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2468C63B-C964-4FC3-9B16-13DC17CF7D11}.Release|Any CPU.Build.0 = Release|Any CPU - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2B44A8DA-60BC-4577-A2D7-D9D53F164B2E}.Release|Any CPU.Build.0 = Release|Any CPU - {54A4C1F9-6571-4086-BB4B-EC202138AF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54A4C1F9-6571-4086-BB4B-EC202138AF00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54A4C1F9-6571-4086-BB4B-EC202138AF00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54A4C1F9-6571-4086-BB4B-EC202138AF00}.Release|Any CPU.Build.0 = Release|Any CPU - {94F16497-471F-433F-A99E-C455FB2D7724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {94F16497-471F-433F-A99E-C455FB2D7724}.Debug|Any CPU.Build.0 = Debug|Any CPU - {94F16497-471F-433F-A99E-C455FB2D7724}.Release|Any CPU.ActiveCfg = Release|Any CPU - {94F16497-471F-433F-A99E-C455FB2D7724}.Release|Any CPU.Build.0 = Release|Any CPU - {989B7357-800E-46B9-91AF-A4CE8A55F389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {989B7357-800E-46B9-91AF-A4CE8A55F389}.Debug|Any CPU.Build.0 = Debug|Any CPU - {989B7357-800E-46B9-91AF-A4CE8A55F389}.Release|Any CPU.ActiveCfg = Release|Any CPU - {989B7357-800E-46B9-91AF-A4CE8A55F389}.Release|Any CPU.Build.0 = Release|Any CPU - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12}.Release|Any CPU.Build.0 = Release|Any CPU - {A9D99F80-FEEB-4E74-96C5-66F17103C773}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D99F80-FEEB-4E74-96C5-66F17103C773}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9D99F80-FEEB-4E74-96C5-66F17103C773}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9D99F80-FEEB-4E74-96C5-66F17103C773}.Release|Any CPU.Build.0 = Release|Any CPU - {DC80552B-6730-44AA-9B74-1E036BD909C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DC80552B-6730-44AA-9B74-1E036BD909C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DC80552B-6730-44AA-9B74-1E036BD909C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DC80552B-6730-44AA-9B74-1E036BD909C3}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {A9D99F80-FEEB-4E74-96C5-66F17103C773} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - {DC80552B-6730-44AA-9B74-1E036BD909C3} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - {54A4C1F9-6571-4086-BB4B-EC202138AF00} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - {2468C63B-C964-4FC3-9B16-13DC17CF7D11} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - {94F16497-471F-433F-A99E-C455FB2D7724} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - {9F972AE9-DE47-4C26-AEE0-97E8A14F2E12} = {2B7243CB-60A5-4682-802B-B7EC3DEBCF9A} - EndGlobalSection -EndGlobal From 5fed31711fd63f8e6fc0b89fc68dbcf837daf555 Mon Sep 17 00:00:00 2001 From: Caezary Date: Fri, 21 Sep 2018 02:35:46 +0200 Subject: [PATCH 2/5] add APNS and GCM proxy support (#1) * Update PushShart using a Proxy when connecting to Google Cloud Messaging (GCM) * Options for Proxy Usage Adds properties and methods to use an HTTP-proxy in your connection, either with default network credentials or specified credentials. * Connection via proxy A method to tunnel the TCP-connection via a pre-configured HTTP-Proxy has been added. If Configuration.UseProxy is set to true, a CONNECT-request is sent using the proxy adress specified. The socket is then extracted from the stream of this request's response (using the System.Reflection-Library) and used to create a TcpClient-object. All traffic sent through this TcpClient-object will pass through the proxy used to send the initial CONNECT-Request. NOTE: The method 'getClientViaHTTPProxy' uses code that was not written by the author of this commit. The original code can be found here: https://web.archive.org/web/20160317134733/https://nitormobiledevelopment.wordpress.com/2013/08/13/push-sharp-using-proxy/ * Update ApnsConnection.cs * Update ApnsConfiguration.cs * apns wit proxy withou socket hijack * Handling proxy connection errors on APNS * Using System.Net missing in GcmConfiguration * Http Proxy with Basic authentication without WebRequest in APNS * System.Net was missing in GcmConfiguration * Using http proxy with basic auth (not using socket hijacking) in APNS * New class ProxyHelper to permit proxy connect in any place using a TcpClient * Using ProxyHelper in ApnsConnection * Supporting proxy in ApnsFeedbackService. * Changes in ApnsFeedbackService to return the tokens list intead of raising events * add proxy usage section to readme --- PushSharp.Apple/ApnsConfiguration.cs | 32 +++++++-- PushSharp.Apple/ApnsConnection.cs | 86 ++++++++++++++++------ PushSharp.Apple/ApnsFeedbackService.cs | 90 +++++++++++++++++------- PushSharp.Apple/PushSharp.Apple.csproj | 1 - PushSharp.Common/ProxyHelper.cs | 86 ++++++++++++++++++++++ PushSharp.Google/GcmConfiguration.cs | 25 +++++++ PushSharp.Google/GcmServiceConnection.cs | 36 +++++++++- README.md | 19 +++++ 8 files changed, 321 insertions(+), 54 deletions(-) create mode 100644 PushSharp.Common/ProxyHelper.cs diff --git a/PushSharp.Apple/ApnsConfiguration.cs b/PushSharp.Apple/ApnsConfiguration.cs index 464a5e62..6a16a870 100644 --- a/PushSharp.Apple/ApnsConfiguration.cs +++ b/PushSharp.Apple/ApnsConfiguration.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; @@ -82,7 +83,7 @@ void Initialize (ApnsServerEnvironment serverEnvironment, X509Certificate2 certi FeedbackIntervalMinutes = 10; FeedbackTimeIsUTC = false; - + AdditionalCertificates = new List (); AddLocalAndMachineCertificateStores = false; @@ -100,7 +101,6 @@ void Initialize (ApnsServerEnvironment serverEnvironment, X509Certificate2 certi InternalBatchFailureRetryCount = 1; } - void CheckIsApnsCertificate () { if (Certificate != null) { @@ -136,6 +136,22 @@ public void OverrideFeedbackServer (string host, int port) FeedbackPort = port; } + public void SetProxy(string proxyHost, int proxyPort) + { + UseProxy = true; + ProxyHost = proxyHost; + ProxyPort = proxyPort; + ProxyCredentials = CredentialCache.DefaultNetworkCredentials; + } + + public void SetProxy(string proxyHost, int proxyPort, string userName, string password, string domain) + { + UseProxy = true; + ProxyHost = proxyHost; + ProxyPort = proxyPort; + ProxyCredentials = new NetworkCredential(userName, password, domain); + } + public string Host { get; private set; } public int Port { get; private set; } @@ -144,6 +160,14 @@ public void OverrideFeedbackServer (string host, int port) public int FeedbackPort { get; private set; } + public bool UseProxy { get; private set; } + + public string ProxyHost { get; private set; } + + public int ProxyPort { get; private set; } + + public NetworkCredential ProxyCredentials { get; private set; } + public X509Certificate2 Certificate { get; private set; } public List AdditionalCertificates { get; private set; } @@ -205,4 +229,4 @@ public enum ApnsServerEnvironment { Production } } -} \ No newline at end of file +} diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index d516e65b..8bb0d1c9 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -7,7 +7,11 @@ using System.Collections.Generic; using System.Threading; using System.Net; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; using PushSharp.Common; +using System.Diagnostics; namespace PushSharp.Apple { @@ -68,7 +72,6 @@ public ApnsConnection (ApnsConfiguration configuration) byte[] buffer = new byte[6]; int id; - SemaphoreSlim connectingSemaphore = new SemaphoreSlim (1); SemaphoreSlim batchSendSemaphore = new SemaphoreSlim (1); object notificationBatchQueueLock = new object (); @@ -127,28 +130,37 @@ async Task SendBatch () Log.Info ("APNS-Client[{0}]: Sending Batch ID={1}, Count={2}", id, batchId, toSend.Count); - try { + try + { var data = createBatch (toSend); - if (data != null && data.Length > 0) { + if (data != null && data.Length > 0) + { - for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) { + for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++) + { await connectingSemaphore.WaitAsync (); - try { + try + { // See if we need to connect if (!socketCanWrite () || i > 0) await connect (); - } finally { + } + finally + { connectingSemaphore.Release (); } - try { + try + { await networkStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); break; - } catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) { + } + catch (Exception ex) when (i != Configuration.InternalBatchFailureRetryCount) + { Log.Info("APNS-CLIENT[{0}]: Retrying Batch: Batch ID={1}, Error={2}", id, batchId, ex); } } @@ -156,11 +168,18 @@ async Task SendBatch () foreach (var n in toSend) sent.Add (new SentNotification (n)); } - - } catch (Exception ex) { + } + catch (ApnsConnectionException ex) + { Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); foreach (var n in toSend) n.CompleteFailed (new ApnsNotificationException (ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); + return; + } + catch (Exception ex) { + Log.Error ("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex); + foreach (var n in toSend) + n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex)); } Log.Info ("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id); @@ -239,7 +258,7 @@ async Task Reader () sent.Clear (); return; } - + // If we make it here, we did get data back, so we have errors Log.Info ("APNS-Client[{0}]: Batch (ID={1}) completed with error response...", id, batchId); @@ -321,25 +340,48 @@ async Task connect () Log.Info ("APNS-Client[{0}]: Connecting (Batch ID={1})", id, batchId); - client = new TcpClient (); + client = new TcpClient(); - try { - await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); + try + { + if (!Configuration.UseProxy) + { + await client.ConnectAsync (Configuration.Host, Configuration.Port).ConfigureAwait (false); + } + else + { + var proxyHelper = new ProxyHelper { ProxyConnectionExceptionCreator = (message) => new ApnsConnectionException(message) }; + proxyHelper.BeforeConnect += () => Log.Info("APNS-Client[{0}]: Connecting Proxy (Batch ID={1})", id, batchId); + proxyHelper.AfterConnect += (status) => Log.Info("APNS-Client[{0}]: Proxy Connected (Batch ID={1}) : {2}", id, batchId, status); + await proxyHelper.Connect(client, Configuration.Host, Configuration.Port, Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials).ConfigureAwait(false); + } //Set keep alive on the socket may help maintain our APNS connection - try { - client.Client.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - } catch { + try + { + client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); + } + catch + { } //Really not sure if this will work on MONO.... // This may help windows azure users - try { - SetSocketKeepAliveValues (client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); - } catch { + try + { + SetSocketKeepAliveValues(client.Client, (int)Configuration.KeepAlivePeriod.TotalMilliseconds, (int)Configuration.KeepAliveRetryPeriod.TotalMilliseconds); } - } catch (Exception ex) { - throw new ApnsConnectionException ("Failed to Connect, check your firewall settings!", ex); + catch + { + } + } + catch (ApnsConnectionException) + { + throw; + } + catch (Exception ex) + { + throw new ApnsConnectionException("Failed to Connect, check your firewall settings!", ex); } // We can configure skipping ssl all together, ie: if we want to hit a test server diff --git a/PushSharp.Apple/ApnsFeedbackService.cs b/PushSharp.Apple/ApnsFeedbackService.cs index 57fb887a..53659ac9 100644 --- a/PushSharp.Apple/ApnsFeedbackService.cs +++ b/PushSharp.Apple/ApnsFeedbackService.cs @@ -8,6 +8,7 @@ using System.Net.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using PushSharp.Common; namespace PushSharp.Apple { @@ -24,6 +25,11 @@ public FeedbackService (ApnsConfiguration configuration) public event FeedbackReceivedDelegate FeedbackReceived; public void Check () + { + this.GetTokenExpirations(); + + } + public IEnumerable GetTokenExpirations() { var encoding = Encoding.ASCII; @@ -32,9 +38,22 @@ public void Check () var certificates = new X509CertificateCollection(); certificates.Add(certificate); - var client = new TcpClient (Configuration.FeedbackHost, Configuration.FeedbackPort); + var client = new TcpClient(); + Log.Info("APNS-FeedbackService: Connecting"); + if (Configuration.UseProxy) + { + var proxyHelper = new ProxyHelper { ProxyConnectionExceptionCreator = (message) => new ApnsConnectionException(message) }; + proxyHelper.BeforeConnect += () => Log.Info("APNS-FeedbackService: Connecting Proxy"); + proxyHelper.AfterConnect += (status) => Log.Info("APNS-FeedbackService: Proxy Connected : {0}", status); + proxyHelper.Connect(client, Configuration.FeedbackHost, Configuration.FeedbackPort, Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials).Wait(); + } + else + { + client.Connect(Configuration.FeedbackHost, Configuration.FeedbackPort); + } + Log.Info("APNS-FeedbackService: Connected"); - var stream = new SslStream (client.GetStream(), true, + var stream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslErrs) => { return true; }, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => { return certificate; }); @@ -44,32 +63,35 @@ public void Check () //Set up byte[] buffer = new byte[4096]; int recd = 0; - var data = new List (); + var data = new List(); + + Log.Info("APNS-FeedbackService: Getting expirations"); //Get the first feedback recd = stream.Read(buffer, 0, buffer.Length); + var tokenBatch = new List(); //Continue while we have results and are not disposing while (recd > 0) { // Add the received data to a list buffer to work with (easier to manipulate) for (int i = 0; i < recd; i++) - data.Add (buffer [i]); - + data.Add(buffer[i]); + //Process each complete notification "packet" available in the buffer while (data.Count >= (4 + 2 + 32)) // Minimum size for a valid packet { - var secondsBuffer = data.GetRange (0, 4).ToArray (); - var tokenLengthBuffer = data.GetRange (4, 2).ToArray (); + var secondsBuffer = data.GetRange(0, 4).ToArray(); + var tokenLengthBuffer = data.GetRange(4, 2).ToArray(); // Get our seconds since epoch // Check endianness and reverse if needed if (BitConverter.IsLittleEndian) - Array.Reverse (secondsBuffer); - var seconds = BitConverter.ToInt32 (secondsBuffer, 0); + Array.Reverse(secondsBuffer); + var seconds = BitConverter.ToInt32(secondsBuffer, 0); //Add seconds since 1970 to that date, in UTC - var timestamp = new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds (seconds); + var timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds); //flag to allow feedback times in UTC or local, but default is local if (!Configuration.FeedbackTimeIsUTC) @@ -77,50 +99,68 @@ public void Check () if (BitConverter.IsLittleEndian) - Array.Reverse (tokenLengthBuffer); - var tokenLength = BitConverter.ToInt16 (tokenLengthBuffer, 0); + Array.Reverse(tokenLengthBuffer); + var tokenLength = BitConverter.ToInt16(tokenLengthBuffer, 0); - if (data.Count >= 4 + 2 + tokenLength) { + if (data.Count >= 4 + 2 + tokenLength) + { - var tokenBuffer = data.GetRange (6, tokenLength).ToArray (); + var tokenBuffer = data.GetRange(6, tokenLength).ToArray(); // Strings shouldn't care about endian-ness... this shouldn't be reversed //if (BitConverter.IsLittleEndian) // Array.Reverse (tokenBuffer); - var token = BitConverter.ToString (tokenBuffer).Replace ("-", "").ToLower ().Trim (); + var token = BitConverter.ToString(tokenBuffer).Replace("-", "").ToLower().Trim(); // Remove what we parsed from the buffer - data.RemoveRange (0, 4 + 2 + tokenLength); + data.RemoveRange(0, 4 + 2 + tokenLength); + tokenBatch.Add(new ApnsTokeExpirationInfo(token, timestamp)); // Raise the event to the consumer var evt = FeedbackReceived; if (evt != null) - evt (token, timestamp); - } else { + evt(token, timestamp); + + } + else + { continue; } - } //Read the next feedback - recd = stream.Read (buffer, 0, buffer.Length); + recd = stream.Read(buffer, 0, buffer.Length); } try { - stream.Close (); + stream.Close(); stream.Dispose(); } catch { } - try + try { - client.Client.Shutdown (SocketShutdown.Both); - client.Client.Dispose (); + client.Client.Shutdown(SocketShutdown.Both); + client.Client.Dispose(); } catch { } - try { client.Close (); } catch { } + try { client.Close(); } catch { } + Log.Info("APNS-FeedbackService: {0} expiration(s) received.", tokenBatch.Count); + return tokenBatch; } } + + public class ApnsTokeExpirationInfo + { + public ApnsTokeExpirationInfo(string token, DateTime timestamp) + { + this.Token = token; + this.Timestamp = timestamp; + } + + public string Token { get; private set; } + public DateTime Timestamp { get; private set; } + } } diff --git a/PushSharp.Apple/PushSharp.Apple.csproj b/PushSharp.Apple/PushSharp.Apple.csproj index 7ab6804d..e87e650f 100644 --- a/PushSharp.Apple/PushSharp.Apple.csproj +++ b/PushSharp.Apple/PushSharp.Apple.csproj @@ -1,5 +1,4 @@ - netstandard2.0;net45; PushSharp.Apple diff --git a/PushSharp.Common/ProxyHelper.cs b/PushSharp.Common/ProxyHelper.cs new file mode 100644 index 00000000..3a55051a --- /dev/null +++ b/PushSharp.Common/ProxyHelper.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace PushSharp.Common +{ + public delegate void BeforeConnectDelegate(); + + public delegate void AfterConnectDelegate(string status); + + public class ProxyHelper + { + public Func ProxyConnectionExceptionCreator { get; set; } + public event BeforeConnectDelegate BeforeConnect; + public event AfterConnectDelegate AfterConnect; + + public async Task Connect(TcpClient client, string targetHost, int targetPort, string proxyHost, int proxyPort, NetworkCredential proxyCredential = null) + { + this.OnBeforeConnect(); + await client.ConnectAsync(proxyHost, proxyPort).ConfigureAwait(false); + var stream = client.GetStream(); + var authorization = string.Empty; + if (proxyCredential != null) + { + var credential = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{proxyCredential.UserName}:{proxyCredential.Password}")); + authorization = $"\r\nAuthorization: Basic {credential}"; + } + var buffer = + Encoding.UTF8.GetBytes( + string.Format("CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}{2}\r\nProxy-Connection: keep-alive\r\n\r\n", targetHost, targetPort, authorization)); + await stream.WriteAsync(buffer, 0, buffer.Length); + await stream.FlushAsync(); + buffer = new byte[client.Client.ReceiveBufferSize]; + using (var resp = new MemoryStream()) + { + do + { + var len = await stream.ReadAsync(buffer, 0, buffer.Length); + resp.Write(buffer, 0, len); + } + while (client.Client.Available > 0); + buffer = resp.ToArray(); + } + var content = Encoding.UTF8.GetString(buffer); + var i = content.IndexOf('\r'); + string statusCode; + if (i > 9) + { + statusCode = content.Substring(9, i - 9); + } + else + { + var max = content.Length; + if (max > 50) + { + max = 50; + } + var append = max == 50 ? "..." : string.Empty; + statusCode = content.Substring(0, max) + append; + } + this.OnAfterConnect(statusCode); + if (!statusCode.StartsWith("200")) + { + var fac = this.ProxyConnectionExceptionCreator; + if (fac != null) + { + throw fac($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); + } + throw new Exception($"Proxy returned {statusCode}. Check proxy settings and if it allows ssl through ports different of 443."); + } + } + + protected virtual void OnBeforeConnect() + { + this.BeforeConnect?.Invoke(); + } + + protected virtual void OnAfterConnect(string status) + { + this.AfterConnect?.Invoke(status); + } + } +} \ No newline at end of file diff --git a/PushSharp.Google/GcmConfiguration.cs b/PushSharp.Google/GcmConfiguration.cs index 91f320ff..f0939105 100644 --- a/PushSharp.Google/GcmConfiguration.cs +++ b/PushSharp.Google/GcmConfiguration.cs @@ -1,4 +1,5 @@ using System; +using System.Net; namespace PushSharp.Google { @@ -24,6 +25,30 @@ public GcmConfiguration (string optionalSenderID, string senderAuthToken, string this.ValidateServerCertificate = false; } + public void SetProxy(string host, int port) + { + UseProxy = true; + ProxyHost = host; + ProxyPort = port; + ProxyCredentials = CredentialCache.DefaultNetworkCredentials; + } + + public void SetProxy(string host, int port, string username, string pass, string domain) + { + UseProxy = true; + ProxyHost = host; + ProxyPort = port; + ProxyCredentials = new NetworkCredential(username, pass, domain); + } + + public bool UseProxy { get; private set; } + + public string ProxyHost { get; private set; } + + public int ProxyPort { get; private set; } + + public NetworkCredential ProxyCredentials { get; private set; } + public string SenderID { get; private set; } public string SenderAuthToken { get; private set; } diff --git a/PushSharp.Google/GcmServiceConnection.cs b/PushSharp.Google/GcmServiceConnection.cs index 78ce77e8..299ec9c4 100644 --- a/PushSharp.Google/GcmServiceConnection.cs +++ b/PushSharp.Google/GcmServiceConnection.cs @@ -38,8 +38,14 @@ public class GcmServiceConnection : IServiceConnection public GcmServiceConnection (GcmConfiguration configuration) { Configuration = configuration; - http = new HttpClient (); - + if (Configuration.UseProxy) + { + http = GetClientHTTPProxy(Configuration.ProxyHost, Configuration.ProxyPort, Configuration.ProxyCredentials); + } + else + { + http = new HttpClient(); + } http.DefaultRequestHeaders.UserAgent.Clear (); http.DefaultRequestHeaders.UserAgent.Add (new ProductInfoHeaderValue ("PushSharp", "3.0")); http.DefaultRequestHeaders.TryAddWithoutValidation ("Authorization", "key=" + Configuration.SenderAuthToken); @@ -212,5 +218,31 @@ static GcmResponseStatus GetGcmResponseStatus (string str) //Default return GcmResponseStatus.Error; } + + private HttpClient GetClientHTTPProxy(string host, int port, NetworkCredential credentials) + { + HttpClient client = null; + try + { + string proxyUri = string.Format("{0}:{1}", host, port); + WebProxy proxy = new WebProxy(proxyUri, false) + { + Credentials = credentials + }; + HttpClientHandler httpClientHandler = new HttpClientHandler() + { + Proxy = proxy, + PreAuthenticate = false + }; + client = new HttpClient(httpClientHandler); + } + catch (Exception ex) + { + client = null; + Log.Error(ex.Message); + throw; + } + return client; + } } } diff --git a/README.md b/README.md index b3ae1f9b..0a046c62 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,25 @@ foreach (var uri in MY_DEVICE_CHANNEL_URIS) { wnsBroker.Stop (); ``` +### Use a Proxy with APNS or GCM + +To configure a proxy call `SetProxy` method on the appropriate configuration object. + +```csharp +// create ApnsConfiguration or GcmConfiguration as needed +var config = new ApnsConfiguration (ApnsConfiguration.ApnsServerEnvironment.Sandbox, + "push-cert.p12", "push-cert-pwd"); + +// set up a proxy +if (useProxy) +{ + config.SetProxy(proxyAddress, proxyPort); +} + +// continue as in the examples above +// ... +``` + ## How to Migrate from PushSharp 2.x to 3.x and higher From 28ec3eba5228f68753e7e8b008ceb3e0ceb2b95c Mon Sep 17 00:00:00 2001 From: rvidis Date: Sat, 22 Sep 2018 00:52:25 +0100 Subject: [PATCH 3/5] Update readme - syntax error (#2) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a046c62..d77d1179 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ gcmBroker.OnNotificationFailed += (notification, aggregateEx) => { Console.WriteLine ($"Device RegistrationId Expired: {oldId}"); - if (!string.IsNullOrWhitespace (newId)) { + if (!string.IsNullOrWhiteSpace (newId)) { // If this value isn't null, our subscription changed and we should update our database Console.WriteLine ($"Device RegistrationId Changed To: {newId}"); } From cd7a81da555d6841b680d4f7a1a935279f47ab76 Mon Sep 17 00:00:00 2001 From: Roman <56258775+rmidyk-cashplus@users.noreply.github.com> Date: Tue, 21 Jan 2020 02:06:12 +0200 Subject: [PATCH 4/5] APNS - add support of TLS1.1 and TLS1.2 (#3) --- PushSharp.Apple/ApnsConnection.cs | 3 ++- PushSharp.Apple/ApnsFeedbackService.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PushSharp.Apple/ApnsConnection.cs b/PushSharp.Apple/ApnsConnection.cs index 8bb0d1c9..3d65c18f 100644 --- a/PushSharp.Apple/ApnsConnection.cs +++ b/PushSharp.Apple/ApnsConnection.cs @@ -396,7 +396,8 @@ async Task connect () (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => certificate); try { - stream.AuthenticateAsClient (Configuration.Host, certificates, System.Security.Authentication.SslProtocols.Tls, false); + var tls = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12; + stream.AuthenticateAsClient (Configuration.Host, certificates, tls, false); } catch (System.Security.Authentication.AuthenticationException ex) { throw new ApnsConnectionException ("SSL Stream Failed to Authenticate as Client", ex); } diff --git a/PushSharp.Apple/ApnsFeedbackService.cs b/PushSharp.Apple/ApnsFeedbackService.cs index 53659ac9..d238d097 100644 --- a/PushSharp.Apple/ApnsFeedbackService.cs +++ b/PushSharp.Apple/ApnsFeedbackService.cs @@ -57,7 +57,8 @@ public IEnumerable GetTokenExpirations() (sender, cert, chain, sslErrs) => { return true; }, (sender, targetHost, localCerts, remoteCert, acceptableIssuers) => { return certificate; }); - stream.AuthenticateAsClient(Configuration.FeedbackHost, certificates, System.Security.Authentication.SslProtocols.Tls, false); + var tls = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12; + stream.AuthenticateAsClient(Configuration.FeedbackHost, certificates, tls, false); //Set up From 5e42dbe7667eab269df64636c4734533fc9c1851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BD=D0=B8=D0=BD=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Thu, 4 Mar 2021 13:34:03 +0300 Subject: [PATCH 5/5] Update GcmConfiguration.cs (#4) https://gcm-http.googleapis.com/gcm/send is Error=DeprecatedEndpoint --- PushSharp.Google/GcmConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PushSharp.Google/GcmConfiguration.cs b/PushSharp.Google/GcmConfiguration.cs index f0939105..31845726 100644 --- a/PushSharp.Google/GcmConfiguration.cs +++ b/PushSharp.Google/GcmConfiguration.cs @@ -5,7 +5,7 @@ namespace PushSharp.Google { public class GcmConfiguration { - private const string GCM_SEND_URL = "https://gcm-http.googleapis.com/gcm/send"; + private const string GCM_SEND_URL = "https://fcm.googleapis.com/fcm/send"; public GcmConfiguration (string senderAuthToken) {