12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860 |
- /*
- * SIP version 0.7.8
- * Copyright (c) 2014-2017 Junction Networks, Inc <http://www.onsip.com>
- * Homepage: http://sipjs.com
- * License: http://sipjs.com/license/
- *
- *
- * ~~~SIP.js contains substantial portions of JsSIP under the following license~~~
- * Homepage: http://jssip.net
- * Copyright (c) 2012-2013 José Luis Millán - Versatica <http://www.versatica.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * ~~~ end JsSIP license ~~~
- */
- (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SIP = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
- // Copyright Joyent, Inc. and other Node contributors.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to permit
- // persons to whom the Software is furnished to do so, subject to the
- // following conditions:
- //
- // The above copyright notice and this permission notice shall be included
- // in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
- // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- // USE OR OTHER DEALINGS IN THE SOFTWARE.
- function EventEmitter() {
- this._events = this._events || {};
- this._maxListeners = this._maxListeners || undefined;
- }
- module.exports = EventEmitter;
- // Backwards-compat with node 0.10.x
- EventEmitter.EventEmitter = EventEmitter;
- EventEmitter.prototype._events = undefined;
- EventEmitter.prototype._maxListeners = undefined;
- // By default EventEmitters will print a warning if more than 10 listeners are
- // added to it. This is a useful default which helps finding memory leaks.
- EventEmitter.defaultMaxListeners = 10;
- // Obviously not all Emitters should be limited to 10. This function allows
- // that to be increased. Set to zero for unlimited.
- EventEmitter.prototype.setMaxListeners = function(n) {
- if (!isNumber(n) || n < 0 || isNaN(n))
- throw TypeError('n must be a positive number');
- this._maxListeners = n;
- return this;
- };
- EventEmitter.prototype.emit = function(type) {
- var er, handler, len, args, i, listeners;
- if (!this._events)
- this._events = {};
- // If there is no 'error' event listener then throw.
- if (type === 'error') {
- if (!this._events.error ||
- (isObject(this._events.error) && !this._events.error.length)) {
- er = arguments[1];
- if (er instanceof Error) {
- throw er; // Unhandled 'error' event
- }
- throw TypeError('Uncaught, unspecified "error" event.');
- }
- }
- handler = this._events[type];
- if (isUndefined(handler))
- return false;
- if (isFunction(handler)) {
- switch (arguments.length) {
- // fast cases
- case 1:
- handler.call(this);
- break;
- case 2:
- handler.call(this, arguments[1]);
- break;
- case 3:
- handler.call(this, arguments[1], arguments[2]);
- break;
- // slower
- default:
- len = arguments.length;
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- handler.apply(this, args);
- }
- } else if (isObject(handler)) {
- len = arguments.length;
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- listeners = handler.slice();
- len = listeners.length;
- for (i = 0; i < len; i++)
- listeners[i].apply(this, args);
- }
- return true;
- };
- EventEmitter.prototype.addListener = function(type, listener) {
- var m;
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
- if (!this._events)
- this._events = {};
- // To avoid recursion in the case that type === "newListener"! Before
- // adding it to the listeners, first emit "newListener".
- if (this._events.newListener)
- this.emit('newListener', type,
- isFunction(listener.listener) ?
- listener.listener : listener);
- if (!this._events[type])
- // Optimize the case of one listener. Don't need the extra array object.
- this._events[type] = listener;
- else if (isObject(this._events[type]))
- // If we've already got an array, just append.
- this._events[type].push(listener);
- else
- // Adding the second element, need to change to array.
- this._events[type] = [this._events[type], listener];
- // Check for listener leak
- if (isObject(this._events[type]) && !this._events[type].warned) {
- var m;
- if (!isUndefined(this._maxListeners)) {
- m = this._maxListeners;
- } else {
- m = EventEmitter.defaultMaxListeners;
- }
- if (m && m > 0 && this._events[type].length > m) {
- this._events[type].warned = true;
- console.error('(node) warning: possible EventEmitter memory ' +
- 'leak detected. %d listeners added. ' +
- 'Use emitter.setMaxListeners() to increase limit.',
- this._events[type].length);
- if (typeof console.trace === 'function') {
- // not supported in IE 10
- console.trace();
- }
- }
- }
- return this;
- };
- EventEmitter.prototype.on = EventEmitter.prototype.addListener;
- EventEmitter.prototype.once = function(type, listener) {
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
- var fired = false;
- function g() {
- this.removeListener(type, g);
- if (!fired) {
- fired = true;
- listener.apply(this, arguments);
- }
- }
- g.listener = listener;
- this.on(type, g);
- return this;
- };
- // emits a 'removeListener' event iff the listener was removed
- EventEmitter.prototype.removeListener = function(type, listener) {
- var list, position, length, i;
- if (!isFunction(listener))
- throw TypeError('listener must be a function');
- if (!this._events || !this._events[type])
- return this;
- list = this._events[type];
- length = list.length;
- position = -1;
- if (list === listener ||
- (isFunction(list.listener) && list.listener === listener)) {
- delete this._events[type];
- if (this._events.removeListener)
- this.emit('removeListener', type, listener);
- } else if (isObject(list)) {
- for (i = length; i-- > 0;) {
- if (list[i] === listener ||
- (list[i].listener && list[i].listener === listener)) {
- position = i;
- break;
- }
- }
- if (position < 0)
- return this;
- if (list.length === 1) {
- list.length = 0;
- delete this._events[type];
- } else {
- list.splice(position, 1);
- }
- if (this._events.removeListener)
- this.emit('removeListener', type, listener);
- }
- return this;
- };
- EventEmitter.prototype.removeAllListeners = function(type) {
- var key, listeners;
- if (!this._events)
- return this;
- // not listening for removeListener, no need to emit
- if (!this._events.removeListener) {
- if (arguments.length === 0)
- this._events = {};
- else if (this._events[type])
- delete this._events[type];
- return this;
- }
- // emit removeListener for all listeners on all events
- if (arguments.length === 0) {
- for (key in this._events) {
- if (key === 'removeListener') continue;
- this.removeAllListeners(key);
- }
- this.removeAllListeners('removeListener');
- this._events = {};
- return this;
- }
- listeners = this._events[type];
- if (isFunction(listeners)) {
- this.removeListener(type, listeners);
- } else {
- // LIFO order
- while (listeners.length)
- this.removeListener(type, listeners[listeners.length - 1]);
- }
- delete this._events[type];
- return this;
- };
- EventEmitter.prototype.listeners = function(type) {
- var ret;
- if (!this._events || !this._events[type])
- ret = [];
- else if (isFunction(this._events[type]))
- ret = [this._events[type]];
- else
- ret = this._events[type].slice();
- return ret;
- };
- EventEmitter.listenerCount = function(emitter, type) {
- var ret;
- if (!emitter._events || !emitter._events[type])
- ret = 0;
- else if (isFunction(emitter._events[type]))
- ret = 1;
- else
- ret = emitter._events[type].length;
- return ret;
- };
- function isFunction(arg) {
- return typeof arg === 'function';
- }
- function isNumber(arg) {
- return typeof arg === 'number';
- }
- function isObject(arg) {
- return typeof arg === 'object' && arg !== null;
- }
- function isUndefined(arg) {
- return arg === void 0;
- }
- },{}],2:[function(require,module,exports){
- module.exports={
- "name": "sip.js",
- "title": "SIP.js",
- "description": "A simple, intuitive, and powerful JavaScript signaling library",
- "version": "0.7.8",
- "main": "src/index.js",
- "browser": {
- "./src/environment.js": "./src/environment_browser.js"
- },
- "homepage": "http://sipjs.com",
- "author": "OnSIP <[email protected]> (http://sipjs.com/authors/)",
- "contributors": [
- {
- "url": "https://github.com/onsip/SIP.js/blob/master/THANKS.md"
- }
- ],
- "repository": {
- "type": "git",
- "url": "https://github.com/onsip/SIP.js.git"
- },
- "keywords": [
- "sip",
- "websocket",
- "webrtc",
- "library",
- "javascript"
- ],
- "devDependencies": {
- "beefy": "^2.1.5",
- "browserify": "^4.1.8",
- "grunt": "~0.4.0",
- "grunt-browserify": "^4.0.1",
- "grunt-cli": "~0.1.6",
- "grunt-contrib-copy": "^0.5.0",
- "grunt-contrib-jasmine": "^1.0.3",
- "grunt-contrib-jshint": ">0.5.0",
- "grunt-contrib-uglify": "~0.2.0",
- "grunt-peg": "~1.3.1",
- "grunt-trimtrailingspaces": "^0.4.0",
- "pegjs": "^0.8.0"
- },
- "engines": {
- "node": ">=0.12"
- },
- "license": "MIT",
- "scripts": {
- "repl": "beefy test/repl.js --open",
- "build": "grunt build",
- "prepublish": "cd src/Grammar && mkdir -p dist && pegjs --extra-options-file peg.json src/Grammar.pegjs dist/Grammar.js",
- "test": "grunt travis --verbose"
- },
- "dependencies": {
- "ws": "^1.0.1"
- },
- "optionalDependencies": {
- "promiscuous": "^0.6.0"
- }
- }
- },{}],3:[function(require,module,exports){
- "use strict";
- module.exports = function (SIP) {
- var ClientContext;
- ClientContext = function (ua, method, target, options) {
- var originalTarget = target;
- // Validate arguments
- if (target === undefined) {
- throw new TypeError('Not enough arguments');
- }
- this.ua = ua;
- this.logger = ua.getLogger('sip.clientcontext');
- this.method = method;
- target = ua.normalizeTarget(target);
- if (!target) {
- throw new TypeError('Invalid target: ' + originalTarget);
- }
- /* Options
- * - extraHeaders
- * - params
- * - contentType
- * - body
- */
- options = Object.create(options || Object.prototype);
- options.extraHeaders = (options.extraHeaders || []).slice();
- // Build the request
- this.request = new SIP.OutgoingRequest(this.method,
- target,
- this.ua,
- options.params,
- options.extraHeaders);
- if (options.body) {
- this.body = {};
- this.body.body = options.body;
- if (options.contentType) {
- this.body.contentType = options.contentType;
- }
- this.request.body = this.body;
- }
- /* Set other properties from the request */
- this.localIdentity = this.request.from;
- this.remoteIdentity = this.request.to;
- this.data = {};
- };
- ClientContext.prototype = Object.create(SIP.EventEmitter.prototype);
- ClientContext.prototype.send = function () {
- (new SIP.RequestSender(this, this.ua)).send();
- return this;
- };
- ClientContext.prototype.cancel = function (options) {
- options = options || {};
- options.extraHeaders = (options.extraHeaders || []).slice();
- var cancel_reason = SIP.Utils.getCancelReason(options.status_code, options.reason_phrase);
- this.request.cancel(cancel_reason, options.extraHeaders);
- this.emit('cancel');
- };
- ClientContext.prototype.receiveResponse = function (response) {
- var cause = SIP.Utils.getReasonPhrase(response.status_code);
- switch(true) {
- case /^1[0-9]{2}$/.test(response.status_code):
- this.emit('progress', response, cause);
- break;
- case /^2[0-9]{2}$/.test(response.status_code):
- if(this.ua.applicants[this]) {
- delete this.ua.applicants[this];
- }
- this.emit('accepted', response, cause);
- break;
- default:
- if(this.ua.applicants[this]) {
- delete this.ua.applicants[this];
- }
- this.emit('rejected', response, cause);
- this.emit('failed', response, cause);
- break;
- }
- };
- ClientContext.prototype.onRequestTimeout = function () {
- this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
- };
- ClientContext.prototype.onTransportError = function () {
- this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
- };
- SIP.ClientContext = ClientContext;
- };
- },{}],4:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Constants
- */
- /**
- * SIP Constants.
- * @augments SIP
- */
- module.exports = function (name, version) {
- return {
- USER_AGENT: name +'/'+ version,
- // SIP scheme
- SIP: 'sip',
- SIPS: 'sips',
- // End and Failure causes
- causes: {
- // Generic error causes
- CONNECTION_ERROR: 'Connection Error',
- REQUEST_TIMEOUT: 'Request Timeout',
- SIP_FAILURE_CODE: 'SIP Failure Code',
- INTERNAL_ERROR: 'Internal Error',
- // SIP error causes
- BUSY: 'Busy',
- REJECTED: 'Rejected',
- REDIRECTED: 'Redirected',
- UNAVAILABLE: 'Unavailable',
- NOT_FOUND: 'Not Found',
- ADDRESS_INCOMPLETE: 'Address Incomplete',
- INCOMPATIBLE_SDP: 'Incompatible SDP',
- AUTHENTICATION_ERROR: 'Authentication Error',
- DIALOG_ERROR: 'Dialog Error',
- // Session error causes
- WEBRTC_NOT_SUPPORTED: 'WebRTC Not Supported',
- WEBRTC_ERROR: 'WebRTC Error',
- CANCELED: 'Canceled',
- NO_ANSWER: 'No Answer',
- EXPIRES: 'Expires',
- NO_ACK: 'No ACK',
- NO_PRACK: 'No PRACK',
- USER_DENIED_MEDIA_ACCESS: 'User Denied Media Access',
- BAD_MEDIA_DESCRIPTION: 'Bad Media Description',
- RTP_TIMEOUT: 'RTP Timeout'
- },
- supported: {
- UNSUPPORTED: 'none',
- SUPPORTED: 'supported',
- REQUIRED: 'required'
- },
- SIP_ERROR_CAUSES: {
- REDIRECTED: [300,301,302,305,380],
- BUSY: [486,600],
- REJECTED: [403,603],
- NOT_FOUND: [404,604],
- UNAVAILABLE: [480,410,408,430],
- ADDRESS_INCOMPLETE: [484],
- INCOMPATIBLE_SDP: [488,606],
- AUTHENTICATION_ERROR:[401,407]
- },
- // SIP Methods
- ACK: 'ACK',
- BYE: 'BYE',
- CANCEL: 'CANCEL',
- INFO: 'INFO',
- INVITE: 'INVITE',
- MESSAGE: 'MESSAGE',
- NOTIFY: 'NOTIFY',
- OPTIONS: 'OPTIONS',
- REGISTER: 'REGISTER',
- UPDATE: 'UPDATE',
- SUBSCRIBE: 'SUBSCRIBE',
- REFER: 'REFER',
- PRACK: 'PRACK',
- /* SIP Response Reasons
- * DOC: http://www.iana.org/assignments/sip-parameters
- * Copied from https://github.com/versatica/OverSIP/blob/master/lib/oversip/sip/constants.rb#L7
- */
- REASON_PHRASE: {
- 100: 'Trying',
- 180: 'Ringing',
- 181: 'Call Is Being Forwarded',
- 182: 'Queued',
- 183: 'Session Progress',
- 199: 'Early Dialog Terminated', // draft-ietf-sipcore-199
- 200: 'OK',
- 202: 'Accepted', // RFC 3265
- 204: 'No Notification', //RFC 5839
- 300: 'Multiple Choices',
- 301: 'Moved Permanently',
- 302: 'Moved Temporarily',
- 305: 'Use Proxy',
- 380: 'Alternative Service',
- 400: 'Bad Request',
- 401: 'Unauthorized',
- 402: 'Payment Required',
- 403: 'Forbidden',
- 404: 'Not Found',
- 405: 'Method Not Allowed',
- 406: 'Not Acceptable',
- 407: 'Proxy Authentication Required',
- 408: 'Request Timeout',
- 410: 'Gone',
- 412: 'Conditional Request Failed', // RFC 3903
- 413: 'Request Entity Too Large',
- 414: 'Request-URI Too Long',
- 415: 'Unsupported Media Type',
- 416: 'Unsupported URI Scheme',
- 417: 'Unknown Resource-Priority', // RFC 4412
- 420: 'Bad Extension',
- 421: 'Extension Required',
- 422: 'Session Interval Too Small', // RFC 4028
- 423: 'Interval Too Brief',
- 428: 'Use Identity Header', // RFC 4474
- 429: 'Provide Referrer Identity', // RFC 3892
- 430: 'Flow Failed', // RFC 5626
- 433: 'Anonymity Disallowed', // RFC 5079
- 436: 'Bad Identity-Info', // RFC 4474
- 437: 'Unsupported Certificate', // RFC 4744
- 438: 'Invalid Identity Header', // RFC 4744
- 439: 'First Hop Lacks Outbound Support', // RFC 5626
- 440: 'Max-Breadth Exceeded', // RFC 5393
- 469: 'Bad Info Package', // draft-ietf-sipcore-info-events
- 470: 'Consent Needed', // RFC 5360
- 478: 'Unresolvable Destination', // Custom code copied from Kamailio.
- 480: 'Temporarily Unavailable',
- 481: 'Call/Transaction Does Not Exist',
- 482: 'Loop Detected',
- 483: 'Too Many Hops',
- 484: 'Address Incomplete',
- 485: 'Ambiguous',
- 486: 'Busy Here',
- 487: 'Request Terminated',
- 488: 'Not Acceptable Here',
- 489: 'Bad Event', // RFC 3265
- 491: 'Request Pending',
- 493: 'Undecipherable',
- 494: 'Security Agreement Required', // RFC 3329
- 500: 'Internal Server Error',
- 501: 'Not Implemented',
- 502: 'Bad Gateway',
- 503: 'Service Unavailable',
- 504: 'Server Time-out',
- 505: 'Version Not Supported',
- 513: 'Message Too Large',
- 580: 'Precondition Failure', // RFC 3312
- 600: 'Busy Everywhere',
- 603: 'Decline',
- 604: 'Does Not Exist Anywhere',
- 606: 'Not Acceptable'
- },
- /* SIP Option Tags
- * DOC: http://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml#sip-parameters-4
- */
- OPTION_TAGS: {
- '100rel': true, // RFC 3262
- 199: true, // RFC 6228
- answermode: true, // RFC 5373
- 'early-session': true, // RFC 3959
- eventlist: true, // RFC 4662
- explicitsub: true, // RFC-ietf-sipcore-refer-explicit-subscription-03
- 'from-change': true, // RFC 4916
- 'geolocation-http': true, // RFC 6442
- 'geolocation-sip': true, // RFC 6442
- gin: true, // RFC 6140
- gruu: true, // RFC 5627
- histinfo: true, // RFC 7044
- ice: true, // RFC 5768
- join: true, // RFC 3911
- 'multiple-refer': true, // RFC 5368
- norefersub: true, // RFC 4488
- nosub: true, // RFC-ietf-sipcore-refer-explicit-subscription-03
- outbound: true, // RFC 5626
- path: true, // RFC 3327
- policy: true, // RFC 6794
- precondition: true, // RFC 3312
- pref: true, // RFC 3840
- privacy: true, // RFC 3323
- 'recipient-list-invite': true, // RFC 5366
- 'recipient-list-message': true, // RFC 5365
- 'recipient-list-subscribe': true, // RFC 5367
- replaces: true, // RFC 3891
- 'resource-priority': true, // RFC 4412
- 'sdp-anat': true, // RFC 4092
- 'sec-agree': true, // RFC 3329
- tdialog: true, // RFC 4538
- timer: true, // RFC 4028
- uui: true // RFC 7433
- }
- };
- };
- },{}],5:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview In-Dialog Request Sender
- */
- /**
- * @augments SIP.Dialog
- * @class Class creating an In-dialog request sender.
- * @param {SIP.Dialog} dialog
- * @param {Object} applicant
- * @param {SIP.OutgoingRequest} request
- */
- /**
- * @fileoverview in-Dialog Request Sender
- */
- module.exports = function (SIP) {
- var RequestSender;
- RequestSender = function(dialog, applicant, request) {
- this.dialog = dialog;
- this.applicant = applicant;
- this.request = request;
- // RFC3261 14.1 Modifying an Existing Session. UAC Behavior.
- this.reattempt = false;
- this.reattemptTimer = null;
- };
- RequestSender.prototype = {
- send: function() {
- var self = this,
- request_sender = new SIP.RequestSender(this, this.dialog.owner.ua);
- request_sender.send();
- // RFC3261 14.2 Modifying an Existing Session -UAC BEHAVIOR-
- if (this.request.method === SIP.C.INVITE && request_sender.clientTransaction.state !== SIP.Transactions.C.STATUS_TERMINATED) {
- this.dialog.uac_pending_reply = true;
- request_sender.clientTransaction.on('stateChanged', function stateChanged(){
- if (this.state === SIP.Transactions.C.STATUS_ACCEPTED ||
- this.state === SIP.Transactions.C.STATUS_COMPLETED ||
- this.state === SIP.Transactions.C.STATUS_TERMINATED) {
- this.removeListener('stateChanged', stateChanged);
- self.dialog.uac_pending_reply = false;
- if (self.dialog.uas_pending_reply === false) {
- self.dialog.owner.onReadyToReinvite();
- }
- }
- });
- }
- },
- onRequestTimeout: function() {
- this.applicant.onRequestTimeout();
- },
- onTransportError: function() {
- this.applicant.onTransportError();
- },
- receiveResponse: function(response) {
- var self = this;
- // RFC3261 12.2.1.2 408 or 481 is received for a request within a dialog.
- if (response.status_code === 408 || response.status_code === 481) {
- this.applicant.onDialogError(response);
- } else if (response.method === SIP.C.INVITE && response.status_code === 491) {
- if (this.reattempt) {
- this.applicant.receiveResponse(response);
- } else {
- this.request.cseq.value = this.dialog.local_seqnum += 1;
- this.reattemptTimer = SIP.Timers.setTimeout(
- function() {
- if (self.applicant.owner.status !== SIP.Session.C.STATUS_TERMINATED) {
- self.reattempt = true;
- self.request_sender.send();
- }
- },
- this.getReattemptTimeout()
- );
- }
- } else {
- this.applicant.receiveResponse(response);
- }
- }
- };
- return RequestSender;
- };
- },{}],6:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Dialog
- */
- /**
- * @augments SIP
- * @class Class creating a SIP dialog.
- * @param {SIP.RTCSession} owner
- * @param {SIP.IncomingRequest|SIP.IncomingResponse} message
- * @param {Enum} type UAC / UAS
- * @param {Enum} state SIP.Dialog.C.STATUS_EARLY / SIP.Dialog.C.STATUS_CONFIRMED
- */
- module.exports = function (SIP) {
- var RequestSender = require('./Dialog/RequestSender')(SIP);
- var Dialog,
- C = {
- // Dialog states
- STATUS_EARLY: 1,
- STATUS_CONFIRMED: 2
- };
- // RFC 3261 12.1
- Dialog = function(owner, message, type, state) {
- var contact;
- this.uac_pending_reply = false;
- this.uas_pending_reply = false;
- if(!message.hasHeader('contact')) {
- return {
- error: 'unable to create a Dialog without Contact header field'
- };
- }
- if(message instanceof SIP.IncomingResponse) {
- state = (message.status_code < 200) ? C.STATUS_EARLY : C.STATUS_CONFIRMED;
- } else {
- // Create confirmed dialog if state is not defined
- state = state || C.STATUS_CONFIRMED;
- }
- contact = message.parseHeader('contact');
- // RFC 3261 12.1.1
- if(type === 'UAS') {
- this.id = {
- call_id: message.call_id,
- local_tag: message.to_tag,
- remote_tag: message.from_tag,
- toString: function() {
- return this.call_id + this.local_tag + this.remote_tag;
- }
- };
- this.state = state;
- this.remote_seqnum = message.cseq;
- this.local_uri = message.parseHeader('to').uri;
- this.remote_uri = message.parseHeader('from').uri;
- this.remote_target = contact.uri;
- this.route_set = message.getHeaders('record-route');
- this.invite_seqnum = message.cseq;
- this.local_seqnum = message.cseq;
- }
- // RFC 3261 12.1.2
- else if(type === 'UAC') {
- this.id = {
- call_id: message.call_id,
- local_tag: message.from_tag,
- remote_tag: message.to_tag,
- toString: function() {
- return this.call_id + this.local_tag + this.remote_tag;
- }
- };
- this.state = state;
- this.invite_seqnum = message.cseq;
- this.local_seqnum = message.cseq;
- this.local_uri = message.parseHeader('from').uri;
- this.pracked = [];
- this.remote_uri = message.parseHeader('to').uri;
- this.remote_target = contact.uri;
- this.route_set = message.getHeaders('record-route').reverse();
- //RENDERBODY
- if (this.state === C.STATUS_EARLY && (!owner.hasOffer)) {
- this.mediaHandler = owner.mediaHandlerFactory(owner);
- }
- }
- this.logger = owner.ua.getLogger('sip.dialog', this.id.toString());
- this.owner = owner;
- owner.ua.dialogs[this.id.toString()] = this;
- this.logger.log('new ' + type + ' dialog created with status ' + (this.state === C.STATUS_EARLY ? 'EARLY': 'CONFIRMED'));
- owner.emit('dialog', this);
- };
- Dialog.prototype = {
- /**
- * @param {SIP.IncomingMessage} message
- * @param {Enum} UAC/UAS
- */
- update: function(message, type) {
- this.state = C.STATUS_CONFIRMED;
- this.logger.log('dialog '+ this.id.toString() +' changed to CONFIRMED state');
- if(type === 'UAC') {
- // RFC 3261 13.2.2.4
- this.route_set = message.getHeaders('record-route').reverse();
- }
- },
- terminate: function() {
- this.logger.log('dialog ' + this.id.toString() + ' deleted');
- if (this.mediaHandler && this.state !== C.STATUS_CONFIRMED) {
- this.mediaHandler.peerConnection.close();
- }
- delete this.owner.ua.dialogs[this.id.toString()];
- },
- /**
- * @param {String} method request method
- * @param {Object} extraHeaders extra headers
- * @returns {SIP.OutgoingRequest}
- */
- // RFC 3261 12.2.1.1
- createRequest: function(method, extraHeaders, body) {
- var cseq, request;
- extraHeaders = (extraHeaders || []).slice();
- if(!this.local_seqnum) { this.local_seqnum = Math.floor(Math.random() * 10000); }
- cseq = (method === SIP.C.CANCEL || method === SIP.C.ACK) ? this.invite_seqnum : this.local_seqnum += 1;
- request = new SIP.OutgoingRequest(
- method,
- this.remote_target,
- this.owner.ua, {
- 'cseq': cseq,
- 'call_id': this.id.call_id,
- 'from_uri': this.local_uri,
- 'from_tag': this.id.local_tag,
- 'to_uri': this.remote_uri,
- 'to_tag': this.id.remote_tag,
- 'route_set': this.route_set
- }, extraHeaders, body);
- request.dialog = this;
- return request;
- },
- /**
- * @param {SIP.IncomingRequest} request
- * @returns {Boolean}
- */
- // RFC 3261 12.2.2
- checkInDialogRequest: function(request) {
- var self = this;
- if(!this.remote_seqnum) {
- this.remote_seqnum = request.cseq;
- } else if(request.cseq < this.remote_seqnum) {
- //Do not try to reply to an ACK request.
- if (request.method !== SIP.C.ACK) {
- request.reply(500);
- }
- if (request.cseq === this.invite_seqnum) {
- return true;
- }
- return false;
- } else if(request.cseq > this.remote_seqnum) {
- this.remote_seqnum = request.cseq;
- }
- switch(request.method) {
- // RFC3261 14.2 Modifying an Existing Session -UAS BEHAVIOR-
- case SIP.C.INVITE:
- if (this.uac_pending_reply === true) {
- request.reply(491);
- } else if (this.uas_pending_reply === true) {
- var retryAfter = (Math.random() * 10 | 0) + 1;
- request.reply(500, null, ['Retry-After:' + retryAfter]);
- return false;
- } else {
- this.uas_pending_reply = true;
- request.server_transaction.on('stateChanged', function stateChanged(){
- if (this.state === SIP.Transactions.C.STATUS_ACCEPTED ||
- this.state === SIP.Transactions.C.STATUS_COMPLETED ||
- this.state === SIP.Transactions.C.STATUS_TERMINATED) {
- this.removeListener('stateChanged', stateChanged);
- self.uas_pending_reply = false;
- if (self.uac_pending_reply === false) {
- self.owner.onReadyToReinvite();
- }
- }
- });
- }
- // RFC3261 12.2.2 Replace the dialog`s remote target URI if the request is accepted
- if(request.hasHeader('contact')) {
- request.server_transaction.on('stateChanged', function(){
- if (this.state === SIP.Transactions.C.STATUS_ACCEPTED) {
- self.remote_target = request.parseHeader('contact').uri;
- }
- });
- }
- break;
- case SIP.C.NOTIFY:
- // RFC6665 3.2 Replace the dialog`s remote target URI if the request is accepted
- if(request.hasHeader('contact')) {
- request.server_transaction.on('stateChanged', function(){
- if (this.state === SIP.Transactions.C.STATUS_COMPLETED) {
- self.remote_target = request.parseHeader('contact').uri;
- }
- });
- }
- break;
- }
- return true;
- },
- sendRequest: function(applicant, method, options) {
- options = options || {};
- var extraHeaders = (options.extraHeaders || []).slice();
- var body = null;
- if (options.body) {
- if (options.body.body) {
- body = options.body;
- } else {
- body = {};
- body.body = options.body;
- if (options.contentType) {
- body.contentType = options.contentType;
- }
- }
- }
- var request = this.createRequest(method, extraHeaders, body),
- request_sender = new RequestSender(this, applicant, request);
- request_sender.send();
- return request;
- },
- /**
- * @param {SIP.IncomingRequest} request
- */
- receiveRequest: function(request) {
- //Check in-dialog request
- if(!this.checkInDialogRequest(request)) {
- return;
- }
- this.owner.receiveRequest(request);
- }
- };
- Dialog.C = C;
- SIP.Dialog = Dialog;
- };
- },{"./Dialog/RequestSender":5}],7:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Digest Authentication
- */
- /**
- * SIP Digest Authentication.
- * @augments SIP.
- * @function Digest Authentication
- * @param {SIP.UA} ua
- */
- module.exports = function (Utils) {
- var DigestAuthentication;
- DigestAuthentication = function(ua) {
- this.logger = ua.getLogger('sipjs.digestauthentication');
- this.username = ua.configuration.authorizationUser;
- this.password = ua.configuration.password;
- this.cnonce = null;
- this.nc = 0;
- this.ncHex = '00000000';
- this.response = null;
- };
- /**
- * Performs Digest authentication given a SIP request and the challenge
- * received in a response to that request.
- * Returns true if credentials were successfully generated, false otherwise.
- *
- * @param {SIP.OutgoingRequest} request
- * @param {Object} challenge
- */
- DigestAuthentication.prototype.authenticate = function(request, challenge) {
- // Inspect and validate the challenge.
- this.algorithm = challenge.algorithm;
- this.realm = challenge.realm;
- this.nonce = challenge.nonce;
- this.opaque = challenge.opaque;
- this.stale = challenge.stale;
- if (this.algorithm) {
- if (this.algorithm !== 'MD5') {
- this.logger.warn('challenge with Digest algorithm different than "MD5", authentication aborted');
- return false;
- }
- } else {
- this.algorithm = 'MD5';
- }
- if (! this.realm) {
- this.logger.warn('challenge without Digest realm, authentication aborted');
- return false;
- }
- if (! this.nonce) {
- this.logger.warn('challenge without Digest nonce, authentication aborted');
- return false;
- }
- // 'qop' can contain a list of values (Array). Let's choose just one.
- if (challenge.qop) {
- if (challenge.qop.indexOf('auth') > -1) {
- this.qop = 'auth';
- } else if (challenge.qop.indexOf('auth-int') > -1) {
- this.qop = 'auth-int';
- } else {
- // Otherwise 'qop' is present but does not contain 'auth' or 'auth-int', so abort here.
- this.logger.warn('challenge without Digest qop different than "auth" or "auth-int", authentication aborted');
- return false;
- }
- } else {
- this.qop = null;
- }
- // Fill other attributes.
- this.method = request.method;
- this.uri = request.ruri;
- this.cnonce = Utils.createRandomToken(12);
- this.nc += 1;
- this.updateNcHex();
- // nc-value = 8LHEX. Max value = 'FFFFFFFF'.
- if (this.nc === 4294967296) {
- this.nc = 1;
- this.ncHex = '00000001';
- }
- // Calculate the Digest "response" value.
- this.calculateResponse();
- return true;
- };
- /**
- * Generate Digest 'response' value.
- * @private
- */
- DigestAuthentication.prototype.calculateResponse = function() {
- var ha1, ha2;
- // HA1 = MD5(A1) = MD5(username:realm:password)
- ha1 = Utils.calculateMD5(this.username + ":" + this.realm + ":" + this.password);
- if (this.qop === 'auth') {
- // HA2 = MD5(A2) = MD5(method:digestURI)
- ha2 = Utils.calculateMD5(this.method + ":" + this.uri);
- // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2)
- this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth:" + ha2);
- } else if (this.qop === 'auth-int') {
- // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody))
- ha2 = Utils.calculateMD5(this.method + ":" + this.uri + ":" + Utils.calculateMD5(this.body ? this.body : ""));
- // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2)
- this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth-int:" + ha2);
- } else if (this.qop === null) {
- // HA2 = MD5(A2) = MD5(method:digestURI)
- ha2 = Utils.calculateMD5(this.method + ":" + this.uri);
- // response = MD5(HA1:nonce:HA2)
- this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + ha2);
- }
- };
- /**
- * Return the Proxy-Authorization or WWW-Authorization header value.
- */
- DigestAuthentication.prototype.toString = function() {
- var auth_params = [];
- if (! this.response) {
- throw new Error('response field does not exist, cannot generate Authorization header');
- }
- auth_params.push('algorithm=' + this.algorithm);
- auth_params.push('username="' + this.username + '"');
- auth_params.push('realm="' + this.realm + '"');
- auth_params.push('nonce="' + this.nonce + '"');
- auth_params.push('uri="' + this.uri + '"');
- auth_params.push('response="' + this.response + '"');
- if (this.opaque) {
- auth_params.push('opaque="' + this.opaque + '"');
- }
- if (this.qop) {
- auth_params.push('qop=' + this.qop);
- auth_params.push('cnonce="' + this.cnonce + '"');
- auth_params.push('nc=' + this.ncHex);
- }
- return 'Digest ' + auth_params.join(', ');
- };
- /**
- * Generate the 'nc' value as required by Digest in this.ncHex by reading this.nc.
- * @private
- */
- DigestAuthentication.prototype.updateNcHex = function() {
- var hex = Number(this.nc).toString(16);
- this.ncHex = '00000000'.substr(0, 8-hex.length) + hex;
- };
- return DigestAuthentication;
- };
- },{}],8:[function(require,module,exports){
- "use strict";
- var NodeEventEmitter = require('events').EventEmitter;
- module.exports = function (console) {
- // Don't use `new SIP.EventEmitter()` for inheriting.
- // Use Object.create(SIP.EventEmitter.prototoype);
- function EventEmitter () {
- NodeEventEmitter.call(this);
- }
- EventEmitter.prototype = Object.create(NodeEventEmitter.prototype, {
- constructor: {
- value: EventEmitter,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
- EventEmitter.prototype.off = function off (eventName, listener) {
- var warning = '';
- warning += 'SIP.EventEmitter#off is deprecated and may be removed in future SIP.js versions.\n';
- warning += 'Please use removeListener or removeAllListeners instead.\n';
- warning += 'See here for more details:\n';
- warning += 'http://nodejs.org/api/events.html#events_emitter_removelistener_event_listener';
- console.warn(warning);
- if (arguments.length < 2) {
- return this.removeAllListeners.apply(this, arguments);
- } else {
- return this.removeListener(eventName, listener);
- }
- };
- return EventEmitter;
- };
- },{"events":1}],9:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Exceptions
- */
- /**
- * SIP Exceptions.
- * @augments SIP
- */
- module.exports = {
- ConfigurationError: (function(){
- var exception = function(parameter, value) {
- this.code = 1;
- this.name = 'CONFIGURATION_ERROR';
- this.parameter = parameter;
- this.value = value;
- this.message = (!this.value)? 'Missing parameter: '+ this.parameter : 'Invalid value '+ JSON.stringify(this.value) +' for parameter "'+ this.parameter +'"';
- };
- exception.prototype = new Error();
- return exception;
- }()),
- InvalidStateError: (function(){
- var exception = function(status) {
- this.code = 2;
- this.name = 'INVALID_STATE_ERROR';
- this.status = status;
- this.message = 'Invalid status: ' + status;
- };
- exception.prototype = new Error();
- return exception;
- }()),
- NotSupportedError: (function(){
- var exception = function(message) {
- this.code = 3;
- this.name = 'NOT_SUPPORTED_ERROR';
- this.message = message;
- };
- exception.prototype = new Error();
- return exception;
- }()),
- GetDescriptionError: (function(){
- var exception = function(message) {
- this.code = 4;
- this.name = 'GET_DESCRIPTION_ERROR';
- this.message = message;
- };
- exception.prototype = new Error();
- return exception;
- }())
- };
- },{}],10:[function(require,module,exports){
- "use strict";
- var Grammar = require('./Grammar/dist/Grammar');
- module.exports = function (SIP) {
- return {
- parse: function parseCustom (input, startRule) {
- var options = {startRule: startRule, SIP: SIP};
- try {
- Grammar.parse(input, options);
- } catch (e) {
- options.data = -1;
- }
- return options.data;
- }
- };
- };
- },{"./Grammar/dist/Grammar":11}],11:[function(require,module,exports){
- module.exports = (function() {
- /*
- * Generated by PEG.js 0.8.0.
- *
- * http://pegjs.majda.cz/
- */
- function peg$subclass(child, parent) {
- function ctor() { this.constructor = child; }
- ctor.prototype = parent.prototype;
- child.prototype = new ctor();
- }
- function SyntaxError(message, expected, found, offset, line, column) {
- this.message = message;
- this.expected = expected;
- this.found = found;
- this.offset = offset;
- this.line = line;
- this.column = column;
- this.name = "SyntaxError";
- }
- peg$subclass(SyntaxError, Error);
- function parse(input) {
- var options = arguments.length > 1 ? arguments[1] : {},
- peg$FAILED = {},
- peg$startRuleIndices = { Contact: 118, Name_Addr_Header: 155, Record_Route: 175, Request_Response: 81, SIP_URI: 45, Subscription_State: 185, Supported: 190, Require: 181, Via: 193, absoluteURI: 84, Call_ID: 117, Content_Disposition: 129, Content_Length: 134, Content_Type: 135, CSeq: 145, displayName: 121, Event: 148, From: 150, host: 52, Max_Forwards: 153, Min_SE: 212, Proxy_Authenticate: 156, quoted_string: 40, Refer_To: 177, Replaces: 178, Session_Expires: 209, stun_URI: 216, To: 191, turn_URI: 223, uuid: 226, WWW_Authenticate: 208, challenge: 157 },
- peg$startRuleIndex = 118,
- peg$consts = [
- "\r\n",
- { type: "literal", value: "\r\n", description: "\"\\r\\n\"" },
- /^[0-9]/,
- { type: "class", value: "[0-9]", description: "[0-9]" },
- /^[a-zA-Z]/,
- { type: "class", value: "[a-zA-Z]", description: "[a-zA-Z]" },
- /^[0-9a-fA-F]/,
- { type: "class", value: "[0-9a-fA-F]", description: "[0-9a-fA-F]" },
- /^[\0-\xFF]/,
- { type: "class", value: "[\\0-\\xFF]", description: "[\\0-\\xFF]" },
- /^["]/,
- { type: "class", value: "[\"]", description: "[\"]" },
- " ",
- { type: "literal", value: " ", description: "\" \"" },
- "\t",
- { type: "literal", value: "\t", description: "\"\\t\"" },
- /^[a-zA-Z0-9]/,
- { type: "class", value: "[a-zA-Z0-9]", description: "[a-zA-Z0-9]" },
- ";",
- { type: "literal", value: ";", description: "\";\"" },
- "/",
- { type: "literal", value: "/", description: "\"/\"" },
- "?",
- { type: "literal", value: "?", description: "\"?\"" },
- ":",
- { type: "literal", value: ":", description: "\":\"" },
- "@",
- { type: "literal", value: "@", description: "\"@\"" },
- "&",
- { type: "literal", value: "&", description: "\"&\"" },
- "=",
- { type: "literal", value: "=", description: "\"=\"" },
- "+",
- { type: "literal", value: "+", description: "\"+\"" },
- "$",
- { type: "literal", value: "$", description: "\"$\"" },
- ",",
- { type: "literal", value: ",", description: "\",\"" },
- "-",
- { type: "literal", value: "-", description: "\"-\"" },
- "_",
- { type: "literal", value: "_", description: "\"_\"" },
- ".",
- { type: "literal", value: ".", description: "\".\"" },
- "!",
- { type: "literal", value: "!", description: "\"!\"" },
- "~",
- { type: "literal", value: "~", description: "\"~\"" },
- "*",
- { type: "literal", value: "*", description: "\"*\"" },
- "'",
- { type: "literal", value: "'", description: "\"'\"" },
- "(",
- { type: "literal", value: "(", description: "\"(\"" },
- ")",
- { type: "literal", value: ")", description: "\")\"" },
- peg$FAILED,
- "%",
- { type: "literal", value: "%", description: "\"%\"" },
- null,
- [],
- function() {return " "; },
- function() {return ':'; },
- /^[!-~]/,
- { type: "class", value: "[!-~]", description: "[!-~]" },
- /^[\x80-\uFFFF]/,
- { type: "class", value: "[\\x80-\\uFFFF]", description: "[\\x80-\\uFFFF]" },
- /^[\x80-\xBF]/,
- { type: "class", value: "[\\x80-\\xBF]", description: "[\\x80-\\xBF]" },
- /^[a-f]/,
- { type: "class", value: "[a-f]", description: "[a-f]" },
- "`",
- { type: "literal", value: "`", description: "\"`\"" },
- "<",
- { type: "literal", value: "<", description: "\"<\"" },
- ">",
- { type: "literal", value: ">", description: "\">\"" },
- "\\",
- { type: "literal", value: "\\", description: "\"\\\\\"" },
- "[",
- { type: "literal", value: "[", description: "\"[\"" },
- "]",
- { type: "literal", value: "]", description: "\"]\"" },
- "{",
- { type: "literal", value: "{", description: "\"{\"" },
- "}",
- { type: "literal", value: "}", description: "\"}\"" },
- function() {return "*"; },
- function() {return "/"; },
- function() {return "="; },
- function() {return "("; },
- function() {return ")"; },
- function() {return ">"; },
- function() {return "<"; },
- function() {return ","; },
- function() {return ";"; },
- function() {return ":"; },
- function() {return "\""; },
- /^[!-']/,
- { type: "class", value: "[!-']", description: "[!-']" },
- /^[*-[]/,
- { type: "class", value: "[*-[]", description: "[*-[]" },
- /^[\]-~]/,
- { type: "class", value: "[\\]-~]", description: "[\\]-~]" },
- function(contents) {
- return contents; },
- /^[#-[]/,
- { type: "class", value: "[#-[]", description: "[#-[]" },
- /^[\0-\t]/,
- { type: "class", value: "[\\0-\\t]", description: "[\\0-\\t]" },
- /^[\x0B-\f]/,
- { type: "class", value: "[\\x0B-\\f]", description: "[\\x0B-\\f]" },
- /^[\x0E-]/,
- { type: "class", value: "[\\x0E-]", description: "[\\x0E-]" },
- function() {
- options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port);
- delete options.data.scheme;
- delete options.data.user;
- delete options.data.host;
- delete options.data.host_type;
- delete options.data.port;
- },
- function() {
- options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port, options.data.uri_params, options.data.uri_headers);
- delete options.data.scheme;
- delete options.data.user;
- delete options.data.host;
- delete options.data.host_type;
- delete options.data.port;
- delete options.data.uri_params;
- if (options.startRule === 'SIP_URI') { options.data = options.data.uri;}
- },
- "sips",
- { type: "literal", value: "sips", description: "\"sips\"" },
- "sip",
- { type: "literal", value: "sip", description: "\"sip\"" },
- function(uri_scheme) {
- options.data.scheme = uri_scheme; },
- function() {
- options.data.user = decodeURIComponent(text().slice(0, -1));},
- function() {
- options.data.password = text(); },
- function() {
- options.data.host = text();
- return options.data.host; },
- function() {
- options.data.host_type = 'domain';
- return text(); },
- /^[a-zA-Z0-9_\-]/,
- { type: "class", value: "[a-zA-Z0-9_\\-]", description: "[a-zA-Z0-9_\\-]" },
- /^[a-zA-Z0-9\-]/,
- { type: "class", value: "[a-zA-Z0-9\\-]", description: "[a-zA-Z0-9\\-]" },
- function() {
- options.data.host_type = 'IPv6';
- return text(); },
- "::",
- { type: "literal", value: "::", description: "\"::\"" },
- function() {
- options.data.host_type = 'IPv6';
- return text(); },
- function() {
- options.data.host_type = 'IPv4';
- return text(); },
- "25",
- { type: "literal", value: "25", description: "\"25\"" },
- /^[0-5]/,
- { type: "class", value: "[0-5]", description: "[0-5]" },
- "2",
- { type: "literal", value: "2", description: "\"2\"" },
- /^[0-4]/,
- { type: "class", value: "[0-4]", description: "[0-4]" },
- "1",
- { type: "literal", value: "1", description: "\"1\"" },
- /^[1-9]/,
- { type: "class", value: "[1-9]", description: "[1-9]" },
- function(port) {
- port = parseInt(port.join(''));
- options.data.port = port;
- return port; },
- "transport=",
- { type: "literal", value: "transport=", description: "\"transport=\"" },
- "udp",
- { type: "literal", value: "udp", description: "\"udp\"" },
- "tcp",
- { type: "literal", value: "tcp", description: "\"tcp\"" },
- "sctp",
- { type: "literal", value: "sctp", description: "\"sctp\"" },
- "tls",
- { type: "literal", value: "tls", description: "\"tls\"" },
- function(transport) {
- if(!options.data.uri_params) options.data.uri_params={};
- options.data.uri_params['transport'] = transport.toLowerCase(); },
- "user=",
- { type: "literal", value: "user=", description: "\"user=\"" },
- "phone",
- { type: "literal", value: "phone", description: "\"phone\"" },
- "ip",
- { type: "literal", value: "ip", description: "\"ip\"" },
- function(user) {
- if(!options.data.uri_params) options.data.uri_params={};
- options.data.uri_params['user'] = user.toLowerCase(); },
- "method=",
- { type: "literal", value: "method=", description: "\"method=\"" },
- function(method) {
- if(!options.data.uri_params) options.data.uri_params={};
- options.data.uri_params['method'] = method; },
- "ttl=",
- { type: "literal", value: "ttl=", description: "\"ttl=\"" },
- function(ttl) {
- if(!options.data.params) options.data.params={};
- options.data.params['ttl'] = ttl; },
- "maddr=",
- { type: "literal", value: "maddr=", description: "\"maddr=\"" },
- function(maddr) {
- if(!options.data.uri_params) options.data.uri_params={};
- options.data.uri_params['maddr'] = maddr; },
- "lr",
- { type: "literal", value: "lr", description: "\"lr\"" },
- function() {
- if(!options.data.uri_params) options.data.uri_params={};
- options.data.uri_params['lr'] = undefined; },
- function(param, value) {
- if(!options.data.uri_params) options.data.uri_params = {};
- if (value === null){
- value = undefined;
- }
- else {
- value = value[1];
- }
- options.data.uri_params[param.toLowerCase()] = value && value.toLowerCase();},
- function(hname, hvalue) {
- hname = hname.join('').toLowerCase();
- hvalue = hvalue.join('');
- if(!options.data.uri_headers) options.data.uri_headers = {};
- if (!options.data.uri_headers[hname]) {
- options.data.uri_headers[hname] = [hvalue];
- } else {
- options.data.uri_headers[hname].push(hvalue);
- }},
- function() {
- // lots of tests fail if this isn't guarded...
- if (options.startRule === 'Refer_To') {
- options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port, options.data.uri_params, options.data.uri_headers);
- delete options.data.scheme;
- delete options.data.user;
- delete options.data.host;
- delete options.data.host_type;
- delete options.data.port;
- delete options.data.uri_params;
- }
- },
- "//",
- { type: "literal", value: "//", description: "\"//\"" },
- function() {
- options.data.scheme= text(); },
- { type: "literal", value: "SIP", description: "\"SIP\"" },
- function() {
- options.data.sip_version = text(); },
- "INVITE",
- { type: "literal", value: "INVITE", description: "\"INVITE\"" },
- "ACK",
- { type: "literal", value: "ACK", description: "\"ACK\"" },
- "VXACH",
- { type: "literal", value: "VXACH", description: "\"VXACH\"" },
- "OPTIONS",
- { type: "literal", value: "OPTIONS", description: "\"OPTIONS\"" },
- "BYE",
- { type: "literal", value: "BYE", description: "\"BYE\"" },
- "CANCEL",
- { type: "literal", value: "CANCEL", description: "\"CANCEL\"" },
- "REGISTER",
- { type: "literal", value: "REGISTER", description: "\"REGISTER\"" },
- "SUBSCRIBE",
- { type: "literal", value: "SUBSCRIBE", description: "\"SUBSCRIBE\"" },
- "NOTIFY",
- { type: "literal", value: "NOTIFY", description: "\"NOTIFY\"" },
- "REFER",
- { type: "literal", value: "REFER", description: "\"REFER\"" },
- function() {
- options.data.method = text();
- return options.data.method; },
- function(status_code) {
- options.data.status_code = parseInt(status_code.join('')); },
- function() {
- options.data.reason_phrase = text(); },
- function() {
- options.data = text(); },
- function() {
- var idx, length;
- length = options.data.multi_header.length;
- for (idx = 0; idx < length; idx++) {
- if (options.data.multi_header[idx].parsed === null) {
- options.data = null;
- break;
- }
- }
- if (options.data !== null) {
- options.data = options.data.multi_header;
- } else {
- options.data = -1;
- }},
- function() {
- var header;
- if(!options.data.multi_header) options.data.multi_header = [];
- try {
- header = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- delete options.data.uri;
- delete options.data.displayName;
- delete options.data.params;
- } catch(e) {
- header = null;
- }
- options.data.multi_header.push( { 'position': peg$currPos,
- 'offset': offset(),
- 'parsed': header
- });},
- function(displayName) {
- displayName = text().trim();
- if (displayName[0] === '\"') {
- displayName = displayName.substring(1, displayName.length-1);
- }
- options.data.displayName = displayName; },
- "q",
- { type: "literal", value: "q", description: "\"q\"" },
- function(q) {
- if(!options.data.params) options.data.params = {};
- options.data.params['q'] = q; },
- "expires",
- { type: "literal", value: "expires", description: "\"expires\"" },
- function(expires) {
- if(!options.data.params) options.data.params = {};
- options.data.params['expires'] = expires; },
- function(delta_seconds) {
- return parseInt(delta_seconds.join('')); },
- "0",
- { type: "literal", value: "0", description: "\"0\"" },
- function() {
- return parseFloat(text()); },
- function(param, value) {
- if(!options.data.params) options.data.params = {};
- if (value === null){
- value = undefined;
- }
- else {
- value = value[1];
- }
- options.data.params[param.toLowerCase()] = value;},
- "render",
- { type: "literal", value: "render", description: "\"render\"" },
- "session",
- { type: "literal", value: "session", description: "\"session\"" },
- "icon",
- { type: "literal", value: "icon", description: "\"icon\"" },
- "alert",
- { type: "literal", value: "alert", description: "\"alert\"" },
- function() {
- if (options.startRule === 'Content_Disposition') {
- options.data.type = text().toLowerCase();
- }
- },
- "handling",
- { type: "literal", value: "handling", description: "\"handling\"" },
- "optional",
- { type: "literal", value: "optional", description: "\"optional\"" },
- "required",
- { type: "literal", value: "required", description: "\"required\"" },
- function(length) {
- options.data = parseInt(length.join('')); },
- function() {
- options.data = text(); },
- "text",
- { type: "literal", value: "text", description: "\"text\"" },
- "image",
- { type: "literal", value: "image", description: "\"image\"" },
- "audio",
- { type: "literal", value: "audio", description: "\"audio\"" },
- "video",
- { type: "literal", value: "video", description: "\"video\"" },
- "application",
- { type: "literal", value: "application", description: "\"application\"" },
- "message",
- { type: "literal", value: "message", description: "\"message\"" },
- "multipart",
- { type: "literal", value: "multipart", description: "\"multipart\"" },
- "x-",
- { type: "literal", value: "x-", description: "\"x-\"" },
- function(cseq_value) {
- options.data.value=parseInt(cseq_value.join('')); },
- function(expires) {options.data = expires; },
- function(event_type) {
- options.data.event = event_type.toLowerCase(); },
- function() {
- var tag = options.data.tag;
- options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- if (tag) {options.data.setParam('tag',tag)}
- },
- "tag",
- { type: "literal", value: "tag", description: "\"tag\"" },
- function(tag) {options.data.tag = tag; },
- function(forwards) {
- options.data = parseInt(forwards.join('')); },
- function(min_expires) {options.data = min_expires; },
- function() {
- options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- },
- "digest",
- { type: "literal", value: "Digest", description: "\"Digest\"" },
- "realm",
- { type: "literal", value: "realm", description: "\"realm\"" },
- function(realm) { options.data.realm = realm; },
- "domain",
- { type: "literal", value: "domain", description: "\"domain\"" },
- "nonce",
- { type: "literal", value: "nonce", description: "\"nonce\"" },
- function(nonce) { options.data.nonce=nonce; },
- "opaque",
- { type: "literal", value: "opaque", description: "\"opaque\"" },
- function(opaque) { options.data.opaque=opaque; },
- "stale",
- { type: "literal", value: "stale", description: "\"stale\"" },
- "true",
- { type: "literal", value: "true", description: "\"true\"" },
- function() { options.data.stale=true; },
- "false",
- { type: "literal", value: "false", description: "\"false\"" },
- function() { options.data.stale=false; },
- "algorithm",
- { type: "literal", value: "algorithm", description: "\"algorithm\"" },
- "md5",
- { type: "literal", value: "MD5", description: "\"MD5\"" },
- "md5-sess",
- { type: "literal", value: "MD5-sess", description: "\"MD5-sess\"" },
- function(algorithm) {
- options.data.algorithm=algorithm.toUpperCase(); },
- "qop",
- { type: "literal", value: "qop", description: "\"qop\"" },
- "auth-int",
- { type: "literal", value: "auth-int", description: "\"auth-int\"" },
- "auth",
- { type: "literal", value: "auth", description: "\"auth\"" },
- function(qop_value) {
- options.data.qop || (options.data.qop=[]);
- options.data.qop.push(qop_value.toLowerCase()); },
- function(rack_value) {
- options.data.value=parseInt(rack_value.join('')); },
- function() {
- var idx, length;
- length = options.data.multi_header.length;
- for (idx = 0; idx < length; idx++) {
- if (options.data.multi_header[idx].parsed === null) {
- options.data = null;
- break;
- }
- }
- if (options.data !== null) {
- options.data = options.data.multi_header;
- } else {
- options.data = -1;
- }},
- function() {
- var header;
- if(!options.data.multi_header) options.data.multi_header = [];
- try {
- header = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- delete options.data.uri;
- delete options.data.displayName;
- delete options.data.params;
- } catch(e) {
- header = null;
- }
- options.data.multi_header.push( { 'position': peg$currPos,
- 'offset': offset(),
- 'parsed': header
- });},
- function() {
- options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- },
- function() {
- if (!(options.data.replaces_from_tag && options.data.replaces_to_tag)) {
- options.data = -1;
- }
- },
- function() {
- options.data = {
- call_id: options.data
- };
- },
- "from-tag",
- { type: "literal", value: "from-tag", description: "\"from-tag\"" },
- function(from_tag) {
- options.data.replaces_from_tag = from_tag;
- },
- "to-tag",
- { type: "literal", value: "to-tag", description: "\"to-tag\"" },
- function(to_tag) {
- options.data.replaces_to_tag = to_tag;
- },
- "early-only",
- { type: "literal", value: "early-only", description: "\"early-only\"" },
- function() {
- options.data.early_only = true;
- },
- function(r) {return r;},
- function(first, rest) { return list(first, rest); },
- function(value) {
- if (options.startRule === 'Require') {
- options.data = value || [];
- }
- },
- function(rseq_value) {
- options.data.value=parseInt(rseq_value.join('')); },
- "active",
- { type: "literal", value: "active", description: "\"active\"" },
- "pending",
- { type: "literal", value: "pending", description: "\"pending\"" },
- "terminated",
- { type: "literal", value: "terminated", description: "\"terminated\"" },
- function() {
- options.data.state = text(); },
- "reason",
- { type: "literal", value: "reason", description: "\"reason\"" },
- function(reason) {
- if (typeof reason !== 'undefined') options.data.reason = reason; },
- function(expires) {
- if (typeof expires !== 'undefined') options.data.expires = expires; },
- "retry_after",
- { type: "literal", value: "retry_after", description: "\"retry_after\"" },
- function(retry_after) {
- if (typeof retry_after !== 'undefined') options.data.retry_after = retry_after; },
- "deactivated",
- { type: "literal", value: "deactivated", description: "\"deactivated\"" },
- "probation",
- { type: "literal", value: "probation", description: "\"probation\"" },
- "rejected",
- { type: "literal", value: "rejected", description: "\"rejected\"" },
- "timeout",
- { type: "literal", value: "timeout", description: "\"timeout\"" },
- "giveup",
- { type: "literal", value: "giveup", description: "\"giveup\"" },
- "noresource",
- { type: "literal", value: "noresource", description: "\"noresource\"" },
- "invariant",
- { type: "literal", value: "invariant", description: "\"invariant\"" },
- function(value) {
- if (options.startRule === 'Supported') {
- options.data = value || [];
- }
- },
- function() {
- var tag = options.data.tag;
- options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
- if (tag) {options.data.setParam('tag',tag)}
- },
- "ttl",
- { type: "literal", value: "ttl", description: "\"ttl\"" },
- function(via_ttl_value) {
- options.data.ttl = via_ttl_value; },
- "maddr",
- { type: "literal", value: "maddr", description: "\"maddr\"" },
- function(via_maddr) {
- options.data.maddr = via_maddr; },
- "received",
- { type: "literal", value: "received", description: "\"received\"" },
- function(via_received) {
- options.data.received = via_received; },
- "branch",
- { type: "literal", value: "branch", description: "\"branch\"" },
- function(via_branch) {
- options.data.branch = via_branch; },
- "rport",
- { type: "literal", value: "rport", description: "\"rport\"" },
- function() {
- if(typeof response_port !== 'undefined')
- options.data.rport = response_port.join(''); },
- function(via_protocol) {
- options.data.protocol = via_protocol; },
- { type: "literal", value: "UDP", description: "\"UDP\"" },
- { type: "literal", value: "TCP", description: "\"TCP\"" },
- { type: "literal", value: "TLS", description: "\"TLS\"" },
- { type: "literal", value: "SCTP", description: "\"SCTP\"" },
- function(via_transport) {
- options.data.transport = via_transport; },
- function() {
- options.data.host = text(); },
- function(via_sent_by_port) {
- options.data.port = parseInt(via_sent_by_port.join('')); },
- function(ttl) {
- return parseInt(ttl.join('')); },
- function(deltaSeconds) {
- if (options.startRule === 'Session_Expires') {
- options.data.deltaSeconds = deltaSeconds;
- }
- },
- "refresher",
- { type: "literal", value: "refresher", description: "\"refresher\"" },
- "uas",
- { type: "literal", value: "uas", description: "\"uas\"" },
- "uac",
- { type: "literal", value: "uac", description: "\"uac\"" },
- function(endpoint) {
- if (options.startRule === 'Session_Expires') {
- options.data.refresher = endpoint;
- }
- },
- function(deltaSeconds) {
- if (options.startRule === 'Min_SE') {
- options.data = deltaSeconds;
- }
- },
- "stuns",
- { type: "literal", value: "stuns", description: "\"stuns\"" },
- "stun",
- { type: "literal", value: "stun", description: "\"stun\"" },
- function(scheme) {
- options.data.scheme = scheme; },
- function(host) {
- options.data.host = host; },
- "?transport=",
- { type: "literal", value: "?transport=", description: "\"?transport=\"" },
- "turns",
- { type: "literal", value: "turns", description: "\"turns\"" },
- "turn",
- { type: "literal", value: "turn", description: "\"turn\"" },
- function() {
- options.data.transport = transport; },
- function() {
- options.data = text(); }
- ],
- peg$bytecode = [
- peg$decode(". \"\"2 3!"),
- peg$decode("0\"\"\"1!3#"),
- peg$decode("0$\"\"1!3%"),
- peg$decode("0&\"\"1!3'"),
- peg$decode("7'*# \"7("),
- peg$decode("0(\"\"1!3)"),
- peg$decode("0*\"\"1!3+"),
- peg$decode(".,\"\"2,3-"),
- peg$decode("..\"\"2.3/"),
- peg$decode("00\"\"1!31"),
- peg$decode(".2\"\"2233*\x89 \".4\"\"2435*} \".6\"\"2637*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
- peg$decode("7)*# \"7,"),
- peg$decode(".F\"\"2F3G*} \".H\"\"2H3I*q \".J\"\"2J3K*e \".L\"\"2L3M*Y \".N\"\"2N3O*M \".P\"\"2P3Q*A \".R\"\"2R3S*5 \".T\"\"2T3U*) \".V\"\"2V3W"),
- peg$decode("!!.Y\"\"2Y3Z+7$7#+-%7#+#%'#%$## X$\"# X\"# X+! (%"),
- peg$decode("!! \\7$,#&7$\"+-$7 +#%'\"%$\"# X\"# X*# \" [+@$ \\7$+&$,#&7$\"\"\" X+'%4\"6]\" %$\"# X\"# X"),
- peg$decode("7.*# \" ["),
- peg$decode("! \\7'*# \"7(,)&7'*# \"7(\"+A$.8\"\"2839+1%7/+'%4#6^# %$## X$\"# X\"# X"),
- peg$decode("!! \\72+&$,#&72\"\"\" X+o$ \\! \\7.,#&7.\"+-$72+#%'\"%$\"# X\"# X,@&! \\7.,#&7.\"+-$72+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+! (%"),
- peg$decode("0_\"\"1!3`*# \"73"),
- peg$decode("0a\"\"1!3b"),
- peg$decode("0c\"\"1!3d"),
- peg$decode("7!*) \"0e\"\"1!3f"),
- peg$decode("! \\7)*\x95 \".F\"\"2F3G*\x89 \".J\"\"2J3K*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O+\x9E$,\x9B&7)*\x95 \".F\"\"2F3G*\x89 \".J\"\"2J3K*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O\"\"\" X+! (%"),
- peg$decode("! \\7)*\x89 \".F\"\"2F3G*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O+\x92$,\x8F&7)*\x89 \".F\"\"2F3G*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O\"\"\" X+! (%"),
- peg$decode(".T\"\"2T3U*\xE3 \".V\"\"2V3W*\xD7 \".i\"\"2i3j*\xCB \".k\"\"2k3l*\xBF \".:\"\"2:3;*\xB3 \".D\"\"2D3E*\xA7 \".2\"\"2233*\x9B \".8\"\"2839*\x8F \".m\"\"2m3n*\x83 \"7&*} \".4\"\"2435*q \".o\"\"2o3p*e \".q\"\"2q3r*Y \".6\"\"2637*M \".>\"\"2>3?*A \".s\"\"2s3t*5 \".u\"\"2u3v*) \"7'*# \"7("),
- peg$decode("! \\7)*\u012B \".F\"\"2F3G*\u011F \".J\"\"2J3K*\u0113 \".L\"\"2L3M*\u0107 \".Y\"\"2Y3Z*\xFB \".P\"\"2P3Q*\xEF \".H\"\"2H3I*\xE3 \".@\"\"2@3A*\xD7 \".g\"\"2g3h*\xCB \".R\"\"2R3S*\xBF \".N\"\"2N3O*\xB3 \".T\"\"2T3U*\xA7 \".V\"\"2V3W*\x9B \".i\"\"2i3j*\x8F \".k\"\"2k3l*\x83 \".8\"\"2839*w \".m\"\"2m3n*k \"7&*e \".4\"\"2435*Y \".o\"\"2o3p*M \".q\"\"2q3r*A \".6\"\"2637*5 \".s\"\"2s3t*) \".u\"\"2u3v+\u0134$,\u0131&7)*\u012B \".F\"\"2F3G*\u011F \".J\"\"2J3K*\u0113 \".L\"\"2L3M*\u0107 \".Y\"\"2Y3Z*\xFB \".P\"\"2P3Q*\xEF \".H\"\"2H3I*\xE3 \".@\"\"2@3A*\xD7 \".g\"\"2g3h*\xCB \".R\"\"2R3S*\xBF \".N\"\"2N3O*\xB3 \".T\"\"2T3U*\xA7 \".V\"\"2V3W*\x9B \".i\"\"2i3j*\x8F \".k\"\"2k3l*\x83 \".8\"\"2839*w \".m\"\"2m3n*k \"7&*e \".4\"\"2435*Y \".o\"\"2o3p*M \".q\"\"2q3r*A \".6\"\"2637*5 \".s\"\"2s3t*) \".u\"\"2u3v\"\"\" X+! (%"),
- peg$decode("!7/+A$.P\"\"2P3Q+1%7/+'%4#6w# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.4\"\"2435+1%7/+'%4#6x# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.>\"\"2>3?+1%7/+'%4#6y# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.T\"\"2T3U+1%7/+'%4#6z# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.V\"\"2V3W+1%7/+'%4#6{# %$## X$\"# X\"# X"),
- peg$decode("!.k\"\"2k3l+1$7/+'%4\"6|\" %$\"# X\"# X"),
- peg$decode("!7/+7$.i\"\"2i3j+'%4\"6}\" %$\"# X\"# X"),
- peg$decode("!7/+A$.D\"\"2D3E+1%7/+'%4#6~# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.2\"\"2233+1%7/+'%4#6# %$## X$\"# X\"# X"),
- peg$decode("!7/+A$.8\"\"2839+1%7/+'%4#6\x80# %$## X$\"# X\"# X"),
- peg$decode("!7/+1$7&+'%4\"6\x81\" %$\"# X\"# X"),
- peg$decode("!7&+1$7/+'%4\"6\x81\" %$\"# X\"# X"),
- peg$decode("!7=+W$ \\7G*) \"7K*# \"7F,/&7G*) \"7K*# \"7F\"+-%7>+#%'#%$## X$\"# X\"# X"),
- peg$decode("0\x82\"\"1!3\x83*A \"0\x84\"\"1!3\x85*5 \"0\x86\"\"1!3\x87*) \"73*# \"7."),
- peg$decode("!!7/+U$7&+K% \\7J*# \"7K,)&7J*# \"7K\"+-%7&+#%'$%$$# X$## X$\"# X\"# X+! (%"),
- peg$decode("!7/+`$7&+V%! \\7J*# \"7K,)&7J*# \"7K\"+! (%+2%7&+(%4$6\x88$!!%$$# X$## X$\"# X\"# X"),
- peg$decode("7.*G \".L\"\"2L3M*; \"0\x89\"\"1!3\x8A*/ \"0\x86\"\"1!3\x87*# \"73"),
- peg$decode("!.m\"\"2m3n+K$0\x8B\"\"1!3\x8C*5 \"0\x8D\"\"1!3\x8E*) \"0\x8F\"\"1!3\x90+#%'\"%$\"# X\"# X"),
- peg$decode("!7N+Q$.8\"\"2839+A%7O*# \" [+1%7S+'%4$6\x91$ %$$# X$## X$\"# X\"# X"),
- peg$decode("!7N+k$.8\"\"2839+[%7O*# \" [+K%7S+A%7_+7%7l*# \" [+'%4&6\x92& %$&# X$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!/\x93\"\"1$3\x94*) \"/\x95\"\"1#3\x96+' 4!6\x97!! %"),
- peg$decode("!7P+b$!.8\"\"2839+-$7R+#%'\"%$\"# X\"# X*# \" [+7%.:\"\"2:3;+'%4#6\x98# %$## X$\"# X\"# X"),
- peg$decode(" \\7+*) \"7-*# \"7Q+2$,/&7+*) \"7-*# \"7Q\"\"\" X"),
- peg$decode(".<\"\"2<3=*q \".>\"\"2>3?*e \".@\"\"2@3A*Y \".B\"\"2B3C*M \".D\"\"2D3E*A \".2\"\"2233*5 \".6\"\"2637*) \".4\"\"2435"),
- peg$decode("! \\7+*_ \"7-*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E,e&7+*_ \"7-*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E\"+& 4!6\x99! %"),
- peg$decode("!7T+N$!.8\"\"2839+-$7^+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
- peg$decode("!7U*) \"7\\*# \"7X+& 4!6\x9A! %"),
- peg$decode("! \\!7V+3$.J\"\"2J3K+#%'\"%$\"# X\"# X,>&!7V+3$.J\"\"2J3K+#%'\"%$\"# X\"# X\"+G$7W+=%.J\"\"2J3K*# \" [+'%4#6\x9B# %$## X$\"# X\"# X"),
- peg$decode(" \\0\x9C\"\"1!3\x9D+,$,)&0\x9C\"\"1!3\x9D\"\"\" X"),
- peg$decode("!0$\"\"1!3%+A$ \\0\x9E\"\"1!3\x9F,)&0\x9E\"\"1!3\x9F\"+#%'\"%$\"# X\"# X"),
- peg$decode("!.o\"\"2o3p+A$7Y+7%.q\"\"2q3r+'%4#6\xA0# %$## X$\"# X\"# X"),
- peg$decode("!!7Z+\xBF$.8\"\"2839+\xAF%7Z+\xA5%.8\"\"2839+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'-%$-# X$,# X$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0838 \"!.\xA1\"\"2\xA13\xA2+\xAF$7Z+\xA5%.8\"\"2839+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%',%$,# X$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0795 \"!.\xA1\"\"2\xA13\xA2+\x95$7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'*%$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u070C \"!.\xA1\"\"2\xA13\xA2+{$7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u069D \"!.\xA1\"\"2\xA13\xA2+a$7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'&%$&# X$%# X$$# X$## X$\"# X\"# X*\u0648 \"!.\xA1\"\"2\xA13\xA2+G$7Z+=%.8\"\"2839+-%7[+#%'$%$$# X$## X$\"# X\"# X*\u060D \"!.\xA1\"\"2\xA13\xA2+-$7[+#%'\"%$\"# X\"# X*\u05EC \"!.\xA1\"\"2\xA13\xA2+-$7Z+#%'\"%$\"# X\"# X*\u05CB \"!7Z+\xA5$.\xA1\"\"2\xA13\xA2+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'+%$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0538 \"!7Z+\xB6$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x8B%.\xA1\"\"2\xA13\xA2+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'*%$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0494 \"!7Z+\xC7$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x9C%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+q%.\xA1\"\"2\xA13\xA2+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%')%$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u03DF \"!7Z+\xD8$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xAD%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x82%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+W%.\xA1\"\"2\xA13\xA2+G%7Z+=%.8\"\"2839+-%7[+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0319 \"!7Z+\xE9$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xBE%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x93%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+h%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+=%.\xA1\"\"2\xA13\xA2+-%7[+#%''%$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0242 \"!7Z+\u0114$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xE9%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xBE%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x93%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+h%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+=%.\xA1\"\"2\xA13\xA2+-%7Z+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0140 \"!7Z+\u0135$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\u010A%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xDF%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xB4%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x89%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+^%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+3%.\xA1\"\"2\xA13\xA2+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X+& 4!6\xA3! %"),
- peg$decode("!7#+S$7#*# \" [+C%7#*# \" [+3%7#*# \" [+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("!7Z+=$.8\"\"2839+-%7Z+#%'#%$## X$\"# X\"# X*# \"7\\"),
- peg$decode("!7]+u$.J\"\"2J3K+e%7]+[%.J\"\"2J3K+K%7]+A%.J\"\"2J3K+1%7]+'%4'6\xA4' %$'# X$&# X$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!.\xA5\"\"2\xA53\xA6+3$0\xA7\"\"1!3\xA8+#%'\"%$\"# X\"# X*\xA0 \"!.\xA9\"\"2\xA93\xAA+=$0\xAB\"\"1!3\xAC+-%7!+#%'#%$## X$\"# X\"# X*o \"!.\xAD\"\"2\xAD3\xAE+7$7!+-%7!+#%'#%$## X$\"# X\"# X*D \"!0\xAF\"\"1!3\xB0+-$7!+#%'\"%$\"# X\"# X*# \"7!"),
- peg$decode("!!7!*# \" [+c$7!*# \" [+S%7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'%%$%# X$$# X$## X$\"# X\"# X+' 4!6\xB1!! %"),
- peg$decode(" \\!.2\"\"2233+-$7`+#%'\"%$\"# X\"# X,>&!.2\"\"2233+-$7`+#%'\"%$\"# X\"# X\""),
- peg$decode("7a*A \"7b*; \"7c*5 \"7d*/ \"7e*) \"7f*# \"7g"),
- peg$decode("!/\xB2\"\"1*3\xB3+b$/\xB4\"\"1#3\xB5*G \"/\xB6\"\"1#3\xB7*; \"/\xB8\"\"1$3\xB9*/ \"/\xBA\"\"1#3\xBB*# \"76+(%4\"6\xBC\"! %$\"# X\"# X"),
- peg$decode("!/\xBD\"\"1%3\xBE+J$/\xBF\"\"1%3\xC0*/ \"/\xC1\"\"1\"3\xC2*# \"76+(%4\"6\xC3\"! %$\"# X\"# X"),
- peg$decode("!/\xC4\"\"1'3\xC5+2$7\x8F+(%4\"6\xC6\"! %$\"# X\"# X"),
- peg$decode("!/\xC7\"\"1$3\xC8+2$7\xEF+(%4\"6\xC9\"! %$\"# X\"# X"),
- peg$decode("!/\xCA\"\"1&3\xCB+2$7T+(%4\"6\xCC\"! %$\"# X\"# X"),
- peg$decode("!/\xCD\"\"1\"3\xCE+R$!.>\"\"2>3?+-$76+#%'\"%$\"# X\"# X*# \" [+'%4\"6\xCF\" %$\"# X\"# X"),
- peg$decode("!7h+T$!.>\"\"2>3?+-$7i+#%'\"%$\"# X\"# X*# \" [+)%4\"6\xD0\"\"! %$\"# X\"# X"),
- peg$decode("! \\7j+&$,#&7j\"\"\" X+! (%"),
- peg$decode("! \\7j+&$,#&7j\"\"\" X+! (%"),
- peg$decode("7k*) \"7+*# \"7-"),
- peg$decode(".o\"\"2o3p*e \".q\"\"2q3r*Y \".4\"\"2435*M \".8\"\"2839*A \".<\"\"2<3=*5 \".@\"\"2@3A*) \".B\"\"2B3C"),
- peg$decode("!.6\"\"2637+u$7m+k% \\!.<\"\"2<3=+-$7m+#%'\"%$\"# X\"# X,>&!.<\"\"2<3=+-$7m+#%'\"%$\"# X\"# X\"+#%'#%$## X$\"# X\"# X"),
- peg$decode("!7n+C$.>\"\"2>3?+3%7o+)%4#6\xD1#\"\" %$## X$\"# X\"# X"),
- peg$decode(" \\7p*) \"7+*# \"7-+2$,/&7p*) \"7+*# \"7-\"\"\" X"),
- peg$decode(" \\7p*) \"7+*# \"7-,/&7p*) \"7+*# \"7-\""),
- peg$decode(".o\"\"2o3p*e \".q\"\"2q3r*Y \".4\"\"2435*M \".6\"\"2637*A \".8\"\"2839*5 \".@\"\"2@3A*) \".B\"\"2B3C"),
- peg$decode("7\x90*# \"7r"),
- peg$decode("!7\x8F+K$7'+A%7s+7%7'+-%7\x84+#%'%%$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("7M*# \"7t"),
- peg$decode("!7+G$.8\"\"2839+7%7u*# \"7x+'%4#6\xD2# %$## X$\"# X\"# X"),
- peg$decode("!7v*# \"7w+N$!.6\"\"2637+-$7\x83+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
- peg$decode("!.\xD3\"\"2\xD33\xD4+=$7\x80+3%7w*# \" [+#%'#%$## X$\"# X\"# X"),
- peg$decode("!.4\"\"2435+-$7{+#%'\"%$\"# X\"# X"),
- peg$decode("!7z+5$ \\7y,#&7y\"+#%'\"%$\"# X\"# X"),
- peg$decode("7**) \"7+*# \"7-"),
- peg$decode("7+*\x8F \"7-*\x89 \".2\"\"2233*} \".6\"\"2637*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
- peg$decode("!7|+k$ \\!.4\"\"2435+-$7|+#%'\"%$\"# X\"# X,>&!.4\"\"2435+-$7|+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("! \\7~,#&7~\"+k$ \\!.2\"\"2233+-$7}+#%'\"%$\"# X\"# X,>&!.2\"\"2233+-$7}+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode(" \\7~,#&7~\""),
- peg$decode("7+*w \"7-*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
- peg$decode("!7\"+\x8D$ \\7\"*G \"7!*A \".@\"\"2@3A*5 \".F\"\"2F3G*) \".J\"\"2J3K,M&7\"*G \"7!*A \".@\"\"2@3A*5 \".F\"\"2F3G*) \".J\"\"2J3K\"+'%4\"6\xD5\" %$\"# X\"# X"),
- peg$decode("7\x81*# \"7\x82"),
- peg$decode("!!7O+3$.:\"\"2:3;+#%'\"%$\"# X\"# X*# \" [+-$7S+#%'\"%$\"# X\"# X*# \" ["),
- peg$decode(" \\7+*\x83 \"7-*} \".B\"\"2B3C*q \".D\"\"2D3E*e \".2\"\"2233*Y \".8\"\"2839*M \".:\"\"2:3;*A \".<\"\"2<3=*5 \".>\"\"2>3?*) \".@\"\"2@3A+\x8C$,\x89&7+*\x83 \"7-*} \".B\"\"2B3C*q \".D\"\"2D3E*e \".2\"\"2233*Y \".8\"\"2839*M \".:\"\"2:3;*A \".<\"\"2<3=*5 \".>\"\"2>3?*) \".@\"\"2@3A\"\"\" X"),
- peg$decode(" \\7y,#&7y\""),
- peg$decode("!/\x95\"\"1#3\xD6+y$.4\"\"2435+i% \\7!+&$,#&7!\"\"\" X+P%.J\"\"2J3K+@% \\7!+&$,#&7!\"\"\" X+'%4%6\xD7% %$%# X$$# X$## X$\"# X\"# X"),
- peg$decode(".\xD8\"\"2\xD83\xD9"),
- peg$decode(".\xDA\"\"2\xDA3\xDB"),
- peg$decode(".\xDC\"\"2\xDC3\xDD"),
- peg$decode(".\xDE\"\"2\xDE3\xDF"),
- peg$decode(".\xE0\"\"2\xE03\xE1"),
- peg$decode(".\xE2\"\"2\xE23\xE3"),
- peg$decode(".\xE4\"\"2\xE43\xE5"),
- peg$decode(".\xE6\"\"2\xE63\xE7"),
- peg$decode(".\xE8\"\"2\xE83\xE9"),
- peg$decode(".\xEA\"\"2\xEA3\xEB"),
- peg$decode("!7\x85*S \"7\x86*M \"7\x88*G \"7\x89*A \"7\x8A*; \"7\x8B*5 \"7\x8C*/ \"7\x8D*) \"7\x8E*# \"76+& 4!6\xEC! %"),
- peg$decode("!7\x84+K$7'+A%7\x91+7%7'+-%7\x93+#%'%%$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!7\x92+' 4!6\xED!! %"),
- peg$decode("!7!+7$7!+-%7!+#%'#%$## X$\"# X\"# X"),
- peg$decode("! \\7**A \"7+*; \"7-*5 \"73*/ \"74*) \"7'*# \"7(,G&7**A \"7+*; \"7-*5 \"73*/ \"74*) \"7'*# \"7(\"+& 4!6\xEE! %"),
- peg$decode("!7\xB5+_$ \\!7A+-$7\xB5+#%'\"%$\"# X\"# X,8&!7A+-$7\xB5+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!79+R$!.:\"\"2:3;+-$79+#%'\"%$\"# X\"# X*# \" [+'%4\"6\xEF\" %$\"# X\"# X"),
- peg$decode("!7:*j \"!7\x97+_$ \\!7A+-$7\x97+#%'\"%$\"# X\"# X,8&!7A+-$7\x97+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+& 4!6\xF0! %"),
- peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\x9A+#%'\"%$\"# X\"# X,8&!7B+-$7\x9A+#%'\"%$\"# X\"# X\"+'%4\"6\xF1\" %$\"# X\"# X"),
- peg$decode("!7\x99*# \" [+A$7@+7%7M+-%7?+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("!!76+_$ \\!7.+-$76+#%'\"%$\"# X\"# X,8&!7.+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X*# \"7H+' 4!6\xF2!! %"),
- peg$decode("7\x9B*) \"7\x9C*# \"7\x9F"),
- peg$decode("!/\xF3\"\"1!3\xF4+<$7<+2%7\x9E+(%4#6\xF5#! %$## X$\"# X\"# X"),
- peg$decode("!/\xF6\"\"1'3\xF7+<$7<+2%7\x9D+(%4#6\xF8#! %$## X$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\xF9!! %"),
- peg$decode("!.\xFA\"\"2\xFA3\xFB+x$!.J\"\"2J3K+S$7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'$%$$# X$## X$\"# X\"# X*# \" [+'%4\"6\xFC\" %$\"# X\"# X"),
- peg$decode("!76+N$!7<+-$7\xA0+#%'\"%$\"# X\"# X*# \" [+)%4\"6\xFD\"\"! %$\"# X\"# X"),
- peg$decode("76*) \"7T*# \"7H"),
- peg$decode("!7\xA2+_$ \\!7B+-$7\xA3+#%'\"%$\"# X\"# X,8&!7B+-$7\xA3+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!/\xFE\"\"1&3\xFF*G \"/\u0100\"\"1'3\u0101*; \"/\u0102\"\"1$3\u0103*/ \"/\u0104\"\"1%3\u0105*# \"76+& 4!6\u0106! %"),
- peg$decode("7\xA4*# \"7\x9F"),
- peg$decode("!/\u0107\"\"1(3\u0108+O$7<+E%/\u0109\"\"1(3\u010A*/ \"/\u010B\"\"1(3\u010C*# \"76+#%'#%$## X$\"# X\"# X"),
- peg$decode("!76+_$ \\!7A+-$76+#%'\"%$\"# X\"# X,8&!7A+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u010D!! %"),
- peg$decode("!7\xA8+& 4!6\u010E! %"),
- peg$decode("!7\xA9+s$7;+i%7\xAE+_% \\!7B+-$7\xAF+#%'\"%$\"# X\"# X,8&!7B+-$7\xAF+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("7\xAA*# \"7\xAB"),
- peg$decode("/\u010F\"\"1$3\u0110*S \"/\u0111\"\"1%3\u0112*G \"/\u0113\"\"1%3\u0114*; \"/\u0115\"\"1%3\u0116*/ \"/\u0117\"\"1+3\u0118*# \"7\xAC"),
- peg$decode("/\u0119\"\"1'3\u011A*/ \"/\u011B\"\"1)3\u011C*# \"7\xAC"),
- peg$decode("76*# \"7\xAD"),
- peg$decode("!/\u011D\"\"1\"3\u011E+-$76+#%'\"%$\"# X\"# X"),
- peg$decode("7\xAC*# \"76"),
- peg$decode("!76+7$7<+-%7\xB0+#%'#%$## X$\"# X\"# X"),
- peg$decode("76*# \"7H"),
- peg$decode("!7\xB2+7$7.+-%7\x8F+#%'#%$## X$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u011F!! %"),
- peg$decode("!7\x9D+' 4!6\u0120!! %"),
- peg$decode("!7\xB5+d$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+(%4\"6\u0121\"!!%$\"# X\"# X"),
- peg$decode("!!77+k$ \\!.J\"\"2J3K+-$77+#%'\"%$\"# X\"# X,>&!.J\"\"2J3K+-$77+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+! (%"),
- peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\xB7+#%'\"%$\"# X\"# X,8&!7B+-$7\xB7+#%'\"%$\"# X\"# X\"+'%4\"6\u0122\" %$\"# X\"# X"),
- peg$decode("7\xB8*# \"7\x9F"),
- peg$decode("!/\u0123\"\"1#3\u0124+<$7<+2%76+(%4#6\u0125#! %$## X$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u0126!! %"),
- peg$decode("!7\x9D+' 4!6\u0127!! %"),
- peg$decode("! \\7\x99,#&7\x99\"+\x81$7@+w%7M+m%7?+c% \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4%6\u0128% %$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("7\xBD"),
- peg$decode("!/\u0129\"\"1&3\u012A+s$7.+i%7\xC0+_% \\!7A+-$7\xC0+#%'\"%$\"# X\"# X,8&!7A+-$7\xC0+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X*# \"7\xBE"),
- peg$decode("!76+s$7.+i%7\xBF+_% \\!7A+-$7\xBF+#%'\"%$\"# X\"# X,8&!7A+-$7\xBF+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("!76+=$7<+3%76*# \"7H+#%'#%$## X$\"# X\"# X"),
- peg$decode("7\xC1*G \"7\xC3*A \"7\xC5*; \"7\xC7*5 \"7\xC8*/ \"7\xC9*) \"7\xCA*# \"7\xBF"),
- peg$decode("!/\u012B\"\"1%3\u012C+7$7<+-%7\xC2+#%'#%$## X$\"# X\"# X"),
- peg$decode("!7I+' 4!6\u012D!! %"),
- peg$decode("!/\u012E\"\"1&3\u012F+\xA5$7<+\x9B%7D+\x91%7\xC4+\x87% \\! \\7'+&$,#&7'\"\"\" X+-$7\xC4+#%'\"%$\"# X\"# X,G&! \\7'+&$,#&7'\"\"\" X+-$7\xC4+#%'\"%$\"# X\"# X\"+-%7E+#%'&%$&# X$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("7t*# \"7w"),
- peg$decode("!/\u0130\"\"1%3\u0131+7$7<+-%7\xC6+#%'#%$## X$\"# X\"# X"),
- peg$decode("!7I+' 4!6\u0132!! %"),
- peg$decode("!/\u0133\"\"1&3\u0134+<$7<+2%7I+(%4#6\u0135#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0136\"\"1%3\u0137+_$7<+U%!/\u0138\"\"1$3\u0139+& 4!6\u013A! %*4 \"!/\u013B\"\"1%3\u013C+& 4!6\u013D! %+#%'#%$## X$\"# X\"# X"),
- peg$decode("!/\u013E\"\"1)3\u013F+T$7<+J%/\u0140\"\"1#3\u0141*/ \"/\u0142\"\"1(3\u0143*# \"76+(%4#6\u0144#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0145\"\"1#3\u0146+\x9E$7<+\x94%7D+\x8A%!7\xCB+k$ \\!.D\"\"2D3E+-$7\xCB+#%'\"%$\"# X\"# X,>&!.D\"\"2D3E+-$7\xCB+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+-%7E+#%'%%$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!/\u0147\"\"1(3\u0148*/ \"/\u0149\"\"1$3\u014A*# \"76+' 4!6\u014B!! %"),
- peg$decode("!76+_$ \\!7A+-$76+#%'\"%$\"# X\"# X,8&!7A+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!7\xCE+K$7.+A%7\xCE+7%7.+-%7\x8F+#%'%%$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u014C!! %"),
- peg$decode("!7\xD0+c$ \\!7A+-$7\xD0+#%'\"%$\"# X\"# X,8&!7A+-$7\xD0+#%'\"%$\"# X\"# X\"+'%4\"6\u014D\" %$\"# X\"# X"),
- peg$decode("!7\x98+c$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4\"6\u014E\" %$\"# X\"# X"),
- peg$decode("!7L*T \"7\x98*N \"!7@*# \" [+=$7t+3%7?*# \" [+#%'#%$## X$\"# X\"# X+c$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4\"6\u014F\" %$\"# X\"# X"),
- peg$decode("!7\xD3+c$ \\!7B+-$7\xD4+#%'\"%$\"# X\"# X,8&!7B+-$7\xD4+#%'\"%$\"# X\"# X\"+'%4\"6\u0150\" %$\"# X\"# X"),
- peg$decode("!7\x95+& 4!6\u0151! %"),
- peg$decode("!/\u0152\"\"1(3\u0153+<$7<+2%76+(%4#6\u0154#! %$## X$\"# X\"# X*j \"!/\u0155\"\"1&3\u0156+<$7<+2%76+(%4#6\u0157#! %$## X$\"# X\"# X*: \"!/\u0158\"\"1*3\u0159+& 4!6\u015A! %*# \"7\x9F"),
- peg$decode("!!76+o$ \\!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X,=&!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X\"+)%4\"6\u015C\"\"! %$\"# X\"# X*# \" [+' 4!6\u015D!! %"),
- peg$decode("!7\xD7+_$ \\!7A+-$7\xD7+#%'\"%$\"# X\"# X,8&!7A+-$7\xD7+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!7\x98+_$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u015E!! %"),
- peg$decode("!7\xDA+_$ \\!7B+-$7\xDB+#%'\"%$\"# X\"# X,8&!7B+-$7\xDB+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!/\u015F\"\"1&3\u0160*; \"/\u0161\"\"1'3\u0162*/ \"/\u0163\"\"1*3\u0164*# \"76+& 4!6\u0165! %"),
- peg$decode("!/\u0166\"\"1&3\u0167+<$7<+2%7\xDC+(%4#6\u0168#! %$## X$\"# X\"# X*\x83 \"!/\xF6\"\"1'3\xF7+<$7<+2%7\x9D+(%4#6\u0169#! %$## X$\"# X\"# X*S \"!/\u016A\"\"1+3\u016B+<$7<+2%7\x9D+(%4#6\u016C#! %$## X$\"# X\"# X*# \"7\x9F"),
- peg$decode("/\u016D\"\"1+3\u016E*k \"/\u016F\"\"1)3\u0170*_ \"/\u0171\"\"1(3\u0172*S \"/\u0173\"\"1'3\u0174*G \"/\u0175\"\"1&3\u0176*; \"/\u0177\"\"1*3\u0178*/ \"/\u0179\"\"1)3\u017A*# \"76"),
- peg$decode("71*# \" ["),
- peg$decode("!!76+o$ \\!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X,=&!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X\"+)%4\"6\u015C\"\"! %$\"# X\"# X*# \" [+' 4!6\u017B!! %"),
- peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\xE0+#%'\"%$\"# X\"# X,8&!7B+-$7\xE0+#%'\"%$\"# X\"# X\"+'%4\"6\u017C\" %$\"# X\"# X"),
- peg$decode("7\xB8*# \"7\x9F"),
- peg$decode("!7\xE2+_$ \\!7A+-$7\xE2+#%'\"%$\"# X\"# X,8&!7A+-$7\xE2+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
- peg$decode("!7\xE9+s$7.+i%7\xEC+_% \\!7B+-$7\xE3+#%'\"%$\"# X\"# X,8&!7B+-$7\xE3+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("7\xE4*; \"7\xE5*5 \"7\xE6*/ \"7\xE7*) \"7\xE8*# \"7\x9F"),
- peg$decode("!/\u017D\"\"1#3\u017E+<$7<+2%7\xEF+(%4#6\u017F#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0180\"\"1%3\u0181+<$7<+2%7T+(%4#6\u0182#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0183\"\"1(3\u0184+B$7<+8%7\\*# \"7Y+(%4#6\u0185#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0186\"\"1&3\u0187+<$7<+2%76+(%4#6\u0188#! %$## X$\"# X\"# X"),
- peg$decode("!/\u0189\"\"1%3\u018A+T$!7<+5$ \\7!,#&7!\"+#%'\"%$\"# X\"# X*# \" [+'%4\"6\u018B\" %$\"# X\"# X"),
- peg$decode("!7\xEA+K$7;+A%76+7%7;+-%7\xEB+#%'%%$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!/\x95\"\"1#3\xD6*# \"76+' 4!6\u018C!! %"),
- peg$decode("!/\xB4\"\"1#3\u018D*G \"/\xB6\"\"1#3\u018E*; \"/\xBA\"\"1#3\u018F*/ \"/\xB8\"\"1$3\u0190*# \"76+' 4!6\u0191!! %"),
- peg$decode("!7\xED+H$!7C+-$7\xEE+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
- peg$decode("!7U*) \"7\\*# \"7X+& 4!6\u0192! %"),
- peg$decode("!!7!*# \" [+c$7!*# \" [+S%7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'%%$%# X$$# X$## X$\"# X\"# X+' 4!6\u0193!! %"),
- peg$decode("!!7!+C$7!*# \" [+3%7!*# \" [+#%'#%$## X$\"# X\"# X+' 4!6\u0194!! %"),
- peg$decode("7\xBD"),
- peg$decode("!7\x9D+d$ \\!7B+-$7\xF2+#%'\"%$\"# X\"# X,8&!7B+-$7\xF2+#%'\"%$\"# X\"# X\"+(%4\"6\u0195\"!!%$\"# X\"# X"),
- peg$decode("7\xF3*# \"7\x9F"),
- peg$decode("!.\u0196\"\"2\u01963\u0197+N$7<+D%.\u0198\"\"2\u01983\u0199*) \".\u019A\"\"2\u019A3\u019B+(%4#6\u019C#! %$## X$\"# X\"# X"),
- peg$decode("!7\x9D+d$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+(%4\"6\u019D\"!!%$\"# X\"# X"),
- peg$decode("!76+7$70+-%7\xF6+#%'#%$## X$\"# X\"# X"),
- peg$decode(" \\72*) \"74*# \"7.,/&72*) \"74*# \"7.\""),
- peg$decode(" \\7%,#&7%\""),
- peg$decode("!7\xF9+=$.8\"\"2839+-%7\xFA+#%'#%$## X$\"# X\"# X"),
- peg$decode("!/\u019E\"\"1%3\u019F*) \"/\u01A0\"\"1$3\u01A1+' 4!6\u01A2!! %"),
- peg$decode("!7\xFB+N$!.8\"\"2839+-$7^+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
- peg$decode("!7\\*) \"7X*# \"7\x82+' 4!6\u01A3!! %"),
- peg$decode("! \\7\xFD*) \"7-*# \"7\xFE,/&7\xFD*) \"7-*# \"7\xFE\"+! (%"),
- peg$decode("7\"*S \"7!*M \".F\"\"2F3G*A \".J\"\"2J3K*5 \".H\"\"2H3I*) \".N\"\"2N3O"),
- peg$decode(".L\"\"2L3M*\x95 \".B\"\"2B3C*\x89 \".<\"\"2<3=*} \".R\"\"2R3S*q \".T\"\"2T3U*e \".V\"\"2V3W*Y \".P\"\"2P3Q*M \".@\"\"2@3A*A \".D\"\"2D3E*5 \".2\"\"2233*) \".>\"\"2>3?"),
- peg$decode("!7\u0100+h$.8\"\"2839+X%7\xFA+N%!.\u01A4\"\"2\u01A43\u01A5+-$7\xEB+#%'\"%$\"# X\"# X*# \" [+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("!/\u01A6\"\"1%3\u01A7*) \"/\u01A8\"\"1$3\u01A9+' 4!6\u01A2!! %"),
- peg$decode("!7\xEB+Q$/\xB4\"\"1#3\xB5*7 \"/\xB6\"\"1#3\xB7*+ \" \\7+,#&7+\"+'%4\"6\u01AA\" %$\"# X\"# X"),
- peg$decode("!7\u0104+\x8F$.F\"\"2F3G+%7\u0103+u%.F\"\"2F3G+e%7\u0103+[%.F\"\"2F3G+K%7\u0103+A%.F\"\"2F3G+1%7\u0105+'%4)6\u01AB) %$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X"),
- peg$decode("!7#+A$7#+7%7#+-%7#+#%'$%$$# X$## X$\"# X\"# X"),
- peg$decode("!7\u0103+-$7\u0103+#%'\"%$\"# X\"# X"),
- peg$decode("!7\u0103+7$7\u0103+-%7\u0103+#%'#%$## X$\"# X\"# X")
- ],
- peg$currPos = 0,
- peg$reportedPos = 0,
- peg$cachedPos = 0,
- peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
- peg$maxFailPos = 0,
- peg$maxFailExpected = [],
- peg$silentFails = 0,
- peg$result;
- if ("startRule" in options) {
- if (!(options.startRule in peg$startRuleIndices)) {
- throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
- }
- peg$startRuleIndex = peg$startRuleIndices[options.startRule];
- }
- function text() {
- return input.substring(peg$reportedPos, peg$currPos);
- }
- function offset() {
- return peg$reportedPos;
- }
- function line() {
- return peg$computePosDetails(peg$reportedPos).line;
- }
- function column() {
- return peg$computePosDetails(peg$reportedPos).column;
- }
- function expected(description) {
- throw peg$buildException(
- null,
- [{ type: "other", description: description }],
- peg$reportedPos
- );
- }
- function error(message) {
- throw peg$buildException(message, null, peg$reportedPos);
- }
- function peg$computePosDetails(pos) {
- function advance(details, startPos, endPos) {
- var p, ch;
- for (p = startPos; p < endPos; p++) {
- ch = input.charAt(p);
- if (ch === "\n") {
- if (!details.seenCR) { details.line++; }
- details.column = 1;
- details.seenCR = false;
- } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
- details.line++;
- details.column = 1;
- details.seenCR = true;
- } else {
- details.column++;
- details.seenCR = false;
- }
- }
- }
- if (peg$cachedPos !== pos) {
- if (peg$cachedPos > pos) {
- peg$cachedPos = 0;
- peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
- }
- advance(peg$cachedPosDetails, peg$cachedPos, pos);
- peg$cachedPos = pos;
- }
- return peg$cachedPosDetails;
- }
- function peg$fail(expected) {
- if (peg$currPos < peg$maxFailPos) { return; }
- if (peg$currPos > peg$maxFailPos) {
- peg$maxFailPos = peg$currPos;
- peg$maxFailExpected = [];
- }
- peg$maxFailExpected.push(expected);
- }
- function peg$buildException(message, expected, pos) {
- function cleanupExpected(expected) {
- var i = 1;
- expected.sort(function(a, b) {
- if (a.description < b.description) {
- return -1;
- } else if (a.description > b.description) {
- return 1;
- } else {
- return 0;
- }
- });
- while (i < expected.length) {
- if (expected[i - 1] === expected[i]) {
- expected.splice(i, 1);
- } else {
- i++;
- }
- }
- }
- function buildMessage(expected, found) {
- function stringEscape(s) {
- function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
- return s
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\x08/g, '\\b')
- .replace(/\t/g, '\\t')
- .replace(/\n/g, '\\n')
- .replace(/\f/g, '\\f')
- .replace(/\r/g, '\\r')
- .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
- .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
- .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
- .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
- }
- var expectedDescs = new Array(expected.length),
- expectedDesc, foundDesc, i;
- for (i = 0; i < expected.length; i++) {
- expectedDescs[i] = expected[i].description;
- }
- expectedDesc = expected.length > 1
- ? expectedDescs.slice(0, -1).join(", ")
- + " or "
- + expectedDescs[expected.length - 1]
- : expectedDescs[0];
- foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
- return "Expected " + expectedDesc + " but " + foundDesc + " found.";
- }
- var posDetails = peg$computePosDetails(pos),
- found = pos < input.length ? input.charAt(pos) : null;
- if (expected !== null) {
- cleanupExpected(expected);
- }
- return new SyntaxError(
- message !== null ? message : buildMessage(expected, found),
- expected,
- found,
- pos,
- posDetails.line,
- posDetails.column
- );
- }
- function peg$decode(s) {
- var bc = new Array(s.length), i;
- for (i = 0; i < s.length; i++) {
- bc[i] = s.charCodeAt(i) - 32;
- }
- return bc;
- }
- function peg$parseRule(index) {
- var bc = peg$bytecode[index],
- ip = 0,
- ips = [],
- end = bc.length,
- ends = [],
- stack = [],
- params, i;
- function protect(object) {
- return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;
- }
- while (true) {
- while (ip < end) {
- switch (bc[ip]) {
- case 0:
- stack.push(protect(peg$consts[bc[ip + 1]]));
- ip += 2;
- break;
- case 1:
- stack.push(peg$currPos);
- ip++;
- break;
- case 2:
- stack.pop();
- ip++;
- break;
- case 3:
- peg$currPos = stack.pop();
- ip++;
- break;
- case 4:
- stack.length -= bc[ip + 1];
- ip += 2;
- break;
- case 5:
- stack.splice(-2, 1);
- ip++;
- break;
- case 6:
- stack[stack.length - 2].push(stack.pop());
- ip++;
- break;
- case 7:
- stack.push(stack.splice(stack.length - bc[ip + 1], bc[ip + 1]));
- ip += 2;
- break;
- case 8:
- stack.pop();
- stack.push(input.substring(stack[stack.length - 1], peg$currPos));
- ip++;
- break;
- case 9:
- ends.push(end);
- ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
- if (stack[stack.length - 1]) {
- end = ip + 3 + bc[ip + 1];
- ip += 3;
- } else {
- end = ip + 3 + bc[ip + 1] + bc[ip + 2];
- ip += 3 + bc[ip + 1];
- }
- break;
- case 10:
- ends.push(end);
- ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
- if (stack[stack.length - 1] === peg$FAILED) {
- end = ip + 3 + bc[ip + 1];
- ip += 3;
- } else {
- end = ip + 3 + bc[ip + 1] + bc[ip + 2];
- ip += 3 + bc[ip + 1];
- }
- break;
- case 11:
- ends.push(end);
- ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
- if (stack[stack.length - 1] !== peg$FAILED) {
- end = ip + 3 + bc[ip + 1];
- ip += 3;
- } else {
- end = ip + 3 + bc[ip + 1] + bc[ip + 2];
- ip += 3 + bc[ip + 1];
- }
- break;
- case 12:
- if (stack[stack.length - 1] !== peg$FAILED) {
- ends.push(end);
- ips.push(ip);
- end = ip + 2 + bc[ip + 1];
- ip += 2;
- } else {
- ip += 2 + bc[ip + 1];
- }
- break;
- case 13:
- ends.push(end);
- ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
- if (input.length > peg$currPos) {
- end = ip + 3 + bc[ip + 1];
- ip += 3;
- } else {
- end = ip + 3 + bc[ip + 1] + bc[ip + 2];
- ip += 3 + bc[ip + 1];
- }
- break;
- case 14:
- ends.push(end);
- ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
- if (input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]) {
- end = ip + 4 + bc[ip + 2];
- ip += 4;
- } else {
- end = ip + 4 + bc[ip + 2] + bc[ip + 3];
- ip += 4 + bc[ip + 2];
- }
- break;
- case 15:
- ends.push(end);
- ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
- if (input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]) {
- end = ip + 4 + bc[ip + 2];
- ip += 4;
- } else {
- end = ip + 4 + bc[ip + 2] + bc[ip + 3];
- ip += 4 + bc[ip + 2];
- }
- break;
- case 16:
- ends.push(end);
- ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
- if (peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))) {
- end = ip + 4 + bc[ip + 2];
- ip += 4;
- } else {
- end = ip + 4 + bc[ip + 2] + bc[ip + 3];
- ip += 4 + bc[ip + 2];
- }
- break;
- case 17:
- stack.push(input.substr(peg$currPos, bc[ip + 1]));
- peg$currPos += bc[ip + 1];
- ip += 2;
- break;
- case 18:
- stack.push(peg$consts[bc[ip + 1]]);
- peg$currPos += peg$consts[bc[ip + 1]].length;
- ip += 2;
- break;
- case 19:
- stack.push(peg$FAILED);
- if (peg$silentFails === 0) {
- peg$fail(peg$consts[bc[ip + 1]]);
- }
- ip += 2;
- break;
- case 20:
- peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];
- ip += 2;
- break;
- case 21:
- peg$reportedPos = peg$currPos;
- ip++;
- break;
- case 22:
- params = bc.slice(ip + 4, ip + 4 + bc[ip + 3]);
- for (i = 0; i < bc[ip + 3]; i++) {
- params[i] = stack[stack.length - 1 - params[i]];
- }
- stack.splice(
- stack.length - bc[ip + 2],
- bc[ip + 2],
- peg$consts[bc[ip + 1]].apply(null, params)
- );
- ip += 4 + bc[ip + 3];
- break;
- case 23:
- stack.push(peg$parseRule(bc[ip + 1]));
- ip += 2;
- break;
- case 24:
- peg$silentFails++;
- ip++;
- break;
- case 25:
- peg$silentFails--;
- ip++;
- break;
- default:
- throw new Error("Invalid opcode: " + bc[ip] + ".");
- }
- }
- if (ends.length > 0) {
- end = ends.pop();
- ip = ips.pop();
- } else {
- break;
- }
- }
- return stack[0];
- }
- options.data = {}; // Object to which header attributes will be assigned during parsing
- function list (first, rest) {
- return [first].concat(rest);
- }
- peg$result = peg$parseRule(peg$startRuleIndex);
- if (peg$result !== peg$FAILED && peg$currPos === input.length) {
- return peg$result;
- } else {
- if (peg$result !== peg$FAILED && peg$currPos < input.length) {
- peg$fail({ type: "end", description: "end of input" });
- }
- throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);
- }
- }
- return {
- SyntaxError: SyntaxError,
- parse: parse
- };
- })();
- },{}],12:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Hacks - This file contains all of the things we
- * wish we didn't have to do, just for interop. It is similar to
- * Utils, which provides actually useful and relevant functions for
- * a SIP library. Methods in this file are grouped by vendor, so
- * as to most easily track when particular hacks may not be necessary anymore.
- */
- module.exports = function (SIP) {
- //keep to quiet jshint, and remain consistent with other files
- SIP = SIP;
- var Hacks = {
- AllBrowsers: {
- maskDtls: function (sdp) {
- if (sdp) {
- sdp = sdp.replace(/ UDP\/TLS\/RTP\/SAVP/gmi, " RTP/SAVP");
- }
- return sdp;
- },
- unmaskDtls: function (sdp) {
- /**
- * Chrome does not handle DTLS correctly (Canaray does, but not production)
- * keeping Chrome as SDES until DTLS is fixed (comment out 'is_opera' condition)
- *
- * UPDATE: May 21, 2014
- * Chrome 35 now properly defaults to DTLS. Only Opera remains using SDES
- *
- * UPDATE: 2014-09-24
- * Opera now supports DTLS by default as well.
- *
- **/
- return sdp.replace(/ RTP\/SAVP/gmi, " UDP/TLS/RTP/SAVP");
- }
- },
- Firefox: {
- /* Condition to detect if hacks are applicable */
- isFirefox: function () {
- return typeof mozRTCPeerConnection !== 'undefined';
- },
- cannotHandleExtraWhitespace: function (sdp) {
- if (this.isFirefox() && sdp) {
- sdp = sdp.replace(/ \r\n/g, "\r\n");
- }
- return sdp;
- },
- hasMissingCLineInSDP: function (sdp) {
- /*
- * This is a Firefox hack to insert valid sdp when getDescription is
- * called with the constraint offerToReceiveVideo = false.
- * We search for either a c-line at the top of the sdp above all
- * m-lines. If that does not exist then we search for a c-line
- * beneath each m-line. If it is missing a c-line, we insert
- * a fake c-line with the ip address 0.0.0.0. This is then valid
- * sdp and no media will be sent for that m-line.
- *
- * Valid SDP is:
- * m=
- * i=
- * c=
- */
- var insertAt, mlines;
- if (sdp.indexOf('c=') > sdp.indexOf('m=')) {
- // Find all m= lines
- mlines = sdp.match(/m=.*\r\n.*/g);
- for (var i=0; i<mlines.length; i++) {
- // If it has an i= line, check if the next line is the c= line
- if (mlines[i].toString().search(/i=.*/) >= 0) {
- insertAt = sdp.indexOf(mlines[i].toString())+mlines[i].toString().length;
- if (sdp.substr(insertAt,2)!=='c=') {
- sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP4 0.0.0.0' + sdp.substr(insertAt);
- }
- // else add the C line if it's missing
- } else if (mlines[i].toString().search(/c=.*/) < 0) {
- insertAt = sdp.indexOf(mlines[i].toString().match(/.*/))+mlines[i].toString().match(/.*/).toString().length;
- sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP4 0.0.0.0' + sdp.substr(insertAt);
- }
- }
- }
- return sdp;
- },
- },
- Chrome: {
- needsExplicitlyInactiveSDP: function (sdp) {
- var sub, index;
- if (Hacks.Firefox.isFirefox()) { // Fix this in Firefox before sending
- index = sdp.indexOf('m=video 0');
- if (index !== -1) {
- sub = sdp.substr(index);
- sub = sub.replace(/\r\nc=IN IP4.*\r\n$/,
- '\r\nc=IN IP4 0.0.0.0\r\na=inactive\r\n');
- return sdp.substr(0, index) + sub;
- }
- }
- return sdp;
- },
- getsConfusedAboutGUM: function (session) {
- if (session.mediaHandler) {
- session.mediaHandler.close();
- }
- }
- }
- };
- return Hacks;
- };
- },{}],13:[function(require,module,exports){
- "use strict";
- var levels = {
- 'error': 0,
- 'warn': 1,
- 'log': 2,
- 'debug': 3
- };
- module.exports = function (console) {
- var LoggerFactory = function () {
- var logger,
- level = 2,
- builtinEnabled = true,
- connector = null;
- this.loggers = {};
- logger = this.getLogger('sip.loggerfactory');
- Object.defineProperties(this, {
- builtinEnabled: {
- get: function(){ return builtinEnabled; },
- set: function(value){
- if (typeof value === 'boolean') {
- builtinEnabled = value;
- } else {
- logger.error('invalid "builtinEnabled" parameter value: '+ JSON.stringify(value));
- }
- }
- },
- level: {
- get: function() {return level; },
- set: function(value) {
- if (value >= 0 && value <=3) {
- level = value;
- } else if (value > 3) {
- level = 3;
- } else if (levels.hasOwnProperty(value)) {
- level = levels[value];
- } else {
- logger.error('invalid "level" parameter value: '+ JSON.stringify(value));
- }
- }
- },
- connector: {
- get: function() {return connector; },
- set: function(value){
- if(value === null || value === "" || value === undefined) {
- connector = null;
- } else if (typeof value === 'function') {
- connector = value;
- } else {
- logger.error('invalid "connector" parameter value: '+ JSON.stringify(value));
- }
- }
- }
- });
- };
- LoggerFactory.prototype.print = function(target, category, label, content) {
- if (typeof content === 'string') {
- var prefix = [new Date(), category];
- if (label) {
- prefix.push(label);
- }
- content = prefix.concat(content).join(' | ');
- }
- target.call(console, content);
- };
- function Logger (logger, category, label) {
- this.logger = logger;
- this.category = category;
- this.label = label;
- }
- Object.keys(levels).forEach(function (targetName) {
- Logger.prototype[targetName] = function (content) {
- this.logger[targetName](this.category, this.label, content);
- };
- LoggerFactory.prototype[targetName] = function (category, label, content) {
- if (this.level >= levels[targetName]) {
- if (this.builtinEnabled) {
- this.print(console[targetName], category, label, content);
- }
- if (this.connector) {
- this.connector(targetName, category, label, content);
- }
- }
- };
- });
- LoggerFactory.prototype.getLogger = function(category, label) {
- var logger;
- if (label && this.level === 3) {
- return new Logger(this, category, label);
- } else if (this.loggers[category]) {
- return this.loggers[category];
- } else {
- logger = new Logger(this, category);
- this.loggers[category] = logger;
- return logger;
- }
- };
- return LoggerFactory;
- };
- },{}],14:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview MediaHandler
- */
- /* MediaHandler
- * @class PeerConnection helper Class.
- * @param {SIP.Session} session
- * @param {Object} [options]
- */
- module.exports = function (EventEmitter) {
- var MediaHandler = function(session, options) {
- // keep jshint happy
- session = session;
- options = options;
- };
- MediaHandler.prototype = Object.create(EventEmitter.prototype, {
- isReady: {value: function isReady () {}},
- close: {value: function close () {}},
- /**
- * @param {Object} [mediaHint] A custom object describing the media to be used during this session.
- */
- getDescription: {value: function getDescription (mediaHint) {
- // keep jshint happy
- mediaHint = mediaHint;
- }},
- /**
- * Check if a SIP message contains a session description.
- * @param {SIP.SIPMessage} message
- * @returns {boolean}
- */
- hasDescription: {value: function hasDescription (message) {
- // keep jshint happy
- message = message;
- }},
- /**
- * Set the session description contained in a SIP message.
- * @param {SIP.SIPMessage} message
- * @returns {Promise}
- */
- setDescription: {value: function setDescription (message) {
- // keep jshint happy
- message = message;
- }}
- });
- return MediaHandler;
- };
- },{}],15:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP NameAddrHeader
- */
- /**
- * @augments SIP
- * @class Class creating a Name Address SIP header.
- *
- * @param {SIP.URI} uri
- * @param {String} [displayName]
- * @param {Object} [parameters]
- *
- */
- module.exports = function (SIP) {
- var NameAddrHeader;
- NameAddrHeader = function(uri, displayName, parameters) {
- var param;
- // Checks
- if(!uri || !(uri instanceof SIP.URI)) {
- throw new TypeError('missing or invalid "uri" parameter');
- }
- // Initialize parameters
- this.uri = uri;
- this.parameters = {};
- for (param in parameters) {
- this.setParam(param, parameters[param]);
- }
- Object.defineProperties(this, {
- friendlyName: {
- get: function() { return this.displayName || uri.aor; }
- },
- displayName: {
- get: function() { return displayName; },
- set: function(value) {
- displayName = (value === 0) ? '0' : value;
- }
- }
- });
- };
- NameAddrHeader.prototype = {
- setParam: function (key, value) {
- if(key) {
- this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString();
- }
- },
- getParam: SIP.URI.prototype.getParam,
- hasParam: SIP.URI.prototype.hasParam,
- deleteParam: SIP.URI.prototype.deleteParam,
- clearParams: SIP.URI.prototype.clearParams,
- clone: function() {
- return new NameAddrHeader(
- this.uri.clone(),
- this.displayName,
- JSON.parse(JSON.stringify(this.parameters)));
- },
- toString: function() {
- var body, parameter;
- body = (this.displayName || this.displayName === 0) ? '"' + this.displayName + '" ' : '';
- body += '<' + this.uri.toString() + '>';
- for (parameter in this.parameters) {
- body += ';' + parameter;
- if (this.parameters[parameter] !== null) {
- body += '='+ this.parameters[parameter];
- }
- }
- return body;
- }
- };
- /**
- * Parse the given string and returns a SIP.NameAddrHeader instance or undefined if
- * it is an invalid NameAddrHeader.
- * @public
- * @param {String} name_addr_header
- */
- NameAddrHeader.parse = function(name_addr_header) {
- name_addr_header = SIP.Grammar.parse(name_addr_header,'Name_Addr_Header');
- if (name_addr_header !== -1) {
- return name_addr_header;
- } else {
- return undefined;
- }
- };
- SIP.NameAddrHeader = NameAddrHeader;
- };
- },{}],16:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Message Parser
- */
- /**
- * Extract and parse every header of a SIP message.
- * @augments SIP
- * @namespace
- */
- module.exports = function (SIP) {
- var Parser;
- function getHeader(data, headerStart) {
- var
- // 'start' position of the header.
- start = headerStart,
- // 'end' position of the header.
- end = 0,
- // 'partial end' position of the header.
- partialEnd = 0;
- //End of message.
- if (data.substring(start, start + 2).match(/(^\r\n)/)) {
- return -2;
- }
- while(end === 0) {
- // Partial End of Header.
- partialEnd = data.indexOf('\r\n', start);
- // 'indexOf' returns -1 if the value to be found never occurs.
- if (partialEnd === -1) {
- return partialEnd;
- }
- if(!data.substring(partialEnd + 2, partialEnd + 4).match(/(^\r\n)/) && data.charAt(partialEnd + 2).match(/(^\s+)/)) {
- // Not the end of the message. Continue from the next position.
- start = partialEnd + 2;
- } else {
- end = partialEnd;
- }
- }
- return end;
- }
- function parseHeader(message, data, headerStart, headerEnd) {
- var header, idx, length, parsed,
- hcolonIndex = data.indexOf(':', headerStart),
- headerName = data.substring(headerStart, hcolonIndex).trim(),
- headerValue = data.substring(hcolonIndex + 1, headerEnd).trim();
- // If header-field is well-known, parse it.
- switch(headerName.toLowerCase()) {
- case 'via':
- case 'v':
- message.addHeader('via', headerValue);
- if(message.getHeaders('via').length === 1) {
- parsed = message.parseHeader('Via');
- if(parsed) {
- message.via = parsed;
- message.via_branch = parsed.branch;
- }
- } else {
- parsed = 0;
- }
- break;
- case 'from':
- case 'f':
- message.setHeader('from', headerValue);
- parsed = message.parseHeader('from');
- if(parsed) {
- message.from = parsed;
- message.from_tag = parsed.getParam('tag');
- }
- break;
- case 'to':
- case 't':
- message.setHeader('to', headerValue);
- parsed = message.parseHeader('to');
- if(parsed) {
- message.to = parsed;
- message.to_tag = parsed.getParam('tag');
- }
- break;
- case 'record-route':
- parsed = SIP.Grammar.parse(headerValue, 'Record_Route');
- if (parsed === -1) {
- parsed = undefined;
- break;
- }
- length = parsed.length;
- for (idx = 0; idx < length; idx++) {
- header = parsed[idx];
- message.addHeader('record-route', headerValue.substring(header.position, header.offset));
- message.headers['Record-Route'][message.getHeaders('record-route').length - 1].parsed = header.parsed;
- }
- break;
- case 'call-id':
- case 'i':
- message.setHeader('call-id', headerValue);
- parsed = message.parseHeader('call-id');
- if(parsed) {
- message.call_id = headerValue;
- }
- break;
- case 'contact':
- case 'm':
- parsed = SIP.Grammar.parse(headerValue, 'Contact');
- if (parsed === -1) {
- parsed = undefined;
- break;
- }
- length = parsed.length;
- for (idx = 0; idx < length; idx++) {
- header = parsed[idx];
- message.addHeader('contact', headerValue.substring(header.position, header.offset));
- message.headers['Contact'][message.getHeaders('contact').length - 1].parsed = header.parsed;
- }
- break;
- case 'content-length':
- case 'l':
- message.setHeader('content-length', headerValue);
- parsed = message.parseHeader('content-length');
- break;
- case 'content-type':
- case 'c':
- message.setHeader('content-type', headerValue);
- parsed = message.parseHeader('content-type');
- break;
- case 'cseq':
- message.setHeader('cseq', headerValue);
- parsed = message.parseHeader('cseq');
- if(parsed) {
- message.cseq = parsed.value;
- }
- if(message instanceof SIP.IncomingResponse) {
- message.method = parsed.method;
- }
- break;
- case 'max-forwards':
- message.setHeader('max-forwards', headerValue);
- parsed = message.parseHeader('max-forwards');
- break;
- case 'www-authenticate':
- message.setHeader('www-authenticate', headerValue);
- parsed = message.parseHeader('www-authenticate');
- break;
- case 'proxy-authenticate':
- message.setHeader('proxy-authenticate', headerValue);
- parsed = message.parseHeader('proxy-authenticate');
- break;
- case 'refer-to':
- case 'r':
- message.setHeader('refer-to', headerValue);
- parsed = message.parseHeader('refer-to');
- if (parsed) {
- message.refer_to = parsed;
- }
- break;
- default:
- // Do not parse this header.
- message.setHeader(headerName, headerValue);
- parsed = 0;
- }
- if (parsed === undefined) {
- return {
- error: 'error parsing header "'+ headerName +'"'
- };
- } else {
- return true;
- }
- }
- /** Parse SIP Message
- * @function
- * @param {String} message SIP message.
- * @param {Object} logger object.
- * @returns {SIP.IncomingRequest|SIP.IncomingResponse|undefined}
- */
- Parser = {};
- Parser.parseMessage = function(data, ua) {
- var message, firstLine, contentLength, bodyStart, parsed,
- headerStart = 0,
- headerEnd = data.indexOf('\r\n'),
- logger = ua.getLogger('sip.parser');
- if(headerEnd === -1) {
- logger.warn('no CRLF found, not a SIP message, discarded');
- return;
- }
- // Parse first line. Check if it is a Request or a Reply.
- firstLine = data.substring(0, headerEnd);
- parsed = SIP.Grammar.parse(firstLine, 'Request_Response');
- if(parsed === -1) {
- logger.warn('error parsing first line of SIP message: "' + firstLine + '"');
- return;
- } else if(!parsed.status_code) {
- message = new SIP.IncomingRequest(ua);
- message.method = parsed.method;
- message.ruri = parsed.uri;
- } else {
- message = new SIP.IncomingResponse(ua);
- message.status_code = parsed.status_code;
- message.reason_phrase = parsed.reason_phrase;
- }
- message.data = data;
- headerStart = headerEnd + 2;
- /* Loop over every line in data. Detect the end of each header and parse
- * it or simply add to the headers collection.
- */
- while(true) {
- headerEnd = getHeader(data, headerStart);
- // The SIP message has normally finished.
- if(headerEnd === -2) {
- bodyStart = headerStart + 2;
- break;
- }
- // data.indexOf returned -1 due to a malformed message.
- else if(headerEnd === -1) {
- logger.error('malformed message');
- return;
- }
- parsed = parseHeader(message, data, headerStart, headerEnd);
- if(parsed !== true) {
- logger.error(parsed.error);
- return;
- }
- headerStart = headerEnd + 2;
- }
- /* RFC3261 18.3.
- * If there are additional bytes in the transport packet
- * beyond the end of the body, they MUST be discarded.
- */
- if(message.hasHeader('content-length')) {
- contentLength = message.getHeader('content-length');
- message.body = data.substr(bodyStart, contentLength);
- } else {
- message.body = data.substring(bodyStart);
- }
- return message;
- };
- SIP.Parser = Parser;
- };
- },{}],17:[function(require,module,exports){
- "use strict";
- module.exports = function (SIP) {
- var RegisterContext;
- RegisterContext = function (ua) {
- var params = {},
- regId = 1;
- this.registrar = ua.configuration.registrarServer;
- this.expires = ua.configuration.registerExpires;
- // Contact header
- this.contact = ua.contact.toString();
- if(regId) {
- this.contact += ';reg-id='+ regId;
- this.contact += ';+sip.instance="<urn:uuid:'+ ua.configuration.instanceId+'>"';
- }
- // Call-ID and CSeq values RFC3261 10.2
- this.call_id = SIP.Utils.createRandomToken(22);
- this.cseq = 80;
- this.to_uri = ua.configuration.uri;
- params.to_uri = this.to_uri;
- params.to_displayName = ua.configuration.displayName;
- params.call_id = this.call_id;
- params.cseq = this.cseq;
- // Extends ClientContext
- SIP.Utils.augment(this, SIP.ClientContext, [ua, 'REGISTER', this.registrar, {params: params}]);
- this.registrationTimer = null;
- this.registrationExpiredTimer = null;
- // Set status
- this.registered = false;
- this.logger = ua.getLogger('sip.registercontext');
- };
- RegisterContext.prototype = {
- register: function (options) {
- var self = this, extraHeaders;
- // Handle Options
- this.options = options || {};
- extraHeaders = (this.options.extraHeaders || []).slice();
- extraHeaders.push('Contact: ' + this.contact + ';expires=' + this.expires);
- extraHeaders.push('Allow: ' + SIP.UA.C.ALLOWED_METHODS.toString());
- // Save original extraHeaders to be used in .close
- this.closeHeaders = this.options.closeWithHeaders ?
- (this.options.extraHeaders || []).slice() : [];
- this.receiveResponse = function(response) {
- var contact, expires,
- contacts = response.getHeaders('contact').length,
- cause;
- // Discard responses to older REGISTER/un-REGISTER requests.
- if(response.cseq !== this.cseq) {
- return;
- }
- // Clear registration timer
- if (this.registrationTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationTimer);
- this.registrationTimer = null;
- }
- switch(true) {
- case /^1[0-9]{2}$/.test(response.status_code):
- this.emit('progress', response);
- break;
- case /^2[0-9]{2}$/.test(response.status_code):
- this.emit('accepted', response);
- if(response.hasHeader('expires')) {
- expires = response.getHeader('expires');
- }
- if (this.registrationExpiredTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationExpiredTimer);
- this.registrationExpiredTimer = null;
- }
- // Search the Contact pointing to us and update the expires value accordingly.
- if (!contacts) {
- this.logger.warn('no Contact header in response to REGISTER, response ignored');
- break;
- }
- while(contacts--) {
- contact = response.parseHeader('contact', contacts);
- if(contact.uri.user === this.ua.contact.uri.user) {
- expires = contact.getParam('expires');
- break;
- } else {
- contact = null;
- }
- }
- if (!contact) {
- this.logger.warn('no Contact header pointing to us, response ignored');
- break;
- }
- if(!expires) {
- expires = this.expires;
- }
- // Re-Register before the expiration interval has elapsed.
- // For that, decrease the expires value. ie: 3 seconds
- this.registrationTimer = SIP.Timers.setTimeout(function() {
- self.registrationTimer = null;
- self.register(self.options);
- }, (expires * 1000) - 3000);
- this.registrationExpiredTimer = SIP.Timers.setTimeout(function () {
- self.logger.warn('registration expired');
- if (self.registered) {
- self.unregistered(null, SIP.C.causes.EXPIRES);
- }
- }, expires * 1000);
- //Save gruu values
- if (contact.hasParam('temp-gruu')) {
- this.ua.contact.temp_gruu = SIP.URI.parse(contact.getParam('temp-gruu').replace(/"/g,''));
- }
- if (contact.hasParam('pub-gruu')) {
- this.ua.contact.pub_gruu = SIP.URI.parse(contact.getParam('pub-gruu').replace(/"/g,''));
- }
- this.registered = true;
- this.emit('registered', response || null);
- break;
- // Interval too brief RFC3261 10.2.8
- case /^423$/.test(response.status_code):
- if(response.hasHeader('min-expires')) {
- // Increase our registration interval to the suggested minimum
- this.expires = response.getHeader('min-expires');
- // Attempt the registration again immediately
- this.register(this.options);
- } else { //This response MUST contain a Min-Expires header field
- this.logger.warn('423 response received for REGISTER without Min-Expires');
- this.registrationFailure(response, SIP.C.causes.SIP_FAILURE_CODE);
- }
- break;
- default:
- cause = SIP.Utils.sipErrorCause(response.status_code);
- this.registrationFailure(response, cause);
- }
- };
- this.onRequestTimeout = function() {
- this.registrationFailure(null, SIP.C.causes.REQUEST_TIMEOUT);
- };
- this.onTransportError = function() {
- this.registrationFailure(null, SIP.C.causes.CONNECTION_ERROR);
- };
- this.cseq++;
- this.request.cseq = this.cseq;
- this.request.setHeader('cseq', this.cseq + ' REGISTER');
- this.request.extraHeaders = extraHeaders;
- this.send();
- },
- registrationFailure: function (response, cause) {
- this.emit('failed', response || null, cause || null);
- },
- onTransportClosed: function() {
- this.registered_before = this.registered;
- if (this.registrationTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationTimer);
- this.registrationTimer = null;
- }
- if (this.registrationExpiredTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationExpiredTimer);
- this.registrationExpiredTimer = null;
- }
- if(this.registered) {
- this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
- }
- },
- onTransportConnected: function() {
- this.register(this.options);
- },
- close: function() {
- var options = {
- all: false,
- extraHeaders: this.closeHeaders
- };
- this.registered_before = this.registered;
- this.unregister(options);
- },
- unregister: function(options) {
- var extraHeaders;
- options = options || {};
- if(!this.registered && !options.all) {
- this.logger.warn('already unregistered');
- return;
- }
- extraHeaders = (options.extraHeaders || []).slice();
- this.registered = false;
- // Clear the registration timer.
- if (this.registrationTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationTimer);
- this.registrationTimer = null;
- }
- if(options.all) {
- extraHeaders.push('Contact: *');
- extraHeaders.push('Expires: 0');
- } else {
- extraHeaders.push('Contact: '+ this.contact + ';expires=0');
- }
- this.receiveResponse = function(response) {
- var cause;
- switch(true) {
- case /^1[0-9]{2}$/.test(response.status_code):
- this.emit('progress', response);
- break;
- case /^2[0-9]{2}$/.test(response.status_code):
- this.emit('accepted', response);
- if (this.registrationExpiredTimer !== null) {
- SIP.Timers.clearTimeout(this.registrationExpiredTimer);
- this.registrationExpiredTimer = null;
- }
- this.unregistered(response);
- break;
- default:
- cause = SIP.Utils.sipErrorCause(response.status_code);
- this.unregistered(response,cause);
- }
- };
- this.onRequestTimeout = function() {
- // Not actually unregistered...
- //this.unregistered(null, SIP.C.causes.REQUEST_TIMEOUT);
- };
- this.onTransportError = function() {
- // Not actually unregistered...
- //this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
- };
- this.cseq++;
- this.request.cseq = this.cseq;
- this.request.setHeader('cseq', this.cseq + ' REGISTER');
- this.request.extraHeaders = extraHeaders;
- this.send();
- },
- unregistered: function(response, cause) {
- this.registered = false;
- this.emit('unregistered', response || null, cause || null);
- }
- };
- SIP.RegisterContext = RegisterContext;
- };
- },{}],18:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Request Sender
- */
- /**
- * @augments SIP
- * @class Class creating a request sender.
- * @param {Object} applicant
- * @param {SIP.UA} ua
- */
- module.exports = function (SIP) {
- var RequestSender;
- RequestSender = function(applicant, ua) {
- this.logger = ua.getLogger('sip.requestsender');
- this.ua = ua;
- this.applicant = applicant;
- this.method = applicant.request.method;
- this.request = applicant.request;
- this.credentials = null;
- this.challenged = false;
- this.staled = false;
- // If ua is in closing process or even closed just allow sending Bye and ACK
- if (ua.status === SIP.UA.C.STATUS_USER_CLOSED && (this.method !== SIP.C.BYE || this.method !== SIP.C.ACK)) {
- this.onTransportError();
- }
- };
- /**
- * Create the client transaction and send the message.
- */
- RequestSender.prototype = {
- send: function() {
- switch(this.method) {
- case "INVITE":
- this.clientTransaction = new SIP.Transactions.InviteClientTransaction(this, this.request, this.ua.transport);
- break;
- case "ACK":
- this.clientTransaction = new SIP.Transactions.AckClientTransaction(this, this.request, this.ua.transport);
- break;
- default:
- this.clientTransaction = new SIP.Transactions.NonInviteClientTransaction(this, this.request, this.ua.transport);
- }
- this.clientTransaction.send();
- return this.clientTransaction;
- },
- /**
- * Callback fired when receiving a request timeout error from the client transaction.
- * To be re-defined by the applicant.
- * @event
- */
- onRequestTimeout: function() {
- this.applicant.onRequestTimeout();
- },
- /**
- * Callback fired when receiving a transport error from the client transaction.
- * To be re-defined by the applicant.
- * @event
- */
- onTransportError: function() {
- this.applicant.onTransportError();
- },
- /**
- * Called from client transaction when receiving a correct response to the request.
- * Authenticate request if needed or pass the response back to the applicant.
- * @param {SIP.IncomingResponse} response
- */
- receiveResponse: function(response) {
- var cseq, challenge, authorization_header_name,
- status_code = response.status_code;
- /*
- * Authentication
- * Authenticate once. _challenged_ flag used to avoid infinite authentications.
- */
- if (status_code === 401 || status_code === 407) {
- // Get and parse the appropriate WWW-Authenticate or Proxy-Authenticate header.
- if (response.status_code === 401) {
- challenge = response.parseHeader('www-authenticate');
- authorization_header_name = 'authorization';
- } else {
- challenge = response.parseHeader('proxy-authenticate');
- authorization_header_name = 'proxy-authorization';
- }
- // Verify it seems a valid challenge.
- if (! challenge) {
- this.logger.warn(response.status_code + ' with wrong or missing challenge, cannot authenticate');
- this.applicant.receiveResponse(response);
- return;
- }
- if (!this.challenged || (!this.staled && challenge.stale === true)) {
- if (!this.credentials) {
- this.credentials = this.ua.configuration.authenticationFactory(this.ua);
- }
- // Verify that the challenge is really valid.
- if (!this.credentials.authenticate(this.request, challenge)) {
- this.applicant.receiveResponse(response);
- return;
- }
- this.challenged = true;
- if (challenge.stale) {
- this.staled = true;
- }
- if (response.method === SIP.C.REGISTER) {
- cseq = this.applicant.cseq += 1;
- } else if (this.request.dialog){
- cseq = this.request.dialog.local_seqnum += 1;
- } else {
- cseq = this.request.cseq + 1;
- this.request.cseq = cseq;
- }
- this.request.setHeader('cseq', cseq +' '+ this.method);
- this.request.setHeader(authorization_header_name, this.credentials.toString());
- this.send();
- } else {
- this.applicant.receiveResponse(response);
- }
- } else {
- this.applicant.receiveResponse(response);
- }
- }
- };
- SIP.RequestSender = RequestSender;
- };
- },{}],19:[function(require,module,exports){
- /**
- * @name SIP
- * @namespace
- */
- "use strict";
- module.exports = function (environment) {
- var pkg = require('../package.json'),
- version = pkg.version,
- title = pkg.title;
- var SIP = Object.defineProperties({}, {
- version: {
- get: function(){ return version; }
- },
- name: {
- get: function(){ return title; }
- }
- });
- require('./Utils')(SIP, environment);
- SIP.LoggerFactory = require('./LoggerFactory')(environment.console);
- SIP.EventEmitter = require('./EventEmitter')(environment.console);
- SIP.C = require('./Constants')(SIP.name, SIP.version);
- SIP.Exceptions = require('./Exceptions');
- SIP.Timers = require('./Timers')(environment.timers);
- SIP.Transport = environment.Transport(SIP, environment.WebSocket);
- require('./Parser')(SIP);
- require('./SIPMessage')(SIP);
- require('./URI')(SIP);
- require('./NameAddrHeader')(SIP);
- require('./Transactions')(SIP);
- require('./Dialogs')(SIP);
- require('./RequestSender')(SIP);
- require('./RegisterContext')(SIP);
- SIP.MediaHandler = require('./MediaHandler')(SIP.EventEmitter);
- require('./ClientContext')(SIP);
- require('./ServerContext')(SIP);
- require('./Session')(SIP, environment);
- require('./Subscription')(SIP);
- SIP.WebRTC = require('./WebRTC')(SIP, environment);
- require('./UA')(SIP, environment);
- SIP.Hacks = require('./Hacks')(SIP);
- require('./SanityCheck')(SIP);
- SIP.DigestAuthentication = require('./DigestAuthentication')(SIP.Utils);
- SIP.Grammar = require('./Grammar')(SIP);
- return SIP;
- };
- },{"../package.json":2,"./ClientContext":3,"./Constants":4,"./Dialogs":6,"./DigestAuthentication":7,"./EventEmitter":8,"./Exceptions":9,"./Grammar":10,"./Hacks":12,"./LoggerFactory":13,"./MediaHandler":14,"./NameAddrHeader":15,"./Parser":16,"./RegisterContext":17,"./RequestSender":18,"./SIPMessage":20,"./SanityCheck":21,"./ServerContext":22,"./Session":23,"./Subscription":25,"./Timers":26,"./Transactions":27,"./UA":29,"./URI":30,"./Utils":31,"./WebRTC":32}],20:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Message
- */
- module.exports = function (SIP) {
- var
- OutgoingRequest,
- IncomingMessage,
- IncomingRequest,
- IncomingResponse;
- function getSupportedHeader (request) {
- var allowUnregistered = request.ua.configuration.hackAllowUnregisteredOptionTags;
- var optionTags = [];
- var optionTagSet = {};
- if (request.method === SIP.C.REGISTER) {
- optionTags.push('path', 'gruu');
- } else if (request.method === SIP.C.INVITE &&
- (request.ua.contact.pub_gruu || request.ua.contact.temp_gruu)) {
- optionTags.push('gruu');
- }
- if (request.ua.configuration.rel100 === SIP.C.supported.SUPPORTED) {
- optionTags.push('100rel');
- }
- if (request.ua.configuration.replaces === SIP.C.supported.SUPPORTED) {
- optionTags.push('replaces');
- }
- optionTags.push('outbound');
- optionTags = optionTags.concat(request.ua.configuration.extraSupported);
- optionTags = optionTags.filter(function(optionTag) {
- var registered = SIP.C.OPTION_TAGS[optionTag];
- var unique = !optionTagSet[optionTag];
- optionTagSet[optionTag] = true;
- return (registered || allowUnregistered) && unique;
- });
- return 'Supported: ' + optionTags.join(', ') + '\r\n';
- }
- /**
- * @augments SIP
- * @class Class for outgoing SIP request.
- * @param {String} method request method
- * @param {String} ruri request uri
- * @param {SIP.UA} ua
- * @param {Object} params parameters that will have priority over ua.configuration parameters:
- * <br>
- * - cseq, call_id, from_tag, from_uri, from_displayName, to_uri, to_tag, route_set
- * @param {Object} [headers] extra headers
- * @param {String} [body]
- */
- OutgoingRequest = function(method, ruri, ua, params, extraHeaders, body) {
- var
- to,
- from,
- call_id,
- cseq,
- to_uri,
- from_uri;
- params = params || {};
- // Mandatory parameters check
- if(!method || !ruri || !ua) {
- return null;
- }
- this.logger = ua.getLogger('sip.sipmessage');
- this.ua = ua;
- this.headers = {};
- this.method = method;
- this.ruri = ruri;
- this.body = body;
- this.extraHeaders = (extraHeaders || []).slice();
- this.statusCode = params.status_code;
- this.reasonPhrase = params.reason_phrase;
- // Fill the Common SIP Request Headers
- // Route
- if (params.route_set) {
- this.setHeader('route', params.route_set);
- } else if (ua.configuration.usePreloadedRoute){
- this.setHeader('route', ua.transport.server.sip_uri);
- }
- // Via
- // Empty Via header. Will be filled by the client transaction.
- this.setHeader('via', '');
- // Max-Forwards
- this.setHeader('max-forwards', SIP.UA.C.MAX_FORWARDS);
- // To
- to_uri = params.to_uri || ruri;
- to = (params.to_displayName || params.to_displayName === 0) ? '"' + params.to_displayName + '" ' : '';
- to += '<' + (to_uri && to_uri.toRaw ? to_uri.toRaw() : to_uri) + '>';
- to += params.to_tag ? ';tag=' + params.to_tag : '';
- this.to = new SIP.NameAddrHeader.parse(to);
- this.setHeader('to', to);
- // From
- from_uri = params.from_uri || ua.configuration.uri;
- if (params.from_displayName || params.from_displayName === 0) {
- from = '"' + params.from_displayName + '" ';
- } else if (ua.configuration.displayName) {
- from = '"' + ua.configuration.displayName + '" ';
- } else {
- from = '';
- }
- from += '<' + (from_uri && from_uri.toRaw ? from_uri.toRaw() : from_uri) + '>;tag=';
- from += params.from_tag || SIP.Utils.newTag();
- this.from = new SIP.NameAddrHeader.parse(from);
- this.setHeader('from', from);
- // Call-ID
- call_id = params.call_id || (ua.configuration.sipjsId + SIP.Utils.createRandomToken(15));
- this.call_id = call_id;
- this.setHeader('call-id', call_id);
- // CSeq
- cseq = params.cseq || Math.floor(Math.random() * 10000);
- this.cseq = cseq;
- this.setHeader('cseq', cseq + ' ' + method);
- };
- OutgoingRequest.prototype = {
- /**
- * Replace the the given header by the given value.
- * @param {String} name header name
- * @param {String | Array} value header value
- */
- setHeader: function(name, value) {
- this.headers[SIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value];
- },
- /**
- * Get the value of the given header name at the given position.
- * @param {String} name header name
- * @returns {String|undefined} Returns the specified header, undefined if header doesn't exist.
- */
- getHeader: function(name) {
- var regexp, idx,
- length = this.extraHeaders.length,
- header = this.headers[SIP.Utils.headerize(name)];
- if(header) {
- if(header[0]) {
- return header[0];
- }
- } else {
- regexp = new RegExp('^\\s*' + name + '\\s*:','i');
- for (idx = 0; idx < length; idx++) {
- header = this.extraHeaders[idx];
- if (regexp.test(header)) {
- return header.substring(header.indexOf(':')+1).trim();
- }
- }
- }
- return;
- },
- /**
- * Get the header/s of the given name.
- * @param {String} name header name
- * @returns {Array} Array with all the headers of the specified name.
- */
- getHeaders: function(name) {
- var idx, length, regexp,
- header = this.headers[SIP.Utils.headerize(name)],
- result = [];
- if(header) {
- length = header.length;
- for (idx = 0; idx < length; idx++) {
- result.push(header[idx]);
- }
- return result;
- } else {
- length = this.extraHeaders.length;
- regexp = new RegExp('^\\s*' + name + '\\s*:','i');
- for (idx = 0; idx < length; idx++) {
- header = this.extraHeaders[idx];
- if (regexp.test(header)) {
- result.push(header.substring(header.indexOf(':')+1).trim());
- }
- }
- return result;
- }
- },
- /**
- * Verify the existence of the given header.
- * @param {String} name header name
- * @returns {boolean} true if header with given name exists, false otherwise
- */
- hasHeader: function(name) {
- var regexp, idx,
- length = this.extraHeaders.length;
- if (this.headers[SIP.Utils.headerize(name)]) {
- return true;
- } else {
- regexp = new RegExp('^\\s*' + name + '\\s*:','i');
- for (idx = 0; idx < length; idx++) {
- if (regexp.test(this.extraHeaders[idx])) {
- return true;
- }
- }
- }
- return false;
- },
- toString: function() {
- var msg = '', header, length, idx;
- msg += this.method + ' ' + (this.ruri.toRaw ? this.ruri.toRaw() : this.ruri) + ' SIP/2.0\r\n';
- for (header in this.headers) {
- length = this.headers[header].length;
- for (idx = 0; idx < length; idx++) {
- msg += header + ': ' + this.headers[header][idx] + '\r\n';
- }
- }
- length = this.extraHeaders.length;
- for (idx = 0; idx < length; idx++) {
- msg += this.extraHeaders[idx].trim() +'\r\n';
- }
- msg += getSupportedHeader(this);
- msg += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
- if (this.body) {
- if (typeof this.body === 'string') {
- length = SIP.Utils.str_utf8_length(this.body);
- msg += 'Content-Length: ' + length + '\r\n\r\n';
- msg += this.body;
- } else {
- if (this.body.body && this.body.contentType) {
- length = SIP.Utils.str_utf8_length(this.body.body);
- msg += 'Content-Type: ' + this.body.contentType + '\r\n';
- msg += 'Content-Length: ' + length + '\r\n\r\n';
- msg += this.body.body;
- } else {
- msg += 'Content-Length: ' + 0 + '\r\n\r\n';
- }
- }
- } else {
- msg += 'Content-Length: ' + 0 + '\r\n\r\n';
- }
- return msg;
- }
- };
- /**
- * @augments SIP
- * @class Class for incoming SIP message.
- */
- IncomingMessage = function(){
- this.data = null;
- this.headers = null;
- this.method = null;
- this.via = null;
- this.via_branch = null;
- this.call_id = null;
- this.cseq = null;
- this.from = null;
- this.from_tag = null;
- this.to = null;
- this.to_tag = null;
- this.body = null;
- };
- IncomingMessage.prototype = {
- /**
- * Insert a header of the given name and value into the last position of the
- * header array.
- * @param {String} name header name
- * @param {String} value header value
- */
- addHeader: function(name, value) {
- var header = { raw: value };
- name = SIP.Utils.headerize(name);
- if(this.headers[name]) {
- this.headers[name].push(header);
- } else {
- this.headers[name] = [header];
- }
- },
- /**
- * Get the value of the given header name at the given position.
- * @param {String} name header name
- * @returns {String|undefined} Returns the specified header, null if header doesn't exist.
- */
- getHeader: function(name) {
- var header = this.headers[SIP.Utils.headerize(name)];
- if(header) {
- if(header[0]) {
- return header[0].raw;
- }
- } else {
- return;
- }
- },
- /**
- * Get the header/s of the given name.
- * @param {String} name header name
- * @returns {Array} Array with all the headers of the specified name.
- */
- getHeaders: function(name) {
- var idx, length,
- header = this.headers[SIP.Utils.headerize(name)],
- result = [];
- if(!header) {
- return [];
- }
- length = header.length;
- for (idx = 0; idx < length; idx++) {
- result.push(header[idx].raw);
- }
- return result;
- },
- /**
- * Verify the existence of the given header.
- * @param {String} name header name
- * @returns {boolean} true if header with given name exists, false otherwise
- */
- hasHeader: function(name) {
- return(this.headers[SIP.Utils.headerize(name)]) ? true : false;
- },
- /**
- * Parse the given header on the given index.
- * @param {String} name header name
- * @param {Number} [idx=0] header index
- * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
- */
- parseHeader: function(name, idx) {
- var header, value, parsed;
- name = SIP.Utils.headerize(name);
- idx = idx || 0;
- if(!this.headers[name]) {
- this.logger.log('header "' + name + '" not present');
- return;
- } else if(idx >= this.headers[name].length) {
- this.logger.log('not so many "' + name + '" headers present');
- return;
- }
- header = this.headers[name][idx];
- value = header.raw;
- if(header.parsed) {
- return header.parsed;
- }
- //substitute '-' by '_' for grammar rule matching.
- parsed = SIP.Grammar.parse(value, name.replace(/-/g, '_'));
- if(parsed === -1) {
- this.headers[name].splice(idx, 1); //delete from headers
- this.logger.warn('error parsing "' + name + '" header field with value "' + value + '"');
- return;
- } else {
- header.parsed = parsed;
- return parsed;
- }
- },
- /**
- * Message Header attribute selector. Alias of parseHeader.
- * @param {String} name header name
- * @param {Number} [idx=0] header index
- * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
- *
- * @example
- * message.s('via',3).port
- */
- s: function(name, idx) {
- return this.parseHeader(name, idx);
- },
- /**
- * Replace the value of the given header by the value.
- * @param {String} name header name
- * @param {String} value header value
- */
- setHeader: function(name, value) {
- var header = { raw: value };
- this.headers[SIP.Utils.headerize(name)] = [header];
- },
- toString: function() {
- return this.data;
- }
- };
- /**
- * @augments IncomingMessage
- * @class Class for incoming SIP request.
- */
- IncomingRequest = function(ua) {
- this.logger = ua.getLogger('sip.sipmessage');
- this.ua = ua;
- this.headers = {};
- this.ruri = null;
- this.transport = null;
- this.server_transaction = null;
- };
- IncomingRequest.prototype = new IncomingMessage();
- /**
- * Stateful reply.
- * @param {Number} code status code
- * @param {String} reason reason phrase
- * @param {Object} headers extra headers
- * @param {String} body body
- * @param {Function} [onSuccess] onSuccess callback
- * @param {Function} [onFailure] onFailure callback
- */
- IncomingRequest.prototype.reply = function(code, reason, extraHeaders, body, onSuccess, onFailure) {
- var rr, vias, length, idx, response,
- to = this.getHeader('To'),
- r = 0,
- v = 0;
- response = SIP.Utils.buildStatusLine(code, reason);
- extraHeaders = (extraHeaders || []).slice();
- if(this.method === SIP.C.INVITE && code > 100 && code <= 200) {
- rr = this.getHeaders('record-route');
- length = rr.length;
- for(r; r < length; r++) {
- response += 'Record-Route: ' + rr[r] + '\r\n';
- }
- }
- vias = this.getHeaders('via');
- length = vias.length;
- for(v; v < length; v++) {
- response += 'Via: ' + vias[v] + '\r\n';
- }
- if(!this.to_tag && code > 100) {
- to += ';tag=' + SIP.Utils.newTag();
- } else if(this.to_tag && !this.s('to').hasParam('tag')) {
- to += ';tag=' + this.to_tag;
- }
- response += 'To: ' + to + '\r\n';
- response += 'From: ' + this.getHeader('From') + '\r\n';
- response += 'Call-ID: ' + this.call_id + '\r\n';
- response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';
- length = extraHeaders.length;
- for (idx = 0; idx < length; idx++) {
- response += extraHeaders[idx].trim() +'\r\n';
- }
- response += getSupportedHeader(this);
- response += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
- if (body) {
- if (typeof body === 'string') {
- length = SIP.Utils.str_utf8_length(body);
- response += 'Content-Type: application/sdp\r\n';
- response += 'Content-Length: ' + length + '\r\n\r\n';
- response += body;
- } else {
- if (body.body && body.contentType) {
- length = SIP.Utils.str_utf8_length(body.body);
- response += 'Content-Type: ' + body.contentType + '\r\n';
- response += 'Content-Length: ' + length + '\r\n\r\n';
- response += body.body;
- } else {
- response += 'Content-Length: ' + 0 + '\r\n\r\n';
- }
- }
- } else {
- response += 'Content-Length: ' + 0 + '\r\n\r\n';
- }
- this.server_transaction.receiveResponse(code, response).then(onSuccess, onFailure);
- return response;
- };
- /**
- * Stateless reply.
- * @param {Number} code status code
- * @param {String} reason reason phrase
- */
- IncomingRequest.prototype.reply_sl = function(code, reason) {
- var to, response,
- v = 0,
- vias = this.getHeaders('via'),
- length = vias.length;
- response = SIP.Utils.buildStatusLine(code, reason);
- for(v; v < length; v++) {
- response += 'Via: ' + vias[v] + '\r\n';
- }
- to = this.getHeader('To');
- if(!this.to_tag && code > 100) {
- to += ';tag=' + SIP.Utils.newTag();
- } else if(this.to_tag && !this.s('to').hasParam('tag')) {
- to += ';tag=' + this.to_tag;
- }
- response += 'To: ' + to + '\r\n';
- response += 'From: ' + this.getHeader('From') + '\r\n';
- response += 'Call-ID: ' + this.call_id + '\r\n';
- response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';
- response += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
- response += 'Content-Length: ' + 0 + '\r\n\r\n';
- this.transport.send(response);
- };
- /**
- * @augments IncomingMessage
- * @class Class for incoming SIP response.
- */
- IncomingResponse = function(ua) {
- this.logger = ua.getLogger('sip.sipmessage');
- this.headers = {};
- this.status_code = null;
- this.reason_phrase = null;
- };
- IncomingResponse.prototype = new IncomingMessage();
- SIP.OutgoingRequest = OutgoingRequest;
- SIP.IncomingRequest = IncomingRequest;
- SIP.IncomingResponse = IncomingResponse;
- };
- },{}],21:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Incoming SIP Message Sanity Check
- */
- /**
- * SIP message sanity check.
- * @augments SIP
- * @function
- * @param {SIP.IncomingMessage} message
- * @param {SIP.UA} ua
- * @param {SIP.Transport} transport
- * @returns {Boolean}
- */
- module.exports = function (SIP) {
- var sanityCheck,
- requests = [],
- responses = [],
- all = [];
- // Reply
- function reply(status_code, message, transport) {
- var to,
- response = SIP.Utils.buildStatusLine(status_code),
- vias = message.getHeaders('via'),
- length = vias.length,
- idx = 0;
- for(idx; idx < length; idx++) {
- response += "Via: " + vias[idx] + "\r\n";
- }
- to = message.getHeader('To');
- if(!message.to_tag) {
- to += ';tag=' + SIP.Utils.newTag();
- }
- response += "To: " + to + "\r\n";
- response += "From: " + message.getHeader('From') + "\r\n";
- response += "Call-ID: " + message.call_id + "\r\n";
- response += "CSeq: " + message.cseq + " " + message.method + "\r\n";
- response += "\r\n";
- transport.send(response);
- }
- /*
- * Sanity Check for incoming Messages
- *
- * Requests:
- * - _rfc3261_8_2_2_1_ Receive a Request with a non supported URI scheme
- * - _rfc3261_16_3_4_ Receive a Request already sent by us
- * Does not look at via sent-by but at sipjsId, which is inserted as
- * a prefix in all initial requests generated by the ua
- * - _rfc3261_18_3_request_ Body Content-Length
- * - _rfc3261_8_2_2_2_ Merged Requests
- *
- * Responses:
- * - _rfc3261_8_1_3_3_ Multiple Via headers
- * - _rfc3261_18_1_2_ sent-by mismatch
- * - _rfc3261_18_3_response_ Body Content-Length
- *
- * All:
- * - Minimum headers in a SIP message
- */
- // Sanity Check functions for requests
- function rfc3261_8_2_2_1(message, ua, transport) {
- if(!message.ruri || message.ruri.scheme !== 'sip') {
- reply(416, message, transport);
- return false;
- }
- }
- function rfc3261_16_3_4(message, ua, transport) {
- if(!message.to_tag) {
- if(message.call_id.substr(0, 5) === ua.configuration.sipjsId) {
- reply(482, message, transport);
- return false;
- }
- }
- }
- function rfc3261_18_3_request(message, ua, transport) {
- var len = SIP.Utils.str_utf8_length(message.body),
- contentLength = message.getHeader('content-length');
- if(len < contentLength) {
- reply(400, message, transport);
- return false;
- }
- }
- function rfc3261_8_2_2_2(message, ua, transport) {
- var tr, idx,
- fromTag = message.from_tag,
- call_id = message.call_id,
- cseq = message.cseq;
- if(!message.to_tag) {
- if(message.method === SIP.C.INVITE) {
- tr = ua.transactions.ist[message.via_branch];
- if(tr) {
- return;
- } else {
- for(idx in ua.transactions.ist) {
- tr = ua.transactions.ist[idx];
- if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
- reply(482, message, transport);
- return false;
- }
- }
- }
- } else {
- tr = ua.transactions.nist[message.via_branch];
- if(tr) {
- return;
- } else {
- for(idx in ua.transactions.nist) {
- tr = ua.transactions.nist[idx];
- if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
- reply(482, message, transport);
- return false;
- }
- }
- }
- }
- }
- }
- // Sanity Check functions for responses
- function rfc3261_8_1_3_3(message, ua) {
- if(message.getHeaders('via').length > 1) {
- ua.getLogger('sip.sanitycheck').warn('More than one Via header field present in the response. Dropping the response');
- return false;
- }
- }
- function rfc3261_18_1_2(message, ua) {
- var viaHost = ua.configuration.viaHost;
- if(message.via.host !== viaHost || message.via.port !== undefined) {
- ua.getLogger('sip.sanitycheck').warn('Via sent-by in the response does not match UA Via host value. Dropping the response');
- return false;
- }
- }
- function rfc3261_18_3_response(message, ua) {
- var
- len = SIP.Utils.str_utf8_length(message.body),
- contentLength = message.getHeader('content-length');
- if(len < contentLength) {
- ua.getLogger('sip.sanitycheck').warn('Message body length is lower than the value in Content-Length header field. Dropping the response');
- return false;
- }
- }
- // Sanity Check functions for requests and responses
- function minimumHeaders(message, ua) {
- var
- mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'],
- idx = mandatoryHeaders.length;
- while(idx--) {
- if(!message.hasHeader(mandatoryHeaders[idx])) {
- ua.getLogger('sip.sanitycheck').warn('Missing mandatory header field : '+ mandatoryHeaders[idx] +'. Dropping the response');
- return false;
- }
- }
- }
- requests.push(rfc3261_8_2_2_1);
- requests.push(rfc3261_16_3_4);
- requests.push(rfc3261_18_3_request);
- requests.push(rfc3261_8_2_2_2);
- responses.push(rfc3261_8_1_3_3);
- responses.push(rfc3261_18_1_2);
- responses.push(rfc3261_18_3_response);
- all.push(minimumHeaders);
- sanityCheck = function(message, ua, transport) {
- var len, pass;
- len = all.length;
- while(len--) {
- pass = all[len](message, ua, transport);
- if(pass === false) {
- return false;
- }
- }
- if(message instanceof SIP.IncomingRequest) {
- len = requests.length;
- while(len--) {
- pass = requests[len](message, ua, transport);
- if(pass === false) {
- return false;
- }
- }
- }
- else if(message instanceof SIP.IncomingResponse) {
- len = responses.length;
- while(len--) {
- pass = responses[len](message, ua, transport);
- if(pass === false) {
- return false;
- }
- }
- }
- //Everything is OK
- return true;
- };
- SIP.sanityCheck = sanityCheck;
- };
- },{}],22:[function(require,module,exports){
- "use strict";
- module.exports = function (SIP) {
- var ServerContext;
- ServerContext = function (ua, request) {
- this.ua = ua;
- this.logger = ua.getLogger('sip.servercontext');
- this.request = request;
- if (request.method === SIP.C.INVITE) {
- this.transaction = new SIP.Transactions.InviteServerTransaction(request, ua);
- } else {
- this.transaction = new SIP.Transactions.NonInviteServerTransaction(request, ua);
- }
- if (request.body) {
- this.body = request.body;
- }
- if (request.hasHeader('Content-Type')) {
- this.contentType = request.getHeader('Content-Type');
- }
- this.method = request.method;
- this.data = {};
- this.localIdentity = request.to;
- this.remoteIdentity = request.from;
- };
- ServerContext.prototype = Object.create(SIP.EventEmitter.prototype);
- ServerContext.prototype.progress = function (options) {
- options = Object.create(options || Object.prototype);
- options.statusCode || (options.statusCode = 180);
- options.minCode = 100;
- options.maxCode = 199;
- options.events = ['progress'];
- return this.reply(options);
- };
- ServerContext.prototype.accept = function (options) {
- options = Object.create(options || Object.prototype);
- options.statusCode || (options.statusCode = 200);
- options.minCode = 200;
- options.maxCode = 299;
- options.events = ['accepted'];
- return this.reply(options);
- };
- ServerContext.prototype.reject = function (options) {
- options = Object.create(options || Object.prototype);
- options.statusCode || (options.statusCode = 480);
- options.minCode = 300;
- options.maxCode = 699;
- options.events = ['rejected', 'failed'];
- return this.reply(options);
- };
- ServerContext.prototype.reply = function (options) {
- options = options || {}; // This is okay, so long as we treat options as read-only in this method
- var
- statusCode = options.statusCode || 100,
- minCode = options.minCode || 100,
- maxCode = options.maxCode || 699,
- reasonPhrase = SIP.Utils.getReasonPhrase(statusCode, options.reasonPhrase),
- extraHeaders = options.extraHeaders || [],
- body = options.body,
- events = options.events || [],
- response;
- if (statusCode < minCode || statusCode > maxCode) {
- throw new TypeError('Invalid statusCode: ' + statusCode);
- }
- response = this.request.reply(statusCode, reasonPhrase, extraHeaders, body);
- events.forEach(function (event) {
- this.emit(event, response, reasonPhrase);
- }, this);
- return this;
- };
- ServerContext.prototype.onRequestTimeout = function () {
- this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
- };
- ServerContext.prototype.onTransportError = function () {
- this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
- };
- SIP.ServerContext = ServerContext;
- };
- },{}],23:[function(require,module,exports){
- "use strict";
- module.exports = function (SIP, environment) {
- var DTMF = require('./Session/DTMF')(SIP);
- var Session, InviteServerContext, InviteClientContext,
- C = {
- //Session states
- STATUS_NULL: 0,
- STATUS_INVITE_SENT: 1,
- STATUS_1XX_RECEIVED: 2,
- STATUS_INVITE_RECEIVED: 3,
- STATUS_WAITING_FOR_ANSWER: 4,
- STATUS_ANSWERED: 5,
- STATUS_WAITING_FOR_PRACK: 6,
- STATUS_WAITING_FOR_ACK: 7,
- STATUS_CANCELED: 8,
- STATUS_TERMINATED: 9,
- STATUS_ANSWERED_WAITING_FOR_PRACK: 10,
- STATUS_EARLY_MEDIA: 11,
- STATUS_CONFIRMED: 12
- };
- /*
- * @param {function returning SIP.MediaHandler} [mediaHandlerFactory]
- * (See the documentation for the mediaHandlerFactory argument of the UA constructor.)
- */
- Session = function (mediaHandlerFactory) {
- this.status = C.STATUS_NULL;
- this.dialog = null;
- this.earlyDialogs = {};
- this.mediaHandlerFactory = mediaHandlerFactory || SIP.WebRTC.MediaHandler.defaultFactory;
- // this.mediaHandler gets set by ICC/ISC constructors
- this.hasOffer = false;
- this.hasAnswer = false;
- // Session Timers
- this.timers = {
- ackTimer: null,
- expiresTimer: null,
- invite2xxTimer: null,
- userNoAnswerTimer: null,
- rel1xxTimer: null,
- prackTimer: null
- };
- // Session info
- this.startTime = null;
- this.endTime = null;
- this.tones = null;
- // Mute/Hold state
- this.local_hold = false;
- this.remote_hold = false;
- this.pending_actions = {
- actions: [],
- length: function() {
- return this.actions.length;
- },
- isPending: function(name){
- var
- idx = 0,
- length = this.actions.length;
- for (idx; idx<length; idx++) {
- if (this.actions[idx].name === name) {
- return true;
- }
- }
- return false;
- },
- shift: function() {
- return this.actions.shift();
- },
- push: function(name) {
- this.actions.push({
- name: name
- });
- },
- pop: function(name) {
- var
- idx = 0,
- length = this.actions.length;
- for (idx; idx<length; idx++) {
- if (this.actions[idx].name === name) {
- this.actions.splice(idx,1);
- length --;
- idx--;
- }
- }
- }
- };
- this.early_sdp = null;
- this.rel100 = SIP.C.supported.UNSUPPORTED;
- };
- Session.prototype = {
- dtmf: function(tones, options) {
- var tone, dtmfs = [],
- self = this;
- options = options || {};
- if (tones === undefined) {
- throw new TypeError('Not enough arguments');
- }
- // Check Session Status
- if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_WAITING_FOR_ACK) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- // Check tones
- if ((typeof tones !== 'string' && typeof tones !== 'number') || !tones.toString().match(/^[0-9A-D#*,]+$/i)) {
- throw new TypeError('Invalid tones: '+ tones);
- }
- tones = tones.toString().split('');
- while (tones.length > 0) { dtmfs.push(new DTMF(this, tones.shift(), options)); }
- if (this.tones) {
- // Tones are already queued, just add to the queue
- this.tones = this.tones.concat(dtmfs);
- return this;
- }
- var sendDTMF = function () {
- var dtmf, timeout;
- if (self.status === C.STATUS_TERMINATED || !self.tones || self.tones.length === 0) {
- // Stop sending DTMF
- self.tones = null;
- return this;
- }
- dtmf = self.tones.shift();
- if (tone === ',') {
- timeout = 2000;
- } else {
- dtmf.on('failed', function(){self.tones = null;});
- dtmf.send(options);
- timeout = dtmf.duration + dtmf.interToneGap;
- }
- // Set timeout for the next tone
- SIP.Timers.setTimeout(sendDTMF, timeout);
- };
- this.tones = dtmfs;
- sendDTMF();
- return this;
- },
- bye: function(options) {
- options = Object.create(options || Object.prototype);
- var statusCode = options.statusCode;
- // Check Session Status
- if (this.status === C.STATUS_TERMINATED) {
- this.logger.error('Error: Attempted to send BYE in a terminated session.');
- return this;
- }
- this.logger.log('terminating Session');
- if (statusCode && (statusCode < 200 || statusCode >= 700)) {
- throw new TypeError('Invalid statusCode: '+ statusCode);
- }
- options.receiveResponse = function () {};
- return this.
- sendRequest(SIP.C.BYE, options).
- terminated();
- },
- refer: function(target, options) {
- options = options || {};
- var extraHeaders = (options.extraHeaders || []).slice(),
- withReplaces =
- target instanceof SIP.InviteServerContext ||
- target instanceof SIP.InviteClientContext,
- originalTarget = target;
- if (target === undefined) {
- throw new TypeError('Not enough arguments');
- }
- // Check Session Status
- if (this.status !== C.STATUS_CONFIRMED) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- // transform `target` so that it can be a Refer-To header value
- if (withReplaces) {
- //Attended Transfer
- // B.transfer(C)
- target = '"' + target.remoteIdentity.friendlyName + '" ' +
- '<' + target.dialog.remote_target.toString() +
- '?Replaces=' + target.dialog.id.call_id +
- '%3Bto-tag%3D' + target.dialog.id.remote_tag +
- '%3Bfrom-tag%3D' + target.dialog.id.local_tag + '>';
- } else {
- //Blind Transfer
- // normalizeTarget allows instances of SIP.URI to pass through unaltered,
- // so try to make one ahead of time
- try {
- target = SIP.Grammar.parse(target, 'Refer_To').uri || target;
- } catch (e) {
- this.logger.debug(".refer() cannot parse Refer_To from", target);
- this.logger.debug("...falling through to normalizeTarget()");
- }
- // Check target validity
- target = this.ua.normalizeTarget(target);
- if (!target) {
- throw new TypeError('Invalid target: ' + originalTarget);
- }
- }
- extraHeaders.push('Contact: '+ this.contact);
- extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
- extraHeaders.push('Refer-To: '+ target);
- // Send the request
- this.sendRequest(SIP.C.REFER, {
- extraHeaders: extraHeaders,
- body: options.body,
- receiveResponse: function (response) {
- if ( ! /^2[0-9]{2}$/.test(response.status_code) ) {
- return;
- }
- // hang up only if we transferred to a SIP address
- if (withReplaces || (target.scheme && target.scheme.match("^sips?$"))) {
- this.terminate();
- }
- }.bind(this)
- });
- return this;
- },
- followRefer: function followRefer (callback) {
- return function referListener (callback, request) {
- // open non-SIP URIs if possible and keep session open
- var referTo = request.parseHeader('refer-to');
- var target = referTo.uri;
- if (!target.scheme.match("^sips?$")) {
- var targetString = target.toString();
- if (typeof environment.open === "function") {
- environment.open(targetString);
- } else {
- this.logger.warn("referred to non-SIP URI but `open` isn't in the environment: " + targetString);
- }
- return;
- }
- var extraHeaders = [];
- /* Copy the Replaces query into a Replaces header */
- /* TODO - make sure we don't copy a poorly formatted header? */
- var replaces = target.getHeader('Replaces');
- if (replaces !== undefined) {
- extraHeaders.push('Replaces: ' + decodeURIComponent(replaces));
- }
- // don't embed headers into Request-URI of INVITE
- target.clearHeaders();
- /*
- Harmless race condition. Both sides of REFER
- may send a BYE, but in the end the dialogs are destroyed.
- */
- var getReferMedia = this.mediaHandler.getReferMedia;
- var mediaHint = getReferMedia ? getReferMedia.call(this.mediaHandler) : this.mediaHint;
- SIP.Hacks.Chrome.getsConfusedAboutGUM(this);
- var referSession = this.ua.invite(target, {
- media: mediaHint,
- params: {
- to_displayName: referTo.friendlyName
- },
- extraHeaders: extraHeaders
- });
- callback.call(this, request, referSession);
- this.terminate();
- }.bind(this, callback);
- },
- sendRequest: function(method,options) {
- options = options || {};
- var self = this;
- var request = new SIP.OutgoingRequest(
- method,
- this.dialog.remote_target,
- this.ua,
- {
- cseq: options.cseq || (this.dialog.local_seqnum += 1),
- call_id: this.dialog.id.call_id,
- from_uri: this.dialog.local_uri,
- from_tag: this.dialog.id.local_tag,
- to_uri: this.dialog.remote_uri,
- to_tag: this.dialog.id.remote_tag,
- route_set: this.dialog.route_set,
- statusCode: options.statusCode,
- reasonPhrase: options.reasonPhrase
- },
- options.extraHeaders || [],
- options.body
- );
- new SIP.RequestSender({
- request: request,
- onRequestTimeout: function() {
- self.onRequestTimeout();
- },
- onTransportError: function() {
- self.onTransportError();
- },
- receiveResponse: options.receiveResponse || function(response) {
- self.receiveNonInviteResponse(response);
- }
- }, this.ua).send();
- // Emit the request event
- this.emit(method.toLowerCase(), request);
- return this;
- },
- close: function() {
- var idx;
- if(this.status === C.STATUS_TERMINATED) {
- return this;
- }
- this.logger.log('closing INVITE session ' + this.id);
- // 1st Step. Terminate media.
- if (this.mediaHandler){
- this.mediaHandler.close();
- }
- // 2nd Step. Terminate signaling.
- // Clear session timers
- for(idx in this.timers) {
- SIP.Timers.clearTimeout(this.timers[idx]);
- }
- // Terminate dialogs
- // Terminate confirmed dialog
- if(this.dialog) {
- this.dialog.terminate();
- delete this.dialog;
- }
- // Terminate early dialogs
- for(idx in this.earlyDialogs) {
- this.earlyDialogs[idx].terminate();
- delete this.earlyDialogs[idx];
- }
- this.status = C.STATUS_TERMINATED;
- delete this.ua.sessions[this.id];
- return this;
- },
- createDialog: function(message, type, early) {
- var dialog, early_dialog,
- local_tag = message[(type === 'UAS') ? 'to_tag' : 'from_tag'],
- remote_tag = message[(type === 'UAS') ? 'from_tag' : 'to_tag'],
- id = message.call_id + local_tag + remote_tag;
- early_dialog = this.earlyDialogs[id];
- // Early Dialog
- if (early) {
- if (early_dialog) {
- return true;
- } else {
- early_dialog = new SIP.Dialog(this, message, type, SIP.Dialog.C.STATUS_EARLY);
- // Dialog has been successfully created.
- if(early_dialog.error) {
- this.logger.error(early_dialog.error);
- this.failed(message, SIP.C.causes.INTERNAL_ERROR);
- return false;
- } else {
- this.earlyDialogs[id] = early_dialog;
- return true;
- }
- }
- }
- // Confirmed Dialog
- else {
- // In case the dialog is in _early_ state, update it
- if (early_dialog) {
- early_dialog.update(message, type);
- this.dialog = early_dialog;
- delete this.earlyDialogs[id];
- for (var dia in this.earlyDialogs) {
- this.earlyDialogs[dia].terminate();
- delete this.earlyDialogs[dia];
- }
- return true;
- }
- // Otherwise, create a _confirmed_ dialog
- dialog = new SIP.Dialog(this, message, type);
- if(dialog.error) {
- this.logger.error(dialog.error);
- this.failed(message, SIP.C.causes.INTERNAL_ERROR);
- return false;
- } else {
- this.to_tag = message.to_tag;
- this.dialog = dialog;
- return true;
- }
- }
- },
- /**
- * Check if Session is ready for a re-INVITE
- *
- * @returns {Boolean}
- */
- isReadyToReinvite: function() {
- return this.mediaHandler.isReady() &&
- !this.dialog.uac_pending_reply &&
- !this.dialog.uas_pending_reply;
- },
- /**
- * Mute
- */
- mute: function(options) {
- var ret = this.mediaHandler.mute(options);
- if (ret) {
- this.onmute(ret);
- }
- },
- /**
- * Unmute
- */
- unmute: function(options) {
- var ret = this.mediaHandler.unmute(options);
- if (ret) {
- this.onunmute(ret);
- }
- },
- /**
- * Hold
- */
- hold: function(options) {
- if (this.status !== C.STATUS_WAITING_FOR_ACK && this.status !== C.STATUS_CONFIRMED) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- this.mediaHandler.hold();
- // Check if RTCSession is ready to send a reINVITE
- if (!this.isReadyToReinvite()) {
- /* If there is a pending 'unhold' action, cancel it and don't queue this one
- * Else, if there isn't any 'hold' action, add this one to the queue
- * Else, if there is already a 'hold' action, skip
- */
- if (this.pending_actions.isPending('unhold')) {
- this.pending_actions.pop('unhold');
- } else if (!this.pending_actions.isPending('hold')) {
- this.pending_actions.push('hold');
- }
- return;
- } else if (this.local_hold === true) {
- return;
- }
- this.onhold('local');
- this.sendReinvite(options);
- },
- /**
- * Unhold
- */
- unhold: function(options) {
- if (this.status !== C.STATUS_WAITING_FOR_ACK && this.status !== C.STATUS_CONFIRMED) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- this.mediaHandler.unhold();
- if (!this.isReadyToReinvite()) {
- /* If there is a pending 'hold' action, cancel it and don't queue this one
- * Else, if there isn't any 'unhold' action, add this one to the queue
- * Else, if there is already a 'unhold' action, skip
- */
- if (this.pending_actions.isPending('hold')) {
- this.pending_actions.pop('hold');
- } else if (!this.pending_actions.isPending('unhold')) {
- this.pending_actions.push('unhold');
- }
- return;
- } else if (this.local_hold === false) {
- return;
- }
- this.onunhold('local');
- this.sendReinvite(options);
- },
- /**
- * isOnHold
- */
- isOnHold: function() {
- return {
- local: this.local_hold,
- remote: this.remote_hold
- };
- },
- /**
- * In dialog INVITE Reception
- * @private
- */
- receiveReinvite: function(request) {
- var self = this;
- if (!this.mediaHandler.hasDescription(request)) {
- this.logger.warn('invalid Content-Type');
- request.reply(415);
- return;
- }
- this.mediaHandler.setDescription(request)
- .then(this.mediaHandler.getDescription.bind(this.mediaHandler, this.mediaHint))
- .then(function(description) {
- var extraHeaders = ['Contact: ' + self.contact];
- request.reply(200, null, extraHeaders, description,
- function() {
- self.status = C.STATUS_WAITING_FOR_ACK;
- self.setInvite2xxTimer(request, description);
- self.setACKTimer();
- if (self.remote_hold && !self.mediaHandler.remote_hold) {
- self.onunhold('remote');
- } else if (!self.remote_hold && self.mediaHandler.remote_hold) {
- self.onhold('remote');
- }
- });
- })
- .catch(function onFailure (e) {
- var statusCode;
- if (e instanceof SIP.Exceptions.GetDescriptionError) {
- statusCode = 500;
- } else {
- self.logger.error(e);
- statusCode = 488;
- }
- request.reply(statusCode);
- });
- },
- sendReinvite: function(options) {
- options = options || {};
- var
- self = this,
- extraHeaders = (options.extraHeaders || []).slice(),
- eventHandlers = options.eventHandlers || {},
- succeeded;
- if (eventHandlers.succeeded) {
- succeeded = eventHandlers.succeeded;
- }
- this.reinviteSucceeded = function(){
- SIP.Timers.clearTimeout(self.timers.ackTimer);
- SIP.Timers.clearTimeout(self.timers.invite2xxTimer);
- self.status = C.STATUS_CONFIRMED;
- succeeded && succeeded.apply(this, arguments);
- };
- if (eventHandlers.failed) {
- this.reinviteFailed = eventHandlers.failed;
- } else {
- this.reinviteFailed = function(){};
- }
- extraHeaders.push('Contact: ' + this.contact);
- extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
- this.receiveResponse = this.receiveReinviteResponse;
- //REVISIT
- this.mediaHandler.getDescription(self.mediaHint)
- .then(
- function(description){
- self.dialog.sendRequest(self, SIP.C.INVITE, {
- extraHeaders: extraHeaders,
- body: description
- });
- },
- function() {
- if (self.isReadyToReinvite()) {
- self.onReadyToReinvite();
- }
- self.reinviteFailed();
- }
- );
- },
- receiveRequest: function (request) {
- switch (request.method) {
- case SIP.C.BYE:
- request.reply(200);
- if(this.status === C.STATUS_CONFIRMED) {
- this.emit('bye', request);
- this.terminated(request, SIP.C.causes.BYE);
- }
- break;
- case SIP.C.INVITE:
- if(this.status === C.STATUS_CONFIRMED) {
- this.logger.log('re-INVITE received');
- this.receiveReinvite(request);
- }
- break;
- case SIP.C.INFO:
- if(this.status === C.STATUS_CONFIRMED || this.status === C.STATUS_WAITING_FOR_ACK) {
- if (this.onInfo) {
- return this.onInfo(request);
- }
- var body, tone, duration,
- contentType = request.getHeader('content-type'),
- reg_tone = /^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*/,
- reg_duration = /^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*/;
- if (contentType) {
- if (contentType.match(/^application\/dtmf-relay/i)) {
- if (request.body) {
- body = request.body.split('\r\n', 2);
- if (body.length === 2) {
- if (reg_tone.test(body[0])) {
- tone = body[0].replace(reg_tone,"$2");
- }
- if (reg_duration.test(body[1])) {
- duration = parseInt(body[1].replace(reg_duration,"$2"), 10);
- }
- }
- }
- new DTMF(this, tone, {duration: duration}).init_incoming(request);
- } else {
- request.reply(415, null, ["Accept: application/dtmf-relay"]);
- }
- }
- }
- break;
- case SIP.C.REFER:
- if(this.status === C.STATUS_CONFIRMED) {
- this.logger.log('REFER received');
- var hasReferListener = this.listeners('refer').length,
- notifyBody;
- if (hasReferListener) {
- request.reply(202, 'Accepted');
- notifyBody = 'SIP/2.0 100 Trying';
- this.sendRequest(SIP.C.NOTIFY, {
- extraHeaders:[
- 'Event: refer',
- 'Subscription-State: terminated',
- 'Content-Type: message/sipfrag'
- ],
- body: notifyBody,
- receiveResponse: function() {}
- });
- this.emit('refer', request);
- } else {
- // RFC 3515.2.4.2: 'the UA MAY decline the request.'
- request.reply(603, 'Declined');
- }
- }
- break;
- case SIP.C.NOTIFY:
- request.reply(200, 'OK');
- this.emit('notify', request);
- break;
- }
- },
- /**
- * Reception of Response for in-dialog INVITE
- * @private
- */
- receiveReinviteResponse: function(response) {
- var self = this;
- if (this.status === C.STATUS_TERMINATED) {
- return;
- }
- switch(true) {
- case /^1[0-9]{2}$/.test(response.status_code):
- break;
- case /^2[0-9]{2}$/.test(response.status_code):
- this.status = C.STATUS_CONFIRMED;
- this.sendRequest(SIP.C.ACK,{cseq:response.cseq});
- if (!this.mediaHandler.hasDescription(response)) {
- this.reinviteFailed();
- break;
- }
- //REVISIT
- this.mediaHandler.setDescription(response)
- .then(
- function onSuccess () {
- self.reinviteSucceeded();
- },
- function onFailure () {
- self.reinviteFailed();
- }
- );
- break;
- default:
- this.reinviteFailed();
- }
- },
- acceptAndTerminate: function(response, status_code, reason_phrase) {
- var extraHeaders = [];
- if (status_code) {
- extraHeaders.push('Reason: ' + SIP.Utils.getReasonHeaderValue(status_code, reason_phrase));
- }
- // An error on dialog creation will fire 'failed' event
- if (this.dialog || this.createDialog(response, 'UAC')) {
- this.sendRequest(SIP.C.ACK,{cseq: response.cseq});
- this.sendRequest(SIP.C.BYE, {
- extraHeaders: extraHeaders
- });
- }
- return this;
- },
- /**
- * RFC3261 13.3.1.4
- * Response retransmissions cannot be accomplished by transaction layer
- * since it is destroyed when receiving the first 2xx answer
- */
- setInvite2xxTimer: function(request, description) {
- var self = this,
- timeout = SIP.Timers.T1;
- this.timers.invite2xxTimer = SIP.Timers.setTimeout(function invite2xxRetransmission() {
- if (self.status !== C.STATUS_WAITING_FOR_ACK) {
- return;
- }
- self.logger.log('no ACK received, attempting to retransmit OK');
- var extraHeaders = ['Contact: ' + self.contact];
- request.reply(200, null, extraHeaders, description);
- timeout = Math.min(timeout * 2, SIP.Timers.T2);
- self.timers.invite2xxTimer = SIP.Timers.setTimeout(invite2xxRetransmission, timeout);
- }, timeout);
- },
- /**
- * RFC3261 14.2
- * If a UAS generates a 2xx response and never receives an ACK,
- * it SHOULD generate a BYE to terminate the dialog.
- */
- setACKTimer: function() {
- var self = this;
- this.timers.ackTimer = SIP.Timers.setTimeout(function() {
- if(self.status === C.STATUS_WAITING_FOR_ACK) {
- self.logger.log('no ACK received for an extended period of time, terminating the call');
- SIP.Timers.clearTimeout(self.timers.invite2xxTimer);
- self.sendRequest(SIP.C.BYE);
- self.terminated(null, SIP.C.causes.NO_ACK);
- }
- }, SIP.Timers.TIMER_H);
- },
- /*
- * @private
- */
- onReadyToReinvite: function() {
- var action = this.pending_actions.shift();
- if (!action || !this[action.name]) {
- return;
- }
- this[action.name]();
- },
- onTransportError: function() {
- if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.CONNECTION_ERROR);
- }
- },
- onRequestTimeout: function() {
- if (this.status === C.STATUS_CONFIRMED) {
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- } else if (this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- }
- },
- onDialogError: function(response) {
- if (this.status === C.STATUS_CONFIRMED) {
- this.terminated(response, SIP.C.causes.DIALOG_ERROR);
- } else if (this.status !== C.STATUS_TERMINATED) {
- this.failed(response, SIP.C.causes.DIALOG_ERROR);
- this.terminated(response, SIP.C.causes.DIALOG_ERROR);
- }
- },
- /**
- * @private
- */
- onhold: function(originator) {
- this[originator === 'local' ? 'local_hold' : 'remote_hold'] = true;
- this.emit('hold', { originator: originator });
- },
- /**
- * @private
- */
- onunhold: function(originator) {
- this[originator === 'local' ? 'local_hold' : 'remote_hold'] = false;
- this.emit('unhold', { originator: originator });
- },
- /*
- * @private
- */
- onmute: function(options) {
- this.emit('muted', {
- audio: options.audio,
- video: options.video
- });
- },
- /*
- * @private
- */
- onunmute: function(options) {
- this.emit('unmuted', {
- audio: options.audio,
- video: options.video
- });
- },
- failed: function(response, cause) {
- if (this.status === C.STATUS_TERMINATED) {
- return this;
- }
- this.emit('failed', response || null, cause || null);
- return this;
- },
- rejected: function(response, cause) {
- this.emit('rejected',
- response || null,
- cause || null
- );
- return this;
- },
- canceled: function() {
- this.emit('cancel');
- return this;
- },
- accepted: function(response, cause) {
- cause = SIP.Utils.getReasonPhrase(response && response.status_code, cause);
- this.startTime = new Date();
- if (this.replacee) {
- this.replacee.emit('replaced', this);
- this.replacee.terminate();
- }
- this.emit('accepted', response, cause);
- return this;
- },
- terminated: function(message, cause) {
- if (this.status === C.STATUS_TERMINATED) {
- return this;
- }
- this.endTime = new Date();
- this.close();
- this.emit('terminated',
- message || null,
- cause || null
- );
- return this;
- },
- connecting: function(request) {
- this.emit('connecting', { request: request });
- return this;
- }
- };
- Session.desugar = function desugar(options) {
- if (environment.HTMLMediaElement && options instanceof environment.HTMLMediaElement) {
- options = {
- media: {
- constraints: {
- audio: true,
- video: options.tagName === 'VIDEO'
- },
- render: {
- remote: options
- }
- }
- };
- }
- return options || {};
- };
- Session.C = C;
- SIP.Session = Session;
- InviteServerContext = function(ua, request) {
- var expires,
- self = this,
- contentType = request.getHeader('Content-Type'),
- contentDisp = request.parseHeader('Content-Disposition');
- SIP.Utils.augment(this, SIP.ServerContext, [ua, request]);
- SIP.Utils.augment(this, SIP.Session, [ua.configuration.mediaHandlerFactory]);
- //Initialize Media Session
- this.mediaHandler = this.mediaHandlerFactory(this, {
- RTCConstraints: {"optional": [{'DtlsSrtpKeyAgreement': 'true'}]}
- });
- // Check body and content type
- if ((!contentDisp && !this.mediaHandler.hasDescription(request)) || (contentDisp && contentDisp.type === 'render')) {
- this.renderbody = request.body;
- this.rendertype = contentType;
- } else if (!this.mediaHandler.hasDescription(request) && (contentDisp && contentDisp.type === 'session')) {
- request.reply(415);
- //TODO: instead of 415, pass off to the media handler, who can then decide if we can use it
- return;
- }
- this.status = C.STATUS_INVITE_RECEIVED;
- this.from_tag = request.from_tag;
- this.id = request.call_id + this.from_tag;
- this.request = request;
- this.contact = this.ua.contact.toString();
- this.receiveNonInviteResponse = function () {}; // intentional no-op
- this.logger = ua.getLogger('sip.inviteservercontext', this.id);
- //Save the session into the ua sessions collection.
- this.ua.sessions[this.id] = this;
- //Get the Expires header value if exists
- if(request.hasHeader('expires')) {
- expires = request.getHeader('expires') * 1000;
- }
- //Set 100rel if necessary
- function set100rel(h,c) {
- if (request.hasHeader(h) && request.getHeader(h).toLowerCase().indexOf('100rel') >= 0) {
- self.rel100 = c;
- }
- }
- set100rel('require', SIP.C.supported.REQUIRED);
- set100rel('supported', SIP.C.supported.SUPPORTED);
- /* Set the to_tag before
- * replying a response code that will create a dialog.
- */
- request.to_tag = SIP.Utils.newTag();
- // An error on dialog creation will fire 'failed' event
- if(!this.createDialog(request, 'UAS', true)) {
- request.reply(500, 'Missing Contact header field');
- return;
- }
- if (this.mediaHandler && this.mediaHandler.getRemoteStreams) {
- this.getRemoteStreams = this.mediaHandler.getRemoteStreams.bind(this.mediaHandler);
- this.getLocalStreams = this.mediaHandler.getLocalStreams.bind(this.mediaHandler);
- }
- function fireNewSession() {
- var options = {extraHeaders: ['Contact: ' + self.contact]};
- if (self.rel100 !== SIP.C.supported.REQUIRED) {
- self.progress(options);
- }
- self.status = C.STATUS_WAITING_FOR_ANSWER;
- // Set userNoAnswerTimer
- self.timers.userNoAnswerTimer = SIP.Timers.setTimeout(function() {
- request.reply(408);
- self.failed(request, SIP.C.causes.NO_ANSWER);
- self.terminated(request, SIP.C.causes.NO_ANSWER);
- }, self.ua.configuration.noAnswerTimeout);
- /* Set expiresTimer
- * RFC3261 13.3.1
- */
- if (expires) {
- self.timers.expiresTimer = SIP.Timers.setTimeout(function() {
- if(self.status === C.STATUS_WAITING_FOR_ANSWER) {
- request.reply(487);
- self.failed(request, SIP.C.causes.EXPIRES);
- self.terminated(request, SIP.C.causes.EXPIRES);
- }
- }, expires);
- }
- self.emit('invite',request);
- }
- if (!this.mediaHandler.hasDescription(request) || this.renderbody) {
- SIP.Timers.setTimeout(fireNewSession, 0);
- } else {
- this.hasOffer = true;
- this.mediaHandler.setDescription(request)
- .then(
- fireNewSession,
- function onFailure (e) {
- self.logger.warn('invalid description');
- self.logger.warn(e);
- request.reply(488);
- }
- );
- }
- };
- InviteServerContext.prototype = {
- reject: function(options) {
- // Check Session Status
- if (this.status === C.STATUS_TERMINATED) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- this.logger.log('rejecting RTCSession');
- SIP.ServerContext.prototype.reject.call(this, options);
- return this.terminated();
- },
- terminate: function(options) {
- options = options || {};
- var
- extraHeaders = (options.extraHeaders || []).slice(),
- body = options.body,
- dialog,
- self = this;
- if (this.status === C.STATUS_WAITING_FOR_ACK &&
- this.request.server_transaction.state !== SIP.Transactions.C.STATUS_TERMINATED) {
- dialog = this.dialog;
- this.receiveRequest = function(request) {
- if (request.method === SIP.C.ACK) {
- this.sendRequest(SIP.C.BYE, {
- extraHeaders: extraHeaders,
- body: body
- });
- dialog.terminate();
- }
- };
- this.request.server_transaction.on('stateChanged', function(){
- if (this.state === SIP.Transactions.C.STATUS_TERMINATED && this.dialog) {
- this.request = new SIP.OutgoingRequest(
- SIP.C.BYE,
- this.dialog.remote_target,
- this.ua,
- {
- 'cseq': this.dialog.local_seqnum+=1,
- 'call_id': this.dialog.id.call_id,
- 'from_uri': this.dialog.local_uri,
- 'from_tag': this.dialog.id.local_tag,
- 'to_uri': this.dialog.remote_uri,
- 'to_tag': this.dialog.id.remote_tag,
- 'route_set': this.dialog.route_set
- },
- extraHeaders,
- body
- );
- new SIP.RequestSender(
- {
- request: this.request,
- onRequestTimeout: function() {
- self.onRequestTimeout();
- },
- onTransportError: function() {
- self.onTransportError();
- },
- receiveResponse: function() {
- return;
- }
- },
- this.ua
- ).send();
- dialog.terminate();
- }
- });
- this.emit('bye', this.request);
- this.terminated();
- // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-)
- this.dialog = dialog;
- // Restore the dialog into 'ua' so the ACK can reach 'this' session
- this.ua.dialogs[dialog.id.toString()] = dialog;
- } else if (this.status === C.STATUS_CONFIRMED) {
- this.bye(options);
- } else {
- this.reject(options);
- }
- return this;
- },
- /*
- * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
- */
- progress: function (options) {
- options = options || {};
- var
- statusCode = options.statusCode || 180,
- reasonPhrase = options.reasonPhrase,
- extraHeaders = (options.extraHeaders || []).slice(),
- iceServers,
- stunServers = options.stunServers || null,
- turnServers = options.turnServers || null,
- body = options.body,
- response;
- if (statusCode < 100 || statusCode > 199) {
- throw new TypeError('Invalid statusCode: ' + statusCode);
- }
- if (this.isCanceled || this.status === C.STATUS_TERMINATED) {
- return this;
- }
- if (stunServers || turnServers) {
- if (stunServers) {
- iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
- if (!iceServers) {
- throw new TypeError('Invalid stunServers: '+ stunServers);
- } else {
- this.stunServers = iceServers;
- }
- }
- if (turnServers) {
- iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
- if (!iceServers) {
- throw new TypeError('Invalid turnServers: '+ turnServers);
- } else {
- this.turnServers = iceServers;
- }
- }
- this.mediaHandler.updateIceServers({
- stunServers: this.stunServers,
- turnServers: this.turnServers
- });
- }
- function do100rel() {
- /* jshint validthis: true */
- statusCode = options.statusCode || 183;
- // Set status and add extra headers
- this.status = C.STATUS_WAITING_FOR_PRACK;
- extraHeaders.push('Contact: '+ this.contact);
- extraHeaders.push('Require: 100rel');
- extraHeaders.push('RSeq: ' + Math.floor(Math.random() * 10000));
- // Save media hint for later (referred sessions)
- this.mediaHint = options.media;
- // Get the session description to add to preaccept with
- this.mediaHandler.getDescription(options.media)
- .then(
- function onSuccess (description) {
- if (this.isCanceled || this.status === C.STATUS_TERMINATED) {
- return;
- }
- this.early_sdp = description.body;
- this[this.hasOffer ? 'hasAnswer' : 'hasOffer'] = true;
- // Retransmit until we get a response or we time out (see prackTimer below)
- var timeout = SIP.Timers.T1;
- this.timers.rel1xxTimer = SIP.Timers.setTimeout(function rel1xxRetransmission() {
- this.request.reply(statusCode, null, extraHeaders, description);
- timeout *= 2;
- this.timers.rel1xxTimer = SIP.Timers.setTimeout(rel1xxRetransmission.bind(this), timeout);
- }.bind(this), timeout);
- // Timeout and reject INVITE if no response
- this.timers.prackTimer = SIP.Timers.setTimeout(function () {
- if (this.status !== C.STATUS_WAITING_FOR_PRACK) {
- return;
- }
- this.logger.log('no PRACK received, rejecting the call');
- SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
- this.request.reply(504);
- this.terminated(null, SIP.C.causes.NO_PRACK);
- }.bind(this), SIP.Timers.T1 * 64);
- // Send the initial response
- response = this.request.reply(statusCode, reasonPhrase, extraHeaders, description);
- this.emit('progress', response, reasonPhrase);
- }.bind(this),
- function onFailure () {
- this.request.reply(480);
- this.failed(null, SIP.C.causes.WEBRTC_ERROR);
- this.terminated(null, SIP.C.causes.WEBRTC_ERROR);
- }.bind(this)
- );
- } // end do100rel
- function normalReply() {
- /* jshint validthis:true */
- response = this.request.reply(statusCode, reasonPhrase, extraHeaders, body);
- this.emit('progress', response, reasonPhrase);
- }
- if (options.statusCode !== 100 &&
- (this.rel100 === SIP.C.supported.REQUIRED ||
- (this.rel100 === SIP.C.supported.SUPPORTED && options.rel100) ||
- (this.rel100 === SIP.C.supported.SUPPORTED && (this.ua.configuration.rel100 === SIP.C.supported.REQUIRED)))) {
- do100rel.apply(this);
- } else {
- normalReply.apply(this);
- }
- return this;
- },
- /*
- * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
- */
- accept: function(options) {
- options = Object.create(Session.desugar(options));
- SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media);
- this.mediaHint = options.media;
- this.onInfo = options.onInfo;
- // commented out now-unused hold-related variables for jshint. See below. JMF 2014-1-21
- var
- //idx, length, hasAudio, hasVideo,
- self = this,
- request = this.request,
- extraHeaders = (options.extraHeaders || []).slice(),
- //mediaStream = options.mediaStream || null,
- iceServers,
- stunServers = options.stunServers || null,
- turnServers = options.turnServers || null,
- descriptionCreationSucceeded = function(description) {
- var
- response,
- // run for reply success callback
- replySucceeded = function() {
- self.status = C.STATUS_WAITING_FOR_ACK;
- self.setInvite2xxTimer(request, description);
- self.setACKTimer();
- },
- // run for reply failure callback
- replyFailed = function() {
- self.failed(null, SIP.C.causes.CONNECTION_ERROR);
- self.terminated(null, SIP.C.causes.CONNECTION_ERROR);
- };
- // Chrome might call onaddstream before accept() is called, which means
- // mediaHandler.render() was called without a renderHint, so we need to
- // re-render now that mediaHint.render has been set.
- //
- // Chrome seems to be in the right regarding this, see
- // http://dev.w3.org/2011/webrtc/editor/webrtc.html#widl-RTCPeerConnection-onaddstream
- self.mediaHandler.render();
- extraHeaders.push('Contact: ' + self.contact);
- extraHeaders.push('Allow: ' + SIP.UA.C.ALLOWED_METHODS.toString());
- if(!self.hasOffer) {
- self.hasOffer = true;
- } else {
- self.hasAnswer = true;
- }
- response = request.reply(200, null, extraHeaders,
- description,
- replySucceeded,
- replyFailed
- );
- if (self.status !== C.STATUS_TERMINATED) { // Didn't fail
- self.accepted(response, SIP.Utils.getReasonPhrase(200));
- }
- },
- descriptionCreationFailed = function() {
- if (self.status === C.STATUS_TERMINATED) {
- return;
- }
- // TODO - fail out on error
- self.request.reply(480);
- //self.failed(response, SIP.C.causes.USER_DENIED_MEDIA_ACCESS);
- self.failed(null, SIP.C.causes.WEBRTC_ERROR);
- self.terminated(null, SIP.C.causes.WEBRTC_ERROR);
- };
- // Check Session Status
- if (this.status === C.STATUS_WAITING_FOR_PRACK) {
- this.status = C.STATUS_ANSWERED_WAITING_FOR_PRACK;
- return this;
- } else if (this.status === C.STATUS_WAITING_FOR_ANSWER) {
- this.status = C.STATUS_ANSWERED;
- } else if (this.status !== C.STATUS_EARLY_MEDIA) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- if ((stunServers || turnServers) &&
- (this.status !== C.STATUS_EARLY_MEDIA && this.status !== C.STATUS_ANSWERED_WAITING_FOR_PRACK)) {
- if (stunServers) {
- iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
- if (!iceServers) {
- throw new TypeError('Invalid stunServers: '+ stunServers);
- } else {
- this.stunServers = iceServers;
- }
- }
- if (turnServers) {
- iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
- if (!iceServers) {
- throw new TypeError('Invalid turnServers: '+ turnServers);
- } else {
- this.turnServers = iceServers;
- }
- }
- this.mediaHandler.updateIceServers({
- stunServers: this.stunServers,
- turnServers: this.turnServers
- });
- }
- // An error on dialog creation will fire 'failed' event
- if(!this.createDialog(request, 'UAS')) {
- request.reply(500, 'Missing Contact header field');
- return this;
- }
- SIP.Timers.clearTimeout(this.timers.userNoAnswerTimer);
- // this hold-related code breaks FF accepting new calls - JMF 2014-1-21
- /*
- length = this.getRemoteStreams().length;
- for (idx = 0; idx < length; idx++) {
- if (this.mediaHandler.getRemoteStreams()[idx].getVideoTracks().length > 0) {
- hasVideo = true;
- }
- if (this.mediaHandler.getRemoteStreams()[idx].getAudioTracks().length > 0) {
- hasAudio = true;
- }
- }
- if (!hasAudio && this.mediaConstraints.audio === true) {
- this.mediaConstraints.audio = false;
- if (mediaStream) {
- length = mediaStream.getAudioTracks().length;
- for (idx = 0; idx < length; idx++) {
- mediaStream.removeTrack(mediaStream.getAudioTracks()[idx]);
- }
- }
- }
- if (!hasVideo && this.mediaConstraints.video === true) {
- this.mediaConstraints.video = false;
- if (mediaStream) {
- length = mediaStream.getVideoTracks().length;
- for (idx = 0; idx < length; idx++) {
- mediaStream.removeTrack(mediaStream.getVideoTracks()[idx]);
- }
- }
- }
- */
- if (this.status === C.STATUS_EARLY_MEDIA) {
- descriptionCreationSucceeded({});
- } else {
- this.mediaHandler.getDescription(self.mediaHint)
- .then(
- descriptionCreationSucceeded,
- descriptionCreationFailed
- );
- }
- return this;
- },
- receiveRequest: function(request) {
- // ISC RECEIVE REQUEST
- function confirmSession() {
- /* jshint validthis:true */
- var contentType;
- SIP.Timers.clearTimeout(this.timers.ackTimer);
- SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
- this.status = C.STATUS_CONFIRMED;
- this.unmute();
- // TODO - this logic assumes Content-Disposition defaults
- contentType = request.getHeader('Content-Type');
- if (!this.mediaHandler.hasDescription(request)) {
- this.renderbody = request.body;
- this.rendertype = contentType;
- }
- this.emit('confirmed', request);
- }
- switch(request.method) {
- case SIP.C.CANCEL:
- /* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL
- * was in progress and that the UAC MAY continue with the session established by
- * any 2xx response, or MAY terminate with BYE. SIP does continue with the
- * established session. So the CANCEL is processed only if the session is not yet
- * established.
- */
- /*
- * Terminate the whole session in case the user didn't accept (or yet to send the answer) nor reject the
- *request opening the session.
- */
- if(this.status === C.STATUS_WAITING_FOR_ANSWER ||
- this.status === C.STATUS_WAITING_FOR_PRACK ||
- this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK ||
- this.status === C.STATUS_EARLY_MEDIA ||
- this.status === C.STATUS_ANSWERED) {
- this.status = C.STATUS_CANCELED;
- this.request.reply(487);
- this.canceled(request);
- this.rejected(request, SIP.C.causes.CANCELED);
- this.failed(request, SIP.C.causes.CANCELED);
- this.terminated(request, SIP.C.causes.CANCELED);
- }
- break;
- case SIP.C.ACK:
- if(this.status === C.STATUS_WAITING_FOR_ACK) {
- if (!this.hasAnswer) {
- if(this.mediaHandler.hasDescription(request)) {
- // ACK contains answer to an INVITE w/o SDP negotiation
- this.hasAnswer = true;
- this.mediaHandler.setDescription(request)
- .then(
- confirmSession.bind(this),
- function onFailure (e) {
- this.logger.warn(e);
- this.terminate({
- statusCode: '488',
- reasonPhrase: 'Bad Media Description'
- });
- this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }.bind(this)
- );
- } else if (this.early_sdp) {
- confirmSession.apply(this);
- } else {
- //TODO: Pass to mediahandler
- this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }
- } else {
- confirmSession.apply(this);
- }
- }
- break;
- case SIP.C.PRACK:
- if (this.status === C.STATUS_WAITING_FOR_PRACK || this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
- //localMedia = session.mediaHandler.localMedia;
- if(!this.hasAnswer) {
- if(this.mediaHandler.hasDescription(request)) {
- this.hasAnswer = true;
- this.mediaHandler.setDescription(request)
- .then(
- function onSuccess () {
- SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
- SIP.Timers.clearTimeout(this.timers.prackTimer);
- request.reply(200);
- if (this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
- this.status = C.STATUS_EARLY_MEDIA;
- this.accept();
- }
- this.status = C.STATUS_EARLY_MEDIA;
- //REVISIT
- this.mute();
- }.bind(this),
- function onFailure (e) {
- //TODO: Send to media handler
- this.logger.warn(e);
- this.terminate({
- statusCode: '488',
- reasonPhrase: 'Bad Media Description'
- });
- this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }.bind(this)
- );
- } else {
- this.terminate({
- statusCode: '488',
- reasonPhrase: 'Bad Media Description'
- });
- this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }
- } else {
- SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
- SIP.Timers.clearTimeout(this.timers.prackTimer);
- request.reply(200);
- if (this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
- this.status = C.STATUS_EARLY_MEDIA;
- this.accept();
- }
- this.status = C.STATUS_EARLY_MEDIA;
- //REVISIT
- this.mute();
- }
- } else if(this.status === C.STATUS_EARLY_MEDIA) {
- request.reply(200);
- }
- break;
- default:
- Session.prototype.receiveRequest.apply(this, [request]);
- break;
- }
- },
- onTransportError: function() {
- if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.CONNECTION_ERROR);
- }
- },
- onRequestTimeout: function() {
- if (this.status === C.STATUS_CONFIRMED) {
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- } else if (this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- }
- }
- };
- SIP.InviteServerContext = InviteServerContext;
- InviteClientContext = function(ua, target, options) {
- options = Object.create(Session.desugar(options));
- options.params = Object.create(options.params || Object.prototype);
- var iceServers,
- extraHeaders = (options.extraHeaders || []).slice(),
- stunServers = options.stunServers || null,
- turnServers = options.turnServers || null,
- mediaHandlerFactory = options.mediaHandlerFactory || ua.configuration.mediaHandlerFactory,
- isMediaSupported = mediaHandlerFactory.isSupported;
- // Check WebRTC support
- if (isMediaSupported && !isMediaSupported()) {
- throw new SIP.Exceptions.NotSupportedError('Media not supported');
- }
- this.RTCConstraints = options.RTCConstraints || {};
- this.inviteWithoutSdp = options.inviteWithoutSdp || false;
- // Set anonymous property
- this.anonymous = options.anonymous || false;
- // Custom data to be sent either in INVITE or in ACK
- this.renderbody = options.renderbody || null;
- this.rendertype = options.rendertype || 'text/plain';
- options.params.from_tag = this.from_tag;
- /* Do not add ;ob in initial forming dialog requests if the registration over
- * the current connection got a GRUU URI.
- */
- this.contact = ua.contact.toString({
- anonymous: this.anonymous,
- outbound: this.anonymous ? !ua.contact.temp_gruu : !ua.contact.pub_gruu
- });
- if (this.anonymous) {
- options.params.from_displayName = 'Anonymous';
- options.params.from_uri = 'sip:[email protected]';
- extraHeaders.push('P-Preferred-Identity: '+ ua.configuration.uri.toString());
- extraHeaders.push('Privacy: id');
- }
- extraHeaders.push('Contact: '+ this.contact);
- extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
- if (this.inviteWithoutSdp && this.renderbody) {
- extraHeaders.push('Content-Type: ' + this.rendertype);
- extraHeaders.push('Content-Disposition: render;handling=optional');
- }
- if (ua.configuration.rel100 === SIP.C.supported.REQUIRED) {
- extraHeaders.push('Require: 100rel');
- }
- if (ua.configuration.replaces === SIP.C.supported.REQUIRED) {
- extraHeaders.push('Require: replaces');
- }
- options.extraHeaders = extraHeaders;
- SIP.Utils.augment(this, SIP.ClientContext, [ua, SIP.C.INVITE, target, options]);
- SIP.Utils.augment(this, SIP.Session, [mediaHandlerFactory]);
- // Check Session Status
- if (this.status !== C.STATUS_NULL) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- // Session parameter initialization
- this.from_tag = SIP.Utils.newTag();
- // OutgoingSession specific parameters
- this.isCanceled = false;
- this.received_100 = false;
- this.method = SIP.C.INVITE;
- this.receiveNonInviteResponse = this.receiveResponse;
- this.receiveResponse = this.receiveInviteResponse;
- this.logger = ua.getLogger('sip.inviteclientcontext');
- if (stunServers) {
- iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
- if (!iceServers) {
- throw new TypeError('Invalid stunServers: '+ stunServers);
- } else {
- this.stunServers = iceServers;
- }
- }
- if (turnServers) {
- iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
- if (!iceServers) {
- throw new TypeError('Invalid turnServers: '+ turnServers);
- } else {
- this.turnServers = iceServers;
- }
- }
- ua.applicants[this] = this;
- this.id = this.request.call_id + this.from_tag;
- //Initialize Media Session
- this.mediaHandler = this.mediaHandlerFactory(this, {
- RTCConstraints: this.RTCConstraints,
- stunServers: this.stunServers,
- turnServers: this.turnServers
- });
- if (this.mediaHandler && this.mediaHandler.getRemoteStreams) {
- this.getRemoteStreams = this.mediaHandler.getRemoteStreams.bind(this.mediaHandler);
- this.getLocalStreams = this.mediaHandler.getLocalStreams.bind(this.mediaHandler);
- }
- SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media);
- this.mediaHint = options.media;
- this.onInfo = options.onInfo;
- };
- InviteClientContext.prototype = {
- invite: function () {
- var self = this;
- //Save the session into the ua sessions collection.
- //Note: placing in constructor breaks call to request.cancel on close... User does not need this anyway
- this.ua.sessions[this.id] = this;
- //Note: due to the way Firefox handles gUM calls, it is recommended to make the gUM call at the app level
- // and hand sip.js a stream as the mediaHint
- if (this.inviteWithoutSdp) {
- //just send an invite with no sdp...
- this.request.body = self.renderbody;
- this.status = C.STATUS_INVITE_SENT;
- this.send();
- } else {
- this.mediaHandler.getDescription(self.mediaHint)
- .then(
- function onSuccess(description) {
- if (self.isCanceled || self.status === C.STATUS_TERMINATED) {
- return;
- }
- self.hasOffer = true;
- self.request.body = description;
- self.status = C.STATUS_INVITE_SENT;
- self.send();
- },
- function onFailure() {
- if (self.status === C.STATUS_TERMINATED) {
- return;
- }
- // TODO...fail out
- //self.failed(null, SIP.C.causes.USER_DENIED_MEDIA_ACCESS);
- //self.failed(null, SIP.C.causes.WEBRTC_ERROR);
- self.failed(null, SIP.C.causes.WEBRTC_ERROR);
- self.terminated(null, SIP.C.causes.WEBRTC_ERROR);
- }
- );
- }
- return this;
- },
- receiveInviteResponse: function(response) {
- var cause, //localMedia,
- session = this,
- id = response.call_id + response.from_tag + response.to_tag,
- extraHeaders = [],
- options = {};
- if (this.status === C.STATUS_TERMINATED || response.method !== SIP.C.INVITE) {
- return;
- }
- if (this.dialog && (response.status_code >= 200 && response.status_code <= 299)) {
- if (id !== this.dialog.id.toString() ) {
- if (!this.createDialog(response, 'UAC', true)) {
- return;
- }
- this.earlyDialogs[id].sendRequest(this, SIP.C.ACK,
- {
- body: SIP.Utils.generateFakeSDP(response.body)
- });
- this.earlyDialogs[id].sendRequest(this, SIP.C.BYE);
- /* NOTE: This fails because the forking proxy does not recognize that an unanswerable
- * leg (due to peerConnection limitations) has been answered first. If your forking
- * proxy does not hang up all unanswered branches on the first branch answered, remove this.
- */
- if(this.status !== C.STATUS_CONFIRMED) {
- this.failed(response, SIP.C.causes.WEBRTC_ERROR);
- this.terminated(response, SIP.C.causes.WEBRTC_ERROR);
- }
- return;
- } else if (this.status === C.STATUS_CONFIRMED) {
- this.sendRequest(SIP.C.ACK,{cseq: response.cseq});
- return;
- } else if (!this.hasAnswer) {
- // invite w/o sdp is waiting for callback
- //an invite with sdp must go on, and hasAnswer is true
- return;
- }
- }
- if (this.dialog && response.status_code < 200) {
- /*
- Early media has been set up with at least one other different branch,
- but a final 2xx response hasn't been received
- */
- if (this.dialog.pracked.indexOf(response.getHeader('rseq')) !== -1 ||
- (this.dialog.pracked[this.dialog.pracked.length-1] >= response.getHeader('rseq') && this.dialog.pracked.length > 0)) {
- return;
- }
- if (!this.earlyDialogs[id] && !this.createDialog(response, 'UAC', true)) {
- return;
- }
- if (this.earlyDialogs[id].pracked.indexOf(response.getHeader('rseq')) !== -1 ||
- (this.earlyDialogs[id].pracked[this.earlyDialogs[id].pracked.length-1] >= response.getHeader('rseq') && this.earlyDialogs[id].pracked.length > 0)) {
- return;
- }
- extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
- this.earlyDialogs[id].pracked.push(response.getHeader('rseq'));
- this.earlyDialogs[id].sendRequest(this, SIP.C.PRACK, {
- extraHeaders: extraHeaders,
- body: SIP.Utils.generateFakeSDP(response.body)
- });
- return;
- }
- // Proceed to cancellation if the user requested.
- if(this.isCanceled) {
- if(response.status_code >= 100 && response.status_code < 200) {
- this.request.cancel(this.cancelReason, extraHeaders);
- this.canceled(null);
- } else if(response.status_code >= 200 && response.status_code < 299) {
- this.acceptAndTerminate(response);
- this.emit('bye', this.request);
- } else if (response.status_code >= 300) {
- cause = SIP.C.REASON_PHRASE[response.status_code] || SIP.C.causes.CANCELED;
- this.rejected(response, cause);
- this.failed(response, cause);
- this.terminated(response, cause);
- }
- return;
- }
- switch(true) {
- case /^100$/.test(response.status_code):
- this.received_100 = true;
- this.emit('progress', response);
- break;
- case (/^1[0-9]{2}$/.test(response.status_code)):
- // Do nothing with 1xx responses without To tag.
- if(!response.to_tag) {
- this.logger.warn('1xx response received without to tag');
- break;
- }
- // Create Early Dialog if 1XX comes with contact
- if(response.hasHeader('contact')) {
- // An error on dialog creation will fire 'failed' event
- if (!this.createDialog(response, 'UAC', true)) {
- break;
- }
- }
- this.status = C.STATUS_1XX_RECEIVED;
- if(response.hasHeader('require') &&
- response.getHeader('require').indexOf('100rel') !== -1) {
- // Do nothing if this.dialog is already confirmed
- if (this.dialog || !this.earlyDialogs[id]) {
- break;
- }
- if (this.earlyDialogs[id].pracked.indexOf(response.getHeader('rseq')) !== -1 ||
- (this.earlyDialogs[id].pracked[this.earlyDialogs[id].pracked.length-1] >= response.getHeader('rseq') && this.earlyDialogs[id].pracked.length > 0)) {
- return;
- }
- if (!this.mediaHandler.hasDescription(response)) {
- extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
- this.earlyDialogs[id].pracked.push(response.getHeader('rseq'));
- this.earlyDialogs[id].sendRequest(this, SIP.C.PRACK, {
- extraHeaders: extraHeaders
- });
- this.emit('progress', response);
- } else if (this.hasOffer) {
- if (!this.createDialog(response, 'UAC')) {
- break;
- }
- this.hasAnswer = true;
- this.dialog.pracked.push(response.getHeader('rseq'));
- this.mediaHandler.setDescription(response)
- .then(
- function onSuccess () {
- extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
- session.sendRequest(SIP.C.PRACK, {
- extraHeaders: extraHeaders,
- receiveResponse: function() {}
- });
- session.status = C.STATUS_EARLY_MEDIA;
- session.mute();
- session.emit('progress', response);
- /*
- if (session.status === C.STATUS_EARLY_MEDIA) {
- localMedia = session.mediaHandler.localMedia;
- if (localMedia.getAudioTracks().length > 0) {
- localMedia.getAudioTracks()[0].enabled = false;
- }
- if (localMedia.getVideoTracks().length > 0) {
- localMedia.getVideoTracks()[0].enabled = false;
- }
- }*/
- },
- function onFailure (e) {
- session.logger.warn(e);
- session.acceptAndTerminate(response, 488, 'Not Acceptable Here');
- session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }
- );
- } else {
- var earlyDialog = this.earlyDialogs[id];
- var earlyMedia = earlyDialog.mediaHandler;
- earlyDialog.pracked.push(response.getHeader('rseq'));
- earlyMedia.setDescription(response)
- .then(earlyMedia.getDescription.bind(earlyMedia, session.mediaHint))
- .then(function onSuccess(description) {
- extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
- earlyDialog.sendRequest(session, SIP.C.PRACK, {
- extraHeaders: extraHeaders,
- body: description
- });
- session.status = C.STATUS_EARLY_MEDIA;
- session.emit('progress', response);
- })
- .catch(function onFailure(e) {
- if (e instanceof SIP.Exceptions.GetDescriptionError) {
- earlyDialog.pracked.push(response.getHeader('rseq'));
- if (session.status === C.STATUS_TERMINATED) {
- return;
- }
- // TODO - fail out on error
- // session.failed(gum error);
- session.failed(null, SIP.C.causes.WEBRTC_ERROR);
- session.terminated(null, SIP.C.causes.WEBRTC_ERROR);
- } else {
- earlyDialog.pracked.splice(earlyDialog.pracked.indexOf(response.getHeader('rseq')), 1);
- // Could not set remote description
- session.logger.warn('invalid description');
- session.logger.warn(e);
- }
- });
- }
- } else {
- this.emit('progress', response);
- }
- break;
- case /^2[0-9]{2}$/.test(response.status_code):
- var cseq = this.request.cseq + ' ' + this.request.method;
- if (cseq !== response.getHeader('cseq')) {
- break;
- }
- if (this.status === C.STATUS_EARLY_MEDIA && this.dialog) {
- this.status = C.STATUS_CONFIRMED;
- this.unmute();
- /*localMedia = this.mediaHandler.localMedia;
- if (localMedia.getAudioTracks().length > 0) {
- localMedia.getAudioTracks()[0].enabled = true;
- }
- if (localMedia.getVideoTracks().length > 0) {
- localMedia.getVideoTracks()[0].enabled = true;
- }*/
- options = {};
- if (this.renderbody) {
- extraHeaders.push('Content-Type: ' + this.rendertype);
- options.extraHeaders = extraHeaders;
- options.body = this.renderbody;
- }
- options.cseq = response.cseq;
- this.sendRequest(SIP.C.ACK, options);
- this.accepted(response);
- break;
- }
- // Do nothing if this.dialog is already confirmed
- if (this.dialog) {
- break;
- }
- // This is an invite without sdp
- if (!this.hasOffer) {
- if (this.earlyDialogs[id] && this.earlyDialogs[id].mediaHandler.localMedia) {
- //REVISIT
- this.hasOffer = true;
- this.hasAnswer = true;
- this.mediaHandler = this.earlyDialogs[id].mediaHandler;
- if (!this.createDialog(response, 'UAC')) {
- break;
- }
- this.status = C.STATUS_CONFIRMED;
- this.sendRequest(SIP.C.ACK, {cseq:response.cseq});
- this.unmute();
- /*
- localMedia = session.mediaHandler.localMedia;
- if (localMedia.getAudioTracks().length > 0) {
- localMedia.getAudioTracks()[0].enabled = true;
- }
- if (localMedia.getVideoTracks().length > 0) {
- localMedia.getVideoTracks()[0].enabled = true;
- }*/
- this.accepted(response);
- } else {
- if(!this.mediaHandler.hasDescription(response)) {
- this.acceptAndTerminate(response, 400, 'Missing session description');
- this.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- break;
- }
- if (!this.createDialog(response, 'UAC')) {
- break;
- }
- this.hasOffer = true;
- this.mediaHandler.setDescription(response)
- .then(this.mediaHandler.getDescription.bind(this.mediaHandler, this.mediaHint))
- .then(function onSuccess(description) {
- //var localMedia;
- if(session.isCanceled || session.status === C.STATUS_TERMINATED) {
- return;
- }
- session.status = C.STATUS_CONFIRMED;
- session.hasAnswer = true;
- session.unmute();
- /*localMedia = session.mediaHandler.localMedia;
- if (localMedia.getAudioTracks().length > 0) {
- localMedia.getAudioTracks()[0].enabled = true;
- }
- if (localMedia.getVideoTracks().length > 0) {
- localMedia.getVideoTracks()[0].enabled = true;
- }*/
- session.sendRequest(SIP.C.ACK,{
- body: description,
- cseq:response.cseq
- });
- session.accepted(response);
- })
- .catch(function onFailure(e) {
- if (e instanceof SIP.Exceptions.GetDescriptionError) {
- // TODO do something here
- session.logger.warn("there was a problem");
- } else {
- session.logger.warn('invalid description');
- session.logger.warn(e);
- session.acceptAndTerminate(response, 488, 'Invalid session description');
- session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }
- });
- }
- } else if (this.hasAnswer){
- if (this.renderbody) {
- extraHeaders.push('Content-Type: ' + session.rendertype);
- options.extraHeaders = extraHeaders;
- options.body = this.renderbody;
- }
- this.sendRequest(SIP.C.ACK, options);
- } else {
- if(!this.mediaHandler.hasDescription(response)) {
- this.acceptAndTerminate(response, 400, 'Missing session description');
- this.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- break;
- }
- if (!this.createDialog(response, 'UAC')) {
- break;
- }
- this.hasAnswer = true;
- this.mediaHandler.setDescription(response)
- .then(
- function onSuccess () {
- var options = {};//,localMedia;
- session.status = C.STATUS_CONFIRMED;
- session.unmute();
- /*localMedia = session.mediaHandler.localMedia;
- if (localMedia.getAudioTracks().length > 0) {
- localMedia.getAudioTracks()[0].enabled = true;
- }
- if (localMedia.getVideoTracks().length > 0) {
- localMedia.getVideoTracks()[0].enabled = true;
- }*/
- if (session.renderbody) {
- extraHeaders.push('Content-Type: ' + session.rendertype);
- options.extraHeaders = extraHeaders;
- options.body = session.renderbody;
- }
- options.cseq = response.cseq;
- session.sendRequest(SIP.C.ACK, options);
- session.accepted(response);
- },
- function onFailure (e) {
- session.logger.warn(e);
- session.acceptAndTerminate(response, 488, 'Not Acceptable Here');
- session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
- }
- );
- }
- break;
- default:
- cause = SIP.Utils.sipErrorCause(response.status_code);
- this.rejected(response, cause);
- this.failed(response, cause);
- this.terminated(response, cause);
- }
- },
- cancel: function(options) {
- options = options || {};
- options.extraHeaders = (options.extraHeaders || []).slice();
- // Check Session Status
- if (this.status === C.STATUS_TERMINATED || this.status === C.STATUS_CONFIRMED) {
- throw new SIP.Exceptions.InvalidStateError(this.status);
- }
- this.logger.log('canceling RTCSession');
- var cancel_reason = SIP.Utils.getCancelReason(options.status_code, options.reason_phrase);
- // Check Session Status
- if (this.status === C.STATUS_NULL ||
- (this.status === C.STATUS_INVITE_SENT && !this.received_100)) {
- this.isCanceled = true;
- this.cancelReason = cancel_reason;
- } else if (this.status === C.STATUS_INVITE_SENT ||
- this.status === C.STATUS_1XX_RECEIVED ||
- this.status === C.STATUS_EARLY_MEDIA) {
- this.request.cancel(cancel_reason, options.extraHeaders);
- }
- return this.canceled();
- },
- terminate: function(options) {
- if (this.status === C.STATUS_TERMINATED) {
- return this;
- }
- if (this.status === C.STATUS_WAITING_FOR_ACK || this.status === C.STATUS_CONFIRMED) {
- this.bye(options);
- } else {
- this.cancel(options);
- }
- return this;
- },
- receiveRequest: function(request) {
- // ICC RECEIVE REQUEST
- // Reject CANCELs
- if (request.method === SIP.C.CANCEL) {
- // TODO; make this a switch when it gets added
- }
- if (request.method === SIP.C.ACK && this.status === C.STATUS_WAITING_FOR_ACK) {
- SIP.Timers.clearTimeout(this.timers.ackTimer);
- SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
- this.status = C.STATUS_CONFIRMED;
- this.unmute();
- this.accepted();
- }
- return Session.prototype.receiveRequest.apply(this, [request]);
- },
- onTransportError: function() {
- if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.CONNECTION_ERROR);
- }
- },
- onRequestTimeout: function() {
- if (this.status === C.STATUS_CONFIRMED) {
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- } else if (this.status !== C.STATUS_TERMINATED) {
- this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
- this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
- }
- }
- };
- SIP.InviteClientContext = InviteClientContext;
- };
- },{"./Session/DTMF":24}],24:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview DTMF
- */
- /**
- * @class DTMF
- * @param {SIP.Session} session
- */
- module.exports = function (SIP) {
- var DTMF,
- C = {
- MIN_DURATION: 70,
- MAX_DURATION: 6000,
- DEFAULT_DURATION: 100,
- MIN_INTER_TONE_GAP: 50,
- DEFAULT_INTER_TONE_GAP: 500
- };
- DTMF = function(session, tone, options) {
- var duration, interToneGap;
- if (tone === undefined) {
- throw new TypeError('Not enough arguments');
- }
- this.logger = session.ua.getLogger('sip.invitecontext.dtmf', session.id);
- this.owner = session;
- this.direction = null;
- options = options || {};
- duration = options.duration || null;
- interToneGap = options.interToneGap || null;
- // Check tone type
- if (typeof tone === 'string' ) {
- tone = tone.toUpperCase();
- } else if (typeof tone === 'number') {
- tone = tone.toString();
- } else {
- throw new TypeError('Invalid tone: '+ tone);
- }
- // Check tone value
- if (!tone.match(/^[0-9A-D#*]$/)) {
- throw new TypeError('Invalid tone: '+ tone);
- } else {
- this.tone = tone;
- }
- // Check duration
- if (duration && !SIP.Utils.isDecimal(duration)) {
- throw new TypeError('Invalid tone duration: '+ duration);
- } else if (!duration) {
- duration = DTMF.C.DEFAULT_DURATION;
- } else if (duration < DTMF.C.MIN_DURATION) {
- this.logger.warn('"duration" value is lower than the minimum allowed, setting it to '+ DTMF.C.MIN_DURATION+ ' milliseconds');
- duration = DTMF.C.MIN_DURATION;
- } else if (duration > DTMF.C.MAX_DURATION) {
- this.logger.warn('"duration" value is greater than the maximum allowed, setting it to '+ DTMF.C.MAX_DURATION +' milliseconds');
- duration = DTMF.C.MAX_DURATION;
- } else {
- duration = Math.abs(duration);
- }
- this.duration = duration;
- // Check interToneGap
- if (interToneGap && !SIP.Utils.isDecimal(interToneGap)) {
- throw new TypeError('Invalid interToneGap: '+ interToneGap);
- } else if (!interToneGap) {
- interToneGap = DTMF.C.DEFAULT_INTER_TONE_GAP;
- } else if (interToneGap < DTMF.C.MIN_INTER_TONE_GAP) {
- this.logger.warn('"interToneGap" value is lower than the minimum allowed, setting it to '+ DTMF.C.MIN_INTER_TONE_GAP +' milliseconds');
- interToneGap = DTMF.C.MIN_INTER_TONE_GAP;
- } else {
- interToneGap = Math.abs(interToneGap);
- }
- this.interToneGap = interToneGap;
- };
- DTMF.prototype = Object.create(SIP.EventEmitter.prototype);
- DTMF.prototype.send = function(options) {
- var extraHeaders,
- body = {};
- this.direction = 'outgoing';
- // Check RTCSession Status
- if (this.owner.status !== SIP.Session.C.STATUS_CONFIRMED &&
- this.owner.status !== SIP.Session.C.STATUS_WAITING_FOR_ACK) {
- throw new SIP.Exceptions.InvalidStateError(this.owner.status);
- }
- // Get DTMF options
- options = options || {};
- extraHeaders = options.extraHeaders ? options.extraHeaders.slice() : [];
- body.contentType = 'application/dtmf-relay';
- body.body = "Signal= " + this.tone + "\r\n";
- body.body += "Duration= " + this.duration;
- this.request = this.owner.dialog.sendRequest(this, SIP.C.INFO, {
- extraHeaders: extraHeaders,
- body: body
- });
- this.owner.emit('dtmf', this.request, this);
- };
- /**
- * @private
- */
- DTMF.prototype.receiveResponse = function(response) {
- var cause;
- 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.emit('succeeded', {
- originator: 'remote',
- response: response
- });
- break;
- default:
- cause = SIP.Utils.sipErrorCause(response.status_code);
- this.emit('failed', response, cause);
- break;
- }
- };
- /**
- * @private
- */
- DTMF.prototype.onRequestTimeout = function() {
- this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
- this.owner.onRequestTimeout();
- };
- /**
- * @private
- */
- DTMF.prototype.onTransportError = function() {
- this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
- this.owner.onTransportError();
- };
- /**
- * @private
- */
- DTMF.prototype.onDialogError = function(response) {
- this.emit('failed', response, SIP.C.causes.DIALOG_ERROR);
- this.owner.onDialogError(response);
- };
- /**
- * @private
- */
- DTMF.prototype.init_incoming = function(request) {
- this.direction = 'incoming';
- this.request = request;
- request.reply(200);
- if (!this.tone || !this.duration) {
- this.logger.warn('invalid INFO DTMF received, discarded');
- } else {
- this.owner.emit('dtmf', request, this);
- }
- };
- DTMF.C = C;
- return DTMF;
- };
- },{}],25:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Subscriber (SIP-Specific Event Notifications RFC6665)
- */
- /**
- * @augments SIP
- * @class Class creating a SIP Subscription.
- */
- module.exports = function (SIP) {
- SIP.Subscription = function (ua, target, event, options) {
- options = Object.create(options || Object.prototype);
- this.extraHeaders = options.extraHeaders = (options.extraHeaders || []).slice();
- this.id = null;
- this.state = 'init';
- if (!event) {
- throw new TypeError('Event necessary to create a subscription.');
- } else {
- //TODO: check for valid events here probably make a list in SIP.C; or leave it up to app to check?
- //The check may need to/should probably occur on the other side,
- this.event = event;
- }
- if(typeof options.expires !== 'number'){
- ua.logger.warn('expires must be a number. Using default of 3600.');
- this.expires = 3600;
- } else {
- this.expires = options.expires;
- }
- options.extraHeaders.push('Event: ' + this.event);
- options.extraHeaders.push('Expires: ' + this.expires);
- if (options.body) {
- this.body = options.body;
- }
- this.contact = ua.contact.toString();
- options.extraHeaders.push('Contact: '+ this.contact);
- options.extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
- SIP.Utils.augment(this, SIP.ClientContext, [ua, SIP.C.SUBSCRIBE, target, options]);
- this.logger = ua.getLogger('sip.subscription');
- this.dialog = null;
- this.timers = {N: null, sub_duration: null};
- this.errorCodes = [404,405,410,416,480,481,482,483,484,485,489,501,604];
- };
- SIP.Subscription.prototype = {
- subscribe: function() {
- var sub = this;
- //these states point to an existing subscription, no subscribe is necessary
- if (this.state === 'active') {
- this.refresh();
- return this;
- } else if (this.state === 'notify_wait') {
- return this;
- }
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- SIP.Timers.clearTimeout(this.timers.N);
- this.timers.N = SIP.Timers.setTimeout(sub.timer_fire.bind(sub), SIP.Timers.TIMER_N);
- this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event] = this;
- this.send();
- this.state = 'notify_wait';
- return this;
- },
- refresh: function () {
- if (this.state === 'terminated' || this.state === 'pending' || this.state === 'notify_wait') {
- return;
- }
- this.dialog.sendRequest(this, SIP.C.SUBSCRIBE, {
- extraHeaders: this.extraHeaders,
- body: this.body
- });
- },
- receiveResponse: function(response) {
- var expires, sub = this,
- cause = SIP.Utils.getReasonPhrase(response.status_code);
- if ((this.state === 'notify_wait' && response.status_code >= 300) ||
- (this.state !== 'notify_wait' && this.errorCodes.indexOf(response.status_code) !== -1)) {
- this.failed(response, null);
- } else if (/^2[0-9]{2}$/.test(response.status_code)){
- this.emit('accepted', response, cause);
- //As we don't support RFC 5839 or other extensions where the NOTIFY is optional, timer N will not be cleared
- //SIP.Timers.clearTimeout(this.timers.N);
- expires = response.getHeader('Expires');
- if (expires && expires <= this.expires) {
- // Preserve new expires value for subsequent requests
- this.expires = expires;
- this.timers.sub_duration = SIP.Timers.setTimeout(sub.refresh.bind(sub), expires * 900);
- } else {
- if (!expires) {
- this.logger.warn('Expires header missing in a 200-class response to SUBSCRIBE');
- this.failed(response, SIP.C.EXPIRES_HEADER_MISSING);
- } else {
- this.logger.warn('Expires header in a 200-class response to SUBSCRIBE with a higher value than the one in the request');
- this.failed(response, SIP.C.INVALID_EXPIRES_HEADER);
- }
- }
- } else if (response.statusCode > 300) {
- this.emit('failed', response, cause);
- this.emit('rejected', response, cause);
- }
- },
- unsubscribe: function() {
- var extraHeaders = [], sub = this;
- this.state = 'terminated';
- extraHeaders.push('Event: ' + this.event);
- extraHeaders.push('Expires: 0');
- extraHeaders.push('Contact: '+ this.contact);
- extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
- //makes sure expires isn't set, and other typical resubscribe behavior
- this.receiveResponse = function(){};
- this.dialog.sendRequest(this, this.method, {
- extraHeaders: extraHeaders,
- body: this.body
- });
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- SIP.Timers.clearTimeout(this.timers.N);
- this.timers.N = SIP.Timers.setTimeout(sub.timer_fire.bind(sub), SIP.Timers.TIMER_N);
- },
- /**
- * @private
- */
- timer_fire: function(){
- if (this.state === 'terminated') {
- this.terminateDialog();
- SIP.Timers.clearTimeout(this.timers.N);
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- delete this.ua.subscriptions[this.id];
- } else if (this.state === 'notify_wait' || this.state === 'pending') {
- this.close();
- } else {
- this.refresh();
- }
- },
- /**
- * @private
- */
- close: function() {
- if (this.state === 'notify_wait') {
- this.state = 'terminated';
- SIP.Timers.clearTimeout(this.timers.N);
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- this.receiveResponse = function(){};
- delete this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event];
- } else if (this.state !== 'terminated') {
- this.unsubscribe();
- }
- },
- /**
- * @private
- */
- createConfirmedDialog: function(message, type) {
- var dialog;
- this.terminateDialog();
- dialog = new SIP.Dialog(this, message, type);
- dialog.invite_seqnum = this.request.cseq;
- dialog.local_seqnum = this.request.cseq;
- if(!dialog.error) {
- this.dialog = dialog;
- return true;
- }
- // Dialog not created due to an error
- else {
- return false;
- }
- },
- /**
- * @private
- */
- terminateDialog: function() {
- if(this.dialog) {
- delete this.ua.subscriptions[this.id];
- this.dialog.terminate();
- delete this.dialog;
- }
- },
- /**
- * @private
- */
- receiveRequest: function(request) {
- var sub_state, sub = this;
- function setExpiresTimeout() {
- if (sub_state.expires) {
- SIP.Timers.clearTimeout(sub.timers.sub_duration);
- sub_state.expires = Math.min(sub.expires,
- Math.max(sub_state.expires, 0));
- sub.timers.sub_duration = SIP.Timers.setTimeout(sub.refresh.bind(sub),
- sub_state.expires * 900);
- }
- }
- if (!this.matchEvent(request)) { //checks event and subscription_state headers
- request.reply(489);
- return;
- }
- if (!this.dialog) {
- if (this.createConfirmedDialog(request,'UAS')) {
- this.id = this.dialog.id.toString();
- delete this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event];
- this.ua.subscriptions[this.id] = this;
- // UPDATE ROUTE SET TO BE BACKWARDS COMPATIBLE?
- }
- }
- sub_state = request.parseHeader('Subscription-State');
- request.reply(200, SIP.C.REASON_200);
- SIP.Timers.clearTimeout(this.timers.N);
- this.emit('notify', {request: request});
- // if we've set state to terminated, no further processing should take place
- // and we are only interested in cleaning up after the appropriate NOTIFY
- if (this.state === 'terminated') {
- if (sub_state.state === 'terminated') {
- this.terminateDialog();
- SIP.Timers.clearTimeout(this.timers.N);
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- delete this.ua.subscriptions[this.id];
- }
- return;
- }
- switch (sub_state.state) {
- case 'active':
- this.state = 'active';
- setExpiresTimeout();
- break;
- case 'pending':
- if (this.state === 'notify_wait') {
- setExpiresTimeout();
- }
- this.state = 'pending';
- break;
- case 'terminated':
- SIP.Timers.clearTimeout(this.timers.sub_duration);
- if (sub_state.reason) {
- this.logger.log('terminating subscription with reason '+ sub_state.reason);
- switch (sub_state.reason) {
- case 'deactivated':
- case 'timeout':
- this.subscribe();
- return;
- case 'probation':
- case 'giveup':
- if(sub_state.params && sub_state.params['retry-after']) {
- this.timers.sub_duration = SIP.Timers.setTimeout(sub.subscribe.bind(sub), sub_state.params['retry-after']);
- } else {
- this.subscribe();
- }
- return;
- case 'rejected':
- case 'noresource':
- case 'invariant':
- break;
- }
- }
- this.close();
- break;
- }
- },
- failed: function(response, cause) {
- this.close();
- this.emit('failed', response, cause);
- this.emit('rejected', response, cause);
- return this;
- },
- onDialogError: function(response) {
- this.failed(response, SIP.C.causes.DIALOG_ERROR);
- },
- /**
- * @private
- */
- matchEvent: function(request) {
- var event;
- // Check mandatory header Event
- if (!request.hasHeader('Event')) {
- this.logger.warn('missing Event header');
- return false;
- }
- // Check mandatory header Subscription-State
- if (!request.hasHeader('Subscription-State')) {
- this.logger.warn('missing Subscription-State header');
- return false;
- }
- // Check whether the event in NOTIFY matches the event in SUBSCRIBE
- event = request.parseHeader('event').event;
- if (this.event !== event) {
- this.logger.warn('event match failed');
- request.reply(481, 'Event Match Failed');
- return false;
- } else {
- return true;
- }
- }
- };
- };
- },{}],26:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP TIMERS
- */
- /**
- * @augments SIP
- */
- var
- T1 = 500,
- T2 = 4000,
- T4 = 5000;
- module.exports = function (timers) {
- var Timers = {
- T1: T1,
- T2: T2,
- T4: T4,
- TIMER_B: 64 * T1,
- TIMER_D: 0 * T1,
- TIMER_F: 64 * T1,
- TIMER_H: 64 * T1,
- TIMER_I: 0 * T1,
- TIMER_J: 0 * T1,
- TIMER_K: 0 * T4,
- TIMER_L: 64 * T1,
- TIMER_M: 64 * T1,
- TIMER_N: 64 * T1,
- PROVISIONAL_RESPONSE_INTERVAL: 60000 // See RFC 3261 Section 13.3.1.1
- };
- ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']
- .forEach(function (name) {
- // can't just use timers[name].bind(timers) since it bypasses jasmine's
- // clock-mocking
- Timers[name] = function () {
- return timers[name].apply(timers, arguments);
- };
- });
- return Timers;
- };
- },{}],27:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP Transactions
- */
- /**
- * SIP Transactions module.
- * @augments SIP
- */
- module.exports = function (SIP) {
- var
- C = {
- // Transaction states
- STATUS_TRYING: 1,
- STATUS_PROCEEDING: 2,
- STATUS_CALLING: 3,
- STATUS_ACCEPTED: 4,
- STATUS_COMPLETED: 5,
- STATUS_TERMINATED: 6,
- STATUS_CONFIRMED: 7,
- // Transaction types
- NON_INVITE_CLIENT: 'nict',
- NON_INVITE_SERVER: 'nist',
- INVITE_CLIENT: 'ict',
- INVITE_SERVER: 'ist'
- };
- function buildViaHeader (request_sender, transport, id) {
- var via;
- via = 'SIP/2.0/' + (request_sender.ua.configuration.hackViaTcp ? 'TCP' : transport.server.scheme);
- via += ' ' + request_sender.ua.configuration.viaHost + ';branch=' + id;
- if (request_sender.ua.configuration.forceRport) {
- via += ';rport';
- }
- return via;
- }
- /**
- * @augments SIP.Transactions
- * @class Non Invite Client Transaction
- * @param {SIP.RequestSender} request_sender
- * @param {SIP.OutgoingRequest} request
- * @param {SIP.Transport} transport
- */
- var NonInviteClientTransaction = function(request_sender, request, transport) {
- var via;
- this.type = C.NON_INVITE_CLIENT;
- this.transport = transport;
- this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
- this.request_sender = request_sender;
- this.request = request;
- this.logger = request_sender.ua.getLogger('sip.transaction.nict', this.id);
- via = buildViaHeader(request_sender, transport, this.id);
- this.request.setHeader('via', via);
- this.request_sender.ua.newTransaction(this);
- };
- NonInviteClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
- NonInviteClientTransaction.prototype.stateChanged = function(state) {
- this.state = state;
- this.emit('stateChanged');
- };
- NonInviteClientTransaction.prototype.send = function() {
- var tr = this;
- this.stateChanged(C.STATUS_TRYING);
- this.F = SIP.Timers.setTimeout(tr.timer_F.bind(tr), SIP.Timers.TIMER_F);
- if(!this.transport.send(this.request)) {
- this.onTransportError();
- }
- };
- NonInviteClientTransaction.prototype.onTransportError = function() {
- this.logger.log('transport error occurred, deleting non-INVITE client transaction ' + this.id);
- SIP.Timers.clearTimeout(this.F);
- SIP.Timers.clearTimeout(this.K);
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- this.request_sender.onTransportError();
- };
- NonInviteClientTransaction.prototype.timer_F = function() {
- this.logger.log('Timer F expired for non-INVITE client transaction ' + this.id);
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- this.request_sender.onRequestTimeout();
- };
- NonInviteClientTransaction.prototype.timer_K = function() {
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- };
- NonInviteClientTransaction.prototype.receiveResponse = function(response) {
- var
- tr = this,
- status_code = response.status_code;
- if(status_code < 200) {
- switch(this.state) {
- case C.STATUS_TRYING:
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_PROCEEDING);
- this.request_sender.receiveResponse(response);
- break;
- }
- } else {
- switch(this.state) {
- case C.STATUS_TRYING:
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_COMPLETED);
- SIP.Timers.clearTimeout(this.F);
- if(status_code === 408) {
- this.request_sender.onRequestTimeout();
- } else {
- this.request_sender.receiveResponse(response);
- }
- this.K = SIP.Timers.setTimeout(tr.timer_K.bind(tr), SIP.Timers.TIMER_K);
- break;
- case C.STATUS_COMPLETED:
- break;
- }
- }
- };
- /**
- * @augments SIP.Transactions
- * @class Invite Client Transaction
- * @param {SIP.RequestSender} request_sender
- * @param {SIP.OutgoingRequest} request
- * @param {SIP.Transport} transport
- */
- var InviteClientTransaction = function(request_sender, request, transport) {
- var via,
- tr = this;
- this.type = C.INVITE_CLIENT;
- this.transport = transport;
- this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
- this.request_sender = request_sender;
- this.request = request;
- this.logger = request_sender.ua.getLogger('sip.transaction.ict', this.id);
- via = buildViaHeader(request_sender, transport, this.id);
- this.request.setHeader('via', via);
- this.request_sender.ua.newTransaction(this);
- // Add the cancel property to the request.
- //Will be called from the request instance, not the transaction itself.
- this.request.cancel = function(reason, extraHeaders) {
- extraHeaders = (extraHeaders || []).slice();
- var length = extraHeaders.length;
- var extraHeadersString = null;
- for (var idx = 0; idx < length; idx++) {
- extraHeadersString = (extraHeadersString || '') + extraHeaders[idx].trim() + '\r\n';
- }
- tr.cancel_request(tr, reason, extraHeadersString);
- };
- };
- InviteClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
- InviteClientTransaction.prototype.stateChanged = function(state) {
- this.state = state;
- this.emit('stateChanged');
- };
- InviteClientTransaction.prototype.send = function() {
- var tr = this;
- this.stateChanged(C.STATUS_CALLING);
- this.B = SIP.Timers.setTimeout(tr.timer_B.bind(tr), SIP.Timers.TIMER_B);
- if(!this.transport.send(this.request)) {
- this.onTransportError();
- }
- };
- InviteClientTransaction.prototype.onTransportError = function() {
- this.logger.log('transport error occurred, deleting INVITE client transaction ' + this.id);
- SIP.Timers.clearTimeout(this.B);
- SIP.Timers.clearTimeout(this.D);
- SIP.Timers.clearTimeout(this.M);
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- if (this.state !== C.STATUS_ACCEPTED) {
- this.request_sender.onTransportError();
- }
- };
- // RFC 6026 7.2
- InviteClientTransaction.prototype.timer_M = function() {
- this.logger.log('Timer M expired for INVITE client transaction ' + this.id);
- if(this.state === C.STATUS_ACCEPTED) {
- SIP.Timers.clearTimeout(this.B);
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- }
- };
- // RFC 3261 17.1.1
- InviteClientTransaction.prototype.timer_B = function() {
- this.logger.log('Timer B expired for INVITE client transaction ' + this.id);
- if(this.state === C.STATUS_CALLING) {
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- this.request_sender.onRequestTimeout();
- }
- };
- InviteClientTransaction.prototype.timer_D = function() {
- this.logger.log('Timer D expired for INVITE client transaction ' + this.id);
- SIP.Timers.clearTimeout(this.B);
- this.stateChanged(C.STATUS_TERMINATED);
- this.request_sender.ua.destroyTransaction(this);
- };
- InviteClientTransaction.prototype.sendACK = function(response) {
- var tr = this;
- this.ack = 'ACK ' + this.request.ruri + ' SIP/2.0\r\n';
- this.ack += 'Via: ' + this.request.headers['Via'].toString() + '\r\n';
- if(this.request.headers['Route']) {
- this.ack += 'Route: ' + this.request.headers['Route'].toString() + '\r\n';
- }
- this.ack += 'To: ' + response.getHeader('to') + '\r\n';
- this.ack += 'From: ' + this.request.headers['From'].toString() + '\r\n';
- this.ack += 'Call-ID: ' + this.request.headers['Call-ID'].toString() + '\r\n';
- this.ack += 'Content-Length: 0\r\n';
- this.ack += 'CSeq: ' + this.request.headers['CSeq'].toString().split(' ')[0];
- this.ack += ' ACK\r\n\r\n';
- this.D = SIP.Timers.setTimeout(tr.timer_D.bind(tr), SIP.Timers.TIMER_D);
- this.transport.send(this.ack);
- };
- InviteClientTransaction.prototype.cancel_request = function(tr, reason, extraHeaders) {
- var request = tr.request;
- this.cancel = SIP.C.CANCEL + ' ' + request.ruri + ' SIP/2.0\r\n';
- this.cancel += 'Via: ' + request.headers['Via'].toString() + '\r\n';
- if(this.request.headers['Route']) {
- this.cancel += 'Route: ' + request.headers['Route'].toString() + '\r\n';
- }
- this.cancel += 'To: ' + request.headers['To'].toString() + '\r\n';
- this.cancel += 'From: ' + request.headers['From'].toString() + '\r\n';
- this.cancel += 'Call-ID: ' + request.headers['Call-ID'].toString() + '\r\n';
- this.cancel += 'CSeq: ' + request.headers['CSeq'].toString().split(' ')[0] +
- ' CANCEL\r\n';
- if(reason) {
- this.cancel += 'Reason: ' + reason + '\r\n';
- }
- if (extraHeaders) {
- this.cancel += extraHeaders;
- }
- this.cancel += 'Content-Length: 0\r\n\r\n';
- // Send only if a provisional response (>100) has been received.
- if(this.state === C.STATUS_PROCEEDING) {
- this.transport.send(this.cancel);
- }
- };
- InviteClientTransaction.prototype.receiveResponse = function(response) {
- var
- tr = this,
- status_code = response.status_code;
- if(status_code >= 100 && status_code <= 199) {
- switch(this.state) {
- case C.STATUS_CALLING:
- this.stateChanged(C.STATUS_PROCEEDING);
- this.request_sender.receiveResponse(response);
- if(this.cancel) {
- this.transport.send(this.cancel);
- }
- break;
- case C.STATUS_PROCEEDING:
- this.request_sender.receiveResponse(response);
- break;
- }
- } else if(status_code >= 200 && status_code <= 299) {
- switch(this.state) {
- case C.STATUS_CALLING:
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_ACCEPTED);
- this.M = SIP.Timers.setTimeout(tr.timer_M.bind(tr), SIP.Timers.TIMER_M);
- this.request_sender.receiveResponse(response);
- break;
- case C.STATUS_ACCEPTED:
- this.request_sender.receiveResponse(response);
- break;
- }
- } else if(status_code >= 300 && status_code <= 699) {
- switch(this.state) {
- case C.STATUS_CALLING:
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_COMPLETED);
- this.sendACK(response);
- this.request_sender.receiveResponse(response);
- break;
- case C.STATUS_COMPLETED:
- this.sendACK(response);
- break;
- }
- }
- };
- /**
- * @augments SIP.Transactions
- * @class ACK Client Transaction
- * @param {SIP.RequestSender} request_sender
- * @param {SIP.OutgoingRequest} request
- * @param {SIP.Transport} transport
- */
- var AckClientTransaction = function(request_sender, request, transport) {
- var via;
- this.transport = transport;
- this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
- this.request_sender = request_sender;
- this.request = request;
- this.logger = request_sender.ua.getLogger('sip.transaction.nict', this.id);
- via = buildViaHeader(request_sender, transport, this.id);
- this.request.setHeader('via', via);
- };
- AckClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
- AckClientTransaction.prototype.send = function() {
- if(!this.transport.send(this.request)) {
- this.onTransportError();
- }
- };
- AckClientTransaction.prototype.onTransportError = function() {
- this.logger.log('transport error occurred, for an ACK client transaction ' + this.id);
- this.request_sender.onTransportError();
- };
- /**
- * @augments SIP.Transactions
- * @class Non Invite Server Transaction
- * @param {SIP.IncomingRequest} request
- * @param {SIP.UA} ua
- */
- var NonInviteServerTransaction = function(request, ua) {
- this.type = C.NON_INVITE_SERVER;
- this.id = request.via_branch;
- this.request = request;
- this.transport = request.transport;
- this.ua = ua;
- this.last_response = '';
- request.server_transaction = this;
- this.logger = ua.getLogger('sip.transaction.nist', this.id);
- this.state = C.STATUS_TRYING;
- ua.newTransaction(this);
- };
- NonInviteServerTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
- NonInviteServerTransaction.prototype.stateChanged = function(state) {
- this.state = state;
- this.emit('stateChanged');
- };
- NonInviteServerTransaction.prototype.timer_J = function() {
- this.logger.log('Timer J expired for non-INVITE server transaction ' + this.id);
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- };
- NonInviteServerTransaction.prototype.onTransportError = function() {
- if (!this.transportError) {
- this.transportError = true;
- this.logger.log('transport error occurred, deleting non-INVITE server transaction ' + this.id);
- SIP.Timers.clearTimeout(this.J);
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- }
- };
- NonInviteServerTransaction.prototype.receiveResponse = function(status_code, response) {
- var tr = this;
- var deferred = SIP.Utils.defer();
- if(status_code === 100) {
- /* RFC 4320 4.1
- * 'A SIP element MUST NOT
- * send any provisional response with a
- * Status-Code other than 100 to a non-INVITE request.'
- */
- switch(this.state) {
- case C.STATUS_TRYING:
- this.stateChanged(C.STATUS_PROCEEDING);
- if(!this.transport.send(response)) {
- this.onTransportError();
- }
- break;
- case C.STATUS_PROCEEDING:
- this.last_response = response;
- if(!this.transport.send(response)) {
- this.onTransportError();
- deferred.reject();
- } else {
- deferred.resolve();
- }
- break;
- }
- } else if(status_code >= 200 && status_code <= 699) {
- switch(this.state) {
- case C.STATUS_TRYING:
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_COMPLETED);
- this.last_response = response;
- this.J = SIP.Timers.setTimeout(tr.timer_J.bind(tr), SIP.Timers.TIMER_J);
- if(!this.transport.send(response)) {
- this.onTransportError();
- deferred.reject();
- } else {
- deferred.resolve();
- }
- break;
- case C.STATUS_COMPLETED:
- break;
- }
- }
- return deferred.promise;
- };
- /**
- * @augments SIP.Transactions
- * @class Invite Server Transaction
- * @param {SIP.IncomingRequest} request
- * @param {SIP.UA} ua
- */
- var InviteServerTransaction = function(request, ua) {
- this.type = C.INVITE_SERVER;
- this.id = request.via_branch;
- this.request = request;
- this.transport = request.transport;
- this.ua = ua;
- this.last_response = '';
- request.server_transaction = this;
- this.logger = ua.getLogger('sip.transaction.ist', this.id);
- this.state = C.STATUS_PROCEEDING;
- ua.newTransaction(this);
- this.resendProvisionalTimer = null;
- request.reply(100);
- };
- InviteServerTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
- InviteServerTransaction.prototype.stateChanged = function(state) {
- this.state = state;
- this.emit('stateChanged');
- };
- InviteServerTransaction.prototype.timer_H = function() {
- this.logger.log('Timer H expired for INVITE server transaction ' + this.id);
- if(this.state === C.STATUS_COMPLETED) {
- this.logger.warn('transactions', 'ACK for INVITE server transaction was never received, call will be terminated');
- }
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- };
- InviteServerTransaction.prototype.timer_I = function() {
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- };
- // RFC 6026 7.1
- InviteServerTransaction.prototype.timer_L = function() {
- this.logger.log('Timer L expired for INVITE server transaction ' + this.id);
- if(this.state === C.STATUS_ACCEPTED) {
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- }
- };
- InviteServerTransaction.prototype.onTransportError = function() {
- if (!this.transportError) {
- this.transportError = true;
- this.logger.log('transport error occurred, deleting INVITE server transaction ' + this.id);
- if (this.resendProvisionalTimer !== null) {
- SIP.Timers.clearInterval(this.resendProvisionalTimer);
- this.resendProvisionalTimer = null;
- }
- SIP.Timers.clearTimeout(this.L);
- SIP.Timers.clearTimeout(this.H);
- SIP.Timers.clearTimeout(this.I);
- this.stateChanged(C.STATUS_TERMINATED);
- this.ua.destroyTransaction(this);
- }
- };
- InviteServerTransaction.prototype.resend_provisional = function() {
- if(!this.transport.send(this.last_response)) {
- this.onTransportError();
- }
- };
- // INVITE Server Transaction RFC 3261 17.2.1
- InviteServerTransaction.prototype.receiveResponse = function(status_code, response) {
- var tr = this;
- var deferred = SIP.Utils.defer();
- if(status_code >= 100 && status_code <= 199) {
- switch(this.state) {
- case C.STATUS_PROCEEDING:
- if(!this.transport.send(response)) {
- this.onTransportError();
- }
- this.last_response = response;
- break;
- }
- }
- if(status_code > 100 && status_code <= 199 && this.state === C.STATUS_PROCEEDING) {
- // Trigger the resendProvisionalTimer only for the first non 100 provisional response.
- if(this.resendProvisionalTimer === null) {
- this.resendProvisionalTimer = SIP.Timers.setInterval(tr.resend_provisional.bind(tr),
- SIP.Timers.PROVISIONAL_RESPONSE_INTERVAL);
- }
- } else if(status_code >= 200 && status_code <= 299) {
- switch(this.state) {
- case C.STATUS_PROCEEDING:
- this.stateChanged(C.STATUS_ACCEPTED);
- this.last_response = response;
- this.L = SIP.Timers.setTimeout(tr.timer_L.bind(tr), SIP.Timers.TIMER_L);
- if (this.resendProvisionalTimer !== null) {
- SIP.Timers.clearInterval(this.resendProvisionalTimer);
- this.resendProvisionalTimer = null;
- }
- /* falls through */
- case C.STATUS_ACCEPTED:
- // Note that this point will be reached for proceeding tr.state also.
- if(!this.transport.send(response)) {
- this.onTransportError();
- deferred.reject();
- } else {
- deferred.resolve();
- }
- break;
- }
- } else if(status_code >= 300 && status_code <= 699) {
- switch(this.state) {
- case C.STATUS_PROCEEDING:
- if (this.resendProvisionalTimer !== null) {
- SIP.Timers.clearInterval(this.resendProvisionalTimer);
- this.resendProvisionalTimer = null;
- }
- if(!this.transport.send(response)) {
- this.onTransportError();
- deferred.reject();
- } else {
- this.stateChanged(C.STATUS_COMPLETED);
- this.H = SIP.Timers.setTimeout(tr.timer_H.bind(tr), SIP.Timers.TIMER_H);
- deferred.resolve();
- }
- break;
- }
- }
- return deferred.promise;
- };
- /**
- * @function
- * @param {SIP.UA} ua
- * @param {SIP.IncomingRequest} request
- *
- * @return {boolean}
- * INVITE:
- * _true_ if retransmission
- * _false_ new request
- *
- * ACK:
- * _true_ ACK to non2xx response
- * _false_ ACK must be passed to TU (accepted state)
- * ACK to 2xx response
- *
- * CANCEL:
- * _true_ no matching invite transaction
- * _false_ matching invite transaction and no final response sent
- *
- * OTHER:
- * _true_ retransmission
- * _false_ new request
- */
- var checkTransaction = function(ua, request) {
- var tr;
- switch(request.method) {
- case SIP.C.INVITE:
- tr = ua.transactions.ist[request.via_branch];
- if(tr) {
- switch(tr.state) {
- case C.STATUS_PROCEEDING:
- tr.transport.send(tr.last_response);
- break;
- // RFC 6026 7.1 Invite retransmission
- //received while in C.STATUS_ACCEPTED state. Absorb it.
- case C.STATUS_ACCEPTED:
- break;
- }
- return true;
- }
- break;
- case SIP.C.ACK:
- tr = ua.transactions.ist[request.via_branch];
- // RFC 6026 7.1
- if(tr) {
- if(tr.state === C.STATUS_ACCEPTED) {
- return false;
- } else if(tr.state === C.STATUS_COMPLETED) {
- tr.stateChanged(C.STATUS_CONFIRMED);
- tr.I = SIP.Timers.setTimeout(tr.timer_I.bind(tr), SIP.Timers.TIMER_I);
- return true;
- }
- }
- // ACK to 2XX Response.
- else {
- return false;
- }
- break;
- case SIP.C.CANCEL:
- tr = ua.transactions.ist[request.via_branch];
- if(tr) {
- request.reply_sl(200);
- if(tr.state === C.STATUS_PROCEEDING) {
- return false;
- } else {
- return true;
- }
- } else {
- request.reply_sl(481);
- return true;
- }
- break;
- default:
- // Non-INVITE Server Transaction RFC 3261 17.2.2
- tr = ua.transactions.nist[request.via_branch];
- if(tr) {
- switch(tr.state) {
- case C.STATUS_TRYING:
- break;
- case C.STATUS_PROCEEDING:
- case C.STATUS_COMPLETED:
- tr.transport.send(tr.last_response);
- break;
- }
- return true;
- }
- break;
- }
- };
- SIP.Transactions = {
- C: C,
- checkTransaction: checkTransaction,
- NonInviteClientTransaction: NonInviteClientTransaction,
- InviteClientTransaction: InviteClientTransaction,
- AckClientTransaction: AckClientTransaction,
- NonInviteServerTransaction: NonInviteServerTransaction,
- InviteServerTransaction: InviteServerTransaction
- };
- };
- },{}],28:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Transport
- */
- /**
- * @augments SIP
- * @class Transport
- * @param {SIP.UA} ua
- * @param {Object} server ws_server Object
- */
- module.exports = function (SIP, WebSocket) {
- var Transport,
- C = {
- // Transport status codes
- STATUS_READY: 0,
- STATUS_DISCONNECTED: 1,
- STATUS_ERROR: 2
- };
- /**
- * Compute an amount of time in seconds to wait before sending another
- * keep-alive.
- * @returns {Number}
- */
- function computeKeepAliveTimeout(upperBound) {
- var lowerBound = upperBound * 0.8;
- return 1000 * (Math.random() * (upperBound - lowerBound) + lowerBound);
- }
- Transport = function(ua, server) {
- this.logger = ua.getLogger('sip.transport');
- this.ua = ua;
- this.ws = null;
- this.server = server;
- this.reconnection_attempts = 0;
- this.closed = false;
- this.connected = false;
- this.reconnectTimer = null;
- this.lastTransportError = {};
- this.keepAliveInterval = ua.configuration.keepAliveInterval;
- this.keepAliveTimeout = null;
- this.keepAliveTimer = null;
- this.ua.transport = this;
- // Connect
- this.connect();
- };
- Transport.prototype = {
- /**
- * Send a message.
- * @param {SIP.OutgoingRequest|String} msg
- * @returns {Boolean}
- */
- send: function(msg) {
- var message = msg.toString();
- if(this.ws && this.ws.readyState === WebSocket.OPEN) {
- if (this.ua.configuration.traceSip === true) {
- this.logger.log('sending WebSocket message:\n\n' + message + '\n');
- }
- this.ws.send(message);
- return true;
- } else {
- this.logger.warn('unable to send message, WebSocket is not open');
- return false;
- }
- },
- /**
- * Send a keep-alive (a double-CRLF sequence).
- * @private
- * @returns {Boolean}
- */
- sendKeepAlive: function() {
- if(this.keepAliveTimeout) { return; }
- this.keepAliveTimeout = SIP.Timers.setTimeout(function() {
- this.ua.emit('keepAliveTimeout');
- }.bind(this), 10000);
- return this.send('\r\n\r\n');
- },
- /**
- * Start sending keep-alives.
- * @private
- */
- startSendingKeepAlives: function() {
- if (this.keepAliveInterval && !this.keepAliveTimer) {
- this.keepAliveTimer = SIP.Timers.setTimeout(function() {
- this.sendKeepAlive();
- this.keepAliveTimer = null;
- this.startSendingKeepAlives();
- }.bind(this), computeKeepAliveTimeout(this.keepAliveInterval));
- }
- },
- /**
- * Stop sending keep-alives.
- * @private
- */
- stopSendingKeepAlives: function() {
- SIP.Timers.clearTimeout(this.keepAliveTimer);
- SIP.Timers.clearTimeout(this.keepAliveTimeout);
- this.keepAliveTimer = null;
- this.keepAliveTimeout = null;
- },
- /**
- * Disconnect socket.
- */
- disconnect: function() {
- if(this.ws) {
- // Clear reconnectTimer
- SIP.Timers.clearTimeout(this.reconnectTimer);
- this.stopSendingKeepAlives();
- this.closed = true;
- this.logger.log('closing WebSocket ' + this.server.ws_uri);
- this.ws.close();
- this.ws = null;
- }
- if (this.reconnectTimer !== null) {
- SIP.Timers.clearTimeout(this.reconnectTimer);
- this.reconnectTimer = null;
- this.ua.emit('disconnected', {
- transport: this,
- code: this.lastTransportError.code,
- reason: this.lastTransportError.reason
- });
- }
- },
- /**
- * Connect socket.
- */
- connect: function() {
- var transport = this;
- if(this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
- this.logger.log('WebSocket ' + this.server.ws_uri + ' is already connected');
- return false;
- }
- if(this.ws) {
- this.ws.close();
- this.ws = null;
- }
- this.logger.log('connecting to WebSocket ' + this.server.ws_uri);
- this.ua.onTransportConnecting(this,
- (this.reconnection_attempts === 0)?1:this.reconnection_attempts);
- try {
- this.ws = new WebSocket(this.server.ws_uri, 'sip');
- } catch(e) {
- this.logger.warn('error connecting to WebSocket ' + this.server.ws_uri + ': ' + e);
- }
- this.ws.binaryType = 'arraybuffer';
- this.ws.onopen = function() {
- transport.onOpen();
- };
- this.ws.onclose = function(e) {
- transport.onClose(e);
- // Always cleanup. Eases GC, prevents potential memory leaks.
- this.onopen = null;
- this.onclose = null;
- this.onmessage = null;
- this.onerror = null;
- };
- this.ws.onmessage = function(e) {
- transport.onMessage(e);
- };
- this.ws.onerror = function(e) {
- transport.onError(e);
- };
- },
- // Transport Event Handlers
- /**
- * @event
- * @param {event} e
- */
- onOpen: function() {
- this.connected = true;
- this.logger.log('WebSocket ' + this.server.ws_uri + ' connected');
- // Clear reconnectTimer since we are not disconnected
- if (this.reconnectTimer !== null) {
- SIP.Timers.clearTimeout(this.reconnectTimer);
- this.reconnectTimer = null;
- }
- // Reset reconnection_attempts
- this.reconnection_attempts = 0;
- // Disable closed
- this.closed = false;
- // Trigger onTransportConnected callback
- this.ua.onTransportConnected(this);
- // Start sending keep-alives
- this.startSendingKeepAlives();
- },
- /**
- * @event
- * @param {event} e
- */
- onClose: function(e) {
- var connected_before = this.connected;
- this.lastTransportError.code = e.code;
- this.lastTransportError.reason = e.reason;
- this.stopSendingKeepAlives();
- if (this.reconnection_attempts > 0) {
- this.logger.log('Reconnection attempt ' + this.reconnection_attempts + ' failed (code: ' + e.code + (e.reason? '| reason: ' + e.reason : '') +')');
- this.reconnect();
- } else {
- this.connected = false;
- this.logger.log('WebSocket disconnected (code: ' + e.code + (e.reason? '| reason: ' + e.reason : '') +')');
- if(e.wasClean === false) {
- this.logger.warn('WebSocket abrupt disconnection');
- }
- // Transport was connected
- if(connected_before === true) {
- this.ua.onTransportClosed(this);
- // Check whether the user requested to close.
- if(!this.closed) {
- this.reconnect();
- } else {
- this.ua.emit('disconnected', {
- transport: this,
- code: this.lastTransportError.code,
- reason: this.lastTransportError.reason
- });
- }
- } else {
- // This is the first connection attempt
- //Network error
- this.ua.onTransportError(this);
- }
- }
- },
- /**
- * @event
- * @param {event} e
- */
- onMessage: function(e) {
- var message, transaction,
- data = e.data;
- // CRLF Keep Alive response from server. Ignore it.
- if(data === '\r\n') {
- SIP.Timers.clearTimeout(this.keepAliveTimeout);
- this.keepAliveTimeout = null;
- if (this.ua.configuration.traceSip === true) {
- this.logger.log('received WebSocket message with CRLF Keep Alive response');
- }
- return;
- }
- // WebSocket binary message.
- else if (typeof data !== 'string') {
- try {
- data = String.fromCharCode.apply(null, new Uint8Array(data));
- } catch(evt) {
- this.logger.warn('received WebSocket binary message failed to be converted into string, message discarded');
- return;
- }
- if (this.ua.configuration.traceSip === true) {
- this.logger.log('received WebSocket binary message:\n\n' + data + '\n');
- }
- }
- // WebSocket text message.
- else {
- if (this.ua.configuration.traceSip === true) {
- this.logger.log('received WebSocket text message:\n\n' + data + '\n');
- }
- }
- message = SIP.Parser.parseMessage(data, this.ua);
- if (!message) {
- return;
- }
- if(this.ua.status === SIP.UA.C.STATUS_USER_CLOSED && message instanceof SIP.IncomingRequest) {
- return;
- }
- // Do some sanity check
- if(SIP.sanityCheck(message, this.ua, this)) {
- if(message instanceof SIP.IncomingRequest) {
- message.transport = this;
- this.ua.receiveRequest(message);
- } else if(message instanceof SIP.IncomingResponse) {
- /* Unike stated in 18.1.2, if a response does not match
- * any transaction, it is discarded here and no passed to the core
- * in order to be discarded there.
- */
- switch(message.method) {
- case SIP.C.INVITE:
- transaction = this.ua.transactions.ict[message.via_branch];
- if(transaction) {
- transaction.receiveResponse(message);
- }
- break;
- case SIP.C.ACK:
- // Just in case ;-)
- break;
- default:
- transaction = this.ua.transactions.nict[message.via_branch];
- if(transaction) {
- transaction.receiveResponse(message);
- }
- break;
- }
- }
- }
- },
- /**
- * @event
- * @param {event} e
- */
- onError: function(e) {
- this.logger.warn('WebSocket connection error: ' + JSON.stringify(e));
- },
- /**
- * Reconnection attempt logic.
- * @private
- */
- reconnect: function() {
- var transport = this;
- this.reconnection_attempts += 1;
- if(this.reconnection_attempts > this.ua.configuration.wsServerMaxReconnection) {
- this.logger.warn('maximum reconnection attempts for WebSocket ' + this.server.ws_uri);
- this.ua.onTransportError(this);
- } else if (this.reconnection_attempts === 1) {
- this.logger.log('Connection to WebSocket ' + this.server.ws_uri + ' severed, attempting first reconnect');
- transport.connect();
- } else {
- this.logger.log('trying to reconnect to WebSocket ' + this.server.ws_uri + ' (reconnection attempt ' + this.reconnection_attempts + ')');
- this.reconnectTimer = SIP.Timers.setTimeout(function() {
- transport.connect();
- transport.reconnectTimer = null;
- }, this.ua.configuration.wsServerReconnectionTimeout * 1000);
- }
- }
- };
- Transport.C = C;
- return Transport;
- };
- },{}],29:[function(require,module,exports){
- (function (global){
- "use strict";
- /**
- * @augments SIP
- * @class Class creating a SIP User Agent.
- * @param {function returning SIP.MediaHandler} [configuration.mediaHandlerFactory]
- * A function will be invoked by each of the UA's Sessions to build the MediaHandler for that Session.
- * If no (or a falsy) value is provided, each Session will use a default (WebRTC) MediaHandler.
- *
- * @param {Object} [configuration.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
- */
- module.exports = function (SIP, environment) {
- var UA,
- C = {
- // UA status codes
- STATUS_INIT: 0,
- STATUS_STARTING: 1,
- STATUS_READY: 2,
- STATUS_USER_CLOSED: 3,
- STATUS_NOT_READY: 4,
- // UA error codes
- CONFIGURATION_ERROR: 1,
- NETWORK_ERROR: 2,
- ALLOWED_METHODS: [
- 'ACK',
- 'CANCEL',
- 'INVITE',
- 'MESSAGE',
- 'BYE',
- 'OPTIONS',
- 'INFO',
- 'NOTIFY',
- 'REFER'
- ],
- ACCEPTED_BODY_TYPES: [
- 'application/sdp',
- 'application/dtmf-relay'
- ],
- MAX_FORWARDS: 70,
- TAG_LENGTH: 10
- };
- UA = function(configuration) {
- var self = this;
- // Helper function for forwarding events
- function selfEmit(type) {
- //registrationFailed handler is invoked with two arguments. Allow event handlers to be invoked with a variable no. of arguments
- return self.emit.bind(self, type);
- }
- // Set Accepted Body Types
- C.ACCEPTED_BODY_TYPES = C.ACCEPTED_BODY_TYPES.toString();
- this.log = new SIP.LoggerFactory();
- this.logger = this.getLogger('sip.ua');
- this.cache = {
- credentials: {}
- };
- this.configuration = {};
- this.dialogs = {};
- //User actions outside any session/dialog (MESSAGE)
- this.applicants = {};
- this.data = {};
- this.sessions = {};
- this.subscriptions = {};
- this.earlySubscriptions = {};
- this.transport = null;
- this.contact = null;
- this.status = C.STATUS_INIT;
- this.error = null;
- this.transactions = {
- nist: {},
- nict: {},
- ist: {},
- ict: {}
- };
- this.transportRecoverAttempts = 0;
- this.transportRecoveryTimer = null;
- Object.defineProperties(this, {
- transactionsCount: {
- get: function() {
- var type,
- transactions = ['nist','nict','ist','ict'],
- count = 0;
- for (type in transactions) {
- count += Object.keys(this.transactions[transactions[type]]).length;
- }
- return count;
- }
- },
- nictTransactionsCount: {
- get: function() {
- return Object.keys(this.transactions['nict']).length;
- }
- },
- nistTransactionsCount: {
- get: function() {
- return Object.keys(this.transactions['nist']).length;
- }
- },
- ictTransactionsCount: {
- get: function() {
- return Object.keys(this.transactions['ict']).length;
- }
- },
- istTransactionsCount: {
- get: function() {
- return Object.keys(this.transactions['ist']).length;
- }
- }
- });
- /**
- * Load configuration
- *
- * @throws {SIP.Exceptions.ConfigurationError}
- * @throws {TypeError}
- */
- if(configuration === undefined) {
- configuration = {};
- } else if (typeof configuration === 'string' || configuration instanceof String) {
- configuration = {
- uri: configuration
- };
- }
- // Apply log configuration if present
- if (configuration.log) {
- if (configuration.log.hasOwnProperty('builtinEnabled')) {
- this.log.builtinEnabled = configuration.log.builtinEnabled;
- }
- if (configuration.log.hasOwnProperty('level')) {
- this.log.level = configuration.log.level;
- }
- if (configuration.log.hasOwnProperty('connector')) {
- this.log.connector = configuration.log.connector;
- }
- }
- try {
- this.loadConfig(configuration);
- } catch(e) {
- this.status = C.STATUS_NOT_READY;
- this.error = C.CONFIGURATION_ERROR;
- throw e;
- }
- // Initialize registerContext
- this.registerContext = new SIP.RegisterContext(this);
- this.registerContext.on('failed', selfEmit('registrationFailed'));
- this.registerContext.on('registered', selfEmit('registered'));
- this.registerContext.on('unregistered', selfEmit('unregistered'));
- if(this.configuration.autostart) {
- this.start();
- }
- };
- UA.prototype = Object.create(SIP.EventEmitter.prototype);
- //=================
- // High Level API
- //=================
- UA.prototype.register = function(options) {
- this.configuration.register = true;
- this.registerContext.register(options);
- return this;
- };
- /**
- * Unregister.
- *
- * @param {Boolean} [all] unregister all user bindings.
- *
- */
- UA.prototype.unregister = function(options) {
- this.configuration.register = false;
- var context = this.registerContext;
- this.afterConnected(context.unregister.bind(context, options));
- return this;
- };
- UA.prototype.isRegistered = function() {
- return this.registerContext.registered;
- };
- /**
- * Connection state.
- * @param {Boolean}
- */
- UA.prototype.isConnected = function() {
- return this.transport ? this.transport.connected : false;
- };
- UA.prototype.afterConnected = function afterConnected (callback) {
- if (this.isConnected()) {
- callback();
- } else {
- this.once('connected', callback);
- }
- };
- /**
- * Make an outgoing call.
- *
- * @param {String} target
- * @param {Object} views
- * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
- *
- * @throws {TypeError}
- *
- */
- UA.prototype.invite = function(target, options) {
- var context = new SIP.InviteClientContext(this, target, options);
- this.afterConnected(context.invite.bind(context));
- this.emit('inviteSent', context);
- return context;
- };
- UA.prototype.subscribe = function(target, event, options) {
- var sub = new SIP.Subscription(this, target, event, options);
- this.afterConnected(sub.subscribe.bind(sub));
- return sub;
- };
- /**
- * Send a message.
- *
- * @param {String} target
- * @param {String} body
- * @param {Object} [options]
- *
- * @throws {TypeError}
- *
- */
- UA.prototype.message = function(target, body, options) {
- if (body === undefined) {
- throw new TypeError('Not enough arguments');
- }
- // There is no Message module, so it is okay that the UA handles defaults here.
- options = Object.create(options || Object.prototype);
- options.contentType || (options.contentType = 'text/plain');
- options.body = body;
- return this.request(SIP.C.MESSAGE, target, options);
- };
- UA.prototype.request = function (method, target, options) {
- var req = new SIP.ClientContext(this, method, target, options);
- this.afterConnected(req.send.bind(req));
- return req;
- };
- /**
- * Gracefully close.
- *
- */
- UA.prototype.stop = function() {
- var session, subscription, applicant,
- ua = this;
- function transactionsListener() {
- if (ua.nistTransactionsCount === 0 && ua.nictTransactionsCount === 0) {
- ua.removeListener('transactionDestroyed', transactionsListener);
- ua.transport.disconnect();
- }
- }
- this.logger.log('user requested closure...');
- if(this.status === C.STATUS_USER_CLOSED) {
- this.logger.warn('UA already closed');
- return this;
- }
- // Clear transportRecoveryTimer
- SIP.Timers.clearTimeout(this.transportRecoveryTimer);
- // Close registerContext
- this.logger.log('closing registerContext');
- this.registerContext.close();
- // Run _terminate_ on every Session
- for(session in this.sessions) {
- this.logger.log('closing session ' + session);
- this.sessions[session].terminate();
- }
- //Run _close_ on every confirmed Subscription
- for(subscription in this.subscriptions) {
- this.logger.log('unsubscribing from subscription ' + subscription);
- this.subscriptions[subscription].close();
- }
- //Run _close_ on every early Subscription
- for(subscription in this.earlySubscriptions) {
- this.logger.log('unsubscribing from early subscription ' + subscription);
- this.earlySubscriptions[subscription].close();
- }
- // Run _close_ on every applicant
- for(applicant in this.applicants) {
- this.applicants[applicant].close();
- }
- this.status = C.STATUS_USER_CLOSED;
- /*
- * If the remaining transactions are all INVITE transactions, there is no need to
- * wait anymore because every session has already been closed by this method.
- * - locally originated sessions where terminated (CANCEL or BYE)
- * - remotely originated sessions where rejected (4XX) or terminated (BYE)
- * Remaining INVITE transactions belong tho sessions that where answered. This are in
- * 'accepted' state due to timers 'L' and 'M' defined in [RFC 6026]
- */
- if (this.nistTransactionsCount === 0 && this.nictTransactionsCount === 0) {
- this.transport.disconnect();
- } else {
- this.on('transactionDestroyed', transactionsListener);
- }
- if (typeof environment.removeEventListener === 'function') {
- // Google Chrome Packaged Apps don't allow 'unload' listeners:
- // unload is not available in packaged apps
- if (!(global.chrome && global.chrome.app && global.chrome.app.runtime)) {
- environment.removeEventListener('unload', this.environListener);
- }
- }
- return this;
- };
- /**
- * Connect to the WS server if status = STATUS_INIT.
- * Resume UA after being closed.
- *
- */
- UA.prototype.start = function() {
- var server;
- this.logger.log('user requested startup...');
- if (this.status === C.STATUS_INIT) {
- server = this.getNextWsServer();
- this.status = C.STATUS_STARTING;
- new SIP.Transport(this, server);
- } else if(this.status === C.STATUS_USER_CLOSED) {
- this.logger.log('resuming');
- this.status = C.STATUS_READY;
- this.transport.connect();
- } else if (this.status === C.STATUS_STARTING) {
- this.logger.log('UA is in STARTING status, not opening new connection');
- } else if (this.status === C.STATUS_READY) {
- this.logger.log('UA is in READY status, not resuming');
- } else {
- this.logger.error('Connection is down. Auto-Recovery system is trying to connect');
- }
- if (this.configuration.autostop && typeof environment.addEventListener === 'function') {
- // Google Chrome Packaged Apps don't allow 'unload' listeners:
- // unload is not available in packaged apps
- if (!(global.chrome && global.chrome.app && global.chrome.app.runtime)) {
- this.environListener = this.stop.bind(this);
- environment.addEventListener('unload', this.environListener);
- }
- }
- return this;
- };
- /**
- * Normalize a string into a valid SIP request URI
- *
- * @param {String} target
- *
- * @returns {SIP.URI|undefined}
- */
- UA.prototype.normalizeTarget = function(target) {
- return SIP.Utils.normalizeTarget(target, this.configuration.hostportParams);
- };
- //===============================
- // Private (For internal use)
- //===============================
- UA.prototype.saveCredentials = function(credentials) {
- this.cache.credentials[credentials.realm] = this.cache.credentials[credentials.realm] || {};
- this.cache.credentials[credentials.realm][credentials.uri] = credentials;
- return this;
- };
- UA.prototype.getCredentials = function(request) {
- var realm, credentials;
- realm = request.ruri.host;
- if (this.cache.credentials[realm] && this.cache.credentials[realm][request.ruri]) {
- credentials = this.cache.credentials[realm][request.ruri];
- credentials.method = request.method;
- }
- return credentials;
- };
- UA.prototype.getLogger = function(category, label) {
- return this.log.getLogger(category, label);
- };
- //==============================
- // Event Handlers
- //==============================
- /**
- * Transport Close event
- * @private
- * @event
- * @param {SIP.Transport} transport.
- */
- UA.prototype.onTransportClosed = function(transport) {
- // Run _onTransportError_ callback on every client transaction using _transport_
- var type, idx, length,
- client_transactions = ['nict', 'ict', 'nist', 'ist'];
- transport.server.status = SIP.Transport.C.STATUS_DISCONNECTED;
- this.logger.log('connection state set to '+ SIP.Transport.C.STATUS_DISCONNECTED);
- length = client_transactions.length;
- for (type = 0; type < length; type++) {
- for(idx in this.transactions[client_transactions[type]]) {
- this.transactions[client_transactions[type]][idx].onTransportError();
- }
- }
- // Close sessions if GRUU is not being used
- if (!this.contact.pub_gruu) {
- this.closeSessionsOnTransportError();
- }
- };
- /**
- * Unrecoverable transport event.
- * Connection reattempt logic has been done and didn't success.
- * @private
- * @event
- * @param {SIP.Transport} transport.
- */
- UA.prototype.onTransportError = function(transport) {
- var server;
- this.logger.log('transport ' + transport.server.ws_uri + ' failed | connection state set to '+ SIP.Transport.C.STATUS_ERROR);
- // Close sessions.
- //Mark this transport as 'down'
- transport.server.status = SIP.Transport.C.STATUS_ERROR;
- this.emit('disconnected', {
- transport: transport
- });
- // try the next transport if the UA isn't closed
- if(this.status === C.STATUS_USER_CLOSED) {
- return;
- }
- server = this.getNextWsServer();
- if(server) {
- new SIP.Transport(this, server);
- }else {
- this.closeSessionsOnTransportError();
- if (!this.error || this.error !== C.NETWORK_ERROR) {
- this.status = C.STATUS_NOT_READY;
- this.error = C.NETWORK_ERROR;
- }
- // Transport Recovery process
- this.recoverTransport();
- }
- };
- /**
- * Transport connection event.
- * @private
- * @event
- * @param {SIP.Transport} transport.
- */
- UA.prototype.onTransportConnected = function(transport) {
- this.transport = transport;
- // Reset transport recovery counter
- this.transportRecoverAttempts = 0;
- transport.server.status = SIP.Transport.C.STATUS_READY;
- this.logger.log('connection state set to '+ SIP.Transport.C.STATUS_READY);
- if(this.status === C.STATUS_USER_CLOSED) {
- return;
- }
- this.status = C.STATUS_READY;
- this.error = null;
- if(this.configuration.register) {
- this.configuration.authenticationFactory.initialize().then(function () {
- this.registerContext.onTransportConnected();
- }.bind(this));
- }
- this.emit('connected', {
- transport: transport
- });
- };
- /**
- * Transport connecting event
- * @private
- * @param {SIP.Transport} transport.
- * #param {Integer} attempts.
- */
- UA.prototype.onTransportConnecting = function(transport, attempts) {
- this.emit('connecting', {
- transport: transport,
- attempts: attempts
- });
- };
- /**
- * new Transaction
- * @private
- * @param {SIP.Transaction} transaction.
- */
- UA.prototype.newTransaction = function(transaction) {
- this.transactions[transaction.type][transaction.id] = transaction;
- this.emit('newTransaction', {transaction: transaction});
- };
- /**
- * destroy Transaction
- * @private
- * @param {SIP.Transaction} transaction.
- */
- UA.prototype.destroyTransaction = function(transaction) {
- delete this.transactions[transaction.type][transaction.id];
- this.emit('transactionDestroyed', {
- transaction: transaction
- });
- };
- //=========================
- // receiveRequest
- //=========================
- /**
- * Request reception
- * @private
- * @param {SIP.IncomingRequest} request.
- */
- UA.prototype.receiveRequest = function(request) {
- var dialog, session, message, earlySubscription,
- method = request.method,
- transaction,
- replaces,
- replacedDialog,
- self = this;
- function ruriMatches (uri) {
- return uri && uri.user === request.ruri.user;
- }
- // Check that request URI points to us
- if(!(ruriMatches(this.configuration.uri) ||
- ruriMatches(this.contact.uri) ||
- ruriMatches(this.contact.pub_gruu) ||
- ruriMatches(this.contact.temp_gruu))) {
- this.logger.warn('Request-URI does not point to us');
- if (request.method !== SIP.C.ACK) {
- request.reply_sl(404);
- }
- return;
- }
- // Check request URI scheme
- if(request.ruri.scheme === SIP.C.SIPS) {
- request.reply_sl(416);
- return;
- }
- // Check transaction
- if(SIP.Transactions.checkTransaction(this, request)) {
- return;
- }
- /* RFC3261 12.2.2
- * Requests that do not change in any way the state of a dialog may be
- * received within a dialog (for example, an OPTIONS request).
- * They are processed as if they had been received outside the dialog.
- */
- if(method === SIP.C.OPTIONS) {
- new SIP.Transactions.NonInviteServerTransaction(request, this);
- request.reply(200, null, [
- 'Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString(),
- 'Accept: '+ C.ACCEPTED_BODY_TYPES
- ]);
- } else if (method === SIP.C.MESSAGE) {
- message = new SIP.ServerContext(this, request);
- message.body = request.body;
- message.content_type = request.getHeader('Content-Type') || 'text/plain';
- request.reply(200, null);
- this.emit('message', message);
- } else if (method !== SIP.C.INVITE &&
- method !== SIP.C.ACK) {
- // Let those methods pass through to normal processing for now.
- transaction = new SIP.ServerContext(this, request);
- }
- // Initial Request
- if(!request.to_tag) {
- switch(method) {
- case SIP.C.INVITE:
- replaces =
- this.configuration.replaces !== SIP.C.supported.UNSUPPORTED &&
- request.parseHeader('replaces');
- if (replaces) {
- replacedDialog = this.dialogs[replaces.call_id + replaces.replaces_to_tag + replaces.replaces_from_tag];
- if (!replacedDialog) {
- //Replaced header without a matching dialog, reject
- request.reply_sl(481, null);
- return;
- } else if (replacedDialog.owner.status === SIP.Session.C.STATUS_TERMINATED) {
- request.reply_sl(603, null);
- return;
- } else if (replacedDialog.state === SIP.Dialog.C.STATUS_CONFIRMED && replaces.early_only) {
- request.reply_sl(486, null);
- return;
- }
- }
- var isMediaSupported = this.configuration.mediaHandlerFactory.isSupported;
- if(!isMediaSupported || isMediaSupported()) {
- session = new SIP.InviteServerContext(this, request);
- session.replacee = replacedDialog && replacedDialog.owner;
- session.on('invite', function() {
- self.emit('invite', this);
- });
- } else {
- this.logger.warn('INVITE received but WebRTC is not supported');
- request.reply(488);
- }
- break;
- case SIP.C.BYE:
- // Out of dialog BYE received
- request.reply(481);
- break;
- case SIP.C.CANCEL:
- session = this.findSession(request);
- if(session) {
- session.receiveRequest(request);
- } else {
- this.logger.warn('received CANCEL request for a non existent session');
- }
- break;
- case SIP.C.ACK:
- /* Absorb it.
- * ACK request without a corresponding Invite Transaction
- * and without To tag.
- */
- break;
- case SIP.C.NOTIFY:
- if (this.configuration.allowLegacyNotifications && this.listeners('notify').length > 0) {
- request.reply(200, null);
- self.emit('notify', {request: request});
- } else {
- request.reply(481, 'Subscription does not exist');
- }
- break;
- default:
- request.reply(405);
- break;
- }
- }
- // In-dialog request
- else {
- dialog = this.findDialog(request);
- if(dialog) {
- if (method === SIP.C.INVITE) {
- new SIP.Transactions.InviteServerTransaction(request, this);
- }
- dialog.receiveRequest(request);
- } else if (method === SIP.C.NOTIFY) {
- session = this.findSession(request);
- earlySubscription = this.findEarlySubscription(request);
- if(session) {
- session.receiveRequest(request);
- } else if(earlySubscription) {
- earlySubscription.receiveRequest(request);
- } else {
- this.logger.warn('received NOTIFY request for a non existent session or subscription');
- request.reply(481, 'Subscription does not exist');
- }
- }
- /* RFC3261 12.2.2
- * Request with to tag, but no matching dialog found.
- * Exception: ACK for an Invite request for which a dialog has not
- * been created.
- */
- else {
- if(method !== SIP.C.ACK) {
- request.reply(481);
- }
- }
- }
- };
- //=================
- // Utils
- //=================
- /**
- * Get the session to which the request belongs to, if any.
- * @private
- * @param {SIP.IncomingRequest} request.
- * @returns {SIP.OutgoingSession|SIP.IncomingSession|null}
- */
- UA.prototype.findSession = function(request) {
- return this.sessions[request.call_id + request.from_tag] ||
- this.sessions[request.call_id + request.to_tag] ||
- null;
- };
- /**
- * Get the dialog to which the request belongs to, if any.
- * @private
- * @param {SIP.IncomingRequest}
- * @returns {SIP.Dialog|null}
- */
- UA.prototype.findDialog = function(request) {
- return this.dialogs[request.call_id + request.from_tag + request.to_tag] ||
- this.dialogs[request.call_id + request.to_tag + request.from_tag] ||
- null;
- };
- /**
- * Get the subscription which has not been confirmed to which the request belongs to, if any
- * @private
- * @param {SIP.IncomingRequest}
- * @returns {SIP.Subscription|null}
- */
- UA.prototype.findEarlySubscription = function(request) {
- return this.earlySubscriptions[request.call_id + request.to_tag + request.getHeader('event')] || null;
- };
- /**
- * Retrieve the next server to which connect.
- * @private
- * @returns {Object} ws_server
- */
- UA.prototype.getNextWsServer = function() {
- // Order servers by weight
- var idx, length, ws_server,
- candidates = [];
- length = this.configuration.wsServers.length;
- for (idx = 0; idx < length; idx++) {
- ws_server = this.configuration.wsServers[idx];
- if (ws_server.status === SIP.Transport.C.STATUS_ERROR) {
- continue;
- } else if (candidates.length === 0) {
- candidates.push(ws_server);
- } else if (ws_server.weight > candidates[0].weight) {
- candidates = [ws_server];
- } else if (ws_server.weight === candidates[0].weight) {
- candidates.push(ws_server);
- }
- }
- idx = Math.floor(Math.random() * candidates.length);
- return candidates[idx];
- };
- /**
- * Close all sessions on transport error.
- * @private
- */
- UA.prototype.closeSessionsOnTransportError = function() {
- var idx;
- // Run _transportError_ for every Session
- for(idx in this.sessions) {
- this.sessions[idx].onTransportError();
- }
- // Call registerContext _onTransportClosed_
- this.registerContext.onTransportClosed();
- };
- UA.prototype.recoverTransport = function(ua) {
- var idx, length, k, nextRetry, count, server;
- ua = ua || this;
- count = ua.transportRecoverAttempts;
- length = ua.configuration.wsServers.length;
- for (idx = 0; idx < length; idx++) {
- ua.configuration.wsServers[idx].status = 0;
- }
- server = ua.getNextWsServer();
- k = Math.floor((Math.random() * Math.pow(2,count)) +1);
- nextRetry = k * ua.configuration.connectionRecoveryMinInterval;
- if (nextRetry > ua.configuration.connectionRecoveryMaxInterval) {
- this.logger.log('time for next connection attempt exceeds connectionRecoveryMaxInterval, resetting counter');
- nextRetry = ua.configuration.connectionRecoveryMinInterval;
- count = 0;
- }
- this.logger.log('next connection attempt in '+ nextRetry +' seconds');
- this.transportRecoveryTimer = SIP.Timers.setTimeout(
- function(){
- ua.transportRecoverAttempts = count + 1;
- new SIP.Transport(ua, server);
- }, nextRetry * 1000);
- };
- function checkAuthenticationFactory (authenticationFactory) {
- if (!(authenticationFactory instanceof Function)) {
- return;
- }
- if (!authenticationFactory.initialize) {
- authenticationFactory.initialize = function initialize () {
- return SIP.Utils.Promise.resolve();
- };
- }
- return authenticationFactory;
- }
- /**
- * Configuration load.
- * @private
- * returns {Boolean}
- */
- UA.prototype.loadConfig = function(configuration) {
- // Settings and default values
- var parameter, value, checked_value, hostportParams, registrarServer,
- settings = {
- /* Host address
- * Value to be set in Via sent_by and host part of Contact FQDN
- */
- viaHost: SIP.Utils.createRandomToken(12) + '.invalid',
- uri: new SIP.URI('sip', 'anonymous.' + SIP.Utils.createRandomToken(6), 'anonymous.invalid', null, null),
- wsServers: [{
- scheme: 'WSS',
- sip_uri: '<sip:edge.sip.onsip.com;transport=ws;lr>',
- status: 0,
- weight: 0,
- ws_uri: 'wss://edge.sip.onsip.com'
- }],
- //Custom Configuration Settings
- custom: {},
- //Display name
- displayName: '',
- // Password
- password: null,
- // Registration parameters
- registerExpires: 600,
- register: true,
- registrarServer: null,
- // Transport related parameters
- wsServerMaxReconnection: 3,
- wsServerReconnectionTimeout: 4,
- connectionRecoveryMinInterval: 2,
- connectionRecoveryMaxInterval: 30,
- keepAliveInterval: 0,
- extraSupported: [],
- usePreloadedRoute: false,
- //string to be inserted into User-Agent request header
- userAgentString: SIP.C.USER_AGENT,
- // Session parameters
- iceCheckingTimeout: 5000,
- noAnswerTimeout: 60,
- stunServers: ['stun:stun.l.google.com:19302'],
- turnServers: [],
- // Logging parameters
- traceSip: false,
- // Hacks
- hackViaTcp: false,
- hackIpInContact: false,
- hackWssInTransport: false,
- hackAllowUnregisteredOptionTags: false,
- hackCleanJitsiSdpImageattr: false,
- hackStripTcp: false,
- contactTransport: 'ws',
- forceRport: false,
- //autostarting
- autostart: true,
- autostop: true,
- //Reliable Provisional Responses
- rel100: SIP.C.supported.UNSUPPORTED,
- // Replaces header (RFC 3891)
- // http://tools.ietf.org/html/rfc3891
- replaces: SIP.C.supported.UNSUPPORTED,
- mediaHandlerFactory: SIP.WebRTC.MediaHandler.defaultFactory,
- authenticationFactory: checkAuthenticationFactory(function authenticationFactory (ua) {
- return new SIP.DigestAuthentication(ua);
- }),
- allowLegacyNotifications: false
- };
- // Pre-Configuration
- function aliasUnderscored (parameter, logger) {
- var underscored = parameter.replace(/([a-z][A-Z])/g, function (m) {
- return m[0] + '_' + m[1].toLowerCase();
- });
- if (parameter === underscored) {
- return;
- }
- var hasParameter = configuration.hasOwnProperty(parameter);
- if (configuration.hasOwnProperty(underscored)) {
- logger.warn(underscored + ' is deprecated, please use ' + parameter);
- if (hasParameter) {
- logger.warn(parameter + ' overriding ' + underscored);
- }
- }
- configuration[parameter] = hasParameter ? configuration[parameter] : configuration[underscored];
- }
- var configCheck = this.getConfigurationCheck();
- // Check Mandatory parameters
- for(parameter in configCheck.mandatory) {
- aliasUnderscored(parameter, this.logger);
- if(!configuration.hasOwnProperty(parameter)) {
- throw new SIP.Exceptions.ConfigurationError(parameter);
- } else {
- value = configuration[parameter];
- checked_value = configCheck.mandatory[parameter](value);
- if (checked_value !== undefined) {
- settings[parameter] = checked_value;
- } else {
- throw new SIP.Exceptions.ConfigurationError(parameter, value);
- }
- }
- }
- SIP.Utils.optionsOverride(configuration, 'rel100', 'reliable', true, this.logger, SIP.C.supported.UNSUPPORTED);
- var emptyArraysAllowed = ['stunServers', 'turnServers'];
- // Check Optional parameters
- for(parameter in configCheck.optional) {
- aliasUnderscored(parameter, this.logger);
- if(configuration.hasOwnProperty(parameter)) {
- value = configuration[parameter];
- // If the parameter value is an empty array, but shouldn't be, apply its default value.
- if (value instanceof Array && value.length === 0 && emptyArraysAllowed.indexOf(parameter) < 0) { continue; }
- // If the parameter value is null, empty string, or undefined then apply its default value.
- if(value === null || value === "" || value === undefined) { continue; }
- // If it's a number with NaN value then also apply its default value.
- // NOTE: JS does not allow "value === NaN", the following does the work:
- else if(typeof(value) === 'number' && isNaN(value)) { continue; }
- checked_value = configCheck.optional[parameter](value);
- if (checked_value !== undefined) {
- settings[parameter] = checked_value;
- } else {
- throw new SIP.Exceptions.ConfigurationError(parameter, value);
- }
- }
- }
- // Sanity Checks
- // Connection recovery intervals
- if(settings.connectionRecoveryMaxInterval < settings.connectionRecoveryMinInterval) {
- throw new SIP.Exceptions.ConfigurationError('connectionRecoveryMaxInterval', settings.connectionRecoveryMaxInterval);
- }
- // Post Configuration Process
- // Allow passing 0 number as displayName.
- if (settings.displayName === 0) {
- settings.displayName = '0';
- }
- // Instance-id for GRUU
- if (!settings.instanceId) {
- settings.instanceId = SIP.Utils.newUUID();
- }
- // sipjsId instance parameter. Static random tag of length 5
- settings.sipjsId = SIP.Utils.createRandomToken(5);
- // String containing settings.uri without scheme and user.
- hostportParams = settings.uri.clone();
- hostportParams.user = null;
- settings.hostportParams = hostportParams.toRaw().replace(/^sip:/i, '');
- /* Check whether authorizationUser is explicitly defined.
- * Take 'settings.uri.user' value if not.
- */
- if (!settings.authorizationUser) {
- settings.authorizationUser = settings.uri.user;
- }
- /* If no 'registrarServer' is set use the 'uri' value without user portion. */
- if (!settings.registrarServer) {
- registrarServer = settings.uri.clone();
- registrarServer.user = null;
- settings.registrarServer = registrarServer;
- }
- // User noAnswerTimeout
- settings.noAnswerTimeout = settings.noAnswerTimeout * 1000;
- // Via Host
- if (settings.hackIpInContact) {
- if (typeof settings.hackIpInContact === 'boolean') {
- settings.viaHost = SIP.Utils.getRandomTestNetIP();
- }
- else if (typeof settings.hackIpInContact === 'string') {
- settings.viaHost = settings.hackIpInContact;
- }
- }
- // Contact transport parameter
- if (settings.hackWssInTransport) {
- settings.contactTransport = 'wss';
- }
- this.contact = {
- pub_gruu: null,
- temp_gruu: null,
- uri: new SIP.URI('sip', SIP.Utils.createRandomToken(8), settings.viaHost, null, {transport: settings.contactTransport}),
- toString: function(options){
- options = options || {};
- var
- anonymous = options.anonymous || null,
- outbound = options.outbound || null,
- contact = '<';
- if (anonymous) {
- contact += (this.temp_gruu || ('sip:[email protected];transport='+settings.contactTransport)).toString();
- } else {
- contact += (this.pub_gruu || this.uri).toString();
- }
- if (outbound) {
- contact += ';ob';
- }
- contact += '>';
- return contact;
- }
- };
- // media overrides mediaConstraints
- SIP.Utils.optionsOverride(settings, 'media', 'mediaConstraints', true, this.logger);
- var skeleton = {};
- // Fill the value of the configuration_skeleton
- for(parameter in settings) {
- skeleton[parameter] = {
- value: settings[parameter],
- writable: (parameter === 'register' || parameter === 'custom'),
- configurable: false
- };
- }
- Object.defineProperties(this.configuration, skeleton);
- this.logger.log('configuration parameters after validation:');
- for(parameter in settings) {
- switch(parameter) {
- case 'uri':
- case 'registrarServer':
- case 'mediaHandlerFactory':
- this.logger.log('· ' + parameter + ': ' + settings[parameter]);
- break;
- case 'password':
- this.logger.log('· ' + parameter + ': ' + 'NOT SHOWN');
- break;
- default:
- this.logger.log('· ' + parameter + ': ' + JSON.stringify(settings[parameter]));
- }
- }
- return;
- };
- /**
- * Configuration checker.
- * @private
- * @return {Boolean}
- */
- UA.prototype.getConfigurationCheck = function () {
- return {
- mandatory: {
- },
- optional: {
- uri: function(uri) {
- var parsed;
- if (!(/^sip:/i).test(uri)) {
- uri = SIP.C.SIP + ':' + uri;
- }
- parsed = SIP.URI.parse(uri);
- if(!parsed) {
- return;
- } else if(!parsed.user) {
- return;
- } else {
- return parsed;
- }
- },
- //Note: this function used to call 'this.logger.error' but calling 'this' with anything here is invalid
- wsServers: function(wsServers) {
- var idx, length, url;
- /* Allow defining wsServers parameter as:
- * String: "host"
- * Array of Strings: ["host1", "host2"]
- * Array of Objects: [{ws_uri:"host1", weight:1}, {ws_uri:"host2", weight:0}]
- * Array of Objects and Strings: [{ws_uri:"host1"}, "host2"]
- */
- if (typeof wsServers === 'string') {
- wsServers = [{ws_uri: wsServers}];
- } else if (wsServers instanceof Array) {
- length = wsServers.length;
- for (idx = 0; idx < length; idx++) {
- if (typeof wsServers[idx] === 'string'){
- wsServers[idx] = {ws_uri: wsServers[idx]};
- }
- }
- } else {
- return;
- }
- if (wsServers.length === 0) {
- return false;
- }
- length = wsServers.length;
- for (idx = 0; idx < length; idx++) {
- if (!wsServers[idx].ws_uri) {
- return;
- }
- if (wsServers[idx].weight && !Number(wsServers[idx].weight)) {
- return;
- }
- url = SIP.Grammar.parse(wsServers[idx].ws_uri, 'absoluteURI');
- if(url === -1) {
- return;
- } else if(['wss', 'ws', 'udp'].indexOf(url.scheme) < 0) {
- return;
- } else {
- wsServers[idx].sip_uri = '<sip:' + url.host + (url.port ? ':' + url.port : '') + ';transport=' + url.scheme.replace(/^wss$/i, 'ws') + ';lr>';
- if (!wsServers[idx].weight) {
- wsServers[idx].weight = 0;
- }
- wsServers[idx].status = 0;
- wsServers[idx].scheme = url.scheme.toUpperCase();
- }
- }
- return wsServers;
- },
- authorizationUser: function(authorizationUser) {
- if(SIP.Grammar.parse('"'+ authorizationUser +'"', 'quoted_string') === -1) {
- return;
- } else {
- return authorizationUser;
- }
- },
- connectionRecoveryMaxInterval: function(connectionRecoveryMaxInterval) {
- var value;
- if(SIP.Utils.isDecimal(connectionRecoveryMaxInterval)) {
- value = Number(connectionRecoveryMaxInterval);
- if(value > 0) {
- return value;
- }
- }
- },
- connectionRecoveryMinInterval: function(connectionRecoveryMinInterval) {
- var value;
- if(SIP.Utils.isDecimal(connectionRecoveryMinInterval)) {
- value = Number(connectionRecoveryMinInterval);
- if(value > 0) {
- return value;
- }
- }
- },
- displayName: function(displayName) {
- if(SIP.Grammar.parse('"' + displayName + '"', 'displayName') === -1) {
- return;
- } else {
- return displayName;
- }
- },
- hackViaTcp: function(hackViaTcp) {
- if (typeof hackViaTcp === 'boolean') {
- return hackViaTcp;
- }
- },
- hackIpInContact: function(hackIpInContact) {
- if (typeof hackIpInContact === 'boolean') {
- return hackIpInContact;
- }
- else if (typeof hackIpInContact === 'string' && SIP.Grammar.parse(hackIpInContact, 'host') !== -1) {
- return hackIpInContact;
- }
- },
- iceCheckingTimeout: function(iceCheckingTimeout) {
- if(SIP.Utils.isDecimal(iceCheckingTimeout)) {
- return Math.max(500, iceCheckingTimeout);
- }
- },
- hackWssInTransport: function(hackWssInTransport) {
- if (typeof hackWssInTransport === 'boolean') {
- return hackWssInTransport;
- }
- },
- hackAllowUnregisteredOptionTags: function(hackAllowUnregisteredOptionTags) {
- if (typeof hackAllowUnregisteredOptionTags === 'boolean') {
- return hackAllowUnregisteredOptionTags;
- }
- },
- hackCleanJitsiSdpImageattr: function(hackCleanJitsiSdpImageattr) {
- if (typeof hackCleanJitsiSdpImageattr === 'boolean') {
- return hackCleanJitsiSdpImageattr;
- }
- },
- hackStripTcp: function(hackStripTcp) {
- if (typeof hackStripTcp === 'boolean') {
- return hackStripTcp;
- }
- },
- contactTransport: function(contactTransport) {
- if (typeof contactTransport === 'string') {
- return contactTransport;
- }
- },
- forceRport: function(forceRport) {
- if (typeof forceRport === 'boolean') {
- return forceRport;
- }
- },
- instanceId: function(instanceId) {
- if(typeof instanceId !== 'string') {
- return;
- }
- if ((/^uuid:/i.test(instanceId))) {
- instanceId = instanceId.substr(5);
- }
- if(SIP.Grammar.parse(instanceId, 'uuid') === -1) {
- return;
- } else {
- return instanceId;
- }
- },
- keepAliveInterval: function(keepAliveInterval) {
- var value;
- if (SIP.Utils.isDecimal(keepAliveInterval)) {
- value = Number(keepAliveInterval);
- if (value > 0) {
- return value;
- }
- }
- },
- extraSupported: function(optionTags) {
- var idx, length;
- if (!(optionTags instanceof Array)) {
- return;
- }
- length = optionTags.length;
- for (idx = 0; idx < length; idx++) {
- if (typeof optionTags[idx] !== 'string') {
- return;
- }
- }
- return optionTags;
- },
- noAnswerTimeout: function(noAnswerTimeout) {
- var value;
- if (SIP.Utils.isDecimal(noAnswerTimeout)) {
- value = Number(noAnswerTimeout);
- if (value > 0) {
- return value;
- }
- }
- },
- password: function(password) {
- return String(password);
- },
- rel100: function(rel100) {
- if(rel100 === SIP.C.supported.REQUIRED) {
- return SIP.C.supported.REQUIRED;
- } else if (rel100 === SIP.C.supported.SUPPORTED) {
- return SIP.C.supported.SUPPORTED;
- } else {
- return SIP.C.supported.UNSUPPORTED;
- }
- },
- replaces: function(replaces) {
- if(replaces === SIP.C.supported.REQUIRED) {
- return SIP.C.supported.REQUIRED;
- } else if (replaces === SIP.C.supported.SUPPORTED) {
- return SIP.C.supported.SUPPORTED;
- } else {
- return SIP.C.supported.UNSUPPORTED;
- }
- },
- register: function(register) {
- if (typeof register === 'boolean') {
- return register;
- }
- },
- registerExpires: function(registerExpires) {
- var value;
- if (SIP.Utils.isDecimal(registerExpires)) {
- value = Number(registerExpires);
- if (value > 0) {
- return value;
- }
- }
- },
- registrarServer: function(registrarServer) {
- var parsed;
- if(typeof registrarServer !== 'string') {
- return;
- }
- if (!/^sip:/i.test(registrarServer)) {
- registrarServer = SIP.C.SIP + ':' + registrarServer;
- }
- parsed = SIP.URI.parse(registrarServer);
- if(!parsed) {
- return;
- } else if(parsed.user) {
- return;
- } else {
- return parsed;
- }
- },
- stunServers: function(stunServers) {
- var idx, length, stun_server;
- if (typeof stunServers === 'string') {
- stunServers = [stunServers];
- } else if (!(stunServers instanceof Array)) {
- return;
- }
- length = stunServers.length;
- for (idx = 0; idx < length; idx++) {
- stun_server = stunServers[idx];
- if (!(/^stuns?:/.test(stun_server))) {
- stun_server = 'stun:' + stun_server;
- }
- if(SIP.Grammar.parse(stun_server, 'stun_URI') === -1) {
- return;
- } else {
- stunServers[idx] = stun_server;
- }
- }
- return stunServers;
- },
- traceSip: function(traceSip) {
- if (typeof traceSip === 'boolean') {
- return traceSip;
- }
- },
- turnServers: function(turnServers) {
- var idx, jdx, length, turn_server, num_turn_server_urls, url;
- if (turnServers instanceof Array) {
- // Do nothing
- } else {
- turnServers = [turnServers];
- }
- length = turnServers.length;
- for (idx = 0; idx < length; idx++) {
- turn_server = turnServers[idx];
- //Backwards compatibility: Allow defining the turn_server url with the 'server' property.
- if (turn_server.server) {
- turn_server.urls = [turn_server.server];
- }
- if (!turn_server.urls) {
- return;
- }
- if (turn_server.urls instanceof Array) {
- num_turn_server_urls = turn_server.urls.length;
- } else {
- turn_server.urls = [turn_server.urls];
- num_turn_server_urls = 1;
- }
- for (jdx = 0; jdx < num_turn_server_urls; jdx++) {
- url = turn_server.urls[jdx];
- if (!(/^turns?:/.test(url))) {
- url = 'turn:' + url;
- }
- if(SIP.Grammar.parse(url, 'turn_URI') === -1) {
- return;
- }
- }
- }
- return turnServers;
- },
- rtcpMuxPolicy: function(rtcpMuxPolicy) {
- if (typeof rtcpMuxPolicy === 'string') {
- return rtcpMuxPolicy;
- }
- },
- userAgentString: function(userAgentString) {
- if (typeof userAgentString === 'string') {
- return userAgentString;
- }
- },
- usePreloadedRoute: function(usePreloadedRoute) {
- if (typeof usePreloadedRoute === 'boolean') {
- return usePreloadedRoute;
- }
- },
- wsServerMaxReconnection: function(wsServerMaxReconnection) {
- var value;
- if (SIP.Utils.isDecimal(wsServerMaxReconnection)) {
- value = Number(wsServerMaxReconnection);
- if (value > 0) {
- return value;
- }
- }
- },
- wsServerReconnectionTimeout: function(wsServerReconnectionTimeout) {
- var value;
- if (SIP.Utils.isDecimal(wsServerReconnectionTimeout)) {
- value = Number(wsServerReconnectionTimeout);
- if (value > 0) {
- return value;
- }
- }
- },
- autostart: function(autostart) {
- if (typeof autostart === 'boolean') {
- return autostart;
- }
- },
- autostop: function(autostop) {
- if (typeof autostop === 'boolean') {
- return autostop;
- }
- },
- mediaHandlerFactory: function(mediaHandlerFactory) {
- if (mediaHandlerFactory instanceof Function) {
- var promisifiedFactory = function promisifiedFactory () {
- var mediaHandler = mediaHandlerFactory.apply(this, arguments);
- function patchMethod (methodName) {
- var method = mediaHandler[methodName];
- if (method.length > 1) {
- var callbacksFirst = methodName === 'getDescription';
- mediaHandler[methodName] = SIP.Utils.promisify(mediaHandler, methodName, callbacksFirst);
- }
- }
- patchMethod('getDescription');
- patchMethod('setDescription');
- return mediaHandler;
- };
- promisifiedFactory.isSupported = mediaHandlerFactory.isSupported;
- return promisifiedFactory;
- }
- },
- authenticationFactory: checkAuthenticationFactory,
- allowLegacyNotifications: function(allowLegacyNotifications) {
- if (typeof allowLegacyNotifications === 'boolean') {
- return allowLegacyNotifications;
- }
- },
- custom: function(custom) {
- if (typeof custom === 'object') {
- return custom;
- }
- }
- }
- };
- };
- UA.C = C;
- SIP.UA = UA;
- };
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
- },{}],30:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview SIP URI
- */
- /**
- * @augments SIP
- * @class Class creating a SIP URI.
- *
- * @param {String} [scheme]
- * @param {String} [user]
- * @param {String} host
- * @param {String} [port]
- * @param {Object} [parameters]
- * @param {Object} [headers]
- *
- */
- module.exports = function (SIP) {
- var URI;
- URI = function(scheme, user, host, port, parameters, headers) {
- var param, header, raw, normal;
- // Checks
- if(!host) {
- throw new TypeError('missing or invalid "host" parameter');
- }
- // Initialize parameters
- scheme = scheme || SIP.C.SIP;
- this.parameters = {};
- this.headers = {};
- for (param in parameters) {
- this.setParam(param, parameters[param]);
- }
- for (header in headers) {
- this.setHeader(header, headers[header]);
- }
- // Raw URI
- raw = {
- scheme: scheme,
- user: user,
- host: host,
- port: port
- };
- // Normalized URI
- normal = {
- scheme: scheme.toLowerCase(),
- user: user,
- host: host.toLowerCase(),
- port: port
- };
- Object.defineProperties(this, {
- _normal: {
- get: function() { return normal; }
- },
- _raw: {
- get: function() { return raw; }
- },
- scheme: {
- get: function() { return normal.scheme; },
- set: function(value) {
- raw.scheme = value;
- normal.scheme = value.toLowerCase();
- }
- },
- user: {
- get: function() { return normal.user; },
- set: function(value) {
- normal.user = raw.user = value;
- }
- },
- host: {
- get: function() { return normal.host; },
- set: function(value) {
- raw.host = value;
- normal.host = value.toLowerCase();
- }
- },
- aor: {
- get: function() { return normal.user + '@' + normal.host; }
- },
- port: {
- get: function() { return normal.port; },
- set: function(value) {
- normal.port = raw.port = value === 0 ? value : (parseInt(value,10) || null);
- }
- }
- });
- };
- URI.prototype = {
- setParam: function(key, value) {
- if(key) {
- this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString().toLowerCase();
- }
- },
- getParam: function(key) {
- if(key) {
- return this.parameters[key.toLowerCase()];
- }
- },
- hasParam: function(key) {
- if(key) {
- return (this.parameters.hasOwnProperty(key.toLowerCase()) && true) || false;
- }
- },
- deleteParam: function(parameter) {
- var value;
- parameter = parameter.toLowerCase();
- if (this.parameters.hasOwnProperty(parameter)) {
- value = this.parameters[parameter];
- delete this.parameters[parameter];
- return value;
- }
- },
- clearParams: function() {
- this.parameters = {};
- },
- setHeader: function(name, value) {
- this.headers[SIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value];
- },
- getHeader: function(name) {
- if(name) {
- return this.headers[SIP.Utils.headerize(name)];
- }
- },
- hasHeader: function(name) {
- if(name) {
- return (this.headers.hasOwnProperty(SIP.Utils.headerize(name)) && true) || false;
- }
- },
- deleteHeader: function(header) {
- var value;
- header = SIP.Utils.headerize(header);
- if(this.headers.hasOwnProperty(header)) {
- value = this.headers[header];
- delete this.headers[header];
- return value;
- }
- },
- clearHeaders: function() {
- this.headers = {};
- },
- clone: function() {
- return new URI(
- this._raw.scheme,
- this._raw.user,
- this._raw.host,
- this._raw.port,
- JSON.parse(JSON.stringify(this.parameters)),
- JSON.parse(JSON.stringify(this.headers)));
- },
- toRaw: function() {
- return this._toString(this._raw);
- },
- toString: function() {
- return this._toString(this._normal);
- },
- _toString: function(uri) {
- var header, parameter, idx, uriString, headers = [];
- uriString = uri.scheme + ':';
- // add slashes if it's not a sip(s) URI
- if (!uri.scheme.toLowerCase().match("^sips?$")) {
- uriString += "//";
- }
- if (uri.user) {
- uriString += SIP.Utils.escapeUser(uri.user) + '@';
- }
- uriString += uri.host;
- if (uri.port || uri.port === 0) {
- uriString += ':' + uri.port;
- }
- for (parameter in this.parameters) {
- uriString += ';' + parameter;
- if (this.parameters[parameter] !== null) {
- uriString += '='+ this.parameters[parameter];
- }
- }
- for(header in this.headers) {
- for(idx in this.headers[header]) {
- headers.push(header + '=' + this.headers[header][idx]);
- }
- }
- if (headers.length > 0) {
- uriString += '?' + headers.join('&');
- }
- return uriString;
- }
- };
- /**
- * Parse the given string and returns a SIP.URI instance or undefined if
- * it is an invalid URI.
- * @public
- * @param {String} uri
- */
- URI.parse = function(uri) {
- uri = SIP.Grammar.parse(uri,'SIP_URI');
- if (uri !== -1) {
- return uri;
- } else {
- return undefined;
- }
- };
- SIP.URI = URI;
- };
- },{}],31:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview Utils
- */
- module.exports = function (SIP, environment) {
- var Utils;
- Utils= {
- Promise: environment.Promise,
- defer: function defer () {
- var deferred = {};
- deferred.promise = new Utils.Promise(function (resolve, reject) {
- deferred.resolve = resolve;
- deferred.reject = reject;
- });
- return deferred;
- },
- promisify: function promisify (object, methodName, callbacksFirst) {
- var oldMethod = object[methodName];
- return function promisifiedMethod (arg, onSuccess, onFailure) {
- return new Utils.Promise(function (resolve, reject) {
- var oldArgs = [arg, resolve, reject];
- if (callbacksFirst) {
- oldArgs = [resolve, reject, arg];
- }
- oldMethod.apply(object, oldArgs);
- }).then(onSuccess, onFailure);
- };
- },
- augment: function (object, constructor, args, override) {
- var idx, proto;
- // Add public properties from constructor's prototype onto object
- proto = constructor.prototype;
- for (idx in proto) {
- if (override || object[idx] === undefined) {
- object[idx] = proto[idx];
- }
- }
- // Construct the object as though it were just created by constructor
- constructor.apply(object, args);
- },
- optionsOverride: function (options, winner, loser, isDeprecated, logger, defaultValue) {
- if (isDeprecated && options[loser]) {
- logger.warn(loser + ' is deprecated, please use ' + winner + ' instead');
- }
- if (options[winner] && options[loser]) {
- logger.warn(winner + ' overriding ' + loser);
- }
- options[winner] = options[winner] || options[loser] || defaultValue;
- },
- str_utf8_length: function(string) {
- return encodeURIComponent(string).replace(/%[A-F\d]{2}/g, 'U').length;
- },
- generateFakeSDP: function(body) {
- if (!body) {
- return;
- }
- var start = body.indexOf('o=');
- var end = body.indexOf('\r\n', start);
- return 'v=0\r\n' + body.slice(start, end) + '\r\ns=-\r\nt=0 0\r\nc=IN IP4 0.0.0.0';
- },
- isFunction: function(fn) {
- if (fn !== undefined) {
- return Object.prototype.toString.call(fn) === '[object Function]';
- } else {
- return false;
- }
- },
- isDecimal: function (num) {
- return !isNaN(num) && (parseFloat(num) === parseInt(num,10));
- },
- createRandomToken: function(size, base) {
- var i, r,
- token = '';
- base = base || 32;
- for( i=0; i < size; i++ ) {
- r = Math.random() * base|0;
- token += r.toString(base);
- }
- return token;
- },
- newTag: function() {
- return SIP.Utils.createRandomToken(SIP.UA.C.TAG_LENGTH);
- },
- // http://stackoverflow.com/users/109538/broofa
- newUUID: function() {
- var UUID = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
- return v.toString(16);
- });
- return UUID;
- },
- hostType: function(host) {
- if (!host) {
- return;
- } else {
- host = SIP.Grammar.parse(host,'host');
- if (host !== -1) {
- return host.host_type;
- }
- }
- },
- /**
- * Normalize SIP URI.
- * NOTE: It does not allow a SIP URI without username.
- * Accepts 'sip', 'sips' and 'tel' URIs and convert them into 'sip'.
- * Detects the domain part (if given) and properly hex-escapes the user portion.
- * If the user portion has only 'tel' number symbols the user portion is clean of 'tel' visual separators.
- * @private
- * @param {String} target
- * @param {String} [domain]
- */
- normalizeTarget: function(target, domain) {
- var uri, target_array, target_user, target_domain;
- // If no target is given then raise an error.
- if (!target) {
- return;
- // If a SIP.URI instance is given then return it.
- } else if (target instanceof SIP.URI) {
- return target;
- // If a string is given split it by '@':
- // - Last fragment is the desired domain.
- // - Otherwise append the given domain argument.
- } else if (typeof target === 'string') {
- target_array = target.split('@');
- switch(target_array.length) {
- case 1:
- if (!domain) {
- return;
- }
- target_user = target;
- target_domain = domain;
- break;
- case 2:
- target_user = target_array[0];
- target_domain = target_array[1];
- break;
- default:
- target_user = target_array.slice(0, target_array.length-1).join('@');
- target_domain = target_array[target_array.length-1];
- }
- // Remove the URI scheme (if present).
- target_user = target_user.replace(/^(sips?|tel):/i, '');
- // Remove 'tel' visual separators if the user portion just contains 'tel' number symbols.
- if (/^[\-\.\(\)]*\+?[0-9\-\.\(\)]+$/.test(target_user)) {
- target_user = target_user.replace(/[\-\.\(\)]/g, '');
- }
- // Build the complete SIP URI.
- target = SIP.C.SIP + ':' + SIP.Utils.escapeUser(target_user) + '@' + target_domain;
- // Finally parse the resulting URI.
- if (uri = SIP.URI.parse(target)) {
- return uri;
- } else {
- return;
- }
- } else {
- return;
- }
- },
- /**
- * Hex-escape a SIP URI user.
- * @private
- * @param {String} user
- */
- escapeUser: function(user) {
- // Don't hex-escape ':' (%3A), '+' (%2B), '?' (%3F"), '/' (%2F).
- return encodeURIComponent(decodeURIComponent(user)).replace(/%3A/ig, ':').replace(/%2B/ig, '+').replace(/%3F/ig, '?').replace(/%2F/ig, '/');
- },
- headerize: function(string) {
- var exceptions = {
- 'Call-Id': 'Call-ID',
- 'Cseq': 'CSeq',
- 'Min-Se': 'Min-SE',
- 'Rack': 'RAck',
- 'Rseq': 'RSeq',
- 'Www-Authenticate': 'WWW-Authenticate'
- },
- name = string.toLowerCase().replace(/_/g,'-').split('-'),
- hname = '',
- parts = name.length, part;
- for (part = 0; part < parts; part++) {
- if (part !== 0) {
- hname +='-';
- }
- hname += name[part].charAt(0).toUpperCase()+name[part].substring(1);
- }
- if (exceptions[hname]) {
- hname = exceptions[hname];
- }
- return hname;
- },
- sipErrorCause: function(status_code) {
- var cause;
- for (cause in SIP.C.SIP_ERROR_CAUSES) {
- if (SIP.C.SIP_ERROR_CAUSES[cause].indexOf(status_code) !== -1) {
- return SIP.C.causes[cause];
- }
- }
- return SIP.C.causes.SIP_FAILURE_CODE;
- },
- getReasonPhrase: function getReasonPhrase (code, specific) {
- return specific || SIP.C.REASON_PHRASE[code] || '';
- },
- getReasonHeaderValue: function getReasonHeaderValue (code, reason) {
- reason = SIP.Utils.getReasonPhrase(code, reason);
- return 'SIP ;cause=' + code + ' ;text="' + reason + '"';
- },
- getCancelReason: function getCancelReason (code, reason) {
- if (code && code < 200 || code > 699) {
- throw new TypeError('Invalid status_code: ' + code);
- } else if (code) {
- return SIP.Utils.getReasonHeaderValue(code, reason);
- }
- },
- buildStatusLine: function buildStatusLine (code, reason) {
- code = code || null;
- reason = reason || null;
- // Validate code and reason values
- if (!code || (code < 100 || code > 699)) {
- throw new TypeError('Invalid status_code: '+ code);
- } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) {
- throw new TypeError('Invalid reason_phrase: '+ reason);
- }
- reason = Utils.getReasonPhrase(code, reason);
- return 'SIP/2.0 ' + code + ' ' + reason + '\r\n';
- },
- /**
- * Generate a random Test-Net IP (http://tools.ietf.org/html/rfc5735)
- * @private
- */
- getRandomTestNetIP: function() {
- function getOctet(from,to) {
- return Math.floor(Math.random()*(to-from+1)+from);
- }
- return '192.0.2.' + getOctet(1, 254);
- },
- // MD5 (Message-Digest Algorithm) http://www.webtoolkit.info
- calculateMD5: function(string) {
- function RotateLeft(lValue, iShiftBits) {
- return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
- }
- function AddUnsigned(lX,lY) {
- var lX4,lY4,lX8,lY8,lResult;
- lX8 = (lX & 0x80000000);
- lY8 = (lY & 0x80000000);
- lX4 = (lX & 0x40000000);
- lY4 = (lY & 0x40000000);
- lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
- if (lX4 & lY4) {
- return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
- }
- if (lX4 | lY4) {
- if (lResult & 0x40000000) {
- return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
- } else {
- return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
- }
- } else {
- return (lResult ^ lX8 ^ lY8);
- }
- }
- function F(x,y,z) {
- return (x & y) | ((~x) & z);
- }
- function G(x,y,z) {
- return (x & z) | (y & (~z));
- }
- function H(x,y,z) {
- return (x ^ y ^ z);
- }
- function I(x,y,z) {
- return (y ^ (x | (~z)));
- }
- function FF(a,b,c,d,x,s,ac) {
- a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
- return AddUnsigned(RotateLeft(a, s), b);
- }
- function GG(a,b,c,d,x,s,ac) {
- a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
- return AddUnsigned(RotateLeft(a, s), b);
- }
- function HH(a,b,c,d,x,s,ac) {
- a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
- return AddUnsigned(RotateLeft(a, s), b);
- }
- function II(a,b,c,d,x,s,ac) {
- a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
- return AddUnsigned(RotateLeft(a, s), b);
- }
- function ConvertToWordArray(string) {
- var lWordCount;
- var lMessageLength = string.length;
- var lNumberOfWords_temp1=lMessageLength + 8;
- var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
- var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
- var lWordArray=Array(lNumberOfWords-1);
- var lBytePosition = 0;
- var lByteCount = 0;
- while ( lByteCount < lMessageLength ) {
- lWordCount = (lByteCount-(lByteCount % 4))/4;
- lBytePosition = (lByteCount % 4)*8;
- lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
- lByteCount++;
- }
- lWordCount = (lByteCount-(lByteCount % 4))/4;
- lBytePosition = (lByteCount % 4)*8;
- lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
- lWordArray[lNumberOfWords-2] = lMessageLength<<3;
- lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
- return lWordArray;
- }
- function WordToHex(lValue) {
- var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
- for (lCount = 0;lCount<=3;lCount++) {
- lByte = (lValue>>>(lCount*8)) & 255;
- WordToHexValue_temp = "0" + lByte.toString(16);
- WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
- }
- return WordToHexValue;
- }
- function Utf8Encode(string) {
- string = string.replace(/\r\n/g,"\n");
- var utftext = "";
- for (var n = 0; n < string.length; n++) {
- var c = string.charCodeAt(n);
- if (c < 128) {
- utftext += String.fromCharCode(c);
- }
- else if((c > 127) && (c < 2048)) {
- utftext += String.fromCharCode((c >> 6) | 192);
- utftext += String.fromCharCode((c & 63) | 128);
- }
- else {
- utftext += String.fromCharCode((c >> 12) | 224);
- utftext += String.fromCharCode(((c >> 6) & 63) | 128);
- utftext += String.fromCharCode((c & 63) | 128);
- }
- }
- return utftext;
- }
- var x=[];
- var k,AA,BB,CC,DD,a,b,c,d;
- var S11=7, S12=12, S13=17, S14=22;
- var S21=5, S22=9 , S23=14, S24=20;
- var S31=4, S32=11, S33=16, S34=23;
- var S41=6, S42=10, S43=15, S44=21;
- string = Utf8Encode(string);
- x = ConvertToWordArray(string);
- a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
- for (k=0;k<x.length;k+=16) {
- AA=a; BB=b; CC=c; DD=d;
- a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
- d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
- c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
- b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
- a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
- d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
- c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
- b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
- a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
- d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
- c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
- b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
- a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
- d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
- c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
- b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
- a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
- d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
- c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
- b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
- a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
- d=GG(d,a,b,c,x[k+10],S22,0x2441453);
- c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
- b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
- a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
- d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
- c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
- b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
- a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
- d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
- c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
- b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
- a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
- d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
- c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
- b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
- a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
- d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
- c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
- b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
- a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
- d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
- c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
- b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
- a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
- d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
- c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
- b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
- a=II(a,b,c,d,x[k+0], S41,0xF4292244);
- d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
- c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
- b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
- a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
- d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
- c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
- b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
- a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
- d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
- c=II(c,d,a,b,x[k+6], S43,0xA3014314);
- b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
- a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
- d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
- c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
- b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
- a=AddUnsigned(a,AA);
- b=AddUnsigned(b,BB);
- c=AddUnsigned(c,CC);
- d=AddUnsigned(d,DD);
- }
- var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
- return temp.toLowerCase();
- }
- };
- SIP.Utils = Utils;
- };
- },{}],32:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview WebRTC
- */
- module.exports = function (SIP, environment) {
- var WebRTC;
- WebRTC = {};
- WebRTC.MediaHandler = require('./WebRTC/MediaHandler')(SIP);
- WebRTC.MediaStreamManager = require('./WebRTC/MediaStreamManager')(SIP, environment);
- var _isSupported;
- WebRTC.isSupported = function () {
- if (_isSupported !== undefined) {
- return _isSupported;
- }
- WebRTC.MediaStream = environment.MediaStream;
- WebRTC.getUserMedia = environment.getUserMedia;
- WebRTC.RTCPeerConnection = environment.RTCPeerConnection;
- WebRTC.RTCSessionDescription = environment.RTCSessionDescription;
- if (WebRTC.RTCPeerConnection && WebRTC.RTCSessionDescription) {
- if (WebRTC.getUserMedia) {
- WebRTC.getUserMedia = SIP.Utils.promisify(environment, 'getUserMedia');
- }
- _isSupported = true;
- }
- else {
- _isSupported = false;
- }
- return _isSupported;
- };
- return WebRTC;
- };
- },{"./WebRTC/MediaHandler":33,"./WebRTC/MediaStreamManager":34}],33:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview MediaHandler
- */
- /* MediaHandler
- * @class PeerConnection helper Class.
- * @param {SIP.Session} session
- * @param {Object} [options]
- * @param {SIP.WebRTC.MediaStreamManager} [options.mediaStreamManager]
- * The MediaStreamManager to acquire/release streams from/to.
- * If not provided, a default MediaStreamManager will be used.
- */
- module.exports = function (SIP) {
- var MediaHandler = function(session, options) {
- options = options || {};
- this.logger = session.ua.getLogger('sip.invitecontext.mediahandler', session.id);
- this.session = session;
- this.localMedia = null;
- this.ready = true;
- this.mediaStreamManager = options.mediaStreamManager || new SIP.WebRTC.MediaStreamManager(this.logger);
- this.audioMuted = false;
- this.videoMuted = false;
- this.local_hold = false;
- this.remote_hold = false;
- // old init() from here on
- var servers = this.prepareIceServers(options.stunServers, options.turnServers);
- this.RTCConstraints = options.RTCConstraints || {};
- this.initPeerConnection(servers);
- function selfEmit(mh, event) {
- if (mh.mediaStreamManager.on) {
- mh.mediaStreamManager.on(event, function () {
- mh.emit.apply(mh, [event].concat(Array.prototype.slice.call(arguments)));
- });
- }
- }
- selfEmit(this, 'userMediaRequest');
- selfEmit(this, 'userMedia');
- selfEmit(this, 'userMediaFailed');
- };
- MediaHandler.defaultFactory = function defaultFactory (session, options) {
- return new MediaHandler(session, options);
- };
- MediaHandler.defaultFactory.isSupported = function () {
- return SIP.WebRTC.isSupported();
- };
- MediaHandler.prototype = Object.create(SIP.MediaHandler.prototype, {
- // Functions the session can use
- isReady: {writable: true, value: function isReady () {
- return this.ready;
- }},
- close: {writable: true, value: function close () {
- this.logger.log('closing PeerConnection');
- this._remoteStreams = [];
- // have to check signalingState since this.close() gets called multiple times
- // TODO figure out why that happens
- if(this.peerConnection && this.peerConnection.signalingState !== 'closed') {
- this.peerConnection.close();
- if(this.localMedia) {
- this.mediaStreamManager.release(this.localMedia);
- }
- }
- }},
- /**
- * @param {SIP.WebRTC.MediaStream | (getUserMedia constraints)} [mediaHint]
- * the MediaStream (or the constraints describing it) to be used for the session
- */
- getDescription: {writable: true, value: function getDescription (mediaHint) {
- var self = this;
- var acquire = self.mediaStreamManager.acquire;
- if (acquire.length > 1) {
- acquire = SIP.Utils.promisify(this.mediaStreamManager, 'acquire', true);
- }
- mediaHint = mediaHint || {};
- if (mediaHint.dataChannel === true) {
- mediaHint.dataChannel = {};
- }
- this.mediaHint = mediaHint;
- /*
- * 1. acquire streams (skip if MediaStreams passed in)
- * 2. addStreams
- * 3. createOffer/createAnswer
- */
- var streamPromise;
- if (self.localMedia) {
- self.logger.log('already have local media');
- streamPromise = SIP.Utils.Promise.resolve(self.localMedia);
- }
- else {
- self.logger.log('acquiring local media');
- streamPromise = acquire.call(self.mediaStreamManager, mediaHint)
- .then(function acquireSucceeded(streams) {
- self.logger.log('acquired local media streams');
- self.localMedia = streams;
- self.session.connecting();
- return streams;
- }, function acquireFailed(err) {
- self.logger.error('unable to acquire streams');
- self.logger.error(err);
- self.session.connecting();
- throw err;
- })
- .then(this.addStreams.bind(this))
- ;
- }
- return streamPromise
- .then(function streamAdditionSucceeded() {
- if (self.hasOffer('remote')) {
- self.peerConnection.ondatachannel = function (evt) {
- self.dataChannel = evt.channel;
- self.emit('dataChannel', self.dataChannel);
- };
- } else if (mediaHint.dataChannel &&
- self.peerConnection.createDataChannel) {
- self.dataChannel = self.peerConnection.createDataChannel(
- 'sipjs',
- mediaHint.dataChannel
- );
- self.emit('dataChannel', self.dataChannel);
- }
- self.render();
- return self.createOfferOrAnswer(self.RTCConstraints);
- })
- .then(function(sdp) {
- sdp = SIP.Hacks.Firefox.hasMissingCLineInSDP(sdp);
- if (self.local_hold) {
- // Don't receive media
- // TODO - This will break for media streams with different directions.
- if (!(/a=(sendrecv|sendonly|recvonly|inactive)/).test(sdp)) {
- sdp = sdp.replace(/(m=[^\r]*\r\n)/g, '$1a=sendonly\r\n');
- } else {
- sdp = sdp.replace(/a=sendrecv\r\n/g, 'a=sendonly\r\n');
- sdp = sdp.replace(/a=recvonly\r\n/g, 'a=inactive\r\n');
- }
- }
- return {
- body: sdp,
- contentType: 'application/sdp'
- };
- })
- ;
- }},
- /**
- * Check if a SIP message contains a session description.
- * @param {SIP.SIPMessage} message
- * @returns {boolean}
- */
- hasDescription: {writeable: true, value: function hasDescription (message) {
- return message.getHeader('Content-Type') === 'application/sdp' && !!message.body;
- }},
- /**
- * Set the session description contained in a SIP message.
- * @param {SIP.SIPMessage} message
- * @returns {Promise}
- */
- setDescription: {writable: true, value: function setDescription (message) {
- var self = this;
- var sdp = message.body;
- this.remote_hold = /a=(sendonly|inactive)/.test(sdp);
- sdp = SIP.Hacks.Firefox.cannotHandleExtraWhitespace(sdp);
- sdp = SIP.Hacks.AllBrowsers.maskDtls(sdp);
- var rawDescription = {
- type: this.hasOffer('local') ? 'answer' : 'offer',
- sdp: sdp
- };
- this.emit('setDescription', rawDescription);
- var description = new SIP.WebRTC.RTCSessionDescription(rawDescription);
- return SIP.Utils.promisify(this.peerConnection, 'setRemoteDescription')(description)
- .catch(function setRemoteDescriptionError(e) {
- self.emit('peerConnection-setRemoteDescriptionFailed', e);
- throw e;
- });
- }},
- /**
- * If the Session associated with this MediaHandler were to be referred,
- * what mediaHint should be provided to the UA's invite method?
- */
- getReferMedia: {writable: true, value: function getReferMedia () {
- function hasTracks (trackGetter, stream) {
- return stream[trackGetter]().length > 0;
- }
- function bothHaveTracks (trackGetter) {
- /* jshint validthis:true */
- return this.getLocalStreams().some(hasTracks.bind(null, trackGetter)) &&
- this.getRemoteStreams().some(hasTracks.bind(null, trackGetter));
- }
- return {
- constraints: {
- audio: bothHaveTracks.call(this, 'getAudioTracks'),
- video: bothHaveTracks.call(this, 'getVideoTracks')
- }
- };
- }},
- updateIceServers: {writeable:true, value: function (options) {
- var servers = this.prepareIceServers(options.stunServers, options.turnServers);
- this.RTCConstraints = options.RTCConstraints || this.RTCConstraints;
- this.initPeerConnection(servers);
- /* once updateIce is implemented correctly, this is better than above
- //no op if browser does not support this
- if (!this.peerConnection.updateIce) {
- return;
- }
- this.peerConnection.updateIce({'iceServers': servers}, this.RTCConstraints);
- */
- }},
- // Functions the session can use, but only because it's convenient for the application
- isMuted: {writable: true, value: function isMuted () {
- return {
- audio: this.audioMuted,
- video: this.videoMuted
- };
- }},
- mute: {writable: true, value: function mute (options) {
- if (this.getLocalStreams().length === 0) {
- return;
- }
- options = options || {
- audio: this.getLocalStreams()[0].getAudioTracks().length > 0,
- video: this.getLocalStreams()[0].getVideoTracks().length > 0
- };
- var audioMuted = false,
- videoMuted = false;
- if (options.audio && !this.audioMuted) {
- audioMuted = true;
- this.audioMuted = true;
- this.toggleMuteAudio(true);
- }
- if (options.video && !this.videoMuted) {
- videoMuted = true;
- this.videoMuted = true;
- this.toggleMuteVideo(true);
- }
- //REVISIT
- if (audioMuted || videoMuted) {
- return {
- audio: audioMuted,
- video: videoMuted
- };
- /*this.session.onmute({
- audio: audioMuted,
- video: videoMuted
- });*/
- }
- }},
- unmute: {writable: true, value: function unmute (options) {
- if (this.getLocalStreams().length === 0) {
- return;
- }
- options = options || {
- audio: this.getLocalStreams()[0].getAudioTracks().length > 0,
- video: this.getLocalStreams()[0].getVideoTracks().length > 0
- };
- var audioUnMuted = false,
- videoUnMuted = false;
- if (options.audio && this.audioMuted) {
- audioUnMuted = true;
- this.audioMuted = false;
- this.toggleMuteAudio(false);
- }
- if (options.video && this.videoMuted) {
- videoUnMuted = true;
- this.videoMuted = false;
- this.toggleMuteVideo(false);
- }
- //REVISIT
- if (audioUnMuted || videoUnMuted) {
- return {
- audio: audioUnMuted,
- video: videoUnMuted
- };
- /*this.session.onunmute({
- audio: audioUnMuted,
- video: videoUnMuted
- });*/
- }
- }},
- hold: {writable: true, value: function hold () {
- this.local_hold = true;
- this.toggleMuteAudio(true);
- this.toggleMuteVideo(true);
- }},
- unhold: {writable: true, value: function unhold () {
- this.local_hold = false;
- if (!this.audioMuted) {
- this.toggleMuteAudio(false);
- }
- if (!this.videoMuted) {
- this.toggleMuteVideo(false);
- }
- }},
- // Functions the application can use, but not the session
- getLocalStreams: {writable: true, value: function getLocalStreams () {
- var pc = this.peerConnection;
- if (pc && pc.signalingState === 'closed') {
- this.logger.warn('peerConnection is closed, getLocalStreams returning []');
- return [];
- }
- return (pc.getLocalStreams && pc.getLocalStreams()) ||
- pc.localStreams || [];
- }},
- getRemoteStreams: {writable: true, value: function getRemoteStreams () {
- var pc = this.peerConnection;
- if (pc && pc.signalingState === 'closed') {
- this.logger.warn('peerConnection is closed, getRemoteStreams returning this._remoteStreams');
- return this._remoteStreams;
- }
- return(pc.getRemoteStreams && pc.getRemoteStreams()) ||
- pc.remoteStreams || [];
- }},
- render: {writable: true, value: function render (renderHint) {
- renderHint = renderHint || (this.mediaHint && this.mediaHint.render);
- if (!renderHint) {
- return false;
- }
- var streamGetters = {
- local: 'getLocalStreams',
- remote: 'getRemoteStreams'
- };
- Object.keys(streamGetters).forEach(function (loc) {
- var streamGetter = streamGetters[loc];
- var streams = this[streamGetter]();
- SIP.WebRTC.MediaStreamManager.render(streams, renderHint[loc]);
- }.bind(this));
- }},
- // Internal functions
- hasOffer: {writable: true, value: function hasOffer (where) {
- var offerState = 'have-' + where + '-offer';
- return this.peerConnection.signalingState === offerState;
- // TODO consider signalingStates with 'pranswer'?
- }},
- prepareIceServers: {writable: true, value: function prepareIceServers (stunServers, turnServers) {
- var servers = [],
- config = this.session.ua.configuration;
- stunServers = stunServers || config.stunServers;
- turnServers = turnServers || config.turnServers;
- [].concat(stunServers).forEach(function (server) {
- servers.push({'urls': server});
- });
- [].concat(turnServers).forEach(function (server) {
- var turnServer = {'urls': server.urls};
- if (server.username) {
- turnServer.username = server.username;
- }
- if (server.password) {
- turnServer.credential = server.password;
- }
- servers.push(turnServer);
- });
- return servers;
- }},
- initPeerConnection: {writable: true, value: function initPeerConnection(servers) {
- var self = this,
- config = this.session.ua.configuration;
- this.onIceCompleted = SIP.Utils.defer();
- this.onIceCompleted.promise.then(function(pc) {
- self.emit('iceGatheringComplete', pc);
- if (self.iceCheckingTimer) {
- SIP.Timers.clearTimeout(self.iceCheckingTimer);
- self.iceCheckingTimer = null;
- }
- });
- if (this.peerConnection) {
- this.peerConnection.close();
- }
- var connConfig = {
- iceServers: servers
- };
- if (config.rtcpMuxPolicy) {
- connConfig.rtcpMuxPolicy = config.rtcpMuxPolicy;
- }
- this.peerConnection = new SIP.WebRTC.RTCPeerConnection(connConfig);
- // Firefox (35.0.1) sometimes throws on calls to peerConnection.getRemoteStreams
- // even if peerConnection.onaddstream was just called. In order to make
- // MediaHandler.prototype.getRemoteStreams work, keep track of them manually
- this._remoteStreams = [];
- this.peerConnection.onaddstream = function(e) {
- self.logger.log('stream added: '+ e.stream.id);
- self._remoteStreams.push(e.stream);
- self.render();
- self.emit('addStream', e);
- };
- this.peerConnection.onremovestream = function(e) {
- self.logger.log('stream removed: '+ e.stream.id);
- };
- this.startIceCheckingTimer = function () {
- if (!self.iceCheckingTimer) {
- self.iceCheckingTimer = SIP.Timers.setTimeout(function() {
- self.logger.log('RTCIceChecking Timeout Triggered after '+config.iceCheckingTimeout+' milliseconds');
- self.onIceCompleted.resolve(this);
- }.bind(this.peerConnection), config.iceCheckingTimeout);
- }
- };
- this.peerConnection.onicecandidate = function(e) {
- self.emit('iceCandidate', e);
- if (e.candidate) {
- self.logger.log('ICE candidate received: '+ (e.candidate.candidate === null ? null : e.candidate.candidate.trim()));
- self.startIceCheckingTimer();
- } else {
- self.onIceCompleted.resolve(this);
- }
- };
- this.peerConnection.onicegatheringstatechange = function () {
- self.logger.log('RTCIceGatheringState changed: ' + this.iceGatheringState);
- if (this.iceGatheringState === 'gathering') {
- self.emit('iceGathering', this);
- }
- if (this.iceGatheringState === 'complete') {
- self.onIceCompleted.resolve(this);
- }
- };
- this.peerConnection.oniceconnectionstatechange = function() { //need e for commented out case
- var stateEvent;
- if (this.iceConnectionState === 'checking') {
- self.startIceCheckingTimer();
- }
- switch (this.iceConnectionState) {
- case 'new':
- stateEvent = 'iceConnection';
- break;
- case 'checking':
- stateEvent = 'iceConnectionChecking';
- break;
- case 'connected':
- stateEvent = 'iceConnectionConnected';
- break;
- case 'completed':
- stateEvent = 'iceConnectionCompleted';
- break;
- case 'failed':
- stateEvent = 'iceConnectionFailed';
- break;
- case 'disconnected':
- stateEvent = 'iceConnectionDisconnected';
- break;
- case 'closed':
- stateEvent = 'iceConnectionClosed';
- break;
- default:
- self.logger.warn('Unknown iceConnection state:', this.iceConnectionState);
- return;
- }
- self.emit(stateEvent, this);
- //Bria state changes are always connected -> disconnected -> connected on accept, so session gets terminated
- //normal calls switch from failed to connected in some cases, so checking for failed and terminated
- /*if (this.iceConnectionState === 'failed') {
- self.session.terminate({
- cause: SIP.C.causes.RTP_TIMEOUT,
- status_code: 200,
- reason_phrase: SIP.C.causes.RTP_TIMEOUT
- });
- } else if (e.currentTarget.iceGatheringState === 'complete' && this.iceConnectionState !== 'closed') {
- self.onIceCompleted(this);
- }*/
- };
- this.peerConnection.onstatechange = function() {
- self.logger.log('PeerConnection state changed to "'+ this.readyState +'"');
- };
- }},
- createOfferOrAnswer: {writable: true, value: function createOfferOrAnswer (constraints) {
- var self = this;
- var methodName;
- var pc = self.peerConnection;
- self.ready = false;
- methodName = self.hasOffer('remote') ? 'createAnswer' : 'createOffer';
- return SIP.Utils.promisify(pc, methodName, true)(constraints)
- .catch(function methodError(e) {
- self.emit('peerConnection-' + methodName + 'Failed', e);
- throw e;
- })
- .then(SIP.Utils.promisify(pc, 'setLocalDescription'))
- .catch(function localDescError(e) {
- self.emit('peerConnection-selLocalDescriptionFailed', e);
- throw e;
- })
- .then(function onSetLocalDescriptionSuccess() {
- var deferred = SIP.Utils.defer();
- if (pc.iceGatheringState === 'complete' && (pc.iceConnectionState === 'connected' || pc.iceConnectionState === 'completed')) {
- deferred.resolve();
- } else {
- self.onIceCompleted.promise.then(deferred.resolve);
- }
- return deferred.promise;
- })
- .then(function readySuccess () {
- var sdp = pc.localDescription.sdp;
- sdp = SIP.Hacks.Chrome.needsExplicitlyInactiveSDP(sdp);
- sdp = SIP.Hacks.AllBrowsers.unmaskDtls(sdp);
- var sdpWrapper = {
- type: methodName === 'createOffer' ? 'offer' : 'answer',
- sdp: sdp
- };
- self.emit('getDescription', sdpWrapper);
- if (self.session.ua.configuration.hackStripTcp) {
- sdpWrapper.sdp = sdpWrapper.sdp.replace(/^a=candidate:\d+ \d+ tcp .*?\r\n/img, "");
- }
- self.ready = true;
- return sdpWrapper.sdp;
- })
- .catch(function createOfferAnswerError (e) {
- self.logger.error(e);
- self.ready = true;
- throw new SIP.Exceptions.GetDescriptionError(e);
- })
- ;
- }},
- addStreams: {writable: true, value: function addStreams (streams) {
- try {
- streams = [].concat(streams);
- streams.forEach(function (stream) {
- this.peerConnection.addStream(stream);
- }, this);
- } catch(e) {
- this.logger.error('error adding stream');
- this.logger.error(e);
- return SIP.Utils.Promise.reject(e);
- }
- return SIP.Utils.Promise.resolve();
- }},
- toggleMuteHelper: {writable: true, value: function toggleMuteHelper (trackGetter, mute) {
- this.getLocalStreams().forEach(function (stream) {
- stream[trackGetter]().forEach(function (track) {
- track.enabled = !mute;
- });
- });
- }},
- toggleMuteAudio: {writable: true, value: function toggleMuteAudio (mute) {
- this.toggleMuteHelper('getAudioTracks', mute);
- }},
- toggleMuteVideo: {writable: true, value: function toggleMuteVideo (mute) {
- this.toggleMuteHelper('getVideoTracks', mute);
- }}
- });
- // Return since it will be assigned to a variable.
- return MediaHandler;
- };
- },{}],34:[function(require,module,exports){
- "use strict";
- /**
- * @fileoverview MediaStreamManager
- */
- /* MediaStreamManager
- * @class Manages the acquisition and release of MediaStreams.
- * @param {mediaHint} [defaultMediaHint] The mediaHint to use if none is provided to acquire()
- */
- module.exports = function (SIP, environment) {
- // Default MediaStreamManager provides single-use streams created with getUserMedia
- var MediaStreamManager = function MediaStreamManager (logger, defaultMediaHint) {
- if (!SIP.WebRTC.isSupported()) {
- throw new SIP.Exceptions.NotSupportedError('Media not supported');
- }
- this.mediaHint = defaultMediaHint || {
- constraints: {audio: true, video: true}
- };
- // map of streams to acquisition manner:
- // true -> passed in as mediaHint.stream
- // false -> getUserMedia
- this.acquisitions = {};
- };
- MediaStreamManager.streamId = function (stream) {
- return stream.getAudioTracks().concat(stream.getVideoTracks())
- .map(function trackId (track) {
- return track.id;
- })
- .join('');
- };
- /**
- * @param {(Array of) MediaStream} streams - The streams to render
- *
- * @param {(Array of) HTMLMediaElement} elements
- * - The <audio>/<video> element(s) that should render the streams
- *
- * Each stream in streams renders to the corresponding element in elements,
- * wrapping around elements if needed.
- */
- MediaStreamManager.render = function render (streams, elements) {
- if (!elements) {
- return false;
- }
- if (Array.isArray(elements) && !elements.length) {
- throw new TypeError('elements must not be empty');
- }
- function attachMediaStream(element, stream) {
- element.srcObject = stream;
- }
- function ensureMediaPlaying (mediaElement) {
- var interval = 100;
- mediaElement.ensurePlayingIntervalId = SIP.Timers.setInterval(function () {
- if (mediaElement.paused && mediaElement.srcObject) {
- mediaElement.play();
- }
- else {
- SIP.Timers.clearInterval(mediaElement.ensurePlayingIntervalId);
- }
- }, interval);
- }
- function attachAndPlay (elements, stream, index) {
- var element = elements[index % elements.length];
- if (typeof element === 'function') {
- element = element();
- }
- (environment.attachMediaStream || attachMediaStream)(element, stream);
- ensureMediaPlaying(element);
- }
- // [].concat "casts" `elements` into an array
- // so forEach works even if `elements` was a single element
- elements = [].concat(elements);
- [].concat(streams).forEach(attachAndPlay.bind(null, elements));
- };
- MediaStreamManager.prototype = Object.create(SIP.EventEmitter.prototype, {
- 'acquire': {writable: true, value: function acquire (mediaHint) {
- mediaHint = Object.keys(mediaHint || {}).length ? mediaHint : this.mediaHint;
- var saveSuccess = function (isHintStream, streams) {
- streams = [].concat(streams);
- streams.forEach(function (stream) {
- var streamId = MediaStreamManager.streamId(stream);
- this.acquisitions[streamId] = !!isHintStream;
- }, this);
- return SIP.Utils.Promise.resolve(streams);
- }.bind(this);
- if (mediaHint.stream) {
- return saveSuccess(true, mediaHint.stream);
- } else {
- // Fallback to audio/video enabled if no mediaHint can be found.
- var constraints = mediaHint.constraints ||
- (this.mediaHint && this.mediaHint.constraints) ||
- {audio: true, video: true};
- var deferred = SIP.Utils.defer();
- /*
- * Make the call asynchronous, so that ICCs have a chance
- * to define callbacks to `userMediaRequest`
- */
- SIP.Timers.setTimeout(function () {
- this.emit('userMediaRequest', constraints);
- var emitThenCall = function (eventName, callback) {
- var callbackArgs = Array.prototype.slice.call(arguments, 2);
- // Emit with all of the arguments from the real callback.
- var newArgs = [eventName].concat(callbackArgs);
- this.emit.apply(this, newArgs);
- return callback.apply(null, callbackArgs);
- }.bind(this);
- if (constraints.audio || constraints.video) {
- deferred.resolve(
- SIP.WebRTC.getUserMedia(constraints)
- .then(
- emitThenCall.bind(this, 'userMedia', saveSuccess.bind(null, false)),
- emitThenCall.bind(this, 'userMediaFailed', function(e){throw e;})
- )
- );
- } else {
- // Local streams were explicitly excluded.
- deferred.resolve([]);
- }
- }.bind(this), 0);
- return deferred.promise;
- }
- }},
- 'release': {writable: true, value: function release (streams) {
- streams = [].concat(streams);
- streams.forEach(function (stream) {
- var streamId = MediaStreamManager.streamId(stream);
- if (this.acquisitions[streamId] === false) {
- stream.getTracks().forEach(function (track) {
- track.stop();
- });
- }
- delete this.acquisitions[streamId];
- }, this);
- }},
- });
- // Return since it will be assigned to a variable.
- return MediaStreamManager;
- };
- },{}],35:[function(require,module,exports){
- (function (global){
- "use strict";
- var toplevel = global.window || global;
- function getPrefixedProperty (object, name) {
- if (object == null) {
- return;
- }
- var capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
- var prefixedNames = [name, 'webkit' + capitalizedName, 'moz' + capitalizedName];
- for (var i in prefixedNames) {
- var property = object[prefixedNames[i]];
- if (property) {
- return property.bind(object);
- }
- }
- }
- module.exports = {
- WebSocket: toplevel.WebSocket,
- Transport: require('./Transport'),
- open: toplevel.open,
- Promise: toplevel.Promise,
- timers: toplevel,
- // Console is not defined in ECMAScript, so just in case...
- console: toplevel.console || {
- debug: function () {},
- log: function () {},
- warn: function () {},
- error: function () {}
- },
- MediaStream: getPrefixedProperty(toplevel, 'MediaStream'),
- getUserMedia: getPrefixedProperty(toplevel.navigator, 'getUserMedia'),
- RTCPeerConnection: getPrefixedProperty(toplevel, 'RTCPeerConnection'),
- RTCSessionDescription: getPrefixedProperty(toplevel, 'RTCSessionDescription'),
- addEventListener: getPrefixedProperty(toplevel, 'addEventListener'),
- removeEventListener: getPrefixedProperty(toplevel, 'removeEventListener'),
- HTMLMediaElement: toplevel.HTMLMediaElement,
- attachMediaStream: toplevel.attachMediaStream,
- createObjectURL: toplevel.URL && toplevel.URL.createObjectURL,
- revokeObjectURL: toplevel.URL && toplevel.URL.revokeObjectURL
- };
- }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
- },{"./Transport":28}],36:[function(require,module,exports){
- "use strict";
- module.exports = require('./SIP')(require('./environment'));
- },{"./SIP":19,"./environment":35}]},{},[36])(36)
- });
|