diff --git a/lib/Subscribe.d.ts b/lib/Subscribe.d.ts
new file mode 100644
index 000000000..d2a353ff3
--- /dev/null
+++ b/lib/Subscribe.d.ts
@@ -0,0 +1,23 @@
+///
+import {EventEmitter} from 'events'
+
+import {ExtraHeaders, Originator, OutgoingListener, SessionDirection, TerminateOptions} from './RTCSession'
+import {IncomingResponse} from './SIPMessage'
+import {NameAddrHeader} from './NameAddrHeader'
+import {causes} from './Constants';
+
+export interface AcceptOptions extends ExtraHeaders {
+ body?: string;
+}
+
+export interface SendSubscribeOptions extends ExtraHeaders {
+ contentType?: string;
+ eventHandlers?: Partial;
+}
+
+export class Subscribe extends EventEmitter {
+
+ send(target: string, body: string, options?: SendSubscribeOptions): void;
+
+ on(type: T, listener: MessageEventMap[T]): this;
+}
diff --git a/lib/Subscribe.js b/lib/Subscribe.js
new file mode 100644
index 000000000..5dba1ff7b
--- /dev/null
+++ b/lib/Subscribe.js
@@ -0,0 +1,156 @@
+const EventEmitter = require('events').EventEmitter;
+const Logger = require('./Logger');
+const JsSIP_C = require('./Constants');
+const SIPMessage = require('./SIPMessage');
+const Utils = require('./Utils');
+const RequestSender = require('./RequestSender');
+
+const logger = new Logger('Subscribe');
+
+module.exports = class Subscribe extends EventEmitter
+{
+ constructor(ua)
+ {
+ super();
+
+ this._ua = ua;
+ this._request = null;
+
+ }
+
+ send(target, body, options = {})
+ {
+ const originalTarget = target;
+
+ if (target === undefined || body === undefined)
+ {
+ throw new TypeError('Not enough arguments');
+ }
+
+ // Check target validity.
+ target = this._ua.normalizeTarget(target);
+ if (!target)
+ {
+ throw new TypeError(`Invalid target: ${originalTarget}`);
+ }
+
+ // Get call options.
+ const extraHeaders = Utils.cloneArray(options.extraHeaders);
+ const subscriptionDuration = options.subscriptionDuration || 1;
+ const contentType = options.contentType || 'text/plain';
+ const eventType = options.eventType;
+
+ extraHeaders.push(`Content-Type: ${contentType}`);
+ if (eventType === 'message-summary')
+ {
+ extraHeaders.push(`Contact: ${target}`);
+ extraHeaders.push(`Expires: ${subscriptionDuration}`);
+ extraHeaders.push(`Event: ${eventType}`);
+ }
+
+ this._request = new SIPMessage.OutgoingRequest(
+ JsSIP_C.SUBSCRIBE, target, this._ua, null, extraHeaders);
+
+ if (body)
+ {
+ this._request.body = body;
+ }
+
+ const request_sender = new RequestSender(this._ua, this._request, {
+ onRequestTimeout : () =>
+ {
+ this._onRequestTimeout();
+ },
+ onTransportError : () =>
+ {
+ this._onTransportError();
+ },
+ onReceiveResponse : (response) =>
+ {
+ this._receiveResponse(response);
+ }
+ });
+
+ request_sender.send();
+ }
+
+ _receiveResponse(response)
+ {
+ if (this._closed)
+ {
+ return;
+ }
+ switch (true)
+ {
+ case /^1[0-9]{2}$/.test(response.status_code):
+ // Ignore provisional responses.
+ break;
+
+ case /^2[0-9]{2}$/.test(response.status_code):
+ this._succeeded('remote', response);
+ break;
+
+ default:
+ {
+ const cause = Utils.sipErrorCause(response.status_code);
+
+ this._failed('remote', response, cause);
+ break;
+ }
+ }
+ }
+
+ _onRequestTimeout()
+ {
+ if (this._closed)
+ {
+ return;
+ }
+ this._failed('system', null, JsSIP_C.causes.REQUEST_TIMEOUT);
+ }
+
+ _onTransportError()
+ {
+ if (this._closed)
+ {
+ return;
+ }
+ this._failed('system', null, JsSIP_C.causes.CONNECTION_ERROR);
+ }
+
+ _close()
+ {
+ this._closed = true;
+ this._ua.destroyMessage(this);
+ }
+
+
+ _failed(originator, response, cause)
+ {
+ logger.debug('SUBSCRIBE failed');
+
+ this._close();
+
+ logger.debug('emit "failed"');
+
+ this.emit('failed', {
+ originator,
+ response : response || null,
+ cause
+ });
+ }
+
+ _succeeded(originator, response)
+ {
+ logger.debug('SUBSCRIBEsucceeded');
+
+ this._close();
+
+ logger.debug('emit "succeeded"');
+
+ this.emit('succeeded', {
+ originator,
+ response
+ });
+ }
+};
diff --git a/lib/UA.js b/lib/UA.js
index fa26c2183..221dd98b5 100644
--- a/lib/UA.js
+++ b/lib/UA.js
@@ -4,6 +4,7 @@ const JsSIP_C = require('./Constants');
const Registrator = require('./Registrator');
const RTCSession = require('./RTCSession');
const Message = require('./Message');
+const Subscribe = require('./Subscribe');
const Options = require('./Options');
const Transactions = require('./Transactions');
const Transport = require('./Transport');
@@ -257,6 +258,27 @@ module.exports = class UA extends EventEmitter
return message;
}
+ /**
+ * Send a subscription.
+ *
+ * -param {String} target
+ * -param {String} body
+ * -param {Object} [options]
+ *
+ * -throws {TypeError}
+ *
+ */
+ sendSubscribe(target, body, options)
+ {
+ logger.debug('sendSubscribe()');
+
+ const subscribe = new Subscribe(this);
+
+ subscribe.send(target, body, options);
+
+ return subscribe;
+ }
+
/**
* Send a SIP OPTIONS.
*
@@ -742,6 +764,15 @@ module.exports = class UA extends EventEmitter
{
session.receiveRequest(request);
}
+ // RFC3842 - check for message waiting information
+ else if (request.event['event'] === 'message-summary')
+ {
+ this.emit('sipEvent', {
+ event : request.event,
+ request
+ });
+ request.reply(200);
+ }
else
{
logger.debug('received NOTIFY request for a non existent subscription');