예제 #1
0
        public override async Task                        SendNotificationAsync(Notification notification)
        {
            try {
                byte[] body;

                var timeToLive = (int)((notification.ExpireTime - DateTime.UtcNow).Ticks / TimeSpan.TicksPerSecond);
                if (timeToLive < 60)
                {
                    throw new Exception("Notification expired.");
                }

                using (StringWriter stringWriter = new StringWriter()) {
                    using (JsonWriter jsonWriter = new JsonWriter(stringWriter)) {
                        jsonWriter.WriteStartObject();
                        jsonWriter.WriteNameValue("to", notification.DeviceAddress);

                        jsonWriter.WriteNameValue("time_to_live", timeToLive);

                        if (notification.HighPriority)
                        {
                            jsonWriter.WriteNameValue("priority", "high");
                        }

                        jsonWriter.WriteStartObject("data");
                        foreach (var p in notification.Payload)
                        {
                            jsonWriter.WriteNameValue(p.Key, p.Value);
                        }
                        jsonWriter.WriteEndObject();

                        jsonWriter.WriteEndObject();
                    }

                    var sbody = stringWriter.ToString();
#if DEBUG
                    System.Diagnostics.Debug.WriteLine("https://fcm.googleapis.com/fcm/send: POST " + sbody);
#endif
                    body = System.Text.Encoding.UTF8.GetBytes(sbody);
                }

                HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create("https://fcm.googleapis.com/fcm/send");
                webreq.Method      = "POST";
                webreq.ContentType = "application/json; charset=UTF-8";
                webreq.Headers.Add("Authorization: key=" + Config.AuthorizationKey);
                webreq.Headers.Add("Sender: id=" + Config.SenderId);
                webreq.ContentLength = body.Length;

                string response;

                using (Stream dataStream = await webreq.GetRequestStreamAsync()) {
                    dataStream.Write(body, 0, body.Length);

                    using (HttpWebResponse webresp = (HttpWebResponse)await webreq.GetResponseAsync()) {
                        if (webresp.StatusCode != HttpStatusCode.OK)
                        {
                            throw new Exception("FCM server returns: " + webresp.StatusCode);
                        }

                        if (!webresp.ContentType.StartsWith("application/json;", StringComparison.Ordinal))
                        {
                            throw new Exception("FCM server returns invalid contenttype: " + webresp.ContentType);
                        }

                        using (Stream responseReader = webresp.GetResponseStream()) {
                            using (StreamReader reader = new StreamReader(responseReader, System.Text.Encoding.GetEncoding(webresp.CharacterSet))) {
                                response = await reader.ReadToEndAsync();
                            }
                        }
                    }
                }

                JsonObject jsonResponse = (JsonObject)JsonReader.ParseString(response);

                if (jsonResponse.GetValueInt("failure") != 0 || jsonResponse.GetValueInt("canonical_ids") != 0)
                {
                    var results = jsonResponse["results"];
                    if (results is JsonArray && ((JsonArray)results).Count == 1)
                    {
                        var result = ((JsonArray)results)[0];
                        if (result is JsonObject)
                        {
                            var error = ((JsonObject)result)["error"];

                            if (error is string)
                            {
                                Service.Error((string)error == "NotRegistered" || (string)error == "MismatchSenderId"
                                                ? new PushNotificationInvalidDeviceException(notification)
                                                : new PushNotificationException(notification, "Submit notification to '" + notification.DeviceAddress + "' failed error '" + error + "'."));
                                return;
                            }
                        }
                    }

                    Service.Error(new PushNotificationException(notification, "Submit notification to '" + notification.DeviceAddress + "' failed error '" + response + "'."));
                }
            }
            catch (Exception err) {
                Service.Error(new PushNotificationException(notification, "Failed to submit notification to '" + notification.DeviceAddress + "'.", err));
            }
        }
        public override async Task                        SendNotificationAsync(Notification notification)
        {
            byte[] msg = new byte[2102];

            int pos = 1 + 4;

            try {
                // 3 NotificationIdentifier
                var notificationIdentifier = _notificationIdentifier;
                {
                    msg[pos++] = 0x03;
                    msg[pos++] = 0;
                    msg[pos++] = 4;
                    msg[pos++] = (byte)((notificationIdentifier >> 24) & 0xFF);
                    msg[pos++] = (byte)((notificationIdentifier >> 16) & 0xFF);
                    msg[pos++] = (byte)((notificationIdentifier >> 8) & 0xFF);
                    msg[pos++] = (byte)((notificationIdentifier) & 0xFF);
                }

                if (notification != null)
                {
                    // 1 DeviceToken
                    {
                        var deviceToken = notification.DeviceAddress;
                        if (deviceToken.Length != 64)
                        {
                            throw new FormatException("Invalid DeviceToken.");
                        }

                        msg[pos++] = 0x01;
                        msg[pos++] = 0;
                        msg[pos++] = 32;

                        for (int i = 0; i < deviceToken.Length; i += 2)
                        {
                            msg[pos++] = (byte)(Library.HexToNibble(deviceToken[i]) << 4 | Library.HexToNibble(deviceToken[i + 1]));
                        }
                    }

                    // 2 Payload
                    {
                        byte[] bpayload;

                        using (var x = new StringWriter()) {
                            (new JsonWriter(x)).WriteValue(notification.Payload);
                            bpayload = Encoding.UTF8.GetBytes(x.ToString());
                        }

                        if (bpayload.Length > 2048)
                        {
                            throw new FormatException("Payload to big.");
                        }

                        msg[pos++] = 0x02;
                        msg[pos++] = (byte)((bpayload.Length >> 8) & 0xFF);
                        msg[pos++] = (byte)((bpayload.Length) & 0xFF);

                        Array.Copy(bpayload, 0, msg, pos, bpayload.Length);
                        pos += bpayload.Length;
                    }

                    // 4 ExpirationDate
                    {
                        var expireTime = notification.ExpireTime;
                        if (expireTime.Ticks < DateTime.UtcNow.Ticks + TimeSpan.TicksPerMinute)
                        {
                            throw new FormatException("Notification expired.");
                        }

                        var v = (Int32)((expireTime - Library.UnixEPoch).Ticks / TimeSpan.TicksPerSecond);

                        msg[pos++] = 0x04;
                        msg[pos++] = 0;
                        msg[pos++] = 4;
                        msg[pos++] = (byte)((v >> 24) & 0xFF);
                        msg[pos++] = (byte)((v >> 16) & 0xFF);
                        msg[pos++] = (byte)((v >> 8) & 0xFF);
                        msg[pos++] = (byte)((v) & 0xFF);
                    }

                    // 5 Priority
                    if (notification.HighPriority)
                    {
                        msg[pos++] = 0x05;
                        msg[pos++] = 0;
                        msg[pos++] = 1;
                        msg[pos++] = 0x05;
                    }
                }

                int sz = pos - 5;

                msg[0] = 0x02;
                msg[1] = (byte)((sz >> 24) & 0xFF);
                msg[2] = (byte)((sz >> 16) & 0xFF);
                msg[3] = (byte)((sz >> 8) & 0xFF);
                msg[4] = (byte)((sz) & 0xFF);

                var queued = false;

                lock (_lockObject) {
                    if (_notifications != null)
                    {
                        _notifications.Insert(_notificationIdentifier++, notification);
                        queued = true;
                    }
                }

                if (!queued)
                {
                    if (notification != null)
                    {
                        Service.SendNotification(notification);
                    }
                    return;
                }
            }
            catch (Exception err) {
                if (notification != null)
                {
                    Service.Error(new PushNotificationException(notification, "Notification to '" + notification.DeviceAddress + "' failed. Invalid notification format.", err));
                }
            }

            try {
                await _connection.Send(msg, pos);
            }
            catch (Exception err) {
                Dispose();
                Service.Error(new PushNotificationServiceException("Sending request to APSN failed.", err));
            }
        }