├── .gitignore
├── JdSoft.Apple.Apns.Feedback.Test
├── JdSoft.Apple.Apns.Feedback.Test.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
├── JdSoft.Apple.Apns.Feedback
├── Feedback.cs
├── FeedbackService.cs
├── JdSoft.Apple.Apns.Feedback.csproj
└── Properties
│ └── AssemblyInfo.cs
├── JdSoft.Apple.Apns.Notifications.JsonTest
├── AssemblyInfo.cs
├── JdSoft.Apple.Apns.Notifications.JsonTest.csproj
├── JdSoft.Apple.Apns.Notifications.JsonTest.pidb
└── Main.cs
├── JdSoft.Apple.Apns.Notifications.Test
├── JdSoft.Apple.Apns.Notifications.Test.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
├── JdSoft.Apple.Apns.Notifications
├── BadDeviceTokenException.cs
├── JdSoft.Apple.Apns.Notifications.csproj
├── Notification.cs
├── NotificationAlert.cs
├── NotificationBatchException.cs
├── NotificationChannel.cs
├── NotificationConnection.cs
├── NotificationDeliveryError.cs
├── NotificationException.cs
├── NotificationLengthException.cs
├── NotificationPayload.cs
├── NotificationService.cs
├── NotificationServiceDistributionType.cs
├── Properties
│ └── AssemblyInfo.cs
├── ThreadSafeQueue.cs
└── packages.config
├── JdSoft.Apple.Apns.sln
├── JdSoft.Apple.Apns.vsmdi
├── JdSoft.Apple.AppStore.Test
├── JdSoft.Apple.AppStore.Test.csproj
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── Resources.resources
├── frmReceiptData.Designer.cs
├── frmReceiptData.cs
├── frmReceiptData.resources
└── frmReceiptData.resx
├── JdSoft.Apple.AppStore
├── JdSoft.Apple.AppStore.csproj
├── Properties
│ └── AssemblyInfo.cs
├── Receipt.cs
├── ReceiptVerification.cs
└── packages.config
├── Local.testsettings
├── README.md
├── References
└── Newtonsoft.Json.Compact.dll
├── Tests.Notifications
├── Properties
│ └── AssemblyInfo.cs
├── Tests.Notifications.csproj
└── UnitTest1.cs
├── TraceAndTestImpact.testsettings
└── nuget
├── apns-icon.png
├── apns-sharp.1.0.4.1.nupkg
└── apns-sharp.1.0.4.3.nupkg
/.gitignore:
--------------------------------------------------------------------------------
1 | debug/*
2 | release/*
3 | Bin
4 | Bin/*
5 | obj/*
6 | *.csuser
7 | *.suo
8 | *.ReSharper
9 | *.user
10 | *.tmp
11 | *.db
12 | *.orig
13 | *.bak
14 | *.zip
15 | *.rar
16 | .hg/*
17 | bin
18 | bin/*
19 | obj
20 | TestResults/*
21 | Logs
22 | Logs/*
23 | ErrorLogs
24 | ErrorLogs/*
25 | packages
26 | packages/*
27 | _ReSharper*
28 | _ReSharper*/*
29 | *.pidb
30 | App_Data/HelpIndex
31 | *.userprefs
32 | AutoBidLogs
33 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback.Test/JdSoft.Apple.Apns.Feedback.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {46CAEA10-404B-4E0A-B1A0-C1B128B55A8C}
9 | Exe
10 | Properties
11 | JdSoft.Apple.Apns.Feedback.Test
12 | JdSoft.Apple.Apns.Feedback.Test
13 | v3.5
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {B87C2903-2B6B-49FF-B735-630A5CB69521}
50 | JdSoft.Apple.Apns.Feedback
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback.Test/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using JdSoft.Apple.Apns.Feedback;
5 |
6 | namespace JdSoft.Apple.Apns.Feedback.Test
7 | {
8 | class Program
9 | {
10 | [STAThread]
11 | static void Main(string[] args)
12 | {
13 | //Variables you may need to edit:
14 | //---------------------------------
15 |
16 | //True if you are using sandbox certificate, or false if using production
17 | bool sandbox = true;
18 |
19 | //Put your PKCS12 .p12 or .pfx filename here.
20 | // Assumes it is in the same directory as your app
21 | string p12File = "apn_developer_identity.p12";
22 |
23 | //This is the password that you protected your p12File
24 | // If you did not use a password, set it as null or an empty string
25 | string p12FilePassword = "password";
26 |
27 |
28 | //Actual Code starts below:
29 | //--------------------------------
30 |
31 | //Get the filename assuming the file is in the same directory as this app
32 | string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);
33 |
34 | //Create the feedback service consumer
35 | FeedbackService service = new FeedbackService(sandbox, p12Filename, p12FilePassword);
36 |
37 | //Wireup the events
38 | service.Error += new FeedbackService.OnError(service_Error);
39 | service.Feedback += new FeedbackService.OnFeedback(service_Feedback);
40 |
41 | //Run it. This actually connects and receives the feedback
42 | // the Feedback event will fire for each feedback object
43 | // received from the server
44 | service.Run();
45 |
46 | Console.WriteLine("Done.");
47 | Console.WriteLine("Cleaning up...");
48 |
49 | //Clean up
50 | service.Dispose();
51 |
52 | Console.WriteLine("Press enter to exit...");
53 | Console.ReadLine();
54 | }
55 |
56 | static void service_Feedback(object sender, Feedback feedback)
57 | {
58 | Console.WriteLine(string.Format("Feedback - Timestamp: {0} - DeviceId: {1}", feedback.Timestamp, feedback.DeviceToken));
59 | }
60 |
61 | static void service_Error(object sender, Exception ex)
62 | {
63 | Console.WriteLine(string.Format("Error: {0}", ex.Message));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback.Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("JdSoft.Apple.Apns.Feedback.Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("JdSoft.Apple.Apns.Feedback.Test")]
13 | [assembly: AssemblyCopyright("Copyright © 2009")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("2872e317-d9c4-44f4-9257-4f2266a7bf51")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.4.4")]
36 | [assembly: AssemblyFileVersion("1.0.4.4")]
37 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback/Feedback.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace JdSoft.Apple.Apns.Feedback
6 | {
7 | ///
8 | /// Feedback object
9 | ///
10 | public class Feedback
11 | {
12 |
13 | ///
14 | /// Constructor
15 | ///
16 | public Feedback()
17 | {
18 | this.DeviceToken = string.Empty;
19 | this.Timestamp = DateTime.MinValue;
20 | }
21 |
22 | ///
23 | /// Device Token string in hex form without any spaces or dashes
24 | ///
25 | public string DeviceToken
26 | {
27 | get;
28 | set;
29 | }
30 |
31 | ///
32 | /// Timestamp of the Feedback for when Apple received the notice to stop sending notifications to the device
33 | ///
34 | public DateTime Timestamp
35 | {
36 | get;
37 | set;
38 | }
39 |
40 | ///
41 | /// For whatever use you please :)
42 | ///
43 | public object Tag
44 | {
45 | get;
46 | set;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback/FeedbackService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading;
5 | using System.Net;
6 | using System.Net.Sockets;
7 | using System.Net.Security;
8 | using System.Security.Cryptography;
9 | using System.Security.Cryptography.X509Certificates;
10 |
11 | namespace JdSoft.Apple.Apns.Feedback
12 | {
13 | ///
14 | /// Feedback Service Consumer
15 | ///
16 | public class FeedbackService : IDisposable
17 | {
18 | #region Delegates and Events
19 | ///
20 | /// Handles General Error Exceptions
21 | ///
22 | /// FeedbackService Instance
23 | /// Exception Instance
24 | public delegate void OnError(object sender, Exception ex);
25 | ///
26 | /// Occurs when a General Exception is thrown
27 | ///
28 | public event OnError Error;
29 |
30 | ///
31 | /// Handles Feedback Received Event
32 | ///
33 | /// FeedbackService Instance
34 | /// Feedback Instance
35 | public delegate void OnFeedback(object sender, Feedback feedback);
36 | ///
37 | /// Occurs when Feedback Information is Received
38 | ///
39 | public event OnFeedback Feedback;
40 | #endregion
41 |
42 | #region Constants
43 | private const string hostSandbox = "feedback.sandbox.push.apple.com";
44 | private const string hostProduction = "feedback.push.apple.com";
45 | #endregion
46 |
47 | #region Instance Variables
48 | private bool disposing;
49 | private Encoding encoding;
50 | private X509Certificate certificate;
51 | private X509CertificateCollection certificates;
52 | private string P12FilePassword;
53 | private TcpClient apnsClient;
54 | private SslStream apnsStream;
55 | #endregion
56 |
57 | #region Constructors
58 | ///
59 | /// Constructor
60 | ///
61 | /// Push Notification Feedback Host
62 | /// Push Notification Feedback Port
63 | /// PKCS12 .p12 or .pfx File containing Public and Private Keys
64 | public FeedbackService(string host, int port, string p12File)
65 | {
66 | Id = System.Guid.NewGuid().ToString("N");
67 | Host = host;
68 | Port = port;
69 | P12File = p12File;
70 | ConnectAttempts = 3;
71 | ReconnectDelay = 10000;
72 | }
73 |
74 | ///
75 | /// Constructor
76 | ///
77 | /// Push Notification Feedback Host
78 | /// Push Notification Feedback Port
79 | /// Byte array representation of PKCS12 .p12 or .pfx File containing Public and Private Keys
80 | public FeedbackService(string host, int port, byte[] p12FileBytes)
81 | {
82 | Id = System.Guid.NewGuid().ToString("N");
83 | Host = host;
84 | Port = port;
85 | // Fixed by danielgindi@gmail.com :
86 | // The default is UserKeySet, which has caused internal encryption errors,
87 | // Because of lack of permissions on most hosting services.
88 | // So MachineKeySet should be used instead.
89 | certificate = new X509Certificate2(p12FileBytes, (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
90 | ConnectAttempts = 3;
91 | ReconnectDelay = 10000;
92 | }
93 |
94 | ///
95 | /// Constructor
96 | ///
97 | /// Push Notification Feedback Host
98 | /// Push Notification Feedback Port
99 | /// PKCS12 .p12 or .pfx File containing Public and Private Keys
100 | /// Password protecting the p12File
101 | public FeedbackService(string host, int port, string p12File, string p12FilePassword)
102 | {
103 | Id = System.Guid.NewGuid().ToString("N");
104 | Host = host;
105 | Port = port;
106 | P12File = p12File;
107 | P12FilePassword = p12FilePassword;
108 | ConnectAttempts = 3;
109 | ReconnectDelay = 10000;
110 | }
111 |
112 | ///
113 | /// Constructor
114 | ///
115 | /// Push Notification Feedback Host
116 | /// Push Notification Feedback Port
117 | /// Byte array representation of PKCS12 .p12 or .pfx File containing Public and Private Keys
118 | /// Password protecting the p12File
119 | public FeedbackService(string host, int port, byte[] p12FileBytes, string p12FilePassword)
120 | {
121 | Id = System.Guid.NewGuid().ToString("N");
122 | Host = host;
123 | Port = port;
124 | // Fixed by danielgindi@gmail.com :
125 | // The default is UserKeySet, which has caused internal encryption errors,
126 | // Because of lack of permissions on most hosting services.
127 | // So MachineKeySet should be used instead.
128 | certificate = new X509Certificate2(p12FileBytes, p12FilePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
129 | P12FilePassword = p12FilePassword;
130 | ConnectAttempts = 3;
131 | ReconnectDelay = 10000;
132 | }
133 |
134 | ///
135 | /// Constructor
136 | ///
137 | /// Boolean flag indicating whether the default Sandbox or Production Host and Port should be used
138 | /// PKCS12 .p12 or .pfx File containing Public and Private Keys
139 | public FeedbackService(bool sandbox, string p12File)
140 | {
141 | Id = System.Guid.NewGuid().ToString("N");
142 | Host = sandbox ? hostSandbox : hostProduction;
143 | Port = 2196;
144 | P12File = p12File;
145 | ConnectAttempts = 3;
146 | }
147 |
148 | ///
149 | /// Constructor
150 | ///
151 | /// Boolean flag indicating whether the default Sandbox or Production Host and Port should be used
152 | /// Byte array representation of PKCS12 .p12 or .pfx File containing Public and Private Keys
153 | public FeedbackService(bool sandbox, byte[] p12FileBytes)
154 | {
155 | Id = System.Guid.NewGuid().ToString("N");
156 | Host = sandbox ? hostSandbox : hostProduction;
157 | Port = 2196;
158 | // Fixed by danielgindi@gmail.com :
159 | // The default is UserKeySet, which has caused internal encryption errors,
160 | // Because of lack of permissions on most hosting services.
161 | // So MachineKeySet should be used instead.
162 | certificate = new X509Certificate2(p12FileBytes, (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
163 | ConnectAttempts = 3;
164 | }
165 |
166 | ///
167 | /// Constructor
168 | ///
169 | /// Boolean flag indicating whether the default Sandbox or Production Host and Port should be used
170 | /// PKCS12 .p12 or .pfx File containing Public and Private Keys
171 | /// Password protecting the p12File
172 | public FeedbackService(bool sandbox, string p12File, string p12FilePassword)
173 | {
174 | Id = System.Guid.NewGuid().ToString("N");
175 | Host = sandbox ? hostSandbox : hostProduction;
176 | Port = 2196;
177 | P12File = p12File;
178 | P12FilePassword = p12FilePassword;
179 | ConnectAttempts = 3;
180 | ReconnectDelay = 10000;
181 | }
182 |
183 | ///
184 | /// Constructor
185 | ///
186 | /// Boolean flag indicating whether the default Sandbox or Production Host and Port should be used
187 | /// Byte array representation of PKCS12 .p12 or .pfx File containing Public and Private Keys
188 | /// Password protecting the p12File
189 | public FeedbackService(bool sandbox, byte[] p12FileBytes, string p12FilePassword)
190 | {
191 | Id = System.Guid.NewGuid().ToString("N");
192 | Host = sandbox ? hostSandbox : hostProduction;
193 | Port = 2196;
194 | // Fixed by danielgindi@gmail.com :
195 | // The default is UserKeySet, which has caused internal encryption errors,
196 | // Because of lack of permissions on most hosting services.
197 | // So MachineKeySet should be used instead.
198 | certificate = new X509Certificate2(p12FileBytes, p12FilePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
199 | P12FilePassword = p12FilePassword;
200 | ConnectAttempts = 3;
201 | ReconnectDelay = 10000;
202 | }
203 |
204 | #endregion
205 |
206 | #region Public Methods
207 | ///
208 | /// Ensures the Connection is closed and all resources are cleaned up
209 | ///
210 | public void Dispose()
211 | {
212 | disposing = true;
213 |
214 | ensureDisconnected();
215 | }
216 |
217 | ///
218 | /// Initiates the Connection to the Feedback Server and receives all Feedback data then closes the connection
219 | ///
220 | public void Run()
221 | {
222 | disposing = false;
223 |
224 | encoding = Encoding.ASCII;
225 |
226 | // certificate will already be set if one of the constructors that takes a byte array was used.
227 | if (certificate == null)
228 | {
229 | // Fixed by danielgindi@gmail.com :
230 | // The default is UserKeySet, which has caused internal encryption errors,
231 | // Because of lack of permissions on most hosting services.
232 | // So MachineKeySet should be used instead.
233 | certificate = new X509Certificate2(System.IO.File.ReadAllBytes(P12File), P12FilePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
234 | }
235 |
236 | certificates = new X509CertificateCollection();
237 | certificates.Add(certificate);
238 |
239 | if (ensureConnected() && !disposing)
240 | {
241 | //Set up
242 | byte[] buffer = new byte[38];
243 | int recd = 0;
244 | DateTime minTimestamp = DateTime.Now.AddYears(-1);
245 |
246 | //Get the first feedback
247 | recd = apnsStream.Read(buffer, 0, buffer.Length);
248 |
249 | //Continue while we have results and are not disposing
250 | while (recd > 0 && !disposing)
251 | {
252 | try
253 | {
254 | Feedback fb = new Feedback();
255 |
256 | //Get our seconds since 1970 ?
257 | byte[] bSeconds = new byte[4];
258 | byte[] bDeviceToken = new byte[32];
259 |
260 | Array.Copy(buffer, 0, bSeconds, 0, 4);
261 |
262 | //Check endianness
263 | if (BitConverter.IsLittleEndian)
264 | Array.Reverse(bSeconds);
265 |
266 | int tSeconds = BitConverter.ToInt32(bSeconds, 0);
267 |
268 | //Add seconds since 1970 to that date, in UTC and then get it locally
269 | fb.Timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(tSeconds).ToLocalTime();
270 |
271 |
272 | //Now copy out the device token
273 | Array.Copy(buffer, 6, bDeviceToken, 0, 32);
274 |
275 | fb.DeviceToken = BitConverter.ToString(bDeviceToken).Replace("-", "").ToLower().Trim();
276 |
277 | //Make sure we have a good feedback tuple
278 | if (fb.DeviceToken.Length == 64
279 | && fb.Timestamp > minTimestamp
280 | && this.Feedback != null)
281 | {
282 | //Raise event
283 | this.Feedback(this, fb);
284 | }
285 |
286 | }
287 | catch (Exception ex)
288 | {
289 | if (this.Error != null)
290 | this.Error(this, ex);
291 | }
292 |
293 | //Clear our array to reuse it
294 | Array.Clear(buffer, 0, buffer.Length);
295 |
296 | //Read the next feedback
297 | recd = apnsStream.Read(buffer, 0, buffer.Length);
298 | }
299 | }
300 |
301 | ensureDisconnected();
302 | }
303 | #endregion
304 |
305 | #region Properties
306 | ///
307 | /// Gets the Unique Id for this Instance
308 | ///
309 | public string Id
310 | {
311 | get;
312 | private set;
313 | }
314 |
315 | ///
316 | /// Gets the Push Notification Feedback Host
317 | ///
318 | public string Host
319 | {
320 | get;
321 | private set;
322 | }
323 |
324 | ///
325 | /// Gets the Push Notification Feedback Port
326 | ///
327 | public int Port
328 | {
329 | get;
330 | private set;
331 | }
332 |
333 | ///
334 | /// Gets the PKCS12 .p12 or .pfx File Used
335 | ///
336 | public string P12File
337 | {
338 | get;
339 | private set;
340 | }
341 |
342 | ///
343 | /// How many times to try connecting before giving up
344 | ///
345 | public int ConnectAttempts
346 | {
347 | get;
348 | set;
349 | }
350 |
351 | ///
352 | /// Number of milliseconds to wait between connection attempts
353 | ///
354 | public int ReconnectDelay
355 | {
356 | get;
357 | set;
358 | }
359 |
360 | ///
361 | /// For whatever use you please :)
362 | ///
363 | public object Tag
364 | {
365 | get;
366 | set;
367 | }
368 | #endregion
369 |
370 | #region Private Methods
371 | private bool ensureConnected()
372 | {
373 | bool connected = false;
374 |
375 | if (apnsStream == null || !apnsStream.CanWrite)
376 | connected = false;
377 |
378 | if (apnsClient == null || !apnsClient.Connected)
379 | connected = false;
380 |
381 | int tries = 0;
382 |
383 | while (!connected && !disposing && tries < ConnectAttempts)
384 | {
385 | tries++;
386 |
387 | try
388 | {
389 | apnsClient = new TcpClient(Host, Port);
390 |
391 | apnsStream = new SslStream(apnsClient.GetStream(), true,
392 | new RemoteCertificateValidationCallback(validateServerCertificate),
393 | new LocalCertificateSelectionCallback(selectLocalCertificate));
394 |
395 | apnsStream.AuthenticateAsClient(Host,
396 | certificates,
397 | System.Security.Authentication.SslProtocols.Tls,
398 | false);
399 |
400 | connected = apnsStream.CanWrite;
401 | }
402 | catch (Exception ex)
403 | {
404 | if (this.Error != null)
405 | this.Error(this, ex);
406 |
407 | connected = false;
408 |
409 | }
410 |
411 | if (!connected)
412 | {
413 | int wait = ReconnectDelay;
414 | int waited = 0;
415 |
416 | while (waited < wait && !disposing)
417 | {
418 | System.Threading.Thread.Sleep(250);
419 | waited += 250;
420 | }
421 | }
422 |
423 | }
424 |
425 | return connected;
426 | }
427 |
428 | private void ensureDisconnected()
429 | {
430 | try { apnsStream.Close(); }
431 | catch { }
432 |
433 | try { apnsStream.Dispose(); }
434 | catch { }
435 |
436 | try { apnsClient.Client.Shutdown(SocketShutdown.Both); }
437 | catch { }
438 |
439 | try { apnsClient.Client.Close(); }
440 | catch { }
441 |
442 | try { apnsClient.Close(); }
443 | catch { }
444 | }
445 |
446 |
447 | private bool validateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
448 | {
449 | return true; // Dont care about server's cert
450 | }
451 |
452 | private X509Certificate selectLocalCertificate(object sender, string targetHost, X509CertificateCollection localCertificates,
453 | X509Certificate remoteCertificate, string[] acceptableIssuers)
454 | {
455 | return certificate;
456 | }
457 | #endregion
458 | }
459 | }
460 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback/JdSoft.Apple.Apns.Feedback.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {B87C2903-2B6B-49FF-B735-630A5CB69521}
9 | Library
10 | Properties
11 | JdSoft.Apple.Apns.Feedback
12 | JdSoft.Apns.Feedback
13 | v3.5
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
56 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Feedback/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("JdSoft.Apns.Feedback")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("JdSoft.Apns.Feedback")]
13 | [assembly: AssemblyCopyright("Copyright © 2009")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("46fea548-9a0c-4863-9c89-3f3098aed44c")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.4.4")]
36 | [assembly: AssemblyFileVersion("1.0.4.4")]
37 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.JsonTest/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("JdSoft.Apple.Test")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("")]
12 | [assembly: AssemblyCopyright("")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("1.0.4.4")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | [assembly: AssemblyDelaySign(false)]
26 | [assembly: AssemblyKeyFile("")]
27 | [assembly: AssemblyFileVersion("1.0.4.4")]
28 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.JsonTest/JdSoft.Apple.Apns.Notifications.JsonTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {1A7ACE5F-EF8A-4C5E-87B5-CBBFA432DFE4}
9 | Exe
10 | JdSoft.Apple.Test
11 | v3.5
12 |
13 |
14 |
15 |
16 | 3.5
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug
23 | DEBUG
24 | prompt
25 | 4
26 |
27 |
28 | none
29 | false
30 | bin\Release
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {4CFB9AA8-55F8-46DC-B7BD-9E18B9939110}
45 | JdSoft.Apple.Apns.Notifications
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.JsonTest/JdSoft.Apple.Apns.Notifications.JsonTest.pidb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Redth/APNS-Sharp/e30a9990a466f8fc0d054224bbf8d599a67311f3/JdSoft.Apple.Apns.Notifications.JsonTest/JdSoft.Apple.Apns.Notifications.JsonTest.pidb
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.JsonTest/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JdSoft.Apple.Apns.Notifications;
3 |
4 | namespace JdSoft.Apple.Test
5 | {
6 | class MainClass
7 | {
8 | [STAThread]
9 | public static void Main(string[] args)
10 | {
11 | //Empty aps, just custom
12 | Notification notification = new Notification();
13 | notification.Payload.AddCustom("bar", 42);
14 |
15 | Console.WriteLine(notification.ToString());
16 | Console.WriteLine();
17 | System.Windows.Forms.Clipboard.SetText(notification.ToString());
18 | Console.ReadLine();
19 |
20 |
21 |
22 | //More complex notification
23 | Notification notification1 = new Notification();
24 |
25 | notification1.Payload.Alert.LocalizedKey = "GAME_PLAY_REQUEST_FORMAT";
26 | notification1.Payload.Alert.AddLocalizedArgs("Jenna", "Frank");
27 |
28 | notification1.Payload.Badge = 5;
29 |
30 | notification1.Payload.Sound = "chime";
31 |
32 | notification1.Payload.AddCustom("acme1", "bar");
33 | notification1.Payload.AddCustom("acme2", "bang", "whiz");
34 |
35 |
36 | Console.WriteLine(notification1.ToString());
37 | Console.WriteLine();
38 | System.Windows.Forms.Clipboard.SetText(notification1.ToString());
39 | Console.ReadLine();
40 |
41 |
42 |
43 | ////Simpler notification
44 | Notification notification2 = new Notification();
45 |
46 | notification2.Payload.Alert.Body = "Bob wants to play poker";
47 | notification2.Payload.Alert.ActionLocalizedKey = "PLAY";
48 |
49 | notification2.Payload.Badge = 5;
50 |
51 | notification2.Payload.Sound = "chime";
52 |
53 | notification2.Payload.AddCustom("acme1", "bar");
54 | notification2.Payload.AddCustom("acme2", "bang", "whiz");
55 |
56 |
57 | Console.WriteLine(notification2.ToString());
58 | Console.WriteLine();
59 | System.Windows.Forms.Clipboard.SetText(notification2.ToString());
60 | Console.ReadLine();
61 |
62 |
63 | ////Very simple notification
64 | Notification notification3 = new Notification();
65 |
66 | notification3.Payload.Alert.Body = "Bob wants to play poker";
67 |
68 | notification3.Payload.Badge = 5;
69 |
70 | notification3.Payload.Sound = "chime";
71 |
72 | Console.WriteLine(notification3.ToString());
73 | Console.WriteLine();
74 | System.Windows.Forms.Clipboard.SetText(notification3.ToString());
75 | Console.ReadLine();
76 |
77 |
78 | ////Badge update and sound only
79 | Notification notification4 = new Notification();
80 |
81 | notification4.Payload.Badge = 5;
82 |
83 | notification4.Payload.Sound = "chime";
84 | notification4.Payload.AddCustom("test", 4, 2, 12);
85 |
86 | Console.WriteLine(notification4.ToString());
87 | System.Windows.Forms.Clipboard.SetText(notification4.ToString());
88 |
89 |
90 | Console.WriteLine("Press enter to exit...");
91 | Console.ReadLine();
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.Test/JdSoft.Apple.Apns.Notifications.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {3500A7AA-A8C8-4221-8265-48B02ECF2463}
9 | Exe
10 | Properties
11 | JdSoft.Apple.Apns.Test
12 | JdSoft.Apns.Test
13 | v3.5
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | {4CFB9AA8-55F8-46DC-B7BD-9E18B9939110}
51 | JdSoft.Apple.Apns.Notifications
52 |
53 |
54 |
55 |
62 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.Test/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using JdSoft.Apple.Apns.Notifications;
5 |
6 | namespace JdSoft.Apple.Apns.Test
7 | {
8 | class Program
9 | {
10 | [STAThread]
11 | static void Main(string[] args)
12 | {
13 | //Variables you may need to edit:
14 | //---------------------------------
15 |
16 | //True if you are using sandbox certificate, or false if using production
17 | bool sandbox = true;
18 |
19 | //Put your device token in here
20 | string testDeviceToken = "fe58fc8f527c363d1b775dca133e04bff24dc5032d08836992395cc56bfa62ef";
21 |
22 | //Put your PKCS12 .p12 or .pfx filename here.
23 | // Assumes it is in the same directory as your app
24 | string p12File = "apn_developer_identity.p12";
25 |
26 | //This is the password that you protected your p12File
27 | // If you did not use a password, set it as null or an empty string
28 | string p12FilePassword = "yourpassword";
29 |
30 | //Number of notifications to send
31 | int count = 3;
32 |
33 | //Number of milliseconds to wait in between sending notifications in the loop
34 | // This is just to demonstrate that the APNS connection stays alive between messages
35 | int sleepBetweenNotifications = 15000;
36 |
37 |
38 | //Actual Code starts below:
39 | //--------------------------------
40 |
41 | string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);
42 |
43 | NotificationService service = new NotificationService(sandbox, p12Filename, p12FilePassword, 1);
44 |
45 | service.SendRetries = 5; //5 retries before generating notificationfailed event
46 | service.ReconnectDelay = 5000; //5 seconds
47 |
48 | service.Error += new NotificationService.OnError(service_Error);
49 | service.NotificationTooLong += new NotificationService.OnNotificationTooLong(service_NotificationTooLong);
50 |
51 | service.BadDeviceToken += new NotificationService.OnBadDeviceToken(service_BadDeviceToken);
52 | service.NotificationFailed += new NotificationService.OnNotificationFailed(service_NotificationFailed);
53 | service.NotificationSuccess += new NotificationService.OnNotificationSuccess(service_NotificationSuccess);
54 | service.Connecting += new NotificationService.OnConnecting(service_Connecting);
55 | service.Connected += new NotificationService.OnConnected(service_Connected);
56 | service.Disconnected += new NotificationService.OnDisconnected(service_Disconnected);
57 |
58 | //The notifications will be sent like this:
59 | // Testing: 1...
60 | // Testing: 2...
61 | // Testing: 3...
62 | // etc...
63 | for (int i = 1; i <= count; i++)
64 | {
65 | //Create a new notification to send
66 | Notification alertNotification = new Notification(testDeviceToken);
67 |
68 | alertNotification.Payload.Alert.Body = string.Format("Testing {0}...", i);
69 | alertNotification.Payload.Sound = "default";
70 | alertNotification.Payload.Badge = i;
71 |
72 | //Queue the notification to be sent
73 | if (service.QueueNotification(alertNotification))
74 | Console.WriteLine("Notification Queued!");
75 | else
76 | Console.WriteLine("Notification Failed to be Queued!");
77 |
78 | //Sleep in between each message
79 | if (i < count)
80 | {
81 | Console.WriteLine("Sleeping " + sleepBetweenNotifications + " milliseconds before next Notification...");
82 | System.Threading.Thread.Sleep(sleepBetweenNotifications);
83 | }
84 | }
85 |
86 | Console.WriteLine("Cleaning Up...");
87 |
88 | //First, close the service.
89 | //This ensures any queued notifications get sent befor the connections are closed
90 | service.Close();
91 |
92 | //Clean up
93 | service.Dispose();
94 |
95 | Console.WriteLine("Done!");
96 | Console.WriteLine("Press enter to exit...");
97 | Console.ReadLine();
98 | }
99 |
100 | static void service_BadDeviceToken(object sender, BadDeviceTokenException ex)
101 | {
102 | Console.WriteLine("Bad Device Token: {0}", ex.Message);
103 | }
104 |
105 | static void service_Disconnected(object sender)
106 | {
107 | Console.WriteLine("Disconnected...");
108 | }
109 |
110 | static void service_Connected(object sender)
111 | {
112 | Console.WriteLine("Connected...");
113 | }
114 |
115 | static void service_Connecting(object sender)
116 | {
117 | Console.WriteLine("Connecting...");
118 | }
119 |
120 | static void service_NotificationTooLong(object sender, NotificationLengthException ex)
121 | {
122 | Console.WriteLine(string.Format("Notification Too Long: {0}", ex.Notification.ToString()));
123 | }
124 |
125 | static void service_NotificationSuccess(object sender, Notification notification)
126 | {
127 | Console.WriteLine(string.Format("Notification Success: {0}", notification.ToString()));
128 | }
129 |
130 | static void service_NotificationFailed(object sender, Notification notification)
131 | {
132 | Console.WriteLine(string.Format("Notification Failed: {0}", notification.ToString()));
133 | }
134 |
135 | static void service_Error(object sender, Exception ex)
136 | {
137 | Console.WriteLine(string.Format("Error: {0}", ex.Message));
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications.Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("JdSoft.Apns.Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("JdSoft.Apns.Test")]
13 | [assembly: AssemblyCopyright("Copyright © 2009")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("38d47213-ef7e-45d2-a5e8-516aecd83676")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.4.4")]
36 | [assembly: AssemblyFileVersion("1.0.4.4")]
37 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications/BadDeviceTokenException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace JdSoft.Apple.Apns.Notifications
6 | {
7 | public class BadDeviceTokenException : Exception
8 | {
9 | public BadDeviceTokenException(string deviceToken)
10 | : base(string.Format("Device Token Length ({0}) Is not the required length of {1} characters!", deviceToken.Length, Notification.DEVICE_TOKEN_STRING_SIZE))
11 | {
12 | this.DeviceToken = deviceToken;
13 | }
14 |
15 | public string DeviceToken
16 | {
17 | get;
18 | private set;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications/JdSoft.Apple.Apns.Notifications.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {4CFB9AA8-55F8-46DC-B7BD-9E18B9939110}
9 | Library
10 | Properties
11 | JdSoft.Apple.Apns.Notifications
12 | JdSoft.Apns.Notifications
13 | v3.5
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 | ..\packages\Newtonsoft.Json.4.0.8\lib\net35\Newtonsoft.Json.dll
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications/Notification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Net;
5 | using System.Text;
6 |
7 | namespace JdSoft.Apple.Apns.Notifications
8 | {
9 | public class Notification
10 | {
11 | public string DeviceToken { get; set; }
12 | public NotificationPayload Payload { get; set; }
13 | ///
14 | /// The expiration date after which Apple will no longer store and forward this push notification.
15 | /// If no value is provided, an assumed value of one year from now is used. If you do not wish
16 | /// for Apple to store and forward, set this value to Notification.DoNotStore.
17 | ///
18 | public DateTime? Expiration { get; set; }
19 | public const int DEVICE_TOKEN_BINARY_SIZE = 32;
20 | public const int DEVICE_TOKEN_STRING_SIZE = 64;
21 | public const int MAX_PAYLOAD_SIZE = 256;
22 | public static readonly DateTime DoNotStore = DateTime.MinValue;
23 | private static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
24 |
25 | public Notification()
26 | {
27 | DeviceToken = string.Empty;
28 | Payload = new NotificationPayload();
29 | }
30 |
31 | public Notification(string deviceToken)
32 | {
33 | if (!string.IsNullOrEmpty(deviceToken) && deviceToken.Length != DEVICE_TOKEN_STRING_SIZE)
34 | throw new BadDeviceTokenException(deviceToken);
35 |
36 | DeviceToken = deviceToken;
37 | Payload = new NotificationPayload();
38 | }
39 |
40 | public Notification(string deviceToken, NotificationPayload payload)
41 | {
42 | if (!string.IsNullOrEmpty(deviceToken) && deviceToken.Length != DEVICE_TOKEN_STRING_SIZE)
43 | throw new BadDeviceTokenException(deviceToken);
44 |
45 | DeviceToken = deviceToken;
46 | Payload = payload;
47 | }
48 |
49 | ///
50 | /// Object for storing state. This does not affect the actual notification!
51 | ///
52 | public object Tag
53 | {
54 | get;
55 | set;
56 | }
57 |
58 | public override string ToString()
59 | {
60 | return Payload.ToJson();
61 | }
62 |
63 | public static String HexEncode(byte[] data)
64 | {
65 | int len = data.Length;
66 |
67 | if (len == 0)
68 | {
69 | throw new BadDeviceTokenException(@"");
70 | }
71 |
72 | StringBuilder hexString = new StringBuilder(len);
73 |
74 | foreach (byte tokenByte in data)
75 | {
76 | hexString.Append(tokenByte.ToString(@"x2"));
77 | }
78 |
79 | return hexString.ToString();
80 | }
81 |
82 | public byte[] ToBytes()
83 | {
84 | // Without reading the response which would make any identifier useful, it seems silly to
85 | // expose the value in the object model, although that would be easy enough to do. For
86 | // now we'll just use zero.
87 | int identifier = 0;
88 | byte[] identifierBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(identifier));
89 |
90 | // APNS will not store-and-forward a notification with no expiry, so set it one year in the future
91 | // if the client does not provide it.
92 | int expiryTimeStamp = -1;
93 | if (Expiration != DoNotStore)
94 | {
95 | DateTime concreteExpireDateUtc = (Expiration ?? DateTime.UtcNow.AddMonths(1)).ToUniversalTime();
96 | TimeSpan epochTimeSpan = concreteExpireDateUtc - UNIX_EPOCH;
97 | expiryTimeStamp = (int)epochTimeSpan.TotalSeconds;
98 | }
99 |
100 | byte[] expiry = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(expiryTimeStamp));
101 |
102 |
103 | byte[] deviceToken = new byte[DeviceToken.Length / 2];
104 | for (int i = 0; i < deviceToken.Length; i++)
105 | deviceToken[i] = byte.Parse(DeviceToken.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
106 |
107 | if (deviceToken.Length != DEVICE_TOKEN_BINARY_SIZE)
108 | throw new BadDeviceTokenException(DeviceToken);
109 |
110 |
111 | byte[] deviceTokenSize = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Convert.ToInt16(deviceToken.Length)));
112 |
113 | byte[] payload = Encoding.UTF8.GetBytes(Payload.ToJson());
114 | if (payload.Length > MAX_PAYLOAD_SIZE)
115 | {
116 | int newSize = Payload.Alert.Body.Length - (payload.Length - MAX_PAYLOAD_SIZE);
117 | if (newSize > 0)
118 | {
119 | Payload.Alert.Body = Payload.Alert.Body.Substring(0, newSize);
120 | payload = Encoding.UTF8.GetBytes(Payload.ToString());
121 | }
122 | else
123 | {
124 | do
125 | {
126 | Payload.Alert.Body = Payload.Alert.Body.Remove(Payload.Alert.Body.Length - 1);
127 | payload = Encoding.UTF8.GetBytes(Payload.ToString());
128 | }
129 | while (payload.Length > MAX_PAYLOAD_SIZE && !string.IsNullOrEmpty(Payload.Alert.Body));
130 | }
131 |
132 | if (payload.Length > MAX_PAYLOAD_SIZE)
133 | throw new NotificationLengthException(this);
134 | }
135 | byte[] payloadSize = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Convert.ToInt16(payload.Length)));
136 |
137 | int bufferSize = sizeof(Byte) + deviceTokenSize.Length + deviceToken.Length + payloadSize.Length + payload.Length;
138 | byte[] buffer = new byte[bufferSize];
139 |
140 | List notificationParts = new List();
141 |
142 | notificationParts.Add(new byte[] { 0x01 }); // Enhanced notification format command
143 | notificationParts.Add(identifierBytes);
144 | notificationParts.Add(expiry);
145 | notificationParts.Add(deviceTokenSize);
146 | notificationParts.Add(deviceToken);
147 | notificationParts.Add(payloadSize);
148 | notificationParts.Add(payload);
149 |
150 | return BuildBufferFrom(notificationParts);
151 | }
152 |
153 | private byte[] BuildBufferFrom(IList bufferParts)
154 | {
155 | int bufferSize = 0;
156 | for (int i = 0; i < bufferParts.Count; i++)
157 | bufferSize += bufferParts[i].Length;
158 |
159 | byte[] buffer = new byte[bufferSize];
160 | int position = 0;
161 | for (int i = 0; i < bufferParts.Count; i++)
162 | {
163 | byte[] part = bufferParts[i];
164 | Buffer.BlockCopy(bufferParts[i], 0, buffer, position, part.Length);
165 | position += part.Length;
166 | }
167 | return buffer;
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/JdSoft.Apple.Apns.Notifications/NotificationAlert.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Newtonsoft.Json.Linq;
5 |
6 | namespace JdSoft.Apple.Apns.Notifications
7 | {
8 | ///
9 | /// Alert Portion of the Notification Payload
10 | ///
11 | public class NotificationAlert
12 | {
13 | ///
14 | /// Constructor
15 | ///
16 | public NotificationAlert()
17 | {
18 | Body = null;
19 | ActionLocalizedKey = null;
20 | LocalizedKey = null;
21 | LocalizedArgs = new List