PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/WP7.1/Templates/C#/WPCloud.Mem/WindowsPhoneCloud.Web/Controllers/PushNotificationsController.cs

#
C# | 334 lines | 258 code | 51 blank | 25 comment | 17 complexity | 51ec4f94c5cc3043f3806b2ffad03f8a MD5 | raw file
  1. // ----------------------------------------------------------------------------------
  2. // Microsoft Developer & Platform Evangelism
  3. //
  4. // Copyright (c) Microsoft Corporation. All rights reserved.
  5. //
  6. // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  7. // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
  8. // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  9. // ----------------------------------------------------------------------------------
  10. // The example companies, organizations, products, domain names,
  11. // e-mail addresses, logos, people, places, and events depicted
  12. // herein are fictitious. No association with any real company,
  13. // organization, product, domain name, email address, logo, person,
  14. // places, or events is intended or should be inferred.
  15. // ----------------------------------------------------------------------------------
  16. namespace Microsoft.Samples.WindowsPhoneCloud.Web.Controllers
  17. {
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Net.Security;
  24. using System.Net.Sockets;
  25. using System.Security.Authentication;
  26. using System.Security.Cryptography.X509Certificates;
  27. using System.Text;
  28. using System.Web.Mvc;
  29. using Microsoft.Samples.WindowsPhoneCloud.Web.Infrastructure;
  30. using Microsoft.Samples.WindowsPhoneCloud.Web.Models;
  31. using Microsoft.Samples.WindowsPhoneCloud.Web.UserAccountWrappers;
  32. using Microsoft.WindowsAzure;
  33. using Microsoft.WindowsAzure.StorageClient;
  34. using WindowsPhone.Recipes.Push.Messages;
  35. [CustomAuthorize(Roles = PrivilegeConstants.AdminPrivilege)]
  36. public class PushNotificationsController : Controller
  37. {
  38. private const int DefaultPort = 2195;
  39. private readonly CloudQueueClient cloudQueueClient;
  40. private readonly IPushUserEndpointsRepository pushUserEndpointsRepository;
  41. #if ACS
  42. private readonly IUserRepository userRepository;
  43. public PushNotificationsController()
  44. : this(null, new UserTablesServiceContext(), new UserTablesServiceContext())
  45. {
  46. }
  47. [CLSCompliant(false)]
  48. public PushNotificationsController(CloudQueueClient cloudQueueClient, IPushUserEndpointsRepository pushUserEndpointsRepository, IUserRepository userRepository)
  49. {
  50. if (GetStorageAccountFromConfigurationSetting() == null)
  51. {
  52. if (cloudQueueClient == null)
  53. {
  54. throw new ArgumentNullException("cloudQueueClient", "Cloud Queue Client cannot be null if no configuration is loaded.");
  55. }
  56. }
  57. this.cloudQueueClient = cloudQueueClient ?? GetStorageAccountFromConfigurationSetting().CreateCloudQueueClient();
  58. this.userRepository = userRepository;
  59. this.pushUserEndpointsRepository = pushUserEndpointsRepository;
  60. }
  61. public ActionResult Microsoft()
  62. {
  63. var users = this.pushUserEndpointsRepository
  64. .GetAllPushUsers()
  65. .Select(userId => new UserModel { UserId = userId, UserName = this.userRepository.GetUser(userId).Name });
  66. return this.View(users);
  67. }
  68. #else
  69. private readonly IMembershipService membershipService;
  70. public PushNotificationsController()
  71. : this(null, new UserTablesServiceContext(), new AccountMembershipService())
  72. {
  73. }
  74. [CLSCompliant(false)]
  75. public PushNotificationsController(CloudQueueClient cloudQueueClient, IPushUserEndpointsRepository pushUserEndpointsRepository, IMembershipService membershipService)
  76. {
  77. if (GetStorageAccountFromConfigurationSetting() == null)
  78. {
  79. if (cloudQueueClient == null)
  80. {
  81. throw new ArgumentNullException("cloudQueueClient", "Cloud Queue Client cannot be null if no configuration is loaded.");
  82. }
  83. }
  84. this.cloudQueueClient = cloudQueueClient ?? GetStorageAccountFromConfigurationSetting().CreateCloudQueueClient();
  85. this.membershipService = membershipService;
  86. this.pushUserEndpointsRepository = pushUserEndpointsRepository;
  87. }
  88. public ActionResult Microsoft()
  89. {
  90. var users = this.pushUserEndpointsRepository
  91. .GetAllPushUsers()
  92. .Select(userId => new UserModel { UserId = userId, UserName = this.membershipService.GetUserByProviderUserKey(new Guid(userId)).UserName });
  93. return this.View(users);
  94. }
  95. #endif
  96. public ActionResult Apple()
  97. {
  98. return this.View();
  99. }
  100. [HttpPost]
  101. public ActionResult SendMicrosoftToast(string userId, string message)
  102. {
  103. if (string.IsNullOrWhiteSpace(message))
  104. {
  105. return this.Json("The notification message cannot be null, empty nor white space.", JsonRequestBehavior.AllowGet);
  106. }
  107. var resultList = new List<MessageSendResultLight>();
  108. var uris = this.pushUserEndpointsRepository.GetPushUsersByName(userId).Select(u => u.ChannelUri);
  109. var pushUserEndpoint = this.pushUserEndpointsRepository.GetPushUsersByName(userId).FirstOrDefault();
  110. var toast = new ToastPushNotificationMessage
  111. {
  112. SendPriority = MessageSendPriority.High,
  113. Title = message
  114. };
  115. foreach (var uri in uris)
  116. {
  117. var messageResult = toast.SendAndHandleErrors(new Uri(uri));
  118. resultList.Add(messageResult);
  119. if (messageResult.Status.Equals(MessageSendResultLight.Success))
  120. {
  121. this.QueueMessage(pushUserEndpoint, message);
  122. }
  123. }
  124. return this.Json(resultList, JsonRequestBehavior.AllowGet);
  125. }
  126. [HttpPost]
  127. public ActionResult SendMicrosoftTile(string userId, string message)
  128. {
  129. if (string.IsNullOrWhiteSpace(message))
  130. {
  131. return this.Json("The notification message cannot be null, empty nor white space.", JsonRequestBehavior.AllowGet);
  132. }
  133. var resultList = new List<MessageSendResultLight>();
  134. var pushUserEndpointList = this.pushUserEndpointsRepository.GetPushUsersByName(userId);
  135. foreach (var pushUserEndpoint in pushUserEndpointList)
  136. {
  137. var tile = new TilePushNotificationMessage
  138. {
  139. SendPriority = MessageSendPriority.High,
  140. Count = ++pushUserEndpoint.TileCount
  141. };
  142. var messageResult = tile.SendAndHandleErrors(new Uri(pushUserEndpoint.ChannelUri));
  143. resultList.Add(messageResult);
  144. if (messageResult.Status.Equals(MessageSendResultLight.Success))
  145. {
  146. this.QueueMessage(pushUserEndpoint, message);
  147. this.pushUserEndpointsRepository.UpdatePushUserEndpoint(pushUserEndpoint);
  148. }
  149. }
  150. return this.Json(resultList, JsonRequestBehavior.AllowGet);
  151. }
  152. [HttpPost]
  153. public ActionResult SendMicrosoftRaw(string userId, string message)
  154. {
  155. if (string.IsNullOrWhiteSpace(message))
  156. {
  157. return this.Json("The notification message cannot be null, empty nor white space.", JsonRequestBehavior.AllowGet);
  158. }
  159. var resultList = new List<MessageSendResultLight>();
  160. var uris = this.pushUserEndpointsRepository.GetPushUsersByName(userId).Select(u => u.ChannelUri);
  161. var raw = new RawPushNotificationMessage
  162. {
  163. SendPriority = MessageSendPriority.High,
  164. RawData = Encoding.UTF8.GetBytes(message)
  165. };
  166. foreach (var uri in uris)
  167. {
  168. resultList.Add(raw.SendAndHandleErrors(new Uri(uri)));
  169. }
  170. return this.Json(resultList, JsonRequestBehavior.AllowGet);
  171. }
  172. [HttpPost]
  173. public ActionResult SendAppleMessage(string deviceId, string message)
  174. {
  175. if (string.IsNullOrWhiteSpace(deviceId))
  176. {
  177. return this.Json("The deviceId cannot be null, empty nor white space.", JsonRequestBehavior.AllowGet);
  178. }
  179. if (string.IsNullOrWhiteSpace(message))
  180. {
  181. return this.Json("The notification message cannot be null, empty nor white space.", JsonRequestBehavior.AllowGet);
  182. }
  183. // Get the Apple Notification Service settings.
  184. var thumprint = ConfigReader.GetConfigValue("AppleNotificationService.Thumbprint");
  185. var host = ConfigReader.GetConfigValue("AppleNotificationService.Host");
  186. var port = 0;
  187. if (!int.TryParse(ConfigReader.GetConfigValue("AppleNotificationService.Port"), NumberStyles.Integer, CultureInfo.InvariantCulture, out port))
  188. {
  189. port = DefaultPort;
  190. }
  191. X509Certificate2Collection certCollection = null;
  192. try
  193. {
  194. // Load the Apple Notification Service certificate from the store.
  195. var clientCert = CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, thumprint);
  196. certCollection = new X509Certificate2Collection(clientCert);
  197. }
  198. catch (Exception exception)
  199. {
  200. var errorMessage = string.Format(CultureInfo.InvariantCulture, "Error getting the X509 certificate from the store: {0}", exception.Message);
  201. return this.Json(errorMessage, JsonRequestBehavior.AllowGet);
  202. }
  203. TcpClient client = null;
  204. try
  205. {
  206. // Open connection and connect.
  207. client = new TcpClient(host, port);
  208. var sslStream = new SslStream(client.GetStream(), false);
  209. sslStream.AuthenticateAsClient(host, certCollection, SslProtocols.Tls, false);
  210. byte[] array = null;
  211. using (var memoryStream = new MemoryStream())
  212. {
  213. var writer = new BinaryWriter(memoryStream);
  214. // Construct the message.
  215. writer.Write((byte)0); // Command
  216. writer.Write((byte)0); // First byte of device ID length
  217. writer.Write((byte)32); // Device id length
  218. // Convert to hex and write to message.
  219. byte[] deviceToken = new byte[deviceId.Length / 2];
  220. for (int i = 0; i < deviceToken.Length; i++)
  221. {
  222. deviceToken[i] = byte.Parse(deviceId.Substring(i * 2, 2), NumberStyles.HexNumber);
  223. }
  224. writer.Write(deviceToken);
  225. // Construct payload within JSON message framework.
  226. var payload = string.Format(CultureInfo.InvariantCulture, "{{\"aps\":{{\"alert\":\"{0}\",\"badge\":1}}}}", message);
  227. // Write payload data.
  228. writer.Write((byte)0); // First byte of payload length
  229. writer.Write((byte)payload.Length); // Actual payload length
  230. byte[] b1 = Encoding.UTF8.GetBytes(payload);
  231. writer.Write(b1);
  232. writer.Flush();
  233. array = memoryStream.ToArray();
  234. }
  235. // Send across the wire.
  236. sslStream.Write(array);
  237. sslStream.Flush();
  238. }
  239. catch (AuthenticationException exception)
  240. {
  241. var errorMessage = string.Format(CultureInfo.InvariantCulture, "Error authenticating against APN: {0}", exception.Message);
  242. return this.Json(errorMessage, JsonRequestBehavior.AllowGet);
  243. }
  244. catch (Exception exception)
  245. {
  246. var errorMessage = string.Format(CultureInfo.InvariantCulture, "There was an error sending the notification message: {0}", exception.Message);
  247. return this.Json(errorMessage, JsonRequestBehavior.AllowGet);
  248. }
  249. finally
  250. {
  251. // Close the client connection.
  252. client.Close();
  253. }
  254. // Success.
  255. return this.Json("Success", JsonRequestBehavior.AllowGet);
  256. }
  257. private static CloudStorageAccount GetStorageAccountFromConfigurationSetting()
  258. {
  259. CloudStorageAccount account = null;
  260. try
  261. {
  262. account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
  263. }
  264. catch (InvalidOperationException)
  265. {
  266. account = null;
  267. }
  268. return account;
  269. }
  270. private static string GetQueueName(string applicationId, string deviceId, string userId)
  271. {
  272. var uniqueName = string.Concat(applicationId, deviceId, userId);
  273. return string.Concat("notification", uniqueName.GetHashCode());
  274. }
  275. private void QueueMessage(PushUserEndpoint pushUser, string message)
  276. {
  277. var queueName = GetQueueName(pushUser.PartitionKey, pushUser.RowKey, pushUser.UserId);
  278. var queue = this.cloudQueueClient.GetQueueReference(queueName);
  279. queue.CreateIfNotExist();
  280. queue.AddMessage(new CloudQueueMessage(message));
  281. }
  282. }
  283. }