push.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * This file is part of Threema Web.
  3. *
  4. * Threema Web is free software: you can redistribute it and/or modify it
  5. * under the terms of the GNU Affero General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or (at
  7. * your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. import {sha256} from '../helpers/crypto';
  18. export class PushService {
  19. private static ARG_TYPE = 'type';
  20. private static ARG_TOKEN = 'token';
  21. private static ARG_SESSION = 'session';
  22. private static ARG_VERSION = 'version';
  23. private static ARG_ENDPOINT = 'endpoint';
  24. private static ARG_BUNDLE_ID = 'bundleid';
  25. private logTag: string = '[PushService]';
  26. private $http: ng.IHttpService;
  27. private $log: ng.ILogService;
  28. private $httpParamSerializerJQLike;
  29. private url: string;
  30. private pushToken: string = null;
  31. private pushType = threema.PushTokenType.Gcm;
  32. private version: number = null;
  33. public static $inject = ['$http', '$log', '$httpParamSerializerJQLike', 'CONFIG', 'PROTOCOL_VERSION'];
  34. constructor($http: ng.IHttpService, $log: ng.ILogService, $httpParamSerializerJQLike,
  35. CONFIG: threema.Config, PROTOCOL_VERSION: number) {
  36. this.$http = $http;
  37. this.$log = $log;
  38. this.$httpParamSerializerJQLike = $httpParamSerializerJQLike;
  39. this.url = CONFIG.PUSH_URL;
  40. this.version = PROTOCOL_VERSION;
  41. }
  42. /**
  43. * Initiate the push service with a push token.
  44. */
  45. public init(pushToken: string, pushTokenType: threema.PushTokenType): void {
  46. this.$log.info(this.logTag, 'Initialized with', pushTokenType, 'token');
  47. this.pushToken = pushToken;
  48. this.pushType = pushTokenType;
  49. }
  50. /**
  51. * Reset the push service, remove stored push tokens.
  52. */
  53. public reset(): void {
  54. this.pushToken = null;
  55. }
  56. /**
  57. * Return true if service has been initialized with a push token.
  58. */
  59. public isAvailable(): boolean {
  60. return this.pushToken != null;
  61. }
  62. /**
  63. * Send a push notification for the specified session (public permanent key
  64. * of the initiator). The promise is always resolved to a boolean.
  65. */
  66. public async sendPush(session: Uint8Array): Promise<boolean> {
  67. if (!this.isAvailable()) {
  68. return false;
  69. }
  70. // Calculate session hash
  71. const sessionHash = await sha256(session.buffer);
  72. // Prepare request
  73. const data = {
  74. [PushService.ARG_TYPE]: this.pushType,
  75. [PushService.ARG_SESSION]: sessionHash,
  76. [PushService.ARG_VERSION]: this.version,
  77. };
  78. if (this.pushType === threema.PushTokenType.Apns) {
  79. // APNS token format: "<hex-deviceid>;<endpoint>;<bundle-id>"
  80. const parts = this.pushToken.split(';');
  81. if (parts.length < 3) {
  82. this.$log.warn(this.logTag, 'APNS push token contains', parts.length, 'parts, at least 3 are required');
  83. return false;
  84. }
  85. data[PushService.ARG_TOKEN] = parts[0];
  86. data[PushService.ARG_ENDPOINT] = parts[1];
  87. data[PushService.ARG_BUNDLE_ID] = parts[2];
  88. } else if (this.pushType === threema.PushTokenType.Gcm) {
  89. data[PushService.ARG_TOKEN] = this.pushToken;
  90. } else {
  91. this.$log.warn(this.logTag, 'Invalid push type');
  92. return false;
  93. }
  94. const request = {
  95. method: 'POST',
  96. url: this.url,
  97. headers: {
  98. 'Content-Type': 'application/x-www-form-urlencoded',
  99. },
  100. data: this.$httpParamSerializerJQLike(data),
  101. };
  102. // Send push
  103. return new Promise((resolve) => {
  104. this.$http(request).then(
  105. (successResponse) => {
  106. if (successResponse.status === 204) {
  107. this.$log.debug(this.logTag, 'Sent push');
  108. resolve(true);
  109. } else {
  110. this.$log.warn(this.logTag, 'Sending push failed: HTTP ' + successResponse.status);
  111. resolve(false);
  112. }
  113. },
  114. (errorResponse) => {
  115. this.$log.warn(this.logTag, 'Sending push failed:', errorResponse);
  116. resolve(false);
  117. },
  118. );
  119. }) as Promise<boolean>;
  120. }
  121. }