diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e122304b6..0ec87ae98 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -321,6 +321,7 @@ All definitions files include a header with the author and editors, so at some p * [SignalR](http://www.asp.net/signalr) (by [Boris Yankov](https://github.com/borisyankov)) * [simple-cw-node](https://github.com/astronaughts/simple-cw-node) (by [vvakame](https://github.com/vvakame)) * [Sinon](http://sinonjs.org/) (by [William Sears](https://github.com/mrbigdog2u)) +* [SIPml](http://sipml5.org/) (by [Adriaan Groenenboom](https://github.com/chookies)) * [sjcl](http://crypto.stanford.edu/sjcl/) (by [Eugene Chernyshov](https://github.com/Evgenus)) * [SlickGrid](https://github.com/mleibman/SlickGrid) (by [Josh Baldwin](https://github.com/jbaldwin)) * [smoothie](https://github.com/joewalnes/smoothie) (by [Mike H. Hawley](https://github.com/mikehhawley) and [Drew Noakes](https://drewnoakes.com)) diff --git a/sipml/sipml-test.ts b/sipml/sipml-test.ts new file mode 100644 index 000000000..5fed26fc6 --- /dev/null +++ b/sipml/sipml-test.ts @@ -0,0 +1,169 @@ +/// + +/* Code borrowed from http://sipml5.org/docgen/index.html?svn=224 */ + +var acceptMessage = (e: any)=> { + e.newSession.accept(); // e.newSession.reject(); to reject the message + console.info('SMS-content = ' + e.getContentString() + ' and SMS-content-type = ' + e.getContentType()); +}; +var acceptCall = (e:any)=> { + e.newSession.accept(); // e.newSession.reject() to reject the call +}; + + /* Initialize the engine */ +var readyCallback = (e:any)=> { + createSipStack(); // see next section +}; +var errorCallback = (e:any)=> { + console.error('Failed to initialize the engine: ' + e.message); +} +SIPml.init(readyCallback, errorCallback); + +/* Create a SIP stack */ +var sipStack: SIPml.Stack; +var eventsListener = (e:any)=> { + if(e.type == 'started'){ + login(); + } + else if(e.type == 'i_new_message'){ // incoming new SIP MESSAGE (SMS-like) + acceptMessage(e); + } + else if(e.type == 'i_new_call'){ // incoming audio/video call + acceptCall(e); + } +} + +function createSipStack(){ + sipStack = new SIPml.Stack('blaat'); + sipStack = new SIPml.Stack({ + realm: 'example.org', // mandatory: domain name + impi: 'bob', // mandatory: authorization name (IMS Private Identity) + impu: 'sip:bob@example.org', // mandatory: valid SIP Uri (IMS Public Identity) + password: 'mysecret', // optional + display_name: 'Bob legend', // optional + websocket_proxy_url: 'wss://sipml5.org:10062', // optional + outbound_proxy_url: 'udp://example.org:5060', // optional + enable_rtcweb_breaker: false, // optional + events_listener: { events: '*', listener: eventsListener }, // optional: '*' means all events + sip_headers: [ // optional + { name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.0.0' }, + { name: 'Organization', value: 'Doubango Telecom' } + ] + } + ); +} +sipStack.start(); + +/* Register/login */ +var registerSession: SIPml.Session.Registration; +var eventsListener = (e:any)=>{ + console.info('session event = ' + e.type); + if(e.type == 'connected' && e.session == registerSession){ + makeCall(); + sendMessage(); + publishPresence(); + subscribePresence('johndoe'); // watch johndoe's presence status change + } +} +var login = ()=>{ + registerSession = sipStack.newSession('register', { + events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events + }); + registerSession.register(); +} + +/* Making/receiving audio/video call */ +var callSession: SIPml.Session.Call; +var eventsListener = (e:any)=>{ + console.info('session event = ' + e.type); +} +var makeCall = ()=>{ + callSession = sipStack.newSession('call-audiovideo', { + video_local: document.getElementById('video-local'), + video_remote: document.getElementById('video-remote'), + audio_remote: document.getElementById('audio-remote'), + events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events + }); + callSession.call('johndoe'); +} +var acceptCall = (e:any)=>{ + e.newSession.accept(); // e.newSession.reject() to reject the call +} + +/* Send/receive SIP MESSAGE (SMS-like) */ +var messageSession: SIPml.Session.Message; +var eventsListener = (e:any)=>{ + console.info('session event = ' + e.type); +} +var sendMessage = ()=>{ + messageSession = sipStack.newSession('message', { + events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events + }); + messageSession.send('johndoe', 'Pêche à la moule', 'text/plain;charset=utf-8'); +} + +/* Publish presence status */ +var publishSession: SIPml.Session.Publish; +var eventsListener = (e:any)=>{ + console.info('session event = ' + e.type); +} +var publishPresence = ()=>{ + publishSession = sipStack.newSession('publish', { + events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events + }); + var contentType = 'application/pidf+xml'; + var content = '\n' + + '\n' + + '\n' + + '\n'+ + ' open\n' + + ' away\n' + + '\n' + + 'tel:+33600000000\n' + + 'Bonjour de Paris :)\n' + + '\n' + + ''; + + // send the PUBLISH request + publishSession.publish(content, contentType,{ + expires: 200, + sip_caps: [ + { name: '+g.oma.sip-im' }, + { name: '+sip.ice' }, + { name: 'language', value: '\"en,fr\"' } + ], + sip_headers: [ + { name: 'Event', value: 'presence' }, + { name: 'Organization', value: 'Doubango Telecom' } + ] + }); +} + +/* Subscribe for presence status */ +var subscribeSession: SIPml.Session.Subscribe; +var eventsListener = (e:any)=>{ + console.info('session event = ' + e.type); + if(e.type == 'i_notify'){ + console.info('NOTIFY content = ' + e.getContentString()); + console.info('NOTIFY content-type = ' + e.getContentType()); + } +} +var subscribePresence = (to:string)=>{ + subscribeSession = sipStack.newSession('subscribe', { + expires: 200, + events_listener: { events: '*', listener: eventsListener }, + sip_headers: [ + { name: 'Event', value: 'presence' }, // only notify for 'presence' events + { name: 'Accept', value: 'application/pidf+xml' } // supported content types (COMMA-sparated) + ], + sip_caps: [ + { name: '+g.oma.sip-im', value: null }, + { name: '+audio', value: null }, + { name: 'language', value: '\"en,fr\"' } + ] + }); + // start watching for entity's presence status (You may track event type 'connected' to be sure that the request has been accepted by the server) + subscribeSession.subscribe(to); +} diff --git a/sipml/sipml.d.ts b/sipml/sipml.d.ts new file mode 100644 index 000000000..a4fe32784 --- /dev/null +++ b/sipml/sipml.d.ts @@ -0,0 +1,149 @@ +// Type definitions for SIPml5 +// Project: http://sipml5.org/ +// Definitions by: A. Groenenboom +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module SIPml { + class Event { + public description: string; + public type: string; + + public getContent(): Object; + public getContentString(): string; + public getContentType(): Object; + public getSipResponseCode(): number; + } + + class EventTarget { + public addEventListener(type: any, listener: Function): void; + public removeEventListener(type: any): void; + } + + class Session { + public accept(configuration?: Session.Configuration): number; + public getId(): number; + public getRemoteFriendlyName(): string; + public getRemoteUri(): string; + public reject(configuration?: Session.Configuration): number; + public setConfiguration(configuration?: Session.Configuration): void; + } + + export module Session { + interface Configuration { + audio_remote?: HTMLAudioElement; + bandwidth?: Object; + expires?: number; + from?: string; + sip_caps?: Object[]; + sip_headers?: Object[]; + video_local?: HTMLVideoElement; + video_remote?: HTMLVideoElement; + video_size?: Object; + } + + class Call extends Session { + public acceptTransfer(configuration?: Session.Configuration): number; + public call(to: string, configuration?: Session.Configuration): number; + public dtmf(): number; + public hangup(configuration?: Session.Configuration): number; + public hold(configuration?: Session.Configuration): number; + public info(): number; + public rejectTransfer(): number; + public resume(): number; + public transfer(): number; + } + + class Event extends SIPml.Event { + public session: Session; + + public getTransferDestinationFriendlyName(): string; + } + + class Message extends Session { + public send(to: string, content?: any, contentType?: string, configuration?: Session.Configuration): number; + } + + class Publish extends Session { + public publish(content?: any, contentType?: string, configuration?: Session.Configuration): number; + + public unpublish(configuration?: Session.Configuration): void; + } + + class Registration extends Session { + public register(configuration?: Session.Configuration): void; + public unregister(configuration?: Session.Configuration): void; + } + + class Subscribe extends Session { + public subscribe(to: string, configuration?: Session.Configuration): number; + public unsubscribe(configuration?: Session.Configuration): number; + } + } + + class Stack extends EventTarget { + public constructor(configuration?: Stack.Configuration); + public setConfiguration(configuration: Stack.Configuration): number; + public newSession(type: string, configuration: Stack.Configuration): any; + public start(): number; + public stop(timeout: number): number; + } + + export module Stack { + interface Configuration { + bandwidth?: Object; + display_name?: string; + enable_click2call?: boolean; + enable_early_ims?: boolean; + enable_media_stream_cache?: boolean; + enable_rtcweb_breaker?: boolean; + events_listener?: Object; + ice_servers?: Object[]; + impi?: string; + impu?: string; + outbound_proxy_url?: string; + password?: string; + realm?: string; + sip_headers?: Object[]; + video_size?: Object; + websocket_proxy_url?: string; + } + + class Event extends SIPml.Event { + public description: string; + public newSession: Session; + public type: string; + } + } + + function getNavigatorFriendlyName(): string; + + function getNavigatorVersion(): string; + + function getSystemFriendlyName(): string; + + function getWebRtc4AllVersion(): string; + + function haveMediaStream(): boolean; + + function init(readyCallback?: (e:any) => any, errorCallback?: (e:any) => any): boolean; + + function isInitialized(): boolean; + + function isNavigatorOutdated(): boolean; + + function isReady(): boolean; + + function isScreenShareSupported(): boolean; + + function isWebRtcPluginOutdated(): boolean; + + function isWebRtc4AllSupported(): boolean; + + function isWebRtcSupported(): boolean; + + function isWebSocketSupported(): boolean; + + function setDebugLevel(level: string): void; + + function setWebRtcType(type: string): boolean; +}