mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 00:23:49 +00:00
Maintain two APNS connections and send correct notifications to each
Now we have 2 different Zulip apps out there, and they are signed with two certs: Zulip and Dropbox. The Dropbox-signed apps are going to need to be sent APNS notifications from the appropriate APNS connection (imported from commit 6db50c5811847db4f08e5c997c7bbb4b46cfc462)
This commit is contained in:
@@ -18,6 +18,13 @@ connection = None
|
||||
if settings.APNS_CERT_FILE is not None and os.path.exists(settings.APNS_CERT_FILE):
|
||||
connection = session.get_connection(settings.APNS_SANDBOX, cert_file=settings.APNS_CERT_FILE)
|
||||
|
||||
# We maintain an additional APNS connection for pushing to Zulip apps that have been signed
|
||||
# by the Dropbox certs (and have an app id of com.dropbox.zulip)
|
||||
dbx_session = Session()
|
||||
dbx_connection = None
|
||||
if settings.DBX_APNS_CERT_FILE is not None and os.path.exists(settings.DBX_APNS_CERT_FILE):
|
||||
dbx_connection = session.get_connection(settings.APNS_SANDBOX, cert_file=settings.DBX_APNS_CERT_FILE)
|
||||
|
||||
def num_push_devices_for_user(user_profile, kind = None):
|
||||
if kind is None:
|
||||
return PushDeviceToken.objects.filter(user=user_profile).count()
|
||||
@@ -31,25 +38,11 @@ def b64_to_hex(data):
|
||||
def hex_to_b64(data):
|
||||
return base64.b64encode(binascii.unhexlify(data))
|
||||
|
||||
# Send a push notification to the desired clients
|
||||
# extra_data is a dict that will be passed to the
|
||||
# mobile app
|
||||
@statsd_increment("apple_push_notification")
|
||||
def send_apple_push_notification(user, alert, **extra_data):
|
||||
if not connection:
|
||||
logging.error("Attempting to send push notification, but no connection was found. This may be because we could not find the APNS Certificate file.")
|
||||
return
|
||||
|
||||
b64_tokens = [device.token for device in PushDeviceToken.objects.filter(user=user, kind=PushDeviceToken.APNS)]
|
||||
tokens = [b64_to_hex(token) for token in b64_tokens]
|
||||
|
||||
logging.info("APNS: Sending apple push notification to devices: %s" % (b64_tokens,))
|
||||
message = Message(tokens, alert=alert, **extra_data)
|
||||
|
||||
apns_client = APNs(connection)
|
||||
def _do_push_to_apns_service(user, message, apns_connection):
|
||||
apns_client = APNs(apns_connection)
|
||||
ret = apns_client.send(message)
|
||||
if not ret:
|
||||
logging.warning("APNS: Failed to send push notification for clients %s" % (b64_tokens,))
|
||||
logging.warning("APNS: Failed to send push notification for clients %s" % (message.tokens,))
|
||||
return
|
||||
|
||||
for token, reason in ret.failed.items():
|
||||
@@ -73,6 +66,28 @@ def send_apple_push_notification(user, alert, **extra_data):
|
||||
logging.warning("APNS: Unknown error when delivering APNS: %s" % (errmsg,))
|
||||
|
||||
|
||||
# Send a push notification to the desired clients
|
||||
# extra_data is a dict that will be passed to the
|
||||
# mobile app
|
||||
@statsd_increment("apple_push_notification")
|
||||
def send_apple_push_notification(user, alert, **extra_data):
|
||||
if not connection and not dbx_connection:
|
||||
logging.error("Attempting to send push notification, but no connection was found. This may be because we could not find the APNS Certificate file.")
|
||||
return
|
||||
|
||||
devices = PushDeviceToken.objects.filter(user=user, kind=PushDeviceToken.APNS)
|
||||
# Plain b64 token kept for debugging purposes
|
||||
tokens = [(b64_to_hex(device.token), device.ios_app_id, device.token) for device in devices]
|
||||
|
||||
logging.info("APNS: Sending apple push notification to devices: %s" % (tokens,))
|
||||
zulip_message = Message([token[0] for token in tokens if token[1] in (settings.ZULIP_IOS_APP_ID, None)],
|
||||
alert=alert, **extra_data)
|
||||
dbx_message = Message([token[0] for token in tokens if token[1] in (settings.DBX_IOS_APP_ID,)],
|
||||
alert=alert, **extra_data)
|
||||
|
||||
_do_push_to_apns_service(user, zulip_message, connection)
|
||||
_do_push_to_apns_service(user, dbx_message, dbx_connection)
|
||||
|
||||
# NOTE: This is used by the check_apns_tokens manage.py command. Do not call it otherwise, as the
|
||||
# feedback() call can take up to 15s
|
||||
def check_apns_feedback():
|
||||
|
||||
@@ -2281,7 +2281,7 @@ def json_set_muted_topics(request, user_profile,
|
||||
do_set_muted_topics(user_profile, muted_topics)
|
||||
return json_success()
|
||||
|
||||
def add_push_device_token(request, user_profile, token, kind):
|
||||
def add_push_device_token(request, user_profile, token, kind, ios_app_id=None):
|
||||
if token == '' or len(token) > 4096:
|
||||
return json_error('Empty or invalid length token')
|
||||
|
||||
@@ -2290,7 +2290,10 @@ def add_push_device_token(request, user_profile, token, kind):
|
||||
PushDeviceToken.objects.filter(token=token).delete()
|
||||
|
||||
# Overwrite with the latest value
|
||||
token, created = PushDeviceToken.objects.get_or_create(user=user_profile, token=token, kind=kind)
|
||||
token, created = PushDeviceToken.objects.get_or_create(user=user_profile,
|
||||
token=token,
|
||||
kind=kind,
|
||||
ios_app_id=ios_app_id)
|
||||
if not created:
|
||||
token.last_updated = now()
|
||||
token.save(update_fields=['last_updated'])
|
||||
@@ -2298,8 +2301,8 @@ def add_push_device_token(request, user_profile, token, kind):
|
||||
return json_success()
|
||||
|
||||
@has_request_variables
|
||||
def add_apns_device_token(request, user_profile, token=REQ):
|
||||
return add_push_device_token(request, user_profile, token, PushDeviceToken.APNS)
|
||||
def add_apns_device_token(request, user_profile, token=REQ, appid=REQ(default=settings.ZULIP_IOS_APP_ID)):
|
||||
return add_push_device_token(request, user_profile, token, PushDeviceToken.APNS, ios_app_id=appid)
|
||||
|
||||
@has_request_variables
|
||||
def add_android_reg_id(request, user_profile, token=REQ):
|
||||
|
||||
@@ -126,10 +126,12 @@ if DEPLOYED or STAGING_DEPLOYED:
|
||||
APNS_SANDBOX = "push_production"
|
||||
APNS_FEEDBACK = "feedback_production"
|
||||
APNS_CERT_FILE = "/etc/ssl/django-private/apns-dist.pem"
|
||||
DBX_APNS_CERT_FILE = "/etc/ssl/django-private/dbx-apns-dist.pem"
|
||||
else:
|
||||
APNS_SANDBOX = "push_sandbox"
|
||||
APNS_FEEDBACK = "feedback_sandbox"
|
||||
APNS_CERT_FILE = "/etc/ssl/django-private/apns-dev.pem"
|
||||
DBX_APNS_CERT_FILE = "/etc/ssl/django-private/dbx-apns-dev.pem"
|
||||
|
||||
# GCM tokens are IP-whitelisted; if we deploy to additional
|
||||
# servers you will need to explicitly add their IPs here:
|
||||
@@ -208,3 +210,6 @@ API_SUPER_USERS = set(["tabbott/extra@mit.edu",
|
||||
ADMINS = (
|
||||
('Zulip Error Reports', 'errors@zulip.com'),
|
||||
)
|
||||
|
||||
ZULIP_IOS_APP_ID = 'com.zulip.Zulip'
|
||||
DBX_IOS_APP_ID = 'com.dropbox.Zulip'
|
||||
|
||||
@@ -297,6 +297,7 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
|
||||
'EXTERNAL_URI_SCHEME': "https://",
|
||||
'GOOGLE_CLIENT_ID': '',
|
||||
'REDIS_PASSWORD': None,
|
||||
'DBX_APNS_CERT_FILE': None,
|
||||
}
|
||||
|
||||
for setting_name, setting_val in DEFAULT_SETTINGS.iteritems():
|
||||
|
||||
Reference in New Issue
Block a user