strophe.umd.js 249 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.strophe = {}));
  5. })(this, (function (exports) { 'use strict';
  6. var global$1 = typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};
  7. /*
  8. * This module provides uniform
  9. * Shims APIs and globals that are not present in all JS environments,
  10. * the most common example for Strophe being browser APIs like WebSocket
  11. * and DOM that don't exist under nodejs.
  12. *
  13. * Usually these will be supplied in nodejs by conditionally requiring a
  14. * NPM module that provides a compatible implementation.
  15. */
  16. /* global global */
  17. /**
  18. * WHATWG WebSockets API
  19. * https://www.w3.org/TR/websockets/
  20. *
  21. * Interface to use the web socket protocol
  22. *
  23. * Used implementations:
  24. * - supported browsers: built-in in WebSocket global
  25. * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Browser_compatibility
  26. * - nodejs: use standard-compliant 'ws' module
  27. * https://www.npmjs.com/package/ws
  28. */
  29. function getWebSocketImplementation() {
  30. let WebSocketImplementation = global$1.WebSocket;
  31. if (typeof WebSocketImplementation === 'undefined') {
  32. try {
  33. WebSocketImplementation = require('ws');
  34. } catch (err) {
  35. throw new Error('You must install the "ws" package to use Strophe in nodejs.');
  36. }
  37. }
  38. return WebSocketImplementation;
  39. }
  40. const WebSocket = getWebSocketImplementation();
  41. /**
  42. * DOMParser
  43. * https://w3c.github.io/DOM-Parsing/#the-domparser-interface
  44. *
  45. * Interface to parse XML strings into Document objects
  46. *
  47. * Used implementations:
  48. * - supported browsers: built-in in DOMParser global
  49. * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility
  50. * - nodejs: use '@xmldom/xmldom' module
  51. * https://www.npmjs.com/package/@xmldom/xmldom
  52. */
  53. function getDOMParserImplementation() {
  54. let DOMParserImplementation = global$1.DOMParser;
  55. if (typeof DOMParserImplementation === 'undefined') {
  56. try {
  57. DOMParserImplementation = require('@xmldom/xmldom').DOMParser;
  58. } catch (err) {
  59. throw new Error('You must install the "@xmldom/xmldom" package to use Strophe in nodejs.');
  60. }
  61. }
  62. return DOMParserImplementation;
  63. }
  64. const DOMParser = getDOMParserImplementation();
  65. /**
  66. * Gets IE xml doc object. Used by getDummyXMLDocument shim.
  67. *
  68. * Returns:
  69. * A Microsoft XML DOM Object
  70. * See Also:
  71. * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
  72. */
  73. function _getIEXmlDom() {
  74. const docStrings = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];
  75. for (let d = 0; d < docStrings.length; d++) {
  76. try {
  77. // eslint-disable-next-line no-undef
  78. const doc = new ActiveXObject(docStrings[d]);
  79. return doc;
  80. } catch (e) {// Try next one
  81. }
  82. }
  83. }
  84. /**
  85. * Creates a dummy XML DOM document to serve as an element and text node generator.
  86. *
  87. * Used implementations:
  88. * - IE < 10: avoid using createDocument() due to a memory leak, use ie-specific
  89. * workaround
  90. * - other supported browsers: use document's createDocument
  91. * - nodejs: use '@xmldom/xmldom'
  92. */
  93. function getDummyXMLDOMDocument() {
  94. // nodejs
  95. if (typeof document === 'undefined') {
  96. try {
  97. const DOMImplementation = require('@xmldom/xmldom').DOMImplementation;
  98. return new DOMImplementation().createDocument('jabber:client', 'strophe', null);
  99. } catch (err) {
  100. throw new Error('You must install the "@xmldom/xmldom" package to use Strophe in nodejs.');
  101. }
  102. } // IE < 10
  103. if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) {
  104. const doc = _getIEXmlDom();
  105. doc.appendChild(doc.createElement('strophe'));
  106. return doc;
  107. } // All other supported browsers
  108. return document.implementation.createDocument('jabber:client', 'strophe', null);
  109. }
  110. /*
  111. * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  112. * Digest Algorithm, as defined in RFC 1321.
  113. * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
  114. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  115. * Distributed under the BSD License
  116. * See http://pajhome.org.uk/crypt/md5 for more info.
  117. */
  118. /*
  119. * Everything that isn't used by Strophe has been stripped here!
  120. */
  121. /*
  122. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  123. * to work around bugs in some JS interpreters.
  124. */
  125. const safe_add$1 = function (x, y) {
  126. const lsw = (x & 0xFFFF) + (y & 0xFFFF);
  127. const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  128. return msw << 16 | lsw & 0xFFFF;
  129. };
  130. /*
  131. * Bitwise rotate a 32-bit number to the left.
  132. */
  133. const bit_rol = function (num, cnt) {
  134. return num << cnt | num >>> 32 - cnt;
  135. };
  136. /*
  137. * Convert a string to an array of little-endian words
  138. */
  139. const str2binl = function (str) {
  140. if (typeof str !== "string") {
  141. throw new Error("str2binl was passed a non-string");
  142. }
  143. const bin = [];
  144. for (let i = 0; i < str.length * 8; i += 8) {
  145. bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32;
  146. }
  147. return bin;
  148. };
  149. /*
  150. * Convert an array of little-endian words to a string
  151. */
  152. const binl2str = function (bin) {
  153. let str = "";
  154. for (let i = 0; i < bin.length * 32; i += 8) {
  155. str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255);
  156. }
  157. return str;
  158. };
  159. /*
  160. * Convert an array of little-endian words to a hex string.
  161. */
  162. const binl2hex = function (binarray) {
  163. const hex_tab = "0123456789abcdef";
  164. let str = "";
  165. for (let i = 0; i < binarray.length * 4; i++) {
  166. str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF);
  167. }
  168. return str;
  169. };
  170. /*
  171. * These functions implement the four basic operations the algorithm uses.
  172. */
  173. const md5_cmn = function (q, a, b, x, s, t) {
  174. return safe_add$1(bit_rol(safe_add$1(safe_add$1(a, q), safe_add$1(x, t)), s), b);
  175. };
  176. const md5_ff = function (a, b, c, d, x, s, t) {
  177. return md5_cmn(b & c | ~b & d, a, b, x, s, t);
  178. };
  179. const md5_gg = function (a, b, c, d, x, s, t) {
  180. return md5_cmn(b & d | c & ~d, a, b, x, s, t);
  181. };
  182. const md5_hh = function (a, b, c, d, x, s, t) {
  183. return md5_cmn(b ^ c ^ d, a, b, x, s, t);
  184. };
  185. const md5_ii = function (a, b, c, d, x, s, t) {
  186. return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
  187. };
  188. /*
  189. * Calculate the MD5 of an array of little-endian words, and a bit length
  190. */
  191. const core_md5 = function (x, len) {
  192. /* append padding */
  193. x[len >> 5] |= 0x80 << len % 32;
  194. x[(len + 64 >>> 9 << 4) + 14] = len;
  195. let a = 1732584193;
  196. let b = -271733879;
  197. let c = -1732584194;
  198. let d = 271733878;
  199. let olda, oldb, oldc, oldd;
  200. for (let i = 0; i < x.length; i += 16) {
  201. olda = a;
  202. oldb = b;
  203. oldc = c;
  204. oldd = d;
  205. a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
  206. d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
  207. c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
  208. b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
  209. a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
  210. d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
  211. c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
  212. b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
  213. a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
  214. d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
  215. c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
  216. b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
  217. a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
  218. d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
  219. c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
  220. b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
  221. a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
  222. d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
  223. c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
  224. b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
  225. a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
  226. d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
  227. c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
  228. b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
  229. a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
  230. d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
  231. c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
  232. b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
  233. a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
  234. d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
  235. c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
  236. b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
  237. a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
  238. d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
  239. c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
  240. b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
  241. a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
  242. d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
  243. c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
  244. b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
  245. a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
  246. d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
  247. c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
  248. b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
  249. a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
  250. d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
  251. c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
  252. b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
  253. a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
  254. d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
  255. c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
  256. b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
  257. a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
  258. d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
  259. c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
  260. b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
  261. a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
  262. d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
  263. c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
  264. b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
  265. a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
  266. d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
  267. c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
  268. b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
  269. a = safe_add$1(a, olda);
  270. b = safe_add$1(b, oldb);
  271. c = safe_add$1(c, oldc);
  272. d = safe_add$1(d, oldd);
  273. }
  274. return [a, b, c, d];
  275. };
  276. /*
  277. * These are the functions you'll usually want to call.
  278. * They take string arguments and return either hex or base-64 encoded
  279. * strings.
  280. */
  281. const MD5 = {
  282. hexdigest: function (s) {
  283. return binl2hex(core_md5(str2binl(s), s.length * 8));
  284. },
  285. hash: function (s) {
  286. return binl2str(core_md5(str2binl(s), s.length * 8));
  287. }
  288. };
  289. /** Class: Strophe.SASLMechanism
  290. *
  291. * Encapsulates an SASL authentication mechanism.
  292. *
  293. * User code may override the priority for each mechanism or disable it completely.
  294. * See <priority> for information about changing priority and <test> for informatian on
  295. * how to disable a mechanism.
  296. *
  297. * By default, all mechanisms are enabled and the priorities are
  298. *
  299. * SCRAM-SHA-1 - 60
  300. * PLAIN - 50
  301. * OAUTHBEARER - 40
  302. * X-OAUTH2 - 30
  303. * ANONYMOUS - 20
  304. * EXTERNAL - 10
  305. *
  306. * See: Strophe.Connection.addSupportedSASLMechanisms
  307. */
  308. class SASLMechanism {
  309. /**
  310. * PrivateConstructor: Strophe.SASLMechanism
  311. * SASL auth mechanism abstraction.
  312. *
  313. * Parameters:
  314. * (String) name - SASL Mechanism name.
  315. * (Boolean) isClientFirst - If client should send response first without challenge.
  316. * (Number) priority - Priority.
  317. *
  318. * Returns:
  319. * A new Strophe.SASLMechanism object.
  320. */
  321. constructor(name, isClientFirst, priority) {
  322. /** PrivateVariable: mechname
  323. * Mechanism name.
  324. */
  325. this.mechname = name;
  326. /** PrivateVariable: isClientFirst
  327. * If client sends response without initial server challenge.
  328. */
  329. this.isClientFirst = isClientFirst;
  330. /** Variable: priority
  331. * Determines which <SASLMechanism> is chosen for authentication (Higher is better).
  332. * Users may override this to prioritize mechanisms differently.
  333. *
  334. * Example: (This will cause Strophe to choose the mechanism that the server sent first)
  335. *
  336. * > Strophe.SASLPlain.priority = Strophe.SASLSHA1.priority;
  337. *
  338. * See <SASL mechanisms> for a list of available mechanisms.
  339. *
  340. */
  341. this.priority = priority;
  342. }
  343. /**
  344. * Function: test
  345. * Checks if mechanism able to run.
  346. * To disable a mechanism, make this return false;
  347. *
  348. * To disable plain authentication run
  349. * > Strophe.SASLPlain.test = function() {
  350. * > return false;
  351. * > }
  352. *
  353. * See <SASL mechanisms> for a list of available mechanisms.
  354. *
  355. * Parameters:
  356. * (Strophe.Connection) connection - Target Connection.
  357. *
  358. * Returns:
  359. * (Boolean) If mechanism was able to run.
  360. */
  361. test() {
  362. // eslint-disable-line class-methods-use-this
  363. return true;
  364. }
  365. /** PrivateFunction: onStart
  366. * Called before starting mechanism on some connection.
  367. *
  368. * Parameters:
  369. * (Strophe.Connection) connection - Target Connection.
  370. */
  371. onStart(connection) {
  372. this._connection = connection;
  373. }
  374. /** PrivateFunction: onChallenge
  375. * Called by protocol implementation on incoming challenge.
  376. *
  377. * By deafult, if the client is expected to send data first (isClientFirst === true),
  378. * this method is called with `challenge` as null on the first call,
  379. * unless `clientChallenge` is overridden in the relevant subclass.
  380. *
  381. * Parameters:
  382. * (Strophe.Connection) connection - Target Connection.
  383. * (String) challenge - current challenge to handle.
  384. *
  385. * Returns:
  386. * (String) Mechanism response.
  387. */
  388. onChallenge(connection, challenge) {
  389. // eslint-disable-line
  390. throw new Error("You should implement challenge handling!");
  391. }
  392. /** PrivateFunction: clientChallenge
  393. * Called by the protocol implementation if the client is expected to send
  394. * data first in the authentication exchange (i.e. isClientFirst === true).
  395. *
  396. * Parameters:
  397. * (Strophe.Connection) connection - Target Connection.
  398. *
  399. * Returns:
  400. * (String) Mechanism response.
  401. */
  402. clientChallenge(connection) {
  403. if (!this.isClientFirst) {
  404. throw new Error("clientChallenge should not be called if isClientFirst is false!");
  405. }
  406. return this.onChallenge(connection);
  407. }
  408. /** PrivateFunction: onFailure
  409. * Protocol informs mechanism implementation about SASL failure.
  410. */
  411. onFailure() {
  412. this._connection = null;
  413. }
  414. /** PrivateFunction: onSuccess
  415. * Protocol informs mechanism implementation about SASL success.
  416. */
  417. onSuccess() {
  418. this._connection = null;
  419. }
  420. }
  421. class SASLAnonymous extends SASLMechanism {
  422. /** PrivateConstructor: SASLAnonymous
  423. * SASL ANONYMOUS authentication.
  424. */
  425. constructor() {
  426. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'ANONYMOUS';
  427. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  428. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 20;
  429. super(mechname, isClientFirst, priority);
  430. }
  431. test(connection) {
  432. // eslint-disable-line class-methods-use-this
  433. return connection.authcid === null;
  434. }
  435. }
  436. class SASLExternal extends SASLMechanism {
  437. /** PrivateConstructor: SASLExternal
  438. * SASL EXTERNAL authentication.
  439. *
  440. * The EXTERNAL mechanism allows a client to request the server to use
  441. * credentials established by means external to the mechanism to
  442. * authenticate the client. The external means may be, for instance,
  443. * TLS services.
  444. */
  445. constructor() {
  446. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'EXTERNAL';
  447. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  448. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
  449. super(mechname, isClientFirst, priority);
  450. }
  451. onChallenge(connection) {
  452. // eslint-disable-line class-methods-use-this
  453. /** According to XEP-178, an authzid SHOULD NOT be presented when the
  454. * authcid contained or implied in the client certificate is the JID (i.e.
  455. * authzid) with which the user wants to log in as.
  456. *
  457. * To NOT send the authzid, the user should therefore set the authcid equal
  458. * to the JID when instantiating a new Strophe.Connection object.
  459. */
  460. return connection.authcid === connection.authzid ? '' : connection.authzid;
  461. }
  462. }
  463. const utils = {
  464. utf16to8: function (str) {
  465. var i, c;
  466. var out = "";
  467. var len = str.length;
  468. for (i = 0; i < len; i++) {
  469. c = str.charCodeAt(i);
  470. if (c >= 0x0000 && c <= 0x007F) {
  471. out += str.charAt(i);
  472. } else if (c > 0x07FF) {
  473. out += String.fromCharCode(0xE0 | c >> 12 & 0x0F);
  474. out += String.fromCharCode(0x80 | c >> 6 & 0x3F);
  475. out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
  476. } else {
  477. out += String.fromCharCode(0xC0 | c >> 6 & 0x1F);
  478. out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
  479. }
  480. }
  481. return out;
  482. },
  483. addCookies: function (cookies) {
  484. /* Parameters:
  485. * (Object) cookies - either a map of cookie names
  486. * to string values or to maps of cookie values.
  487. *
  488. * For example:
  489. * { "myCookie": "1234" }
  490. *
  491. * or:
  492. * { "myCookie": {
  493. * "value": "1234",
  494. * "domain": ".example.org",
  495. * "path": "/",
  496. * "expires": expirationDate
  497. * }
  498. * }
  499. *
  500. * These values get passed to Strophe.Connection via
  501. * options.cookies
  502. */
  503. cookies = cookies || {};
  504. for (const cookieName in cookies) {
  505. if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) {
  506. let expires = '';
  507. let domain = '';
  508. let path = '';
  509. const cookieObj = cookies[cookieName];
  510. const isObj = typeof cookieObj === "object";
  511. const cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj));
  512. if (isObj) {
  513. expires = cookieObj.expires ? ";expires=" + cookieObj.expires : '';
  514. domain = cookieObj.domain ? ";domain=" + cookieObj.domain : '';
  515. path = cookieObj.path ? ";path=" + cookieObj.path : '';
  516. }
  517. document.cookie = cookieName + '=' + cookieValue + expires + domain + path;
  518. }
  519. }
  520. }
  521. };
  522. class SASLOAuthBearer extends SASLMechanism {
  523. /** PrivateConstructor: SASLOAuthBearer
  524. * SASL OAuth Bearer authentication.
  525. */
  526. constructor() {
  527. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'OAUTHBEARER';
  528. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  529. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 40;
  530. super(mechname, isClientFirst, priority);
  531. }
  532. test(connection) {
  533. // eslint-disable-line class-methods-use-this
  534. return connection.pass !== null;
  535. }
  536. onChallenge(connection) {
  537. // eslint-disable-line class-methods-use-this
  538. let auth_str = 'n,';
  539. if (connection.authcid !== null) {
  540. auth_str = auth_str + 'a=' + connection.authzid;
  541. }
  542. auth_str = auth_str + ',';
  543. auth_str = auth_str + "\u0001";
  544. auth_str = auth_str + 'auth=Bearer ';
  545. auth_str = auth_str + connection.pass;
  546. auth_str = auth_str + "\u0001";
  547. auth_str = auth_str + "\u0001";
  548. return utils.utf16to8(auth_str);
  549. }
  550. }
  551. class SASLPlain extends SASLMechanism {
  552. /** PrivateConstructor: SASLPlain
  553. * SASL PLAIN authentication.
  554. */
  555. constructor() {
  556. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'PLAIN';
  557. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  558. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 50;
  559. super(mechname, isClientFirst, priority);
  560. }
  561. test(connection) {
  562. // eslint-disable-line class-methods-use-this
  563. return connection.authcid !== null;
  564. }
  565. onChallenge(connection) {
  566. // eslint-disable-line class-methods-use-this
  567. const {
  568. authcid,
  569. authzid,
  570. domain,
  571. pass
  572. } = connection;
  573. if (!domain) {
  574. throw new Error("SASLPlain onChallenge: domain is not defined!");
  575. } // Only include authzid if it differs from authcid.
  576. // See: https://tools.ietf.org/html/rfc6120#section-6.3.8
  577. let auth_str = authzid !== `${authcid}@${domain}` ? authzid : '';
  578. auth_str = auth_str + "\u0000";
  579. auth_str = auth_str + authcid;
  580. auth_str = auth_str + "\u0000";
  581. auth_str = auth_str + pass;
  582. return utils.utf16to8(auth_str);
  583. }
  584. }
  585. /*
  586. * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
  587. * in FIPS PUB 180-1
  588. * Version 2.1a Copyright Paul Johnston 2000 - 2002.
  589. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  590. * Distributed under the BSD License
  591. * See http://pajhome.org.uk/crypt/md5 for details.
  592. */
  593. /* global define */
  594. /* Some functions and variables have been stripped for use with Strophe */
  595. /*
  596. * Calculate the SHA-1 of an array of big-endian words, and a bit length
  597. */
  598. function core_sha1(x, len) {
  599. /* append padding */
  600. x[len >> 5] |= 0x80 << 24 - len % 32;
  601. x[(len + 64 >> 9 << 4) + 15] = len;
  602. var w = new Array(80);
  603. var a = 1732584193;
  604. var b = -271733879;
  605. var c = -1732584194;
  606. var d = 271733878;
  607. var e = -1009589776;
  608. var i, j, t, olda, oldb, oldc, oldd, olde;
  609. for (i = 0; i < x.length; i += 16) {
  610. olda = a;
  611. oldb = b;
  612. oldc = c;
  613. oldd = d;
  614. olde = e;
  615. for (j = 0; j < 80; j++) {
  616. if (j < 16) {
  617. w[j] = x[i + j];
  618. } else {
  619. w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
  620. }
  621. t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j)));
  622. e = d;
  623. d = c;
  624. c = rol(b, 30);
  625. b = a;
  626. a = t;
  627. }
  628. a = safe_add(a, olda);
  629. b = safe_add(b, oldb);
  630. c = safe_add(c, oldc);
  631. d = safe_add(d, oldd);
  632. e = safe_add(e, olde);
  633. }
  634. return [a, b, c, d, e];
  635. }
  636. /*
  637. * Perform the appropriate triplet combination function for the current
  638. * iteration
  639. */
  640. function sha1_ft(t, b, c, d) {
  641. if (t < 20) {
  642. return b & c | ~b & d;
  643. }
  644. if (t < 40) {
  645. return b ^ c ^ d;
  646. }
  647. if (t < 60) {
  648. return b & c | b & d | c & d;
  649. }
  650. return b ^ c ^ d;
  651. }
  652. /*
  653. * Determine the appropriate additive constant for the current iteration
  654. */
  655. function sha1_kt(t) {
  656. return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
  657. }
  658. /*
  659. * Calculate the HMAC-SHA1 of a key and some data
  660. */
  661. function core_hmac_sha1(key, data) {
  662. var bkey = str2binb(key);
  663. if (bkey.length > 16) {
  664. bkey = core_sha1(bkey, key.length * 8);
  665. }
  666. var ipad = new Array(16),
  667. opad = new Array(16);
  668. for (var i = 0; i < 16; i++) {
  669. ipad[i] = bkey[i] ^ 0x36363636;
  670. opad[i] = bkey[i] ^ 0x5C5C5C5C;
  671. }
  672. var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);
  673. return core_sha1(opad.concat(hash), 512 + 160);
  674. }
  675. /*
  676. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  677. * to work around bugs in some JS interpreters.
  678. */
  679. function safe_add(x, y) {
  680. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  681. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  682. return msw << 16 | lsw & 0xFFFF;
  683. }
  684. /*
  685. * Bitwise rotate a 32-bit number to the left.
  686. */
  687. function rol(num, cnt) {
  688. return num << cnt | num >>> 32 - cnt;
  689. }
  690. /*
  691. * Convert an 8-bit or 16-bit string to an array of big-endian words
  692. * In 8-bit function, characters >255 have their hi-byte silently ignored.
  693. */
  694. function str2binb(str) {
  695. var bin = [];
  696. var mask = 255;
  697. for (var i = 0; i < str.length * 8; i += 8) {
  698. bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32;
  699. }
  700. return bin;
  701. }
  702. /*
  703. * Convert an array of big-endian words to a base-64 string
  704. */
  705. function binb2b64(binarray) {
  706. var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  707. var str = "";
  708. var triplet, j;
  709. for (var i = 0; i < binarray.length * 4; i += 3) {
  710. triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF;
  711. for (j = 0; j < 4; j++) {
  712. if (i * 8 + j * 6 > binarray.length * 32) {
  713. str += "=";
  714. } else {
  715. str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F);
  716. }
  717. }
  718. }
  719. return str;
  720. }
  721. /*
  722. * Convert an array of big-endian words to a string
  723. */
  724. function binb2str(bin) {
  725. var str = "";
  726. var mask = 255;
  727. for (var i = 0; i < bin.length * 32; i += 8) {
  728. str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask);
  729. }
  730. return str;
  731. }
  732. /*
  733. * These are the functions you'll usually want to call
  734. * They take string arguments and return either hex or base-64 encoded strings
  735. */
  736. const SHA1 = {
  737. b64_hmac_sha1: function (key, data) {
  738. return binb2b64(core_hmac_sha1(key, data));
  739. },
  740. b64_sha1: function (s) {
  741. return binb2b64(core_sha1(str2binb(s), s.length * 8));
  742. },
  743. binb2str: binb2str,
  744. core_hmac_sha1: core_hmac_sha1,
  745. str_hmac_sha1: function (key, data) {
  746. return binb2str(core_hmac_sha1(key, data));
  747. },
  748. str_sha1: function (s) {
  749. return binb2str(core_sha1(str2binb(s), s.length * 8));
  750. }
  751. };
  752. class SASLSHA1 extends SASLMechanism {
  753. /** PrivateConstructor: SASLSHA1
  754. * SASL SCRAM SHA 1 authentication.
  755. */
  756. constructor() {
  757. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'SCRAM-SHA-1';
  758. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  759. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 60;
  760. super(mechname, isClientFirst, priority);
  761. }
  762. test(connection) {
  763. // eslint-disable-line class-methods-use-this
  764. return connection.authcid !== null;
  765. }
  766. onChallenge(connection, challenge) {
  767. // eslint-disable-line class-methods-use-this
  768. let nonce, salt, iter, Hi, U, U_old, i, k;
  769. let responseText = "c=biws,";
  770. let authMessage = `${connection._sasl_data["client-first-message-bare"]},${challenge},`;
  771. const cnonce = connection._sasl_data.cnonce;
  772. const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
  773. while (challenge.match(attribMatch)) {
  774. const matches = challenge.match(attribMatch);
  775. challenge = challenge.replace(matches[0], "");
  776. switch (matches[1]) {
  777. case "r":
  778. nonce = matches[2];
  779. break;
  780. case "s":
  781. salt = matches[2];
  782. break;
  783. case "i":
  784. iter = matches[2];
  785. break;
  786. }
  787. }
  788. if (nonce.slice(0, cnonce.length) !== cnonce) {
  789. connection._sasl_data = {};
  790. return connection._sasl_failure_cb();
  791. }
  792. responseText += "r=" + nonce;
  793. authMessage += responseText;
  794. salt = atob(salt);
  795. salt += "\x00\x00\x00\x01";
  796. const pass = utils.utf16to8(connection.pass);
  797. Hi = U_old = SHA1.core_hmac_sha1(pass, salt);
  798. for (i = 1; i < iter; i++) {
  799. U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));
  800. for (k = 0; k < 5; k++) {
  801. Hi[k] ^= U[k];
  802. }
  803. U_old = U;
  804. }
  805. Hi = SHA1.binb2str(Hi);
  806. const clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");
  807. const serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");
  808. const clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);
  809. connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);
  810. for (k = 0; k < 5; k++) {
  811. clientKey[k] ^= clientSignature[k];
  812. }
  813. responseText += ",p=" + btoa(SHA1.binb2str(clientKey));
  814. return responseText;
  815. }
  816. clientChallenge(connection, test_cnonce) {
  817. // eslint-disable-line class-methods-use-this
  818. const cnonce = test_cnonce || MD5.hexdigest("" + Math.random() * 1234567890);
  819. let auth_str = "n=" + utils.utf16to8(connection.authcid);
  820. auth_str += ",r=";
  821. auth_str += cnonce;
  822. connection._sasl_data.cnonce = cnonce;
  823. connection._sasl_data["client-first-message-bare"] = auth_str;
  824. auth_str = "n,," + auth_str;
  825. return auth_str;
  826. }
  827. }
  828. class SASLXOAuth2 extends SASLMechanism {
  829. /** PrivateConstructor: SASLXOAuth2
  830. * SASL X-OAuth2 authentication.
  831. */
  832. constructor() {
  833. let mechname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'X-OAUTH2';
  834. let isClientFirst = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  835. let priority = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 30;
  836. super(mechname, isClientFirst, priority);
  837. }
  838. test(connection) {
  839. // eslint-disable-line class-methods-use-this
  840. return connection.pass !== null;
  841. }
  842. onChallenge(connection) {
  843. // eslint-disable-line class-methods-use-this
  844. let auth_str = '\u0000';
  845. if (connection.authcid !== null) {
  846. auth_str = auth_str + connection.authzid;
  847. }
  848. auth_str = auth_str + "\u0000";
  849. auth_str = auth_str + connection.pass;
  850. return utils.utf16to8(auth_str);
  851. }
  852. }
  853. /**
  854. * Implementation of atob() according to the HTML and Infra specs, except that
  855. * instead of throwing INVALID_CHARACTER_ERR we return null.
  856. */
  857. function atob$2(data) {
  858. if (arguments.length === 0) {
  859. throw new TypeError("1 argument required, but only 0 present.");
  860. } // Web IDL requires DOMStrings to just be converted using ECMAScript
  861. // ToString, which in our case amounts to using a template literal.
  862. data = `${data}`; // "Remove all ASCII whitespace from data."
  863. data = data.replace(/[ \t\n\f\r]/g, ""); // "If data's length divides by 4 leaving no remainder, then: if data ends
  864. // with one or two U+003D (=) code points, then remove them from data."
  865. if (data.length % 4 === 0) {
  866. data = data.replace(/==?$/, "");
  867. } // "If data's length divides by 4 leaving a remainder of 1, then return
  868. // failure."
  869. //
  870. // "If data contains a code point that is not one of
  871. //
  872. // U+002B (+)
  873. // U+002F (/)
  874. // ASCII alphanumeric
  875. //
  876. // then return failure."
  877. if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) {
  878. return null;
  879. } // "Let output be an empty byte sequence."
  880. let output = ""; // "Let buffer be an empty buffer that can have bits appended to it."
  881. //
  882. // We append bits via left-shift and or. accumulatedBits is used to track
  883. // when we've gotten to 24 bits.
  884. let buffer = 0;
  885. let accumulatedBits = 0; // "Let position be a position variable for data, initially pointing at the
  886. // start of data."
  887. //
  888. // "While position does not point past the end of data:"
  889. for (let i = 0; i < data.length; i++) {
  890. // "Find the code point pointed to by position in the second column of
  891. // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in
  892. // the first cell of the same row.
  893. //
  894. // "Append to buffer the six bits corresponding to n, most significant bit
  895. // first."
  896. //
  897. // atobLookup() implements the table from RFC 4648.
  898. buffer <<= 6;
  899. buffer |= atobLookup(data[i]);
  900. accumulatedBits += 6; // "If buffer has accumulated 24 bits, interpret them as three 8-bit
  901. // big-endian numbers. Append three bytes with values equal to those
  902. // numbers to output, in the same order, and then empty buffer."
  903. if (accumulatedBits === 24) {
  904. output += String.fromCharCode((buffer & 0xff0000) >> 16);
  905. output += String.fromCharCode((buffer & 0xff00) >> 8);
  906. output += String.fromCharCode(buffer & 0xff);
  907. buffer = accumulatedBits = 0;
  908. } // "Advance position by 1."
  909. } // "If buffer is not empty, it contains either 12 or 18 bits. If it contains
  910. // 12 bits, then discard the last four and interpret the remaining eight as
  911. // an 8-bit big-endian number. If it contains 18 bits, then discard the last
  912. // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append
  913. // the one or two bytes with values equal to those one or two numbers to
  914. // output, in the same order."
  915. if (accumulatedBits === 12) {
  916. buffer >>= 4;
  917. output += String.fromCharCode(buffer);
  918. } else if (accumulatedBits === 18) {
  919. buffer >>= 2;
  920. output += String.fromCharCode((buffer & 0xff00) >> 8);
  921. output += String.fromCharCode(buffer & 0xff);
  922. } // "Return output."
  923. return output;
  924. }
  925. /**
  926. * A lookup table for atob(), which converts an ASCII character to the
  927. * corresponding six-bit number.
  928. */
  929. const keystr$1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  930. function atobLookup(chr) {
  931. const index = keystr$1.indexOf(chr); // Throw exception if character is not in the lookup string; should not be hit in tests
  932. return index < 0 ? undefined : index;
  933. }
  934. var atob_1 = atob$2;
  935. /**
  936. * btoa() as defined by the HTML and Infra specs, which mostly just references
  937. * RFC 4648.
  938. */
  939. function btoa$2(s) {
  940. if (arguments.length === 0) {
  941. throw new TypeError("1 argument required, but only 0 present.");
  942. }
  943. let i; // String conversion as required by Web IDL.
  944. s = `${s}`; // "The btoa() method must throw an "InvalidCharacterError" DOMException if
  945. // data contains any character whose code point is greater than U+00FF."
  946. for (i = 0; i < s.length; i++) {
  947. if (s.charCodeAt(i) > 255) {
  948. return null;
  949. }
  950. }
  951. let out = "";
  952. for (i = 0; i < s.length; i += 3) {
  953. const groupsOfSix = [undefined, undefined, undefined, undefined];
  954. groupsOfSix[0] = s.charCodeAt(i) >> 2;
  955. groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4;
  956. if (s.length > i + 1) {
  957. groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4;
  958. groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2;
  959. }
  960. if (s.length > i + 2) {
  961. groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6;
  962. groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f;
  963. }
  964. for (let j = 0; j < groupsOfSix.length; j++) {
  965. if (typeof groupsOfSix[j] === "undefined") {
  966. out += "=";
  967. } else {
  968. out += btoaLookup(groupsOfSix[j]);
  969. }
  970. }
  971. }
  972. return out;
  973. }
  974. /**
  975. * Lookup table for btoa(), which converts a six-bit number into the
  976. * corresponding ASCII character.
  977. */
  978. const keystr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  979. function btoaLookup(index) {
  980. if (index >= 0 && index < 64) {
  981. return keystr[index];
  982. } // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests.
  983. return undefined;
  984. }
  985. var btoa_1 = btoa$2;
  986. const atob$1 = atob_1;
  987. const btoa$1 = btoa_1;
  988. var abab = {
  989. atob: atob$1,
  990. btoa: btoa$1
  991. };
  992. /*
  993. This program is distributed under the terms of the MIT license.
  994. Please see the LICENSE file for details.
  995. Copyright 2006-2018, OGG, LLC
  996. */
  997. /** Function: $build
  998. * Create a Strophe.Builder.
  999. * This is an alias for 'new Strophe.Builder(name, attrs)'.
  1000. *
  1001. * Parameters:
  1002. * (String) name - The root element name.
  1003. * (Object) attrs - The attributes for the root element in object notation.
  1004. *
  1005. * Returns:
  1006. * A new Strophe.Builder object.
  1007. */
  1008. function $build(name, attrs) {
  1009. return new Strophe.Builder(name, attrs);
  1010. }
  1011. /** Function: $msg
  1012. * Create a Strophe.Builder with a <message/> element as the root.
  1013. *
  1014. * Parameters:
  1015. * (Object) attrs - The <message/> element attributes in object notation.
  1016. *
  1017. * Returns:
  1018. * A new Strophe.Builder object.
  1019. */
  1020. function $msg(attrs) {
  1021. return new Strophe.Builder("message", attrs);
  1022. }
  1023. /** Function: $iq
  1024. * Create a Strophe.Builder with an <iq/> element as the root.
  1025. *
  1026. * Parameters:
  1027. * (Object) attrs - The <iq/> element attributes in object notation.
  1028. *
  1029. * Returns:
  1030. * A new Strophe.Builder object.
  1031. */
  1032. function $iq(attrs) {
  1033. return new Strophe.Builder("iq", attrs);
  1034. }
  1035. /** Function: $pres
  1036. * Create a Strophe.Builder with a <presence/> element as the root.
  1037. *
  1038. * Parameters:
  1039. * (Object) attrs - The <presence/> element attributes in object notation.
  1040. *
  1041. * Returns:
  1042. * A new Strophe.Builder object.
  1043. */
  1044. function $pres(attrs) {
  1045. return new Strophe.Builder("presence", attrs);
  1046. }
  1047. /** Class: Strophe
  1048. * An object container for all Strophe library functions.
  1049. *
  1050. * This class is just a container for all the objects and constants
  1051. * used in the library. It is not meant to be instantiated, but to
  1052. * provide a namespace for library objects, constants, and functions.
  1053. */
  1054. const Strophe = {
  1055. /** Constant: VERSION */
  1056. VERSION: "1.5.0",
  1057. /** Constants: XMPP Namespace Constants
  1058. * Common namespace constants from the XMPP RFCs and XEPs.
  1059. *
  1060. * NS.HTTPBIND - HTTP BIND namespace from XEP 124.
  1061. * NS.BOSH - BOSH namespace from XEP 206.
  1062. * NS.CLIENT - Main XMPP client namespace.
  1063. * NS.AUTH - Legacy authentication namespace.
  1064. * NS.ROSTER - Roster operations namespace.
  1065. * NS.PROFILE - Profile namespace.
  1066. * NS.DISCO_INFO - Service discovery info namespace from XEP 30.
  1067. * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
  1068. * NS.MUC - Multi-User Chat namespace from XEP 45.
  1069. * NS.SASL - XMPP SASL namespace from RFC 3920.
  1070. * NS.STREAM - XMPP Streams namespace from RFC 3920.
  1071. * NS.BIND - XMPP Binding namespace from RFC 3920 and RFC 6120.
  1072. * NS.SESSION - XMPP Session namespace from RFC 3920.
  1073. * NS.XHTML_IM - XHTML-IM namespace from XEP 71.
  1074. * NS.XHTML - XHTML body namespace from XEP 71.
  1075. */
  1076. NS: {
  1077. HTTPBIND: "http://jabber.org/protocol/httpbind",
  1078. BOSH: "urn:xmpp:xbosh",
  1079. CLIENT: "jabber:client",
  1080. AUTH: "jabber:iq:auth",
  1081. ROSTER: "jabber:iq:roster",
  1082. PROFILE: "jabber:iq:profile",
  1083. DISCO_INFO: "http://jabber.org/protocol/disco#info",
  1084. DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
  1085. MUC: "http://jabber.org/protocol/muc",
  1086. SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
  1087. STREAM: "http://etherx.jabber.org/streams",
  1088. FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
  1089. BIND: "urn:ietf:params:xml:ns:xmpp-bind",
  1090. SESSION: "urn:ietf:params:xml:ns:xmpp-session",
  1091. VERSION: "jabber:iq:version",
  1092. STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
  1093. XHTML_IM: "http://jabber.org/protocol/xhtml-im",
  1094. XHTML: "http://www.w3.org/1999/xhtml"
  1095. },
  1096. /** Constants: XHTML_IM Namespace
  1097. * contains allowed tags, tag attributes, and css properties.
  1098. * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.
  1099. * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended
  1100. * allowed tags and their attributes.
  1101. */
  1102. XHTML: {
  1103. tags: ['a', 'blockquote', 'br', 'cite', 'em', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul', 'body'],
  1104. attributes: {
  1105. 'a': ['href'],
  1106. 'blockquote': ['style'],
  1107. 'br': [],
  1108. 'cite': ['style'],
  1109. 'em': [],
  1110. 'img': ['src', 'alt', 'style', 'height', 'width'],
  1111. 'li': ['style'],
  1112. 'ol': ['style'],
  1113. 'p': ['style'],
  1114. 'span': ['style'],
  1115. 'strong': [],
  1116. 'ul': ['style'],
  1117. 'body': []
  1118. },
  1119. css: ['background-color', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'margin-left', 'margin-right', 'text-align', 'text-decoration'],
  1120. /** Function: XHTML.validTag
  1121. *
  1122. * Utility method to determine whether a tag is allowed
  1123. * in the XHTML_IM namespace.
  1124. *
  1125. * XHTML tag names are case sensitive and must be lower case.
  1126. */
  1127. validTag(tag) {
  1128. for (let i = 0; i < Strophe.XHTML.tags.length; i++) {
  1129. if (tag === Strophe.XHTML.tags[i]) {
  1130. return true;
  1131. }
  1132. }
  1133. return false;
  1134. },
  1135. /** Function: XHTML.validAttribute
  1136. *
  1137. * Utility method to determine whether an attribute is allowed
  1138. * as recommended per XEP-0071
  1139. *
  1140. * XHTML attribute names are case sensitive and must be lower case.
  1141. */
  1142. validAttribute(tag, attribute) {
  1143. if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {
  1144. for (let i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
  1145. if (attribute === Strophe.XHTML.attributes[tag][i]) {
  1146. return true;
  1147. }
  1148. }
  1149. }
  1150. return false;
  1151. },
  1152. validCSS(style) {
  1153. for (let i = 0; i < Strophe.XHTML.css.length; i++) {
  1154. if (style === Strophe.XHTML.css[i]) {
  1155. return true;
  1156. }
  1157. }
  1158. return false;
  1159. }
  1160. },
  1161. /** Constants: Connection Status Constants
  1162. * Connection status constants for use by the connection handler
  1163. * callback.
  1164. *
  1165. * Status.ERROR - An error has occurred
  1166. * Status.CONNECTING - The connection is currently being made
  1167. * Status.CONNFAIL - The connection attempt failed
  1168. * Status.AUTHENTICATING - The connection is authenticating
  1169. * Status.AUTHFAIL - The authentication attempt failed
  1170. * Status.CONNECTED - The connection has succeeded
  1171. * Status.DISCONNECTED - The connection has been terminated
  1172. * Status.DISCONNECTING - The connection is currently being terminated
  1173. * Status.ATTACHED - The connection has been attached
  1174. * Status.REDIRECT - The connection has been redirected
  1175. * Status.CONNTIMEOUT - The connection has timed out
  1176. */
  1177. Status: {
  1178. ERROR: 0,
  1179. CONNECTING: 1,
  1180. CONNFAIL: 2,
  1181. AUTHENTICATING: 3,
  1182. AUTHFAIL: 4,
  1183. CONNECTED: 5,
  1184. DISCONNECTED: 6,
  1185. DISCONNECTING: 7,
  1186. ATTACHED: 8,
  1187. REDIRECT: 9,
  1188. CONNTIMEOUT: 10,
  1189. BINDREQUIRED: 11,
  1190. ATTACHFAIL: 12
  1191. },
  1192. ErrorCondition: {
  1193. BAD_FORMAT: "bad-format",
  1194. CONFLICT: "conflict",
  1195. MISSING_JID_NODE: "x-strophe-bad-non-anon-jid",
  1196. NO_AUTH_MECH: "no-auth-mech",
  1197. UNKNOWN_REASON: "unknown"
  1198. },
  1199. /** Constants: Log Level Constants
  1200. * Logging level indicators.
  1201. *
  1202. * LogLevel.DEBUG - Debug output
  1203. * LogLevel.INFO - Informational output
  1204. * LogLevel.WARN - Warnings
  1205. * LogLevel.ERROR - Errors
  1206. * LogLevel.FATAL - Fatal errors
  1207. */
  1208. LogLevel: {
  1209. DEBUG: 0,
  1210. INFO: 1,
  1211. WARN: 2,
  1212. ERROR: 3,
  1213. FATAL: 4
  1214. },
  1215. /** PrivateConstants: DOM Element Type Constants
  1216. * DOM element types.
  1217. *
  1218. * ElementType.NORMAL - Normal element.
  1219. * ElementType.TEXT - Text data element.
  1220. * ElementType.FRAGMENT - XHTML fragment element.
  1221. */
  1222. ElementType: {
  1223. NORMAL: 1,
  1224. TEXT: 3,
  1225. CDATA: 4,
  1226. FRAGMENT: 11
  1227. },
  1228. /** PrivateConstants: Timeout Values
  1229. * Timeout values for error states. These values are in seconds.
  1230. * These should not be changed unless you know exactly what you are
  1231. * doing.
  1232. *
  1233. * TIMEOUT - Timeout multiplier. A waiting request will be considered
  1234. * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
  1235. * This defaults to 1.1, and with default wait, 66 seconds.
  1236. * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
  1237. * Strophe can detect early failure, it will consider the request
  1238. * failed if it doesn't return after
  1239. * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
  1240. * This defaults to 0.1, and with default wait, 6 seconds.
  1241. */
  1242. TIMEOUT: 1.1,
  1243. SECONDARY_TIMEOUT: 0.1,
  1244. /** Function: addNamespace
  1245. * This function is used to extend the current namespaces in
  1246. * Strophe.NS. It takes a key and a value with the key being the
  1247. * name of the new namespace, with its actual value.
  1248. * For example:
  1249. * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
  1250. *
  1251. * Parameters:
  1252. * (String) name - The name under which the namespace will be
  1253. * referenced under Strophe.NS
  1254. * (String) value - The actual namespace.
  1255. */
  1256. addNamespace(name, value) {
  1257. Strophe.NS[name] = value;
  1258. },
  1259. /** Function: forEachChild
  1260. * Map a function over some or all child elements of a given element.
  1261. *
  1262. * This is a small convenience function for mapping a function over
  1263. * some or all of the children of an element. If elemName is null, all
  1264. * children will be passed to the function, otherwise only children
  1265. * whose tag names match elemName will be passed.
  1266. *
  1267. * Parameters:
  1268. * (XMLElement) elem - The element to operate on.
  1269. * (String) elemName - The child element tag name filter.
  1270. * (Function) func - The function to apply to each child. This
  1271. * function should take a single argument, a DOM element.
  1272. */
  1273. forEachChild(elem, elemName, func) {
  1274. for (let i = 0; i < elem.childNodes.length; i++) {
  1275. const childNode = elem.childNodes[i];
  1276. if (childNode.nodeType === Strophe.ElementType.NORMAL && (!elemName || this.isTagEqual(childNode, elemName))) {
  1277. func(childNode);
  1278. }
  1279. }
  1280. },
  1281. /** Function: isTagEqual
  1282. * Compare an element's tag name with a string.
  1283. *
  1284. * This function is case sensitive.
  1285. *
  1286. * Parameters:
  1287. * (XMLElement) el - A DOM element.
  1288. * (String) name - The element name.
  1289. *
  1290. * Returns:
  1291. * true if the element's tag name matches _el_, and false
  1292. * otherwise.
  1293. */
  1294. isTagEqual(el, name) {
  1295. return el.tagName === name;
  1296. },
  1297. /** PrivateVariable: _xmlGenerator
  1298. * _Private_ variable that caches a DOM document to
  1299. * generate elements.
  1300. */
  1301. _xmlGenerator: null,
  1302. /** Function: xmlGenerator
  1303. * Get the DOM document to generate elements.
  1304. *
  1305. * Returns:
  1306. * The currently used DOM document.
  1307. */
  1308. xmlGenerator() {
  1309. if (!Strophe._xmlGenerator) {
  1310. Strophe._xmlGenerator = getDummyXMLDOMDocument();
  1311. }
  1312. return Strophe._xmlGenerator;
  1313. },
  1314. /** Function: xmlElement
  1315. * Create an XML DOM element.
  1316. *
  1317. * This function creates an XML DOM element correctly across all
  1318. * implementations. Note that these are not HTML DOM elements, which
  1319. * aren't appropriate for XMPP stanzas.
  1320. *
  1321. * Parameters:
  1322. * (String) name - The name for the element.
  1323. * (Array|Object) attrs - An optional array or object containing
  1324. * key/value pairs to use as element attributes. The object should
  1325. * be in the format {'key': 'value'} or {key: 'value'}. The array
  1326. * should have the format [['key1', 'value1'], ['key2', 'value2']].
  1327. * (String) text - The text child data for the element.
  1328. *
  1329. * Returns:
  1330. * A new XML DOM element.
  1331. */
  1332. xmlElement(name) {
  1333. if (!name) {
  1334. return null;
  1335. }
  1336. const node = Strophe.xmlGenerator().createElement(name); // FIXME: this should throw errors if args are the wrong type or
  1337. // there are more than two optional args
  1338. for (let a = 1; a < arguments.length; a++) {
  1339. const arg = arguments[a];
  1340. if (!arg) {
  1341. continue;
  1342. }
  1343. if (typeof arg === "string" || typeof arg === "number") {
  1344. node.appendChild(Strophe.xmlTextNode(arg));
  1345. } else if (typeof arg === "object" && typeof arg.sort === "function") {
  1346. for (let i = 0; i < arg.length; i++) {
  1347. const attr = arg[i];
  1348. if (typeof attr === "object" && typeof attr.sort === "function" && attr[1] !== undefined && attr[1] !== null) {
  1349. node.setAttribute(attr[0], attr[1]);
  1350. }
  1351. }
  1352. } else if (typeof arg === "object") {
  1353. for (const k in arg) {
  1354. if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) {
  1355. node.setAttribute(k, arg[k]);
  1356. }
  1357. }
  1358. }
  1359. }
  1360. return node;
  1361. },
  1362. /* Function: xmlescape
  1363. * Excapes invalid xml characters.
  1364. *
  1365. * Parameters:
  1366. * (String) text - text to escape.
  1367. *
  1368. * Returns:
  1369. * Escaped text.
  1370. */
  1371. xmlescape(text) {
  1372. text = text.replace(/\&/g, "&amp;");
  1373. text = text.replace(/</g, "&lt;");
  1374. text = text.replace(/>/g, "&gt;");
  1375. text = text.replace(/'/g, "&apos;");
  1376. text = text.replace(/"/g, "&quot;");
  1377. return text;
  1378. },
  1379. /* Function: xmlunescape
  1380. * Unexcapes invalid xml characters.
  1381. *
  1382. * Parameters:
  1383. * (String) text - text to unescape.
  1384. *
  1385. * Returns:
  1386. * Unescaped text.
  1387. */
  1388. xmlunescape(text) {
  1389. text = text.replace(/\&amp;/g, "&");
  1390. text = text.replace(/&lt;/g, "<");
  1391. text = text.replace(/&gt;/g, ">");
  1392. text = text.replace(/&apos;/g, "'");
  1393. text = text.replace(/&quot;/g, "\"");
  1394. return text;
  1395. },
  1396. /** Function: xmlTextNode
  1397. * Creates an XML DOM text node.
  1398. *
  1399. * Provides a cross implementation version of document.createTextNode.
  1400. *
  1401. * Parameters:
  1402. * (String) text - The content of the text node.
  1403. *
  1404. * Returns:
  1405. * A new XML DOM text node.
  1406. */
  1407. xmlTextNode(text) {
  1408. return Strophe.xmlGenerator().createTextNode(text);
  1409. },
  1410. /** Function: xmlHtmlNode
  1411. * Creates an XML DOM html node.
  1412. *
  1413. * Parameters:
  1414. * (String) html - The content of the html node.
  1415. *
  1416. * Returns:
  1417. * A new XML DOM text node.
  1418. */
  1419. xmlHtmlNode(html) {
  1420. let node; //ensure text is escaped
  1421. if (DOMParser) {
  1422. const parser = new DOMParser();
  1423. node = parser.parseFromString(html, "text/xml");
  1424. } else {
  1425. node = new ActiveXObject("Microsoft.XMLDOM");
  1426. node.async = "false";
  1427. node.loadXML(html);
  1428. }
  1429. return node;
  1430. },
  1431. /** Function: getText
  1432. * Get the concatenation of all text children of an element.
  1433. *
  1434. * Parameters:
  1435. * (XMLElement) elem - A DOM element.
  1436. *
  1437. * Returns:
  1438. * A String with the concatenated text of all text element children.
  1439. */
  1440. getText(elem) {
  1441. if (!elem) {
  1442. return null;
  1443. }
  1444. let str = "";
  1445. if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {
  1446. str += elem.nodeValue;
  1447. }
  1448. for (let i = 0; i < elem.childNodes.length; i++) {
  1449. if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {
  1450. str += elem.childNodes[i].nodeValue;
  1451. }
  1452. }
  1453. return Strophe.xmlescape(str);
  1454. },
  1455. /** Function: copyElement
  1456. * Copy an XML DOM element.
  1457. *
  1458. * This function copies a DOM element and all its descendants and returns
  1459. * the new copy.
  1460. *
  1461. * Parameters:
  1462. * (XMLElement) elem - A DOM element.
  1463. *
  1464. * Returns:
  1465. * A new, copied DOM element tree.
  1466. */
  1467. copyElement(elem) {
  1468. let el;
  1469. if (elem.nodeType === Strophe.ElementType.NORMAL) {
  1470. el = Strophe.xmlElement(elem.tagName);
  1471. for (let i = 0; i < elem.attributes.length; i++) {
  1472. el.setAttribute(elem.attributes[i].nodeName, elem.attributes[i].value);
  1473. }
  1474. for (let i = 0; i < elem.childNodes.length; i++) {
  1475. el.appendChild(Strophe.copyElement(elem.childNodes[i]));
  1476. }
  1477. } else if (elem.nodeType === Strophe.ElementType.TEXT) {
  1478. el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);
  1479. }
  1480. return el;
  1481. },
  1482. /** Function: createHtml
  1483. * Copy an HTML DOM element into an XML DOM.
  1484. *
  1485. * This function copies a DOM element and all its descendants and returns
  1486. * the new copy.
  1487. *
  1488. * Parameters:
  1489. * (HTMLElement) elem - A DOM element.
  1490. *
  1491. * Returns:
  1492. * A new, copied DOM element tree.
  1493. */
  1494. createHtml(elem) {
  1495. let el;
  1496. if (elem.nodeType === Strophe.ElementType.NORMAL) {
  1497. const tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.
  1498. if (Strophe.XHTML.validTag(tag)) {
  1499. try {
  1500. el = Strophe.xmlElement(tag);
  1501. for (let i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
  1502. const attribute = Strophe.XHTML.attributes[tag][i];
  1503. let value = elem.getAttribute(attribute);
  1504. if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {
  1505. continue;
  1506. }
  1507. if (attribute === 'style' && typeof value === 'object' && typeof value.cssText !== 'undefined') {
  1508. value = value.cssText; // we're dealing with IE, need to get CSS out
  1509. } // filter out invalid css styles
  1510. if (attribute === 'style') {
  1511. const css = [];
  1512. const cssAttrs = value.split(';');
  1513. for (let j = 0; j < cssAttrs.length; j++) {
  1514. const attr = cssAttrs[j].split(':');
  1515. const cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
  1516. if (Strophe.XHTML.validCSS(cssName)) {
  1517. const cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
  1518. css.push(cssName + ': ' + cssValue);
  1519. }
  1520. }
  1521. if (css.length > 0) {
  1522. value = css.join('; ');
  1523. el.setAttribute(attribute, value);
  1524. }
  1525. } else {
  1526. el.setAttribute(attribute, value);
  1527. }
  1528. }
  1529. for (let i = 0; i < elem.childNodes.length; i++) {
  1530. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  1531. }
  1532. } catch (e) {
  1533. // invalid elements
  1534. el = Strophe.xmlTextNode('');
  1535. }
  1536. } else {
  1537. el = Strophe.xmlGenerator().createDocumentFragment();
  1538. for (let i = 0; i < elem.childNodes.length; i++) {
  1539. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  1540. }
  1541. }
  1542. } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {
  1543. el = Strophe.xmlGenerator().createDocumentFragment();
  1544. for (let i = 0; i < elem.childNodes.length; i++) {
  1545. el.appendChild(Strophe.createHtml(elem.childNodes[i]));
  1546. }
  1547. } else if (elem.nodeType === Strophe.ElementType.TEXT) {
  1548. el = Strophe.xmlTextNode(elem.nodeValue);
  1549. }
  1550. return el;
  1551. },
  1552. /** Function: escapeNode
  1553. * Escape the node part (also called local part) of a JID.
  1554. *
  1555. * Parameters:
  1556. * (String) node - A node (or local part).
  1557. *
  1558. * Returns:
  1559. * An escaped node (or local part).
  1560. */
  1561. escapeNode(node) {
  1562. if (typeof node !== "string") {
  1563. return node;
  1564. }
  1565. return node.replace(/^\s+|\s+$/g, '').replace(/\\/g, "\\5c").replace(/ /g, "\\20").replace(/\"/g, "\\22").replace(/\&/g, "\\26").replace(/\'/g, "\\27").replace(/\//g, "\\2f").replace(/:/g, "\\3a").replace(/</g, "\\3c").replace(/>/g, "\\3e").replace(/@/g, "\\40");
  1566. },
  1567. /** Function: unescapeNode
  1568. * Unescape a node part (also called local part) of a JID.
  1569. *
  1570. * Parameters:
  1571. * (String) node - A node (or local part).
  1572. *
  1573. * Returns:
  1574. * An unescaped node (or local part).
  1575. */
  1576. unescapeNode(node) {
  1577. if (typeof node !== "string") {
  1578. return node;
  1579. }
  1580. return node.replace(/\\20/g, " ").replace(/\\22/g, '"').replace(/\\26/g, "&").replace(/\\27/g, "'").replace(/\\2f/g, "/").replace(/\\3a/g, ":").replace(/\\3c/g, "<").replace(/\\3e/g, ">").replace(/\\40/g, "@").replace(/\\5c/g, "\\");
  1581. },
  1582. /** Function: getNodeFromJid
  1583. * Get the node portion of a JID String.
  1584. *
  1585. * Parameters:
  1586. * (String) jid - A JID.
  1587. *
  1588. * Returns:
  1589. * A String containing the node.
  1590. */
  1591. getNodeFromJid(jid) {
  1592. if (jid.indexOf("@") < 0) {
  1593. return null;
  1594. }
  1595. return jid.split("@")[0];
  1596. },
  1597. /** Function: getDomainFromJid
  1598. * Get the domain portion of a JID String.
  1599. *
  1600. * Parameters:
  1601. * (String) jid - A JID.
  1602. *
  1603. * Returns:
  1604. * A String containing the domain.
  1605. */
  1606. getDomainFromJid(jid) {
  1607. const bare = Strophe.getBareJidFromJid(jid);
  1608. if (bare.indexOf("@") < 0) {
  1609. return bare;
  1610. } else {
  1611. const parts = bare.split("@");
  1612. parts.splice(0, 1);
  1613. return parts.join('@');
  1614. }
  1615. },
  1616. /** Function: getResourceFromJid
  1617. * Get the resource portion of a JID String.
  1618. *
  1619. * Parameters:
  1620. * (String) jid - A JID.
  1621. *
  1622. * Returns:
  1623. * A String containing the resource.
  1624. */
  1625. getResourceFromJid(jid) {
  1626. if (!jid) {
  1627. return null;
  1628. }
  1629. const s = jid.split("/");
  1630. if (s.length < 2) {
  1631. return null;
  1632. }
  1633. s.splice(0, 1);
  1634. return s.join('/');
  1635. },
  1636. /** Function: getBareJidFromJid
  1637. * Get the bare JID from a JID String.
  1638. *
  1639. * Parameters:
  1640. * (String) jid - A JID.
  1641. *
  1642. * Returns:
  1643. * A String containing the bare JID.
  1644. */
  1645. getBareJidFromJid(jid) {
  1646. return jid ? jid.split("/")[0] : null;
  1647. },
  1648. /** PrivateFunction: _handleError
  1649. * _Private_ function that properly logs an error to the console
  1650. */
  1651. _handleError(e) {
  1652. if (typeof e.stack !== "undefined") {
  1653. Strophe.fatal(e.stack);
  1654. }
  1655. if (e.sourceURL) {
  1656. Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" + e.line + " - " + e.name + ": " + e.message);
  1657. } else if (e.fileName) {
  1658. Strophe.fatal("error: " + this.handler + " " + e.fileName + ":" + e.lineNumber + " - " + e.name + ": " + e.message);
  1659. } else {
  1660. Strophe.fatal("error: " + e.message);
  1661. }
  1662. },
  1663. /** Function: log
  1664. * User overrideable logging function.
  1665. *
  1666. * This function is called whenever the Strophe library calls any
  1667. * of the logging functions. The default implementation of this
  1668. * function logs only fatal errors. If client code wishes to handle the logging
  1669. * messages, it should override this with
  1670. * > Strophe.log = function (level, msg) {
  1671. * > (user code here)
  1672. * > };
  1673. *
  1674. * Please note that data sent and received over the wire is logged
  1675. * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
  1676. *
  1677. * The different levels and their meanings are
  1678. *
  1679. * DEBUG - Messages useful for debugging purposes.
  1680. * INFO - Informational messages. This is mostly information like
  1681. * 'disconnect was called' or 'SASL auth succeeded'.
  1682. * WARN - Warnings about potential problems. This is mostly used
  1683. * to report transient connection errors like request timeouts.
  1684. * ERROR - Some error occurred.
  1685. * FATAL - A non-recoverable fatal error occurred.
  1686. *
  1687. * Parameters:
  1688. * (Integer) level - The log level of the log message. This will
  1689. * be one of the values in Strophe.LogLevel.
  1690. * (String) msg - The log message.
  1691. */
  1692. log(level, msg) {
  1693. if (level === this.LogLevel.FATAL) {
  1694. var _console;
  1695. (_console = console) === null || _console === void 0 ? void 0 : _console.error(msg);
  1696. }
  1697. },
  1698. /** Function: debug
  1699. * Log a message at the Strophe.LogLevel.DEBUG level.
  1700. *
  1701. * Parameters:
  1702. * (String) msg - The log message.
  1703. */
  1704. debug(msg) {
  1705. this.log(this.LogLevel.DEBUG, msg);
  1706. },
  1707. /** Function: info
  1708. * Log a message at the Strophe.LogLevel.INFO level.
  1709. *
  1710. * Parameters:
  1711. * (String) msg - The log message.
  1712. */
  1713. info(msg) {
  1714. this.log(this.LogLevel.INFO, msg);
  1715. },
  1716. /** Function: warn
  1717. * Log a message at the Strophe.LogLevel.WARN level.
  1718. *
  1719. * Parameters:
  1720. * (String) msg - The log message.
  1721. */
  1722. warn(msg) {
  1723. this.log(this.LogLevel.WARN, msg);
  1724. },
  1725. /** Function: error
  1726. * Log a message at the Strophe.LogLevel.ERROR level.
  1727. *
  1728. * Parameters:
  1729. * (String) msg - The log message.
  1730. */
  1731. error(msg) {
  1732. this.log(this.LogLevel.ERROR, msg);
  1733. },
  1734. /** Function: fatal
  1735. * Log a message at the Strophe.LogLevel.FATAL level.
  1736. *
  1737. * Parameters:
  1738. * (String) msg - The log message.
  1739. */
  1740. fatal(msg) {
  1741. this.log(this.LogLevel.FATAL, msg);
  1742. },
  1743. /** Function: serialize
  1744. * Render a DOM element and all descendants to a String.
  1745. *
  1746. * Parameters:
  1747. * (XMLElement) elem - A DOM element.
  1748. *
  1749. * Returns:
  1750. * The serialized element tree as a String.
  1751. */
  1752. serialize(elem) {
  1753. if (!elem) {
  1754. return null;
  1755. }
  1756. if (typeof elem.tree === "function") {
  1757. elem = elem.tree();
  1758. }
  1759. const names = [...Array(elem.attributes.length).keys()].map(i => elem.attributes[i].nodeName);
  1760. names.sort();
  1761. let result = names.reduce((a, n) => `${a} ${n}="${Strophe.xmlescape(elem.attributes.getNamedItem(n).value)}"`, `<${elem.nodeName}`);
  1762. if (elem.childNodes.length > 0) {
  1763. result += ">";
  1764. for (let i = 0; i < elem.childNodes.length; i++) {
  1765. const child = elem.childNodes[i];
  1766. switch (child.nodeType) {
  1767. case Strophe.ElementType.NORMAL:
  1768. // normal element, so recurse
  1769. result += Strophe.serialize(child);
  1770. break;
  1771. case Strophe.ElementType.TEXT:
  1772. // text element to escape values
  1773. result += Strophe.xmlescape(child.nodeValue);
  1774. break;
  1775. case Strophe.ElementType.CDATA:
  1776. // cdata section so don't escape values
  1777. result += "<![CDATA[" + child.nodeValue + "]]>";
  1778. }
  1779. }
  1780. result += "</" + elem.nodeName + ">";
  1781. } else {
  1782. result += "/>";
  1783. }
  1784. return result;
  1785. },
  1786. /** PrivateVariable: _requestId
  1787. * _Private_ variable that keeps track of the request ids for
  1788. * connections.
  1789. */
  1790. _requestId: 0,
  1791. /** PrivateVariable: Strophe.connectionPlugins
  1792. * _Private_ variable Used to store plugin names that need
  1793. * initialization on Strophe.Connection construction.
  1794. */
  1795. _connectionPlugins: {},
  1796. /** Function: addConnectionPlugin
  1797. * Extends the Strophe.Connection object with the given plugin.
  1798. *
  1799. * Parameters:
  1800. * (String) name - The name of the extension.
  1801. * (Object) ptype - The plugin's prototype.
  1802. */
  1803. addConnectionPlugin(name, ptype) {
  1804. Strophe._connectionPlugins[name] = ptype;
  1805. }
  1806. };
  1807. /** Class: Strophe.Builder
  1808. * XML DOM builder.
  1809. *
  1810. * This object provides an interface similar to JQuery but for building
  1811. * DOM elements easily and rapidly. All the functions except for toString()
  1812. * and tree() return the object, so calls can be chained. Here's an
  1813. * example using the $iq() builder helper.
  1814. * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
  1815. * > .c('query', {xmlns: 'strophe:example'})
  1816. * > .c('example')
  1817. * > .toString()
  1818. *
  1819. * The above generates this XML fragment
  1820. * > <iq to='you' from='me' type='get' id='1'>
  1821. * > <query xmlns='strophe:example'>
  1822. * > <example/>
  1823. * > </query>
  1824. * > </iq>
  1825. * The corresponding DOM manipulations to get a similar fragment would be
  1826. * a lot more tedious and probably involve several helper variables.
  1827. *
  1828. * Since adding children makes new operations operate on the child, up()
  1829. * is provided to traverse up the tree. To add two children, do
  1830. * > builder.c('child1', ...).up().c('child2', ...)
  1831. * The next operation on the Builder will be relative to the second child.
  1832. */
  1833. /** Constructor: Strophe.Builder
  1834. * Create a Strophe.Builder object.
  1835. *
  1836. * The attributes should be passed in object notation. For example
  1837. * > let b = new Builder('message', {to: 'you', from: 'me'});
  1838. * or
  1839. * > let b = new Builder('messsage', {'xml:lang': 'en'});
  1840. *
  1841. * Parameters:
  1842. * (String) name - The name of the root element.
  1843. * (Object) attrs - The attributes for the root element in object notation.
  1844. *
  1845. * Returns:
  1846. * A new Strophe.Builder.
  1847. */
  1848. Strophe.Builder = class Builder {
  1849. constructor(name, attrs) {
  1850. // Set correct namespace for jabber:client elements
  1851. if (name === "presence" || name === "message" || name === "iq") {
  1852. if (attrs && !attrs.xmlns) {
  1853. attrs.xmlns = Strophe.NS.CLIENT;
  1854. } else if (!attrs) {
  1855. attrs = {
  1856. xmlns: Strophe.NS.CLIENT
  1857. };
  1858. }
  1859. } // Holds the tree being built.
  1860. this.nodeTree = Strophe.xmlElement(name, attrs); // Points to the current operation node.
  1861. this.node = this.nodeTree;
  1862. }
  1863. /** Function: tree
  1864. * Return the DOM tree.
  1865. *
  1866. * This function returns the current DOM tree as an element object. This
  1867. * is suitable for passing to functions like Strophe.Connection.send().
  1868. *
  1869. * Returns:
  1870. * The DOM tree as a element object.
  1871. */
  1872. tree() {
  1873. return this.nodeTree;
  1874. }
  1875. /** Function: toString
  1876. * Serialize the DOM tree to a String.
  1877. *
  1878. * This function returns a string serialization of the current DOM
  1879. * tree. It is often used internally to pass data to a
  1880. * Strophe.Request object.
  1881. *
  1882. * Returns:
  1883. * The serialized DOM tree in a String.
  1884. */
  1885. toString() {
  1886. return Strophe.serialize(this.nodeTree);
  1887. }
  1888. /** Function: up
  1889. * Make the current parent element the new current element.
  1890. *
  1891. * This function is often used after c() to traverse back up the tree.
  1892. * For example, to add two children to the same element
  1893. * > builder.c('child1', {}).up().c('child2', {});
  1894. *
  1895. * Returns:
  1896. * The Stophe.Builder object.
  1897. */
  1898. up() {
  1899. this.node = this.node.parentNode;
  1900. return this;
  1901. }
  1902. /** Function: root
  1903. * Make the root element the new current element.
  1904. *
  1905. * When at a deeply nested element in the tree, this function can be used
  1906. * to jump back to the root of the tree, instead of having to repeatedly
  1907. * call up().
  1908. *
  1909. * Returns:
  1910. * The Stophe.Builder object.
  1911. */
  1912. root() {
  1913. this.node = this.nodeTree;
  1914. return this;
  1915. }
  1916. /** Function: attrs
  1917. * Add or modify attributes of the current element.
  1918. *
  1919. * The attributes should be passed in object notation. This function
  1920. * does not move the current element pointer.
  1921. *
  1922. * Parameters:
  1923. * (Object) moreattrs - The attributes to add/modify in object notation.
  1924. *
  1925. * Returns:
  1926. * The Strophe.Builder object.
  1927. */
  1928. attrs(moreattrs) {
  1929. for (const k in moreattrs) {
  1930. if (Object.prototype.hasOwnProperty.call(moreattrs, k)) {
  1931. if (moreattrs[k] === undefined) {
  1932. this.node.removeAttribute(k);
  1933. } else {
  1934. this.node.setAttribute(k, moreattrs[k]);
  1935. }
  1936. }
  1937. }
  1938. return this;
  1939. }
  1940. /** Function: c
  1941. * Add a child to the current element and make it the new current
  1942. * element.
  1943. *
  1944. * This function moves the current element pointer to the child,
  1945. * unless text is provided. If you need to add another child, it
  1946. * is necessary to use up() to go back to the parent in the tree.
  1947. *
  1948. * Parameters:
  1949. * (String) name - The name of the child.
  1950. * (Object) attrs - The attributes of the child in object notation.
  1951. * (String) text - The text to add to the child.
  1952. *
  1953. * Returns:
  1954. * The Strophe.Builder object.
  1955. */
  1956. c(name, attrs, text) {
  1957. const child = Strophe.xmlElement(name, attrs, text);
  1958. this.node.appendChild(child);
  1959. if (typeof text !== "string" && typeof text !== "number") {
  1960. this.node = child;
  1961. }
  1962. return this;
  1963. }
  1964. /** Function: cnode
  1965. * Add a child to the current element and make it the new current
  1966. * element.
  1967. *
  1968. * This function is the same as c() except that instead of using a
  1969. * name and an attributes object to create the child it uses an
  1970. * existing DOM element object.
  1971. *
  1972. * Parameters:
  1973. * (XMLElement) elem - A DOM element.
  1974. *
  1975. * Returns:
  1976. * The Strophe.Builder object.
  1977. */
  1978. cnode(elem) {
  1979. let impNode;
  1980. const xmlGen = Strophe.xmlGenerator();
  1981. try {
  1982. impNode = xmlGen.importNode !== undefined;
  1983. } catch (e) {
  1984. impNode = false;
  1985. }
  1986. const newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);
  1987. this.node.appendChild(newElem);
  1988. this.node = newElem;
  1989. return this;
  1990. }
  1991. /** Function: t
  1992. * Add a child text element.
  1993. *
  1994. * This *does not* make the child the new current element since there
  1995. * are no children of text elements.
  1996. *
  1997. * Parameters:
  1998. * (String) text - The text data to append to the current element.
  1999. *
  2000. * Returns:
  2001. * The Strophe.Builder object.
  2002. */
  2003. t(text) {
  2004. const child = Strophe.xmlTextNode(text);
  2005. this.node.appendChild(child);
  2006. return this;
  2007. }
  2008. /** Function: h
  2009. * Replace current element contents with the HTML passed in.
  2010. *
  2011. * This *does not* make the child the new current element
  2012. *
  2013. * Parameters:
  2014. * (String) html - The html to insert as contents of current element.
  2015. *
  2016. * Returns:
  2017. * The Strophe.Builder object.
  2018. */
  2019. h(html) {
  2020. const fragment = Strophe.xmlGenerator().createElement('body'); // force the browser to try and fix any invalid HTML tags
  2021. fragment.innerHTML = html; // copy cleaned html into an xml dom
  2022. const xhtml = Strophe.createHtml(fragment);
  2023. while (xhtml.childNodes.length > 0) {
  2024. this.node.appendChild(xhtml.childNodes[0]);
  2025. }
  2026. return this;
  2027. }
  2028. };
  2029. /** PrivateClass: Strophe.Handler
  2030. * _Private_ helper class for managing stanza handlers.
  2031. *
  2032. * A Strophe.Handler encapsulates a user provided callback function to be
  2033. * executed when matching stanzas are received by the connection.
  2034. * Handlers can be either one-off or persistant depending on their
  2035. * return value. Returning true will cause a Handler to remain active, and
  2036. * returning false will remove the Handler.
  2037. *
  2038. * Users will not use Strophe.Handler objects directly, but instead they
  2039. * will use Strophe.Connection.addHandler() and
  2040. * Strophe.Connection.deleteHandler().
  2041. */
  2042. /** PrivateConstructor: Strophe.Handler
  2043. * Create and initialize a new Strophe.Handler.
  2044. *
  2045. * Parameters:
  2046. * (Function) handler - A function to be executed when the handler is run.
  2047. * (String) ns - The namespace to match.
  2048. * (String) name - The element name to match.
  2049. * (String) type - The element type to match.
  2050. * (String) id - The element id attribute to match.
  2051. * (String) from - The element from attribute to match.
  2052. * (Object) options - Handler options
  2053. *
  2054. * Returns:
  2055. * A new Strophe.Handler object.
  2056. */
  2057. Strophe.Handler = function (handler, ns, name, type, id, from, options) {
  2058. this.handler = handler;
  2059. this.ns = ns;
  2060. this.name = name;
  2061. this.type = type;
  2062. this.id = id;
  2063. this.options = options || {
  2064. 'matchBareFromJid': false,
  2065. 'ignoreNamespaceFragment': false
  2066. }; // BBB: Maintain backward compatibility with old `matchBare` option
  2067. if (this.options.matchBare) {
  2068. Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.');
  2069. this.options.matchBareFromJid = this.options.matchBare;
  2070. delete this.options.matchBare;
  2071. }
  2072. if (this.options.matchBareFromJid) {
  2073. this.from = from ? Strophe.getBareJidFromJid(from) : null;
  2074. } else {
  2075. this.from = from;
  2076. } // whether the handler is a user handler or a system handler
  2077. this.user = true;
  2078. };
  2079. Strophe.Handler.prototype = {
  2080. /** PrivateFunction: getNamespace
  2081. * Returns the XML namespace attribute on an element.
  2082. * If `ignoreNamespaceFragment` was passed in for this handler, then the
  2083. * URL fragment will be stripped.
  2084. *
  2085. * Parameters:
  2086. * (XMLElement) elem - The XML element with the namespace.
  2087. *
  2088. * Returns:
  2089. * The namespace, with optionally the fragment stripped.
  2090. */
  2091. getNamespace(elem) {
  2092. let elNamespace = elem.getAttribute("xmlns");
  2093. if (elNamespace && this.options.ignoreNamespaceFragment) {
  2094. elNamespace = elNamespace.split('#')[0];
  2095. }
  2096. return elNamespace;
  2097. },
  2098. /** PrivateFunction: namespaceMatch
  2099. * Tests if a stanza matches the namespace set for this Strophe.Handler.
  2100. *
  2101. * Parameters:
  2102. * (XMLElement) elem - The XML element to test.
  2103. *
  2104. * Returns:
  2105. * true if the stanza matches and false otherwise.
  2106. */
  2107. namespaceMatch(elem) {
  2108. let nsMatch = false;
  2109. if (!this.ns) {
  2110. return true;
  2111. } else {
  2112. Strophe.forEachChild(elem, null, elem => {
  2113. if (this.getNamespace(elem) === this.ns) {
  2114. nsMatch = true;
  2115. }
  2116. });
  2117. return nsMatch || this.getNamespace(elem) === this.ns;
  2118. }
  2119. },
  2120. /** PrivateFunction: isMatch
  2121. * Tests if a stanza matches the Strophe.Handler.
  2122. *
  2123. * Parameters:
  2124. * (XMLElement) elem - The XML element to test.
  2125. *
  2126. * Returns:
  2127. * true if the stanza matches and false otherwise.
  2128. */
  2129. isMatch(elem) {
  2130. let from = elem.getAttribute('from');
  2131. if (this.options.matchBareFromJid) {
  2132. from = Strophe.getBareJidFromJid(from);
  2133. }
  2134. const elem_type = elem.getAttribute("type");
  2135. if (this.namespaceMatch(elem) && (!this.name || Strophe.isTagEqual(elem, this.name)) && (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) && (!this.id || elem.getAttribute("id") === this.id) && (!this.from || from === this.from)) {
  2136. return true;
  2137. }
  2138. return false;
  2139. },
  2140. /** PrivateFunction: run
  2141. * Run the callback on a matching stanza.
  2142. *
  2143. * Parameters:
  2144. * (XMLElement) elem - The DOM element that triggered the
  2145. * Strophe.Handler.
  2146. *
  2147. * Returns:
  2148. * A boolean indicating if the handler should remain active.
  2149. */
  2150. run(elem) {
  2151. let result = null;
  2152. try {
  2153. result = this.handler(elem);
  2154. } catch (e) {
  2155. Strophe._handleError(e);
  2156. throw e;
  2157. }
  2158. return result;
  2159. },
  2160. /** PrivateFunction: toString
  2161. * Get a String representation of the Strophe.Handler object.
  2162. *
  2163. * Returns:
  2164. * A String.
  2165. */
  2166. toString() {
  2167. return "{Handler: " + this.handler + "(" + this.name + "," + this.id + "," + this.ns + ")}";
  2168. }
  2169. };
  2170. /** PrivateClass: Strophe.TimedHandler
  2171. * _Private_ helper class for managing timed handlers.
  2172. *
  2173. * A Strophe.TimedHandler encapsulates a user provided callback that
  2174. * should be called after a certain period of time or at regular
  2175. * intervals. The return value of the callback determines whether the
  2176. * Strophe.TimedHandler will continue to fire.
  2177. *
  2178. * Users will not use Strophe.TimedHandler objects directly, but instead
  2179. * they will use Strophe.Connection.addTimedHandler() and
  2180. * Strophe.Connection.deleteTimedHandler().
  2181. */
  2182. Strophe.TimedHandler = class TimedHandler {
  2183. /** PrivateConstructor: Strophe.TimedHandler
  2184. * Create and initialize a new Strophe.TimedHandler object.
  2185. *
  2186. * Parameters:
  2187. * (Integer) period - The number of milliseconds to wait before the
  2188. * handler is called.
  2189. * (Function) handler - The callback to run when the handler fires. This
  2190. * function should take no arguments.
  2191. *
  2192. * Returns:
  2193. * A new Strophe.TimedHandler object.
  2194. */
  2195. constructor(period, handler) {
  2196. this.period = period;
  2197. this.handler = handler;
  2198. this.lastCalled = new Date().getTime();
  2199. this.user = true;
  2200. }
  2201. /** PrivateFunction: run
  2202. * Run the callback for the Strophe.TimedHandler.
  2203. *
  2204. * Returns:
  2205. * true if the Strophe.TimedHandler should be called again, and false
  2206. * otherwise.
  2207. */
  2208. run() {
  2209. this.lastCalled = new Date().getTime();
  2210. return this.handler();
  2211. }
  2212. /** PrivateFunction: reset
  2213. * Reset the last called time for the Strophe.TimedHandler.
  2214. */
  2215. reset() {
  2216. this.lastCalled = new Date().getTime();
  2217. }
  2218. /** PrivateFunction: toString
  2219. * Get a string representation of the Strophe.TimedHandler object.
  2220. *
  2221. * Returns:
  2222. * The string representation.
  2223. */
  2224. toString() {
  2225. return "{TimedHandler: " + this.handler + "(" + this.period + ")}";
  2226. }
  2227. };
  2228. /** Class: Strophe.Connection
  2229. * XMPP Connection manager.
  2230. *
  2231. * This class is the main part of Strophe. It manages a BOSH or websocket
  2232. * connection to an XMPP server and dispatches events to the user callbacks
  2233. * as data arrives. It supports SASL PLAIN, SASL SCRAM-SHA-1
  2234. * and legacy authentication.
  2235. *
  2236. * After creating a Strophe.Connection object, the user will typically
  2237. * call connect() with a user supplied callback to handle connection level
  2238. * events like authentication failure, disconnection, or connection
  2239. * complete.
  2240. *
  2241. * The user will also have several event handlers defined by using
  2242. * addHandler() and addTimedHandler(). These will allow the user code to
  2243. * respond to interesting stanzas or do something periodically with the
  2244. * connection. These handlers will be active once authentication is
  2245. * finished.
  2246. *
  2247. * To send data to the connection, use send().
  2248. */
  2249. /** Constructor: Strophe.Connection
  2250. * Create and initialize a Strophe.Connection object.
  2251. *
  2252. * The transport-protocol for this connection will be chosen automatically
  2253. * based on the given service parameter. URLs starting with "ws://" or
  2254. * "wss://" will use WebSockets, URLs starting with "http://", "https://"
  2255. * or without a protocol will use BOSH.
  2256. *
  2257. * To make Strophe connect to the current host you can leave out the protocol
  2258. * and host part and just pass the path, e.g.
  2259. *
  2260. * > let conn = new Strophe.Connection("/http-bind/");
  2261. *
  2262. * Options common to both Websocket and BOSH:
  2263. * ------------------------------------------
  2264. *
  2265. * cookies:
  2266. *
  2267. * The *cookies* option allows you to pass in cookies to be added to the
  2268. * document. These cookies will then be included in the BOSH XMLHttpRequest
  2269. * or in the websocket connection.
  2270. *
  2271. * The passed in value must be a map of cookie names and string values.
  2272. *
  2273. * > { "myCookie": {
  2274. * > "value": "1234",
  2275. * > "domain": ".example.org",
  2276. * > "path": "/",
  2277. * > "expires": expirationDate
  2278. * > }
  2279. * > }
  2280. *
  2281. * Note that cookies can't be set in this way for other domains (i.e. cross-domain).
  2282. * Those cookies need to be set under those domains, for example they can be
  2283. * set server-side by making a XHR call to that domain to ask it to set any
  2284. * necessary cookies.
  2285. *
  2286. * mechanisms:
  2287. *
  2288. * The *mechanisms* option allows you to specify the SASL mechanisms that this
  2289. * instance of Strophe.Connection (and therefore your XMPP client) will
  2290. * support.
  2291. *
  2292. * The value must be an array of objects with Strophe.SASLMechanism
  2293. * prototypes.
  2294. *
  2295. * If nothing is specified, then the following mechanisms (and their
  2296. * priorities) are registered:
  2297. *
  2298. * SCRAM-SHA-1 - 60
  2299. * PLAIN - 50
  2300. * OAUTHBEARER - 40
  2301. * X-OAUTH2 - 30
  2302. * ANONYMOUS - 20
  2303. * EXTERNAL - 10
  2304. *
  2305. * explicitResourceBinding:
  2306. *
  2307. * If `explicitResourceBinding` is set to a truthy value, then the XMPP client
  2308. * needs to explicitly call `Strophe.Connection.prototype.bind` once the XMPP
  2309. * server has advertised the "urn:ietf:params:xml:ns:xmpp-bind" feature.
  2310. *
  2311. * Making this step explicit allows client authors to first finish other
  2312. * stream related tasks, such as setting up an XEP-0198 Stream Management
  2313. * session, before binding the JID resource for this session.
  2314. *
  2315. * WebSocket options:
  2316. * ------------------
  2317. *
  2318. * protocol:
  2319. *
  2320. * If you want to connect to the current host with a WebSocket connection you
  2321. * can tell Strophe to use WebSockets through a "protocol" attribute in the
  2322. * optional options parameter. Valid values are "ws" for WebSocket and "wss"
  2323. * for Secure WebSocket.
  2324. * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call
  2325. *
  2326. * > let conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
  2327. *
  2328. * Note that relative URLs _NOT_ starting with a "/" will also include the path
  2329. * of the current site.
  2330. *
  2331. * Also because downgrading security is not permitted by browsers, when using
  2332. * relative URLs both BOSH and WebSocket connections will use their secure
  2333. * variants if the current connection to the site is also secure (https).
  2334. *
  2335. * worker:
  2336. *
  2337. * Set this option to URL from where the shared worker script should be loaded.
  2338. *
  2339. * To run the websocket connection inside a shared worker.
  2340. * This allows you to share a single websocket-based connection between
  2341. * multiple Strophe.Connection instances, for example one per browser tab.
  2342. *
  2343. * The script to use is the one in `src/shared-connection-worker.js`.
  2344. *
  2345. * BOSH options:
  2346. * -------------
  2347. *
  2348. * By adding "sync" to the options, you can control if requests will
  2349. * be made synchronously or not. The default behaviour is asynchronous.
  2350. * If you want to make requests synchronous, make "sync" evaluate to true.
  2351. * > let conn = new Strophe.Connection("/http-bind/", {sync: true});
  2352. *
  2353. * You can also toggle this on an already established connection.
  2354. * > conn.options.sync = true;
  2355. *
  2356. * The *customHeaders* option can be used to provide custom HTTP headers to be
  2357. * included in the XMLHttpRequests made.
  2358. *
  2359. * The *keepalive* option can be used to instruct Strophe to maintain the
  2360. * current BOSH session across interruptions such as webpage reloads.
  2361. *
  2362. * It will do this by caching the sessions tokens in sessionStorage, and when
  2363. * "restore" is called it will check whether there are cached tokens with
  2364. * which it can resume an existing session.
  2365. *
  2366. * The *withCredentials* option should receive a Boolean value and is used to
  2367. * indicate wether cookies should be included in ajax requests (by default
  2368. * they're not).
  2369. * Set this value to true if you are connecting to a BOSH service
  2370. * and for some reason need to send cookies to it.
  2371. * In order for this to work cross-domain, the server must also enable
  2372. * credentials by setting the Access-Control-Allow-Credentials response header
  2373. * to "true". For most usecases however this setting should be false (which
  2374. * is the default).
  2375. * Additionally, when using Access-Control-Allow-Credentials, the
  2376. * Access-Control-Allow-Origin header can't be set to the wildcard "*", but
  2377. * instead must be restricted to actual domains.
  2378. *
  2379. * The *contentType* option can be set to change the default Content-Type
  2380. * of "text/xml; charset=utf-8", which can be useful to reduce the amount of
  2381. * CORS preflight requests that are sent to the server.
  2382. *
  2383. * Parameters:
  2384. * (String) service - The BOSH or WebSocket service URL.
  2385. * (Object) options - A hash of configuration options
  2386. *
  2387. * Returns:
  2388. * A new Strophe.Connection object.
  2389. */
  2390. Strophe.Connection = class Connection {
  2391. constructor(service, options) {
  2392. // The service URL
  2393. this.service = service; // Configuration options
  2394. this.options = options || {};
  2395. this.setProtocol();
  2396. /* The connected JID. */
  2397. this.jid = "";
  2398. /* the JIDs domain */
  2399. this.domain = null;
  2400. /* stream:features */
  2401. this.features = null; // SASL
  2402. this._sasl_data = {};
  2403. this.do_bind = false;
  2404. this.do_session = false;
  2405. this.mechanisms = {}; // handler lists
  2406. this.timedHandlers = [];
  2407. this.handlers = [];
  2408. this.removeTimeds = [];
  2409. this.removeHandlers = [];
  2410. this.addTimeds = [];
  2411. this.addHandlers = [];
  2412. this.protocolErrorHandlers = {
  2413. 'HTTP': {},
  2414. 'websocket': {}
  2415. };
  2416. this._idleTimeout = null;
  2417. this._disconnectTimeout = null;
  2418. this.authenticated = false;
  2419. this.connected = false;
  2420. this.disconnecting = false;
  2421. this.do_authentication = true;
  2422. this.paused = false;
  2423. this.restored = false;
  2424. this._data = [];
  2425. this._uniqueId = 0;
  2426. this._sasl_success_handler = null;
  2427. this._sasl_failure_handler = null;
  2428. this._sasl_challenge_handler = null; // Max retries before disconnecting
  2429. this.maxRetries = 5; // Call onIdle callback every 1/10th of a second
  2430. this._idleTimeout = setTimeout(() => this._onIdle(), 100);
  2431. utils.addCookies(this.options.cookies);
  2432. this.registerSASLMechanisms(this.options.mechanisms); // A client must always respond to incoming IQ "set" and "get" stanzas.
  2433. // See https://datatracker.ietf.org/doc/html/rfc6120#section-8.2.3
  2434. //
  2435. // This is a fallback handler which gets called when no other handler
  2436. // was called for a received IQ "set" or "get".
  2437. this.iqFallbackHandler = new Strophe.Handler(iq => this.send($iq({
  2438. type: 'error',
  2439. id: iq.getAttribute('id')
  2440. }).c('error', {
  2441. 'type': 'cancel'
  2442. }).c('service-unavailable', {
  2443. 'xmlns': Strophe.NS.STANZAS
  2444. })), null, 'iq', ['get', 'set']); // initialize plugins
  2445. for (const k in Strophe._connectionPlugins) {
  2446. if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
  2447. const F = function () {};
  2448. F.prototype = Strophe._connectionPlugins[k];
  2449. this[k] = new F();
  2450. this[k].init(this);
  2451. }
  2452. }
  2453. }
  2454. /** Function: setProtocol
  2455. * Select protocal based on this.options or this.service
  2456. */
  2457. setProtocol() {
  2458. const proto = this.options.protocol || "";
  2459. if (this.options.worker) {
  2460. this._proto = new Strophe.WorkerWebsocket(this);
  2461. } else if (this.service.indexOf("ws:") === 0 || this.service.indexOf("wss:") === 0 || proto.indexOf("ws") === 0) {
  2462. this._proto = new Strophe.Websocket(this);
  2463. } else {
  2464. this._proto = new Strophe.Bosh(this);
  2465. }
  2466. }
  2467. /** Function: reset
  2468. * Reset the connection.
  2469. *
  2470. * This function should be called after a connection is disconnected
  2471. * before that connection is reused.
  2472. */
  2473. reset() {
  2474. this._proto._reset(); // SASL
  2475. this.do_session = false;
  2476. this.do_bind = false; // handler lists
  2477. this.timedHandlers = [];
  2478. this.handlers = [];
  2479. this.removeTimeds = [];
  2480. this.removeHandlers = [];
  2481. this.addTimeds = [];
  2482. this.addHandlers = [];
  2483. this.authenticated = false;
  2484. this.connected = false;
  2485. this.disconnecting = false;
  2486. this.restored = false;
  2487. this._data = [];
  2488. this._requests = [];
  2489. this._uniqueId = 0;
  2490. }
  2491. /** Function: pause
  2492. * Pause the request manager.
  2493. *
  2494. * This will prevent Strophe from sending any more requests to the
  2495. * server. This is very useful for temporarily pausing
  2496. * BOSH-Connections while a lot of send() calls are happening quickly.
  2497. * This causes Strophe to send the data in a single request, saving
  2498. * many request trips.
  2499. */
  2500. pause() {
  2501. this.paused = true;
  2502. }
  2503. /** Function: resume
  2504. * Resume the request manager.
  2505. *
  2506. * This resumes after pause() has been called.
  2507. */
  2508. resume() {
  2509. this.paused = false;
  2510. }
  2511. /** Function: getUniqueId
  2512. * Generate a unique ID for use in <iq/> elements.
  2513. *
  2514. * All <iq/> stanzas are required to have unique id attributes. This
  2515. * function makes creating these easy. Each connection instance has
  2516. * a counter which starts from zero, and the value of this counter
  2517. * plus a colon followed by the suffix becomes the unique id. If no
  2518. * suffix is supplied, the counter is used as the unique id.
  2519. *
  2520. * Suffixes are used to make debugging easier when reading the stream
  2521. * data, and their use is recommended. The counter resets to 0 for
  2522. * every new connection for the same reason. For connections to the
  2523. * same server that authenticate the same way, all the ids should be
  2524. * the same, which makes it easy to see changes. This is useful for
  2525. * automated testing as well.
  2526. *
  2527. * Parameters:
  2528. * (String) suffix - A optional suffix to append to the id.
  2529. *
  2530. * Returns:
  2531. * A unique string to be used for the id attribute.
  2532. */
  2533. getUniqueId(suffix) {
  2534. // eslint-disable-line class-methods-use-this
  2535. const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  2536. const r = Math.random() * 16 | 0,
  2537. v = c === 'x' ? r : r & 0x3 | 0x8;
  2538. return v.toString(16);
  2539. });
  2540. if (typeof suffix === "string" || typeof suffix === "number") {
  2541. return uuid + ":" + suffix;
  2542. } else {
  2543. return uuid + "";
  2544. }
  2545. }
  2546. /** Function: addProtocolErrorHandler
  2547. * Register a handler function for when a protocol (websocker or HTTP)
  2548. * error occurs.
  2549. *
  2550. * NOTE: Currently only HTTP errors for BOSH requests are handled.
  2551. * Patches that handle websocket errors would be very welcome.
  2552. *
  2553. * Parameters:
  2554. * (String) protocol - 'HTTP' or 'websocket'
  2555. * (Integer) status_code - Error status code (e.g 500, 400 or 404)
  2556. * (Function) callback - Function that will fire on Http error
  2557. *
  2558. * Example:
  2559. * function onError(err_code){
  2560. * //do stuff
  2561. * }
  2562. *
  2563. * let conn = Strophe.connect('http://example.com/http-bind');
  2564. * conn.addProtocolErrorHandler('HTTP', 500, onError);
  2565. * // Triggers HTTP 500 error and onError handler will be called
  2566. * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);
  2567. */
  2568. addProtocolErrorHandler(protocol, status_code, callback) {
  2569. this.protocolErrorHandlers[protocol][status_code] = callback;
  2570. }
  2571. /** Function: connect
  2572. * Starts the connection process.
  2573. *
  2574. * As the connection process proceeds, the user supplied callback will
  2575. * be triggered multiple times with status updates. The callback
  2576. * should take two arguments - the status code and the error condition.
  2577. *
  2578. * The status code will be one of the values in the Strophe.Status
  2579. * constants. The error condition will be one of the conditions
  2580. * defined in RFC 3920 or the condition 'strophe-parsererror'.
  2581. *
  2582. * The Parameters _wait_, _hold_ and _route_ are optional and only relevant
  2583. * for BOSH connections. Please see XEP 124 for a more detailed explanation
  2584. * of the optional parameters.
  2585. *
  2586. * Parameters:
  2587. * (String) jid - The user's JID. This may be a bare JID,
  2588. * or a full JID. If a node is not supplied, SASL OAUTHBEARER or
  2589. * SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will
  2590. * process the provided password value as an access token).
  2591. * (String) pass - The user's password.
  2592. * (Function) callback - The connect callback function.
  2593. * (Integer) wait - The optional HTTPBIND wait value. This is the
  2594. * time the server will wait before returning an empty result for
  2595. * a request. The default setting of 60 seconds is recommended.
  2596. * (Integer) hold - The optional HTTPBIND hold value. This is the
  2597. * number of connections the server will hold at one time. This
  2598. * should almost always be set to 1 (the default).
  2599. * (String) route - The optional route value.
  2600. * (String) authcid - The optional alternative authentication identity
  2601. * (username) if intending to impersonate another user.
  2602. * When using the SASL-EXTERNAL authentication mechanism, for example
  2603. * with client certificates, then the authcid value is used to
  2604. * determine whether an authorization JID (authzid) should be sent to
  2605. * the server. The authzid should NOT be sent to the server if the
  2606. * authzid and authcid are the same. So to prevent it from being sent
  2607. * (for example when the JID is already contained in the client
  2608. * certificate), set authcid to that same JID. See XEP-178 for more
  2609. * details.
  2610. * (Integer) disconnection_timeout - The optional disconnection timeout
  2611. * in milliseconds before _doDisconnect will be called.
  2612. */
  2613. connect(jid, pass, callback, wait, hold, route, authcid) {
  2614. let disconnection_timeout = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 3000;
  2615. this.jid = jid;
  2616. /** Variable: authzid
  2617. * Authorization identity.
  2618. */
  2619. this.authzid = Strophe.getBareJidFromJid(this.jid);
  2620. /** Variable: authcid
  2621. * Authentication identity (User name).
  2622. */
  2623. this.authcid = authcid || Strophe.getNodeFromJid(this.jid);
  2624. /** Variable: pass
  2625. * Authentication identity (User password).
  2626. */
  2627. this.pass = pass;
  2628. this.connect_callback = callback;
  2629. this.disconnecting = false;
  2630. this.connected = false;
  2631. this.authenticated = false;
  2632. this.restored = false;
  2633. this.disconnection_timeout = disconnection_timeout; // parse jid for domain
  2634. this.domain = Strophe.getDomainFromJid(this.jid);
  2635. this._changeConnectStatus(Strophe.Status.CONNECTING, null);
  2636. this._proto._connect(wait, hold, route);
  2637. }
  2638. /** Function: attach
  2639. * Attach to an already created and authenticated BOSH session.
  2640. *
  2641. * This function is provided to allow Strophe to attach to BOSH
  2642. * sessions which have been created externally, perhaps by a Web
  2643. * application. This is often used to support auto-login type features
  2644. * without putting user credentials into the page.
  2645. *
  2646. * Parameters:
  2647. * (String) jid - The full JID that is bound by the session.
  2648. * (String) sid - The SID of the BOSH session.
  2649. * (String) rid - The current RID of the BOSH session. This RID
  2650. * will be used by the next request.
  2651. * (Function) callback The connect callback function.
  2652. * (Integer) wait - The optional HTTPBIND wait value. This is the
  2653. * time the server will wait before returning an empty result for
  2654. * a request. The default setting of 60 seconds is recommended.
  2655. * Other settings will require tweaks to the Strophe.TIMEOUT value.
  2656. * (Integer) hold - The optional HTTPBIND hold value. This is the
  2657. * number of connections the server will hold at one time. This
  2658. * should almost always be set to 1 (the default).
  2659. * (Integer) wind - The optional HTTBIND window value. This is the
  2660. * allowed range of request ids that are valid. The default is 5.
  2661. */
  2662. attach(jid, sid, rid, callback, wait, hold, wind) {
  2663. if (this._proto._attach) {
  2664. return this._proto._attach(jid, sid, rid, callback, wait, hold, wind);
  2665. } else {
  2666. const error = new Error('The "attach" method is not available for your connection protocol');
  2667. error.name = 'StropheSessionError';
  2668. throw error;
  2669. }
  2670. }
  2671. /** Function: restore
  2672. * Attempt to restore a cached BOSH session.
  2673. *
  2674. * This function is only useful in conjunction with providing the
  2675. * "keepalive":true option when instantiating a new Strophe.Connection.
  2676. *
  2677. * When "keepalive" is set to true, Strophe will cache the BOSH tokens
  2678. * RID (Request ID) and SID (Session ID) and then when this function is
  2679. * called, it will attempt to restore the session from those cached
  2680. * tokens.
  2681. *
  2682. * This function must therefore be called instead of connect or attach.
  2683. *
  2684. * For an example on how to use it, please see examples/restore.js
  2685. *
  2686. * Parameters:
  2687. * (String) jid - The user's JID. This may be a bare JID or a full JID.
  2688. * (Function) callback - The connect callback function.
  2689. * (Integer) wait - The optional HTTPBIND wait value. This is the
  2690. * time the server will wait before returning an empty result for
  2691. * a request. The default setting of 60 seconds is recommended.
  2692. * (Integer) hold - The optional HTTPBIND hold value. This is the
  2693. * number of connections the server will hold at one time. This
  2694. * should almost always be set to 1 (the default).
  2695. * (Integer) wind - The optional HTTBIND window value. This is the
  2696. * allowed range of request ids that are valid. The default is 5.
  2697. */
  2698. restore(jid, callback, wait, hold, wind) {
  2699. if (this._sessionCachingSupported()) {
  2700. this._proto._restore(jid, callback, wait, hold, wind);
  2701. } else {
  2702. const error = new Error('The "restore" method can only be used with a BOSH connection.');
  2703. error.name = 'StropheSessionError';
  2704. throw error;
  2705. }
  2706. }
  2707. /** PrivateFunction: _sessionCachingSupported
  2708. * Checks whether sessionStorage and JSON are supported and whether we're
  2709. * using BOSH.
  2710. */
  2711. _sessionCachingSupported() {
  2712. if (this._proto instanceof Strophe.Bosh) {
  2713. if (!JSON) {
  2714. return false;
  2715. }
  2716. try {
  2717. sessionStorage.setItem('_strophe_', '_strophe_');
  2718. sessionStorage.removeItem('_strophe_');
  2719. } catch (e) {
  2720. return false;
  2721. }
  2722. return true;
  2723. }
  2724. return false;
  2725. }
  2726. /** Function: xmlInput
  2727. * User overrideable function that receives XML data coming into the
  2728. * connection.
  2729. *
  2730. * The default function does nothing. User code can override this with
  2731. * > Strophe.Connection.xmlInput = function (elem) {
  2732. * > (user code)
  2733. * > };
  2734. *
  2735. * Due to limitations of current Browsers' XML-Parsers the opening and closing
  2736. * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
  2737. *
  2738. * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
  2739. * <Strophe.Bosh.strip> if you want to strip this tag.
  2740. *
  2741. * Parameters:
  2742. * (XMLElement) elem - The XML data received by the connection.
  2743. */
  2744. xmlInput(elem) {
  2745. // eslint-disable-line
  2746. return;
  2747. }
  2748. /** Function: xmlOutput
  2749. * User overrideable function that receives XML data sent to the
  2750. * connection.
  2751. *
  2752. * The default function does nothing. User code can override this with
  2753. * > Strophe.Connection.xmlOutput = function (elem) {
  2754. * > (user code)
  2755. * > };
  2756. *
  2757. * Due to limitations of current Browsers' XML-Parsers the opening and closing
  2758. * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
  2759. *
  2760. * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
  2761. * <Strophe.Bosh.strip> if you want to strip this tag.
  2762. *
  2763. * Parameters:
  2764. * (XMLElement) elem - The XMLdata sent by the connection.
  2765. */
  2766. xmlOutput(elem) {
  2767. // eslint-disable-line
  2768. return;
  2769. }
  2770. /** Function: rawInput
  2771. * User overrideable function that receives raw data coming into the
  2772. * connection.
  2773. *
  2774. * The default function does nothing. User code can override this with
  2775. * > Strophe.Connection.rawInput = function (data) {
  2776. * > (user code)
  2777. * > };
  2778. *
  2779. * Parameters:
  2780. * (String) data - The data received by the connection.
  2781. */
  2782. rawInput(data) {
  2783. // eslint-disable-line
  2784. return;
  2785. }
  2786. /** Function: rawOutput
  2787. * User overrideable function that receives raw data sent to the
  2788. * connection.
  2789. *
  2790. * The default function does nothing. User code can override this with
  2791. * > Strophe.Connection.rawOutput = function (data) {
  2792. * > (user code)
  2793. * > };
  2794. *
  2795. * Parameters:
  2796. * (String) data - The data sent by the connection.
  2797. */
  2798. rawOutput(data) {
  2799. // eslint-disable-line
  2800. return;
  2801. }
  2802. /** Function: nextValidRid
  2803. * User overrideable function that receives the new valid rid.
  2804. *
  2805. * The default function does nothing. User code can override this with
  2806. * > Strophe.Connection.nextValidRid = function (rid) {
  2807. * > (user code)
  2808. * > };
  2809. *
  2810. * Parameters:
  2811. * (Number) rid - The next valid rid
  2812. */
  2813. nextValidRid(rid) {
  2814. // eslint-disable-line
  2815. return;
  2816. }
  2817. /** Function: send
  2818. * Send a stanza.
  2819. *
  2820. * This function is called to push data onto the send queue to
  2821. * go out over the wire. Whenever a request is sent to the BOSH
  2822. * server, all pending data is sent and the queue is flushed.
  2823. *
  2824. * Parameters:
  2825. * (XMLElement |
  2826. * [XMLElement] |
  2827. * Strophe.Builder) elem - The stanza to send.
  2828. */
  2829. send(elem) {
  2830. if (elem === null) {
  2831. return;
  2832. }
  2833. if (typeof elem.sort === "function") {
  2834. for (let i = 0; i < elem.length; i++) {
  2835. this._queueData(elem[i]);
  2836. }
  2837. } else if (typeof elem.tree === "function") {
  2838. this._queueData(elem.tree());
  2839. } else {
  2840. this._queueData(elem);
  2841. }
  2842. this._proto._send();
  2843. }
  2844. /** Function: flush
  2845. * Immediately send any pending outgoing data.
  2846. *
  2847. * Normally send() queues outgoing data until the next idle period
  2848. * (100ms), which optimizes network use in the common cases when
  2849. * several send()s are called in succession. flush() can be used to
  2850. * immediately send all pending data.
  2851. */
  2852. flush() {
  2853. // cancel the pending idle period and run the idle function
  2854. // immediately
  2855. clearTimeout(this._idleTimeout);
  2856. this._onIdle();
  2857. }
  2858. /** Function: sendPresence
  2859. * Helper function to send presence stanzas. The main benefit is for
  2860. * sending presence stanzas for which you expect a responding presence
  2861. * stanza with the same id (for example when leaving a chat room).
  2862. *
  2863. * Parameters:
  2864. * (XMLElement) elem - The stanza to send.
  2865. * (Function) callback - The callback function for a successful request.
  2866. * (Function) errback - The callback function for a failed or timed
  2867. * out request. On timeout, the stanza will be null.
  2868. * (Integer) timeout - The time specified in milliseconds for a
  2869. * timeout to occur.
  2870. *
  2871. * Returns:
  2872. * The id used to send the presence.
  2873. */
  2874. sendPresence(elem, callback, errback, timeout) {
  2875. let timeoutHandler = null;
  2876. if (typeof elem.tree === "function") {
  2877. elem = elem.tree();
  2878. }
  2879. let id = elem.getAttribute('id');
  2880. if (!id) {
  2881. // inject id if not found
  2882. id = this.getUniqueId("sendPresence");
  2883. elem.setAttribute("id", id);
  2884. }
  2885. if (typeof callback === "function" || typeof errback === "function") {
  2886. const handler = this.addHandler(stanza => {
  2887. // remove timeout handler if there is one
  2888. if (timeoutHandler) {
  2889. this.deleteTimedHandler(timeoutHandler);
  2890. }
  2891. if (stanza.getAttribute('type') === 'error') {
  2892. if (errback) {
  2893. errback(stanza);
  2894. }
  2895. } else if (callback) {
  2896. callback(stanza);
  2897. }
  2898. }, null, 'presence', null, id); // if timeout specified, set up a timeout handler.
  2899. if (timeout) {
  2900. timeoutHandler = this.addTimedHandler(timeout, () => {
  2901. // get rid of normal handler
  2902. this.deleteHandler(handler); // call errback on timeout with null stanza
  2903. if (errback) {
  2904. errback(null);
  2905. }
  2906. return false;
  2907. });
  2908. }
  2909. }
  2910. this.send(elem);
  2911. return id;
  2912. }
  2913. /** Function: sendIQ
  2914. * Helper function to send IQ stanzas.
  2915. *
  2916. * Parameters:
  2917. * (XMLElement) elem - The stanza to send.
  2918. * (Function) callback - The callback function for a successful request.
  2919. * (Function) errback - The callback function for a failed or timed
  2920. * out request. On timeout, the stanza will be null.
  2921. * (Integer) timeout - The time specified in milliseconds for a
  2922. * timeout to occur.
  2923. *
  2924. * Returns:
  2925. * The id used to send the IQ.
  2926. */
  2927. sendIQ(elem, callback, errback, timeout) {
  2928. let timeoutHandler = null;
  2929. if (typeof elem.tree === "function") {
  2930. elem = elem.tree();
  2931. }
  2932. let id = elem.getAttribute('id');
  2933. if (!id) {
  2934. // inject id if not found
  2935. id = this.getUniqueId("sendIQ");
  2936. elem.setAttribute("id", id);
  2937. }
  2938. if (typeof callback === "function" || typeof errback === "function") {
  2939. const handler = this.addHandler(stanza => {
  2940. // remove timeout handler if there is one
  2941. if (timeoutHandler) {
  2942. this.deleteTimedHandler(timeoutHandler);
  2943. }
  2944. const iqtype = stanza.getAttribute('type');
  2945. if (iqtype === 'result') {
  2946. if (callback) {
  2947. callback(stanza);
  2948. }
  2949. } else if (iqtype === 'error') {
  2950. if (errback) {
  2951. errback(stanza);
  2952. }
  2953. } else {
  2954. const error = new Error(`Got bad IQ type of ${iqtype}`);
  2955. error.name = "StropheError";
  2956. throw error;
  2957. }
  2958. }, null, 'iq', ['error', 'result'], id); // if timeout specified, set up a timeout handler.
  2959. if (timeout) {
  2960. timeoutHandler = this.addTimedHandler(timeout, () => {
  2961. // get rid of normal handler
  2962. this.deleteHandler(handler); // call errback on timeout with null stanza
  2963. if (errback) {
  2964. errback(null);
  2965. }
  2966. return false;
  2967. });
  2968. }
  2969. }
  2970. this.send(elem);
  2971. return id;
  2972. }
  2973. /** PrivateFunction: _queueData
  2974. * Queue outgoing data for later sending. Also ensures that the data
  2975. * is a DOMElement.
  2976. */
  2977. _queueData(element) {
  2978. if (element === null || !element.tagName || !element.childNodes) {
  2979. const error = new Error("Cannot queue non-DOMElement.");
  2980. error.name = "StropheError";
  2981. throw error;
  2982. }
  2983. this._data.push(element);
  2984. }
  2985. /** PrivateFunction: _sendRestart
  2986. * Send an xmpp:restart stanza.
  2987. */
  2988. _sendRestart() {
  2989. this._data.push("restart");
  2990. this._proto._sendRestart();
  2991. this._idleTimeout = setTimeout(() => this._onIdle(), 100);
  2992. }
  2993. /** Function: addTimedHandler
  2994. * Add a timed handler to the connection.
  2995. *
  2996. * This function adds a timed handler. The provided handler will
  2997. * be called every period milliseconds until it returns false,
  2998. * the connection is terminated, or the handler is removed. Handlers
  2999. * that wish to continue being invoked should return true.
  3000. *
  3001. * Because of method binding it is necessary to save the result of
  3002. * this function if you wish to remove a handler with
  3003. * deleteTimedHandler().
  3004. *
  3005. * Note that user handlers are not active until authentication is
  3006. * successful.
  3007. *
  3008. * Parameters:
  3009. * (Integer) period - The period of the handler.
  3010. * (Function) handler - The callback function.
  3011. *
  3012. * Returns:
  3013. * A reference to the handler that can be used to remove it.
  3014. */
  3015. addTimedHandler(period, handler) {
  3016. const thand = new Strophe.TimedHandler(period, handler);
  3017. this.addTimeds.push(thand);
  3018. return thand;
  3019. }
  3020. /** Function: deleteTimedHandler
  3021. * Delete a timed handler for a connection.
  3022. *
  3023. * This function removes a timed handler from the connection. The
  3024. * handRef parameter is *not* the function passed to addTimedHandler(),
  3025. * but is the reference returned from addTimedHandler().
  3026. *
  3027. * Parameters:
  3028. * (Strophe.TimedHandler) handRef - The handler reference.
  3029. */
  3030. deleteTimedHandler(handRef) {
  3031. // this must be done in the Idle loop so that we don't change
  3032. // the handlers during iteration
  3033. this.removeTimeds.push(handRef);
  3034. }
  3035. /** Function: addHandler
  3036. * Add a stanza handler for the connection.
  3037. *
  3038. * This function adds a stanza handler to the connection. The
  3039. * handler callback will be called for any stanza that matches
  3040. * the parameters. Note that if multiple parameters are supplied,
  3041. * they must all match for the handler to be invoked.
  3042. *
  3043. * The handler will receive the stanza that triggered it as its argument.
  3044. * *The handler should return true if it is to be invoked again;
  3045. * returning false will remove the handler after it returns.*
  3046. *
  3047. * As a convenience, the ns parameters applies to the top level element
  3048. * and also any of its immediate children. This is primarily to make
  3049. * matching /iq/query elements easy.
  3050. *
  3051. * Options
  3052. * ~~~~~~~
  3053. * With the options argument, you can specify boolean flags that affect how
  3054. * matches are being done.
  3055. *
  3056. * Currently two flags exist:
  3057. *
  3058. * - matchBareFromJid:
  3059. * When set to true, the from parameter and the
  3060. * from attribute on the stanza will be matched as bare JIDs instead
  3061. * of full JIDs. To use this, pass {matchBareFromJid: true} as the
  3062. * value of options. The default value for matchBareFromJid is false.
  3063. *
  3064. * - ignoreNamespaceFragment:
  3065. * When set to true, a fragment specified on the stanza's namespace
  3066. * URL will be ignored when it's matched with the one configured for
  3067. * the handler.
  3068. *
  3069. * This means that if you register like this:
  3070. * > connection.addHandler(
  3071. * > handler,
  3072. * > 'http://jabber.org/protocol/muc',
  3073. * > null, null, null, null,
  3074. * > {'ignoreNamespaceFragment': true}
  3075. * > );
  3076. *
  3077. * Then a stanza with XML namespace of
  3078. * 'http://jabber.org/protocol/muc#user' will also be matched. If
  3079. * 'ignoreNamespaceFragment' is false, then only stanzas with
  3080. * 'http://jabber.org/protocol/muc' will be matched.
  3081. *
  3082. * Deleting the handler
  3083. * ~~~~~~~~~~~~~~~~~~~~
  3084. * The return value should be saved if you wish to remove the handler
  3085. * with deleteHandler().
  3086. *
  3087. * Parameters:
  3088. * (Function) handler - The user callback.
  3089. * (String) ns - The namespace to match.
  3090. * (String) name - The stanza name to match.
  3091. * (String|Array) type - The stanza type (or types if an array) to match.
  3092. * (String) id - The stanza id attribute to match.
  3093. * (String) from - The stanza from attribute to match.
  3094. * (String) options - The handler options
  3095. *
  3096. * Returns:
  3097. * A reference to the handler that can be used to remove it.
  3098. */
  3099. addHandler(handler, ns, name, type, id, from, options) {
  3100. const hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
  3101. this.addHandlers.push(hand);
  3102. return hand;
  3103. }
  3104. /** Function: deleteHandler
  3105. * Delete a stanza handler for a connection.
  3106. *
  3107. * This function removes a stanza handler from the connection. The
  3108. * handRef parameter is *not* the function passed to addHandler(),
  3109. * but is the reference returned from addHandler().
  3110. *
  3111. * Parameters:
  3112. * (Strophe.Handler) handRef - The handler reference.
  3113. */
  3114. deleteHandler(handRef) {
  3115. // this must be done in the Idle loop so that we don't change
  3116. // the handlers during iteration
  3117. this.removeHandlers.push(handRef); // If a handler is being deleted while it is being added,
  3118. // prevent it from getting added
  3119. const i = this.addHandlers.indexOf(handRef);
  3120. if (i >= 0) {
  3121. this.addHandlers.splice(i, 1);
  3122. }
  3123. }
  3124. /** Function: registerSASLMechanisms
  3125. *
  3126. * Register the SASL mechanisms which will be supported by this instance of
  3127. * Strophe.Connection (i.e. which this XMPP client will support).
  3128. *
  3129. * Parameters:
  3130. * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes
  3131. *
  3132. */
  3133. registerSASLMechanisms(mechanisms) {
  3134. this.mechanisms = {};
  3135. mechanisms = mechanisms || [Strophe.SASLAnonymous, Strophe.SASLExternal, Strophe.SASLOAuthBearer, Strophe.SASLXOAuth2, Strophe.SASLPlain, Strophe.SASLSHA1];
  3136. mechanisms.forEach(m => this.registerSASLMechanism(m));
  3137. }
  3138. /** Function: registerSASLMechanism
  3139. *
  3140. * Register a single SASL mechanism, to be supported by this client.
  3141. *
  3142. * Parameters:
  3143. * (Object) mechanism - Object with a Strophe.SASLMechanism prototype
  3144. *
  3145. */
  3146. registerSASLMechanism(Mechanism) {
  3147. const mechanism = new Mechanism();
  3148. this.mechanisms[mechanism.mechname] = mechanism;
  3149. }
  3150. /** Function: disconnect
  3151. * Start the graceful disconnection process.
  3152. *
  3153. * This function starts the disconnection process. This process starts
  3154. * by sending unavailable presence and sending BOSH body of type
  3155. * terminate. A timeout handler makes sure that disconnection happens
  3156. * even if the BOSH server does not respond.
  3157. * If the Connection object isn't connected, at least tries to abort all pending requests
  3158. * so the connection object won't generate successful requests (which were already opened).
  3159. *
  3160. * The user supplied connection callback will be notified of the
  3161. * progress as this process happens.
  3162. *
  3163. * Parameters:
  3164. * (String) reason - The reason the disconnect is occuring.
  3165. */
  3166. disconnect(reason) {
  3167. this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
  3168. if (reason) {
  3169. Strophe.warn("Disconnect was called because: " + reason);
  3170. } else {
  3171. Strophe.info("Disconnect was called");
  3172. }
  3173. if (this.connected) {
  3174. let pres = false;
  3175. this.disconnecting = true;
  3176. if (this.authenticated) {
  3177. pres = $pres({
  3178. 'xmlns': Strophe.NS.CLIENT,
  3179. 'type': 'unavailable'
  3180. });
  3181. } // setup timeout handler
  3182. this._disconnectTimeout = this._addSysTimedHandler(this.disconnection_timeout, this._onDisconnectTimeout.bind(this));
  3183. this._proto._disconnect(pres);
  3184. } else {
  3185. Strophe.warn("Disconnect was called before Strophe connected to the server");
  3186. this._proto._abortAllRequests();
  3187. this._doDisconnect();
  3188. }
  3189. }
  3190. /** PrivateFunction: _changeConnectStatus
  3191. * _Private_ helper function that makes sure plugins and the user's
  3192. * callback are notified of connection status changes.
  3193. *
  3194. * Parameters:
  3195. * (Integer) status - the new connection status, one of the values
  3196. * in Strophe.Status
  3197. * (String) condition - the error condition or null
  3198. * (XMLElement) elem - The triggering stanza.
  3199. */
  3200. _changeConnectStatus(status, condition, elem) {
  3201. // notify all plugins listening for status changes
  3202. for (const k in Strophe._connectionPlugins) {
  3203. if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
  3204. const plugin = this[k];
  3205. if (plugin.statusChanged) {
  3206. try {
  3207. plugin.statusChanged(status, condition);
  3208. } catch (err) {
  3209. Strophe.error(`${k} plugin caused an exception changing status: ${err}`);
  3210. }
  3211. }
  3212. }
  3213. } // notify the user's callback
  3214. if (this.connect_callback) {
  3215. try {
  3216. this.connect_callback(status, condition, elem);
  3217. } catch (e) {
  3218. Strophe._handleError(e);
  3219. Strophe.error(`User connection callback caused an exception: ${e}`);
  3220. }
  3221. }
  3222. }
  3223. /** PrivateFunction: _doDisconnect
  3224. * _Private_ function to disconnect.
  3225. *
  3226. * This is the last piece of the disconnection logic. This resets the
  3227. * connection and alerts the user's connection callback.
  3228. */
  3229. _doDisconnect(condition) {
  3230. if (typeof this._idleTimeout === "number") {
  3231. clearTimeout(this._idleTimeout);
  3232. } // Cancel Disconnect Timeout
  3233. if (this._disconnectTimeout !== null) {
  3234. this.deleteTimedHandler(this._disconnectTimeout);
  3235. this._disconnectTimeout = null;
  3236. }
  3237. Strophe.debug("_doDisconnect was called");
  3238. this._proto._doDisconnect();
  3239. this.authenticated = false;
  3240. this.disconnecting = false;
  3241. this.restored = false; // delete handlers
  3242. this.handlers = [];
  3243. this.timedHandlers = [];
  3244. this.removeTimeds = [];
  3245. this.removeHandlers = [];
  3246. this.addTimeds = [];
  3247. this.addHandlers = []; // tell the parent we disconnected
  3248. this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
  3249. this.connected = false;
  3250. }
  3251. /** PrivateFunction: _dataRecv
  3252. * _Private_ handler to processes incoming data from the the connection.
  3253. *
  3254. * Except for _connect_cb handling the initial connection request,
  3255. * this function handles the incoming data for all requests. This
  3256. * function also fires stanza handlers that match each incoming
  3257. * stanza.
  3258. *
  3259. * Parameters:
  3260. * (Strophe.Request) req - The request that has data ready.
  3261. * (string) req - The stanza a raw string (optiona).
  3262. */
  3263. _dataRecv(req, raw) {
  3264. const elem = this._proto._reqToData(req);
  3265. if (elem === null) {
  3266. return;
  3267. }
  3268. if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
  3269. if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
  3270. this.xmlInput(elem.childNodes[0]);
  3271. } else {
  3272. this.xmlInput(elem);
  3273. }
  3274. }
  3275. if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
  3276. if (raw) {
  3277. this.rawInput(raw);
  3278. } else {
  3279. this.rawInput(Strophe.serialize(elem));
  3280. }
  3281. } // remove handlers scheduled for deletion
  3282. while (this.removeHandlers.length > 0) {
  3283. const hand = this.removeHandlers.pop();
  3284. const i = this.handlers.indexOf(hand);
  3285. if (i >= 0) {
  3286. this.handlers.splice(i, 1);
  3287. }
  3288. } // add handlers scheduled for addition
  3289. while (this.addHandlers.length > 0) {
  3290. this.handlers.push(this.addHandlers.pop());
  3291. } // handle graceful disconnect
  3292. if (this.disconnecting && this._proto._emptyQueue()) {
  3293. this._doDisconnect();
  3294. return;
  3295. }
  3296. const type = elem.getAttribute("type");
  3297. if (type !== null && type === "terminate") {
  3298. // Don't process stanzas that come in after disconnect
  3299. if (this.disconnecting) {
  3300. return;
  3301. } // an error occurred
  3302. let cond = elem.getAttribute("condition");
  3303. const conflict = elem.getElementsByTagName("conflict");
  3304. if (cond !== null) {
  3305. if (cond === "remote-stream-error" && conflict.length > 0) {
  3306. cond = "conflict";
  3307. }
  3308. this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
  3309. } else {
  3310. this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.UNKOWN_REASON);
  3311. }
  3312. this._doDisconnect(cond);
  3313. return;
  3314. } // send each incoming stanza through the handler chain
  3315. Strophe.forEachChild(elem, null, child => {
  3316. const matches = [];
  3317. this.handlers = this.handlers.reduce((handlers, handler) => {
  3318. try {
  3319. if (handler.isMatch(child) && (this.authenticated || !handler.user)) {
  3320. if (handler.run(child)) {
  3321. handlers.push(handler);
  3322. }
  3323. matches.push(handler);
  3324. } else {
  3325. handlers.push(handler);
  3326. }
  3327. } catch (e) {
  3328. // if the handler throws an exception, we consider it as false
  3329. Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);
  3330. }
  3331. return handlers;
  3332. }, []); // If no handler was fired for an incoming IQ with type="set",
  3333. // then we return an IQ error stanza with service-unavailable.
  3334. if (!matches.length && this.iqFallbackHandler.isMatch(child)) {
  3335. this.iqFallbackHandler.run(child);
  3336. }
  3337. });
  3338. }
  3339. /** PrivateFunction: _connect_cb
  3340. * _Private_ handler for initial connection request.
  3341. *
  3342. * This handler is used to process the initial connection request
  3343. * response from the BOSH server. It is used to set up authentication
  3344. * handlers and start the authentication process.
  3345. *
  3346. * SASL authentication will be attempted if available, otherwise
  3347. * the code will fall back to legacy authentication.
  3348. *
  3349. * Parameters:
  3350. * (Strophe.Request) req - The current request.
  3351. * (Function) _callback - low level (xmpp) connect callback function.
  3352. * Useful for plugins with their own xmpp connect callback (when they
  3353. * want to do something special).
  3354. */
  3355. _connect_cb(req, _callback, raw) {
  3356. Strophe.debug("_connect_cb was called");
  3357. this.connected = true;
  3358. let bodyWrap;
  3359. try {
  3360. bodyWrap = this._proto._reqToData(req);
  3361. } catch (e) {
  3362. if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) {
  3363. throw e;
  3364. }
  3365. this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.BAD_FORMAT);
  3366. this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);
  3367. }
  3368. if (!bodyWrap) {
  3369. return;
  3370. }
  3371. if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
  3372. if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
  3373. this.xmlInput(bodyWrap.childNodes[0]);
  3374. } else {
  3375. this.xmlInput(bodyWrap);
  3376. }
  3377. }
  3378. if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
  3379. if (raw) {
  3380. this.rawInput(raw);
  3381. } else {
  3382. this.rawInput(Strophe.serialize(bodyWrap));
  3383. }
  3384. }
  3385. const conncheck = this._proto._connect_cb(bodyWrap);
  3386. if (conncheck === Strophe.Status.CONNFAIL) {
  3387. return;
  3388. } // Check for the stream:features tag
  3389. let hasFeatures;
  3390. if (bodyWrap.getElementsByTagNameNS) {
  3391. hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;
  3392. } else {
  3393. hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || bodyWrap.getElementsByTagName("features").length > 0;
  3394. }
  3395. if (!hasFeatures) {
  3396. this._proto._no_auth_received(_callback);
  3397. return;
  3398. }
  3399. const matched = Array.from(bodyWrap.getElementsByTagName("mechanism")).map(m => this.mechanisms[m.textContent]).filter(m => m);
  3400. if (matched.length === 0) {
  3401. if (bodyWrap.getElementsByTagName("auth").length === 0) {
  3402. // There are no matching SASL mechanisms and also no legacy
  3403. // auth available.
  3404. this._proto._no_auth_received(_callback);
  3405. return;
  3406. }
  3407. }
  3408. if (this.do_authentication !== false) {
  3409. this.authenticate(matched);
  3410. }
  3411. }
  3412. /** Function: sortMechanismsByPriority
  3413. *
  3414. * Sorts an array of objects with prototype SASLMechanism according to
  3415. * their priorities.
  3416. *
  3417. * Parameters:
  3418. * (Array) mechanisms - Array of SASL mechanisms.
  3419. *
  3420. */
  3421. sortMechanismsByPriority(mechanisms) {
  3422. // eslint-disable-line class-methods-use-this
  3423. // Sorting mechanisms according to priority.
  3424. for (let i = 0; i < mechanisms.length - 1; ++i) {
  3425. let higher = i;
  3426. for (let j = i + 1; j < mechanisms.length; ++j) {
  3427. if (mechanisms[j].priority > mechanisms[higher].priority) {
  3428. higher = j;
  3429. }
  3430. }
  3431. if (higher !== i) {
  3432. const swap = mechanisms[i];
  3433. mechanisms[i] = mechanisms[higher];
  3434. mechanisms[higher] = swap;
  3435. }
  3436. }
  3437. return mechanisms;
  3438. }
  3439. /** Function: authenticate
  3440. * Set up authentication
  3441. *
  3442. * Continues the initial connection request by setting up authentication
  3443. * handlers and starting the authentication process.
  3444. *
  3445. * SASL authentication will be attempted if available, otherwise
  3446. * the code will fall back to legacy authentication.
  3447. *
  3448. * Parameters:
  3449. * (Array) matched - Array of SASL mechanisms supported.
  3450. *
  3451. */
  3452. authenticate(matched) {
  3453. if (!this._attemptSASLAuth(matched)) {
  3454. this._attemptLegacyAuth();
  3455. }
  3456. }
  3457. /** PrivateFunction: _attemptSASLAuth
  3458. *
  3459. * Iterate through an array of SASL mechanisms and attempt authentication
  3460. * with the highest priority (enabled) mechanism.
  3461. *
  3462. * Parameters:
  3463. * (Array) mechanisms - Array of SASL mechanisms.
  3464. *
  3465. * Returns:
  3466. * (Boolean) mechanism_found - true or false, depending on whether a
  3467. * valid SASL mechanism was found with which authentication could be
  3468. * started.
  3469. */
  3470. _attemptSASLAuth(mechanisms) {
  3471. mechanisms = this.sortMechanismsByPriority(mechanisms || []);
  3472. let mechanism_found = false;
  3473. for (let i = 0; i < mechanisms.length; ++i) {
  3474. if (!mechanisms[i].test(this)) {
  3475. continue;
  3476. }
  3477. this._sasl_success_handler = this._addSysHandler(this._sasl_success_cb.bind(this), null, "success", null, null);
  3478. this._sasl_failure_handler = this._addSysHandler(this._sasl_failure_cb.bind(this), null, "failure", null, null);
  3479. this._sasl_challenge_handler = this._addSysHandler(this._sasl_challenge_cb.bind(this), null, "challenge", null, null);
  3480. this._sasl_mechanism = mechanisms[i];
  3481. this._sasl_mechanism.onStart(this);
  3482. const request_auth_exchange = $build("auth", {
  3483. 'xmlns': Strophe.NS.SASL,
  3484. 'mechanism': this._sasl_mechanism.mechname
  3485. });
  3486. if (this._sasl_mechanism.isClientFirst) {
  3487. const response = this._sasl_mechanism.clientChallenge(this);
  3488. request_auth_exchange.t(abab.btoa(response));
  3489. }
  3490. this.send(request_auth_exchange.tree());
  3491. mechanism_found = true;
  3492. break;
  3493. }
  3494. return mechanism_found;
  3495. }
  3496. /** PrivateFunction: _sasl_challenge_cb
  3497. * _Private_ handler for the SASL challenge
  3498. *
  3499. */
  3500. _sasl_challenge_cb(elem) {
  3501. const challenge = abab.atob(Strophe.getText(elem));
  3502. const response = this._sasl_mechanism.onChallenge(this, challenge);
  3503. const stanza = $build('response', {
  3504. 'xmlns': Strophe.NS.SASL
  3505. });
  3506. if (response !== "") {
  3507. stanza.t(abab.btoa(response));
  3508. }
  3509. this.send(stanza.tree());
  3510. return true;
  3511. }
  3512. /** PrivateFunction: _attemptLegacyAuth
  3513. *
  3514. * Attempt legacy (i.e. non-SASL) authentication.
  3515. */
  3516. _attemptLegacyAuth() {
  3517. if (Strophe.getNodeFromJid(this.jid) === null) {
  3518. // we don't have a node, which is required for non-anonymous
  3519. // client connections
  3520. this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.MISSING_JID_NODE);
  3521. this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);
  3522. } else {
  3523. // Fall back to legacy authentication
  3524. this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
  3525. this._addSysHandler(this._onLegacyAuthIQResult.bind(this), null, null, null, "_auth_1");
  3526. this.send($iq({
  3527. 'type': "get",
  3528. 'to': this.domain,
  3529. 'id': "_auth_1"
  3530. }).c("query", {
  3531. xmlns: Strophe.NS.AUTH
  3532. }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree());
  3533. }
  3534. }
  3535. /** PrivateFunction: _onLegacyAuthIQResult
  3536. * _Private_ handler for legacy authentication.
  3537. *
  3538. * This handler is called in response to the initial <iq type='get'/>
  3539. * for legacy authentication. It builds an authentication <iq/> and
  3540. * sends it, creating a handler (calling back to _auth2_cb()) to
  3541. * handle the result
  3542. *
  3543. * Parameters:
  3544. * (XMLElement) elem - The stanza that triggered the callback.
  3545. *
  3546. * Returns:
  3547. * false to remove the handler.
  3548. */
  3549. _onLegacyAuthIQResult(elem) {
  3550. // eslint-disable-line no-unused-vars
  3551. // build plaintext auth iq
  3552. const iq = $iq({
  3553. type: "set",
  3554. id: "_auth_2"
  3555. }).c('query', {
  3556. xmlns: Strophe.NS.AUTH
  3557. }).c('username', {}).t(Strophe.getNodeFromJid(this.jid)).up().c('password').t(this.pass);
  3558. if (!Strophe.getResourceFromJid(this.jid)) {
  3559. // since the user has not supplied a resource, we pick
  3560. // a default one here. unlike other auth methods, the server
  3561. // cannot do this for us.
  3562. this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
  3563. }
  3564. iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
  3565. this._addSysHandler(this._auth2_cb.bind(this), null, null, null, "_auth_2");
  3566. this.send(iq.tree());
  3567. return false;
  3568. }
  3569. /** PrivateFunction: _sasl_success_cb
  3570. * _Private_ handler for succesful SASL authentication.
  3571. *
  3572. * Parameters:
  3573. * (XMLElement) elem - The matching stanza.
  3574. *
  3575. * Returns:
  3576. * false to remove the handler.
  3577. */
  3578. _sasl_success_cb(elem) {
  3579. if (this._sasl_data["server-signature"]) {
  3580. let serverSignature;
  3581. const success = abab.atob(Strophe.getText(elem));
  3582. const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
  3583. const matches = success.match(attribMatch);
  3584. if (matches[1] === "v") {
  3585. serverSignature = matches[2];
  3586. }
  3587. if (serverSignature !== this._sasl_data["server-signature"]) {
  3588. // remove old handlers
  3589. this.deleteHandler(this._sasl_failure_handler);
  3590. this._sasl_failure_handler = null;
  3591. if (this._sasl_challenge_handler) {
  3592. this.deleteHandler(this._sasl_challenge_handler);
  3593. this._sasl_challenge_handler = null;
  3594. }
  3595. this._sasl_data = {};
  3596. return this._sasl_failure_cb(null);
  3597. }
  3598. }
  3599. Strophe.info("SASL authentication succeeded.");
  3600. if (this._sasl_mechanism) {
  3601. this._sasl_mechanism.onSuccess();
  3602. } // remove old handlers
  3603. this.deleteHandler(this._sasl_failure_handler);
  3604. this._sasl_failure_handler = null;
  3605. if (this._sasl_challenge_handler) {
  3606. this.deleteHandler(this._sasl_challenge_handler);
  3607. this._sasl_challenge_handler = null;
  3608. }
  3609. const streamfeature_handlers = [];
  3610. const wrapper = (handlers, elem) => {
  3611. while (handlers.length) {
  3612. this.deleteHandler(handlers.pop());
  3613. }
  3614. this._onStreamFeaturesAfterSASL(elem);
  3615. return false;
  3616. };
  3617. streamfeature_handlers.push(this._addSysHandler(elem => wrapper(streamfeature_handlers, elem), null, "stream:features", null, null));
  3618. streamfeature_handlers.push(this._addSysHandler(elem => wrapper(streamfeature_handlers, elem), Strophe.NS.STREAM, "features", null, null)); // we must send an xmpp:restart now
  3619. this._sendRestart();
  3620. return false;
  3621. }
  3622. /** PrivateFunction: _onStreamFeaturesAfterSASL
  3623. * Parameters:
  3624. * (XMLElement) elem - The matching stanza.
  3625. *
  3626. * Returns:
  3627. * false to remove the handler.
  3628. */
  3629. _onStreamFeaturesAfterSASL(elem) {
  3630. // save stream:features for future usage
  3631. this.features = elem;
  3632. for (let i = 0; i < elem.childNodes.length; i++) {
  3633. const child = elem.childNodes[i];
  3634. if (child.nodeName === 'bind') {
  3635. this.do_bind = true;
  3636. }
  3637. if (child.nodeName === 'session') {
  3638. this.do_session = true;
  3639. }
  3640. }
  3641. if (!this.do_bind) {
  3642. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
  3643. return false;
  3644. } else if (!this.options.explicitResourceBinding) {
  3645. this.bind();
  3646. } else {
  3647. this._changeConnectStatus(Strophe.Status.BINDREQUIRED, null);
  3648. }
  3649. return false;
  3650. }
  3651. /** Function: bind
  3652. *
  3653. * Sends an IQ to the XMPP server to bind a JID resource for this session.
  3654. *
  3655. * https://tools.ietf.org/html/rfc6120#section-7.5
  3656. *
  3657. * If `explicitResourceBinding` was set to a truthy value in the options
  3658. * passed to the Strophe.Connection constructor, then this function needs
  3659. * to be called explicitly by the client author.
  3660. *
  3661. * Otherwise it'll be called automatically as soon as the XMPP server
  3662. * advertises the "urn:ietf:params:xml:ns:xmpp-bind" stream feature.
  3663. */
  3664. bind() {
  3665. if (!this.do_bind) {
  3666. Strophe.log(Strophe.LogLevel.INFO, `Strophe.Connection.prototype.bind called but "do_bind" is false`);
  3667. return;
  3668. }
  3669. this._addSysHandler(this._onResourceBindResultIQ.bind(this), null, null, null, "_bind_auth_2");
  3670. const resource = Strophe.getResourceFromJid(this.jid);
  3671. if (resource) {
  3672. this.send($iq({
  3673. type: "set",
  3674. id: "_bind_auth_2"
  3675. }).c('bind', {
  3676. xmlns: Strophe.NS.BIND
  3677. }).c('resource', {}).t(resource).tree());
  3678. } else {
  3679. this.send($iq({
  3680. type: "set",
  3681. id: "_bind_auth_2"
  3682. }).c('bind', {
  3683. xmlns: Strophe.NS.BIND
  3684. }).tree());
  3685. }
  3686. }
  3687. /** PrivateFunction: _onResourceBindIQ
  3688. * _Private_ handler for binding result and session start.
  3689. *
  3690. * Parameters:
  3691. * (XMLElement) elem - The matching stanza.
  3692. *
  3693. * Returns:
  3694. * false to remove the handler.
  3695. */
  3696. _onResourceBindResultIQ(elem) {
  3697. if (elem.getAttribute("type") === "error") {
  3698. Strophe.warn("Resource binding failed.");
  3699. const conflict = elem.getElementsByTagName("conflict");
  3700. let condition;
  3701. if (conflict.length > 0) {
  3702. condition = Strophe.ErrorCondition.CONFLICT;
  3703. }
  3704. this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);
  3705. return false;
  3706. } // TODO - need to grab errors
  3707. const bind = elem.getElementsByTagName("bind");
  3708. if (bind.length > 0) {
  3709. const jidNode = bind[0].getElementsByTagName("jid");
  3710. if (jidNode.length > 0) {
  3711. this.authenticated = true;
  3712. this.jid = Strophe.getText(jidNode[0]);
  3713. if (this.do_session) {
  3714. this._establishSession();
  3715. } else {
  3716. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  3717. }
  3718. }
  3719. } else {
  3720. Strophe.warn("Resource binding failed.");
  3721. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
  3722. return false;
  3723. }
  3724. }
  3725. /** PrivateFunction: _establishSession
  3726. * Send IQ request to establish a session with the XMPP server.
  3727. *
  3728. * See https://xmpp.org/rfcs/rfc3921.html#session
  3729. *
  3730. * Note: The protocol for session establishment has been determined as
  3731. * unnecessary and removed in RFC-6121.
  3732. */
  3733. _establishSession() {
  3734. if (!this.do_session) {
  3735. throw new Error(`Strophe.Connection.prototype._establishSession ` + `called but apparently ${Strophe.NS.SESSION} wasn't advertised by the server`);
  3736. }
  3737. this._addSysHandler(this._onSessionResultIQ.bind(this), null, null, null, "_session_auth_2");
  3738. this.send($iq({
  3739. type: "set",
  3740. id: "_session_auth_2"
  3741. }).c('session', {
  3742. xmlns: Strophe.NS.SESSION
  3743. }).tree());
  3744. }
  3745. /** PrivateFunction: _onSessionResultIQ
  3746. * _Private_ handler for the server's IQ response to a client's session
  3747. * request.
  3748. *
  3749. * This sets Connection.authenticated to true on success, which
  3750. * starts the processing of user handlers.
  3751. *
  3752. * See https://xmpp.org/rfcs/rfc3921.html#session
  3753. *
  3754. * Note: The protocol for session establishment has been determined as
  3755. * unnecessary and removed in RFC-6121.
  3756. *
  3757. * Parameters:
  3758. * (XMLElement) elem - The matching stanza.
  3759. *
  3760. * Returns:
  3761. * false to remove the handler.
  3762. */
  3763. _onSessionResultIQ(elem) {
  3764. if (elem.getAttribute("type") === "result") {
  3765. this.authenticated = true;
  3766. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  3767. } else if (elem.getAttribute("type") === "error") {
  3768. this.authenticated = false;
  3769. Strophe.warn("Session creation failed.");
  3770. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
  3771. return false;
  3772. }
  3773. return false;
  3774. }
  3775. /** PrivateFunction: _sasl_failure_cb
  3776. * _Private_ handler for SASL authentication failure.
  3777. *
  3778. * Parameters:
  3779. * (XMLElement) elem - The matching stanza.
  3780. *
  3781. * Returns:
  3782. * false to remove the handler.
  3783. */
  3784. _sasl_failure_cb(elem) {
  3785. // delete unneeded handlers
  3786. if (this._sasl_success_handler) {
  3787. this.deleteHandler(this._sasl_success_handler);
  3788. this._sasl_success_handler = null;
  3789. }
  3790. if (this._sasl_challenge_handler) {
  3791. this.deleteHandler(this._sasl_challenge_handler);
  3792. this._sasl_challenge_handler = null;
  3793. }
  3794. if (this._sasl_mechanism) this._sasl_mechanism.onFailure();
  3795. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
  3796. return false;
  3797. }
  3798. /** PrivateFunction: _auth2_cb
  3799. * _Private_ handler to finish legacy authentication.
  3800. *
  3801. * This handler is called when the result from the jabber:iq:auth
  3802. * <iq/> stanza is returned.
  3803. *
  3804. * Parameters:
  3805. * (XMLElement) elem - The stanza that triggered the callback.
  3806. *
  3807. * Returns:
  3808. * false to remove the handler.
  3809. */
  3810. _auth2_cb(elem) {
  3811. if (elem.getAttribute("type") === "result") {
  3812. this.authenticated = true;
  3813. this._changeConnectStatus(Strophe.Status.CONNECTED, null);
  3814. } else if (elem.getAttribute("type") === "error") {
  3815. this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
  3816. this.disconnect('authentication failed');
  3817. }
  3818. return false;
  3819. }
  3820. /** PrivateFunction: _addSysTimedHandler
  3821. * _Private_ function to add a system level timed handler.
  3822. *
  3823. * This function is used to add a Strophe.TimedHandler for the
  3824. * library code. System timed handlers are allowed to run before
  3825. * authentication is complete.
  3826. *
  3827. * Parameters:
  3828. * (Integer) period - The period of the handler.
  3829. * (Function) handler - The callback function.
  3830. */
  3831. _addSysTimedHandler(period, handler) {
  3832. const thand = new Strophe.TimedHandler(period, handler);
  3833. thand.user = false;
  3834. this.addTimeds.push(thand);
  3835. return thand;
  3836. }
  3837. /** PrivateFunction: _addSysHandler
  3838. * _Private_ function to add a system level stanza handler.
  3839. *
  3840. * This function is used to add a Strophe.Handler for the
  3841. * library code. System stanza handlers are allowed to run before
  3842. * authentication is complete.
  3843. *
  3844. * Parameters:
  3845. * (Function) handler - The callback function.
  3846. * (String) ns - The namespace to match.
  3847. * (String) name - The stanza name to match.
  3848. * (String) type - The stanza type attribute to match.
  3849. * (String) id - The stanza id attribute to match.
  3850. */
  3851. _addSysHandler(handler, ns, name, type, id) {
  3852. const hand = new Strophe.Handler(handler, ns, name, type, id);
  3853. hand.user = false;
  3854. this.addHandlers.push(hand);
  3855. return hand;
  3856. }
  3857. /** PrivateFunction: _onDisconnectTimeout
  3858. * _Private_ timeout handler for handling non-graceful disconnection.
  3859. *
  3860. * If the graceful disconnect process does not complete within the
  3861. * time allotted, this handler finishes the disconnect anyway.
  3862. *
  3863. * Returns:
  3864. * false to remove the handler.
  3865. */
  3866. _onDisconnectTimeout() {
  3867. Strophe.debug("_onDisconnectTimeout was called");
  3868. this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);
  3869. this._proto._onDisconnectTimeout(); // actually disconnect
  3870. this._doDisconnect();
  3871. return false;
  3872. }
  3873. /** PrivateFunction: _onIdle
  3874. * _Private_ handler to process events during idle cycle.
  3875. *
  3876. * This handler is called every 100ms to fire timed handlers that
  3877. * are ready and keep poll requests going.
  3878. */
  3879. _onIdle() {
  3880. // add timed handlers scheduled for addition
  3881. // NOTE: we add before remove in the case a timed handler is
  3882. // added and then deleted before the next _onIdle() call.
  3883. while (this.addTimeds.length > 0) {
  3884. this.timedHandlers.push(this.addTimeds.pop());
  3885. } // remove timed handlers that have been scheduled for deletion
  3886. while (this.removeTimeds.length > 0) {
  3887. const thand = this.removeTimeds.pop();
  3888. const i = this.timedHandlers.indexOf(thand);
  3889. if (i >= 0) {
  3890. this.timedHandlers.splice(i, 1);
  3891. }
  3892. } // call ready timed handlers
  3893. const now = new Date().getTime();
  3894. const newList = [];
  3895. for (let i = 0; i < this.timedHandlers.length; i++) {
  3896. const thand = this.timedHandlers[i];
  3897. if (this.authenticated || !thand.user) {
  3898. const since = thand.lastCalled + thand.period;
  3899. if (since - now <= 0) {
  3900. if (thand.run()) {
  3901. newList.push(thand);
  3902. }
  3903. } else {
  3904. newList.push(thand);
  3905. }
  3906. }
  3907. }
  3908. this.timedHandlers = newList;
  3909. clearTimeout(this._idleTimeout);
  3910. this._proto._onIdle(); // reactivate the timer only if connected
  3911. if (this.connected) {
  3912. this._idleTimeout = setTimeout(() => this._onIdle(), 100);
  3913. }
  3914. }
  3915. };
  3916. Strophe.SASLMechanism = SASLMechanism;
  3917. /** Constants: SASL mechanisms
  3918. * Available authentication mechanisms
  3919. *
  3920. * Strophe.SASLAnonymous - SASL ANONYMOUS authentication.
  3921. * Strophe.SASLPlain - SASL PLAIN authentication.
  3922. * Strophe.SASLSHA1 - SASL SCRAM-SHA-1 authentication
  3923. * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication
  3924. * Strophe.SASLExternal - SASL EXTERNAL authentication
  3925. * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication
  3926. */
  3927. Strophe.SASLAnonymous = SASLAnonymous;
  3928. Strophe.SASLPlain = SASLPlain;
  3929. Strophe.SASLSHA1 = SASLSHA1;
  3930. Strophe.SASLOAuthBearer = SASLOAuthBearer;
  3931. Strophe.SASLExternal = SASLExternal;
  3932. Strophe.SASLXOAuth2 = SASLXOAuth2;
  3933. var core = {
  3934. 'Strophe': Strophe,
  3935. '$build': $build,
  3936. '$iq': $iq,
  3937. '$msg': $msg,
  3938. '$pres': $pres,
  3939. 'SHA1': SHA1,
  3940. 'MD5': MD5,
  3941. 'b64_hmac_sha1': SHA1.b64_hmac_sha1,
  3942. 'b64_sha1': SHA1.b64_sha1,
  3943. 'str_hmac_sha1': SHA1.str_hmac_sha1,
  3944. 'str_sha1': SHA1.str_sha1
  3945. };
  3946. /*
  3947. This program is distributed under the terms of the MIT license.
  3948. Please see the LICENSE file for details.
  3949. Copyright 2006-2008, OGG, LLC
  3950. */
  3951. /** PrivateClass: Strophe.Request
  3952. * _Private_ helper class that provides a cross implementation abstraction
  3953. * for a BOSH related XMLHttpRequest.
  3954. *
  3955. * The Strophe.Request class is used internally to encapsulate BOSH request
  3956. * information. It is not meant to be used from user's code.
  3957. */
  3958. Strophe.Request = class Request {
  3959. /** PrivateConstructor: Strophe.Request
  3960. * Create and initialize a new Strophe.Request object.
  3961. *
  3962. * Parameters:
  3963. * (XMLElement) elem - The XML data to be sent in the request.
  3964. * (Function) func - The function that will be called when the
  3965. * XMLHttpRequest readyState changes.
  3966. * (Integer) rid - The BOSH rid attribute associated with this request.
  3967. * (Integer) sends - The number of times this same request has been sent.
  3968. */
  3969. constructor(elem, func, rid, sends) {
  3970. this.id = ++Strophe._requestId;
  3971. this.xmlData = elem;
  3972. this.data = Strophe.serialize(elem); // save original function in case we need to make a new request
  3973. // from this one.
  3974. this.origFunc = func;
  3975. this.func = func;
  3976. this.rid = rid;
  3977. this.date = NaN;
  3978. this.sends = sends || 0;
  3979. this.abort = false;
  3980. this.dead = null;
  3981. this.age = function () {
  3982. if (!this.date) {
  3983. return 0;
  3984. }
  3985. const now = new Date();
  3986. return (now - this.date) / 1000;
  3987. };
  3988. this.timeDead = function () {
  3989. if (!this.dead) {
  3990. return 0;
  3991. }
  3992. const now = new Date();
  3993. return (now - this.dead) / 1000;
  3994. };
  3995. this.xhr = this._newXHR();
  3996. }
  3997. /** PrivateFunction: getResponse
  3998. * Get a response from the underlying XMLHttpRequest.
  3999. *
  4000. * This function attempts to get a response from the request and checks
  4001. * for errors.
  4002. *
  4003. * Throws:
  4004. * "parsererror" - A parser error occured.
  4005. * "bad-format" - The entity has sent XML that cannot be processed.
  4006. *
  4007. * Returns:
  4008. * The DOM element tree of the response.
  4009. */
  4010. getResponse() {
  4011. let node = null;
  4012. if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {
  4013. node = this.xhr.responseXML.documentElement;
  4014. if (node.tagName === "parsererror") {
  4015. Strophe.error("invalid response received");
  4016. Strophe.error("responseText: " + this.xhr.responseText);
  4017. Strophe.error("responseXML: " + Strophe.serialize(this.xhr.responseXML));
  4018. throw new Error("parsererror");
  4019. }
  4020. } else if (this.xhr.responseText) {
  4021. // In React Native, we may get responseText but no responseXML. We can try to parse it manually.
  4022. Strophe.debug("Got responseText but no responseXML; attempting to parse it with DOMParser...");
  4023. node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement;
  4024. if (!node) {
  4025. throw new Error('Parsing produced null node');
  4026. } else if (node.querySelector('parsererror')) {
  4027. Strophe.error("invalid response received: " + node.querySelector('parsererror').textContent);
  4028. Strophe.error("responseText: " + this.xhr.responseText);
  4029. const error = new Error();
  4030. error.name = Strophe.ErrorCondition.BAD_FORMAT;
  4031. throw error;
  4032. }
  4033. }
  4034. return node;
  4035. }
  4036. /** PrivateFunction: _newXHR
  4037. * _Private_ helper function to create XMLHttpRequests.
  4038. *
  4039. * This function creates XMLHttpRequests across all implementations.
  4040. *
  4041. * Returns:
  4042. * A new XMLHttpRequest.
  4043. */
  4044. _newXHR() {
  4045. let xhr = null;
  4046. if (window.XMLHttpRequest) {
  4047. xhr = new XMLHttpRequest();
  4048. if (xhr.overrideMimeType) {
  4049. xhr.overrideMimeType("text/xml; charset=utf-8");
  4050. }
  4051. } else if (window.ActiveXObject) {
  4052. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  4053. } // use Function.bind() to prepend ourselves as an argument
  4054. xhr.onreadystatechange = this.func.bind(null, this);
  4055. return xhr;
  4056. }
  4057. };
  4058. /** Class: Strophe.Bosh
  4059. * _Private_ helper class that handles BOSH Connections
  4060. *
  4061. * The Strophe.Bosh class is used internally by Strophe.Connection
  4062. * to encapsulate BOSH sessions. It is not meant to be used from user's code.
  4063. */
  4064. /** File: bosh.js
  4065. * A JavaScript library to enable BOSH in Strophejs.
  4066. *
  4067. * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)
  4068. * to emulate a persistent, stateful, two-way connection to an XMPP server.
  4069. * More information on BOSH can be found in XEP 124.
  4070. */
  4071. /** PrivateConstructor: Strophe.Bosh
  4072. * Create and initialize a Strophe.Bosh object.
  4073. *
  4074. * Parameters:
  4075. * (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.
  4076. *
  4077. * Returns:
  4078. * A new Strophe.Bosh object.
  4079. */
  4080. Strophe.Bosh = class Bosh {
  4081. constructor(connection) {
  4082. this._conn = connection;
  4083. /* request id for body tags */
  4084. this.rid = Math.floor(Math.random() * 4294967295);
  4085. /* The current session ID. */
  4086. this.sid = null; // default BOSH values
  4087. this.hold = 1;
  4088. this.wait = 60;
  4089. this.window = 5;
  4090. this.errors = 0;
  4091. this.inactivity = null;
  4092. this.lastResponseHeaders = null;
  4093. this._requests = [];
  4094. }
  4095. /** PrivateFunction: _buildBody
  4096. * _Private_ helper function to generate the <body/> wrapper for BOSH.
  4097. *
  4098. * Returns:
  4099. * A Strophe.Builder with a <body/> element.
  4100. */
  4101. _buildBody() {
  4102. const bodyWrap = $build('body', {
  4103. 'rid': this.rid++,
  4104. 'xmlns': Strophe.NS.HTTPBIND
  4105. });
  4106. if (this.sid !== null) {
  4107. bodyWrap.attrs({
  4108. 'sid': this.sid
  4109. });
  4110. }
  4111. if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) {
  4112. this._cacheSession();
  4113. }
  4114. return bodyWrap;
  4115. }
  4116. /** PrivateFunction: _reset
  4117. * Reset the connection.
  4118. *
  4119. * This function is called by the reset function of the Strophe Connection
  4120. */
  4121. _reset() {
  4122. this.rid = Math.floor(Math.random() * 4294967295);
  4123. this.sid = null;
  4124. this.errors = 0;
  4125. if (this._conn._sessionCachingSupported()) {
  4126. window.sessionStorage.removeItem('strophe-bosh-session');
  4127. }
  4128. this._conn.nextValidRid(this.rid);
  4129. }
  4130. /** PrivateFunction: _connect
  4131. * _Private_ function that initializes the BOSH connection.
  4132. *
  4133. * Creates and sends the Request that initializes the BOSH connection.
  4134. */
  4135. _connect(wait, hold, route) {
  4136. this.wait = wait || this.wait;
  4137. this.hold = hold || this.hold;
  4138. this.errors = 0;
  4139. const body = this._buildBody().attrs({
  4140. "to": this._conn.domain,
  4141. "xml:lang": "en",
  4142. "wait": this.wait,
  4143. "hold": this.hold,
  4144. "content": "text/xml; charset=utf-8",
  4145. "ver": "1.6",
  4146. "xmpp:version": "1.0",
  4147. "xmlns:xmpp": Strophe.NS.BOSH
  4148. });
  4149. if (route) {
  4150. body.attrs({
  4151. 'route': route
  4152. });
  4153. }
  4154. const _connect_cb = this._conn._connect_cb;
  4155. this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, _connect_cb.bind(this._conn)), body.tree().getAttribute("rid")));
  4156. this._throttledRequestHandler();
  4157. }
  4158. /** PrivateFunction: _attach
  4159. * Attach to an already created and authenticated BOSH session.
  4160. *
  4161. * This function is provided to allow Strophe to attach to BOSH
  4162. * sessions which have been created externally, perhaps by a Web
  4163. * application. This is often used to support auto-login type features
  4164. * without putting user credentials into the page.
  4165. *
  4166. * Parameters:
  4167. * (String) jid - The full JID that is bound by the session.
  4168. * (String) sid - The SID of the BOSH session.
  4169. * (String) rid - The current RID of the BOSH session. This RID
  4170. * will be used by the next request.
  4171. * (Function) callback The connect callback function.
  4172. * (Integer) wait - The optional HTTPBIND wait value. This is the
  4173. * time the server will wait before returning an empty result for
  4174. * a request. The default setting of 60 seconds is recommended.
  4175. * Other settings will require tweaks to the Strophe.TIMEOUT value.
  4176. * (Integer) hold - The optional HTTPBIND hold value. This is the
  4177. * number of connections the server will hold at one time. This
  4178. * should almost always be set to 1 (the default).
  4179. * (Integer) wind - The optional HTTBIND window value. This is the
  4180. * allowed range of request ids that are valid. The default is 5.
  4181. */
  4182. _attach(jid, sid, rid, callback, wait, hold, wind) {
  4183. this._conn.jid = jid;
  4184. this.sid = sid;
  4185. this.rid = rid;
  4186. this._conn.connect_callback = callback;
  4187. this._conn.domain = Strophe.getDomainFromJid(this._conn.jid);
  4188. this._conn.authenticated = true;
  4189. this._conn.connected = true;
  4190. this.wait = wait || this.wait;
  4191. this.hold = hold || this.hold;
  4192. this.window = wind || this.window;
  4193. this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null);
  4194. }
  4195. /** PrivateFunction: _restore
  4196. * Attempt to restore a cached BOSH session
  4197. *
  4198. * Parameters:
  4199. * (String) jid - The full JID that is bound by the session.
  4200. * This parameter is optional but recommended, specifically in cases
  4201. * where prebinded BOSH sessions are used where it's important to know
  4202. * that the right session is being restored.
  4203. * (Function) callback The connect callback function.
  4204. * (Integer) wait - The optional HTTPBIND wait value. This is the
  4205. * time the server will wait before returning an empty result for
  4206. * a request. The default setting of 60 seconds is recommended.
  4207. * Other settings will require tweaks to the Strophe.TIMEOUT value.
  4208. * (Integer) hold - The optional HTTPBIND hold value. This is the
  4209. * number of connections the server will hold at one time. This
  4210. * should almost always be set to 1 (the default).
  4211. * (Integer) wind - The optional HTTBIND window value. This is the
  4212. * allowed range of request ids that are valid. The default is 5.
  4213. */
  4214. _restore(jid, callback, wait, hold, wind) {
  4215. const session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));
  4216. if (typeof session !== "undefined" && session !== null && session.rid && session.sid && session.jid && (typeof jid === "undefined" || jid === null || Strophe.getBareJidFromJid(session.jid) === Strophe.getBareJidFromJid(jid) || // If authcid is null, then it's an anonymous login, so
  4217. // we compare only the domains:
  4218. Strophe.getNodeFromJid(jid) === null && Strophe.getDomainFromJid(session.jid) === jid)) {
  4219. this._conn.restored = true;
  4220. this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);
  4221. } else {
  4222. const error = new Error("_restore: no restoreable session.");
  4223. error.name = "StropheSessionError";
  4224. throw error;
  4225. }
  4226. }
  4227. /** PrivateFunction: _cacheSession
  4228. * _Private_ handler for the beforeunload event.
  4229. *
  4230. * This handler is used to process the Bosh-part of the initial request.
  4231. * Parameters:
  4232. * (Strophe.Request) bodyWrap - The received stanza.
  4233. */
  4234. _cacheSession() {
  4235. if (this._conn.authenticated) {
  4236. if (this._conn.jid && this.rid && this.sid) {
  4237. window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({
  4238. 'jid': this._conn.jid,
  4239. 'rid': this.rid,
  4240. 'sid': this.sid
  4241. }));
  4242. }
  4243. } else {
  4244. window.sessionStorage.removeItem('strophe-bosh-session');
  4245. }
  4246. }
  4247. /** PrivateFunction: _connect_cb
  4248. * _Private_ handler for initial connection request.
  4249. *
  4250. * This handler is used to process the Bosh-part of the initial request.
  4251. * Parameters:
  4252. * (Strophe.Request) bodyWrap - The received stanza.
  4253. */
  4254. _connect_cb(bodyWrap) {
  4255. const typ = bodyWrap.getAttribute("type");
  4256. if (typ !== null && typ === "terminate") {
  4257. // an error occurred
  4258. let cond = bodyWrap.getAttribute("condition");
  4259. Strophe.error("BOSH-Connection failed: " + cond);
  4260. const conflict = bodyWrap.getElementsByTagName("conflict");
  4261. if (cond !== null) {
  4262. if (cond === "remote-stream-error" && conflict.length > 0) {
  4263. cond = "conflict";
  4264. }
  4265. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
  4266. } else {
  4267. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
  4268. }
  4269. this._conn._doDisconnect(cond);
  4270. return Strophe.Status.CONNFAIL;
  4271. } // check to make sure we don't overwrite these if _connect_cb is
  4272. // called multiple times in the case of missing stream:features
  4273. if (!this.sid) {
  4274. this.sid = bodyWrap.getAttribute("sid");
  4275. }
  4276. const wind = bodyWrap.getAttribute('requests');
  4277. if (wind) {
  4278. this.window = parseInt(wind, 10);
  4279. }
  4280. const hold = bodyWrap.getAttribute('hold');
  4281. if (hold) {
  4282. this.hold = parseInt(hold, 10);
  4283. }
  4284. const wait = bodyWrap.getAttribute('wait');
  4285. if (wait) {
  4286. this.wait = parseInt(wait, 10);
  4287. }
  4288. const inactivity = bodyWrap.getAttribute('inactivity');
  4289. if (inactivity) {
  4290. this.inactivity = parseInt(inactivity, 10);
  4291. }
  4292. }
  4293. /** PrivateFunction: _disconnect
  4294. * _Private_ part of Connection.disconnect for Bosh
  4295. *
  4296. * Parameters:
  4297. * (Request) pres - This stanza will be sent before disconnecting.
  4298. */
  4299. _disconnect(pres) {
  4300. this._sendTerminate(pres);
  4301. }
  4302. /** PrivateFunction: _doDisconnect
  4303. * _Private_ function to disconnect.
  4304. *
  4305. * Resets the SID and RID.
  4306. */
  4307. _doDisconnect() {
  4308. this.sid = null;
  4309. this.rid = Math.floor(Math.random() * 4294967295);
  4310. if (this._conn._sessionCachingSupported()) {
  4311. window.sessionStorage.removeItem('strophe-bosh-session');
  4312. }
  4313. this._conn.nextValidRid(this.rid);
  4314. }
  4315. /** PrivateFunction: _emptyQueue
  4316. * _Private_ function to check if the Request queue is empty.
  4317. *
  4318. * Returns:
  4319. * True, if there are no Requests queued, False otherwise.
  4320. */
  4321. _emptyQueue() {
  4322. return this._requests.length === 0;
  4323. }
  4324. /** PrivateFunction: _callProtocolErrorHandlers
  4325. * _Private_ function to call error handlers registered for HTTP errors.
  4326. *
  4327. * Parameters:
  4328. * (Strophe.Request) req - The request that is changing readyState.
  4329. */
  4330. _callProtocolErrorHandlers(req) {
  4331. const reqStatus = Bosh._getRequestStatus(req);
  4332. const err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus];
  4333. if (err_callback) {
  4334. err_callback.call(this, reqStatus);
  4335. }
  4336. }
  4337. /** PrivateFunction: _hitError
  4338. * _Private_ function to handle the error count.
  4339. *
  4340. * Requests are resent automatically until their error count reaches
  4341. * 5. Each time an error is encountered, this function is called to
  4342. * increment the count and disconnect if the count is too high.
  4343. *
  4344. * Parameters:
  4345. * (Integer) reqStatus - The request status.
  4346. */
  4347. _hitError(reqStatus) {
  4348. this.errors++;
  4349. Strophe.warn("request errored, status: " + reqStatus + ", number of errors: " + this.errors);
  4350. if (this.errors > 4) {
  4351. this._conn._onDisconnectTimeout();
  4352. }
  4353. }
  4354. /** PrivateFunction: _no_auth_received
  4355. *
  4356. * Called on stream start/restart when no stream:features
  4357. * has been received and sends a blank poll request.
  4358. */
  4359. _no_auth_received(callback) {
  4360. Strophe.warn("Server did not yet offer a supported authentication " + "mechanism. Sending a blank poll request.");
  4361. if (callback) {
  4362. callback = callback.bind(this._conn);
  4363. } else {
  4364. callback = this._conn._connect_cb.bind(this._conn);
  4365. }
  4366. const body = this._buildBody();
  4367. this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, callback), body.tree().getAttribute("rid")));
  4368. this._throttledRequestHandler();
  4369. }
  4370. /** PrivateFunction: _onDisconnectTimeout
  4371. * _Private_ timeout handler for handling non-graceful disconnection.
  4372. *
  4373. * Cancels all remaining Requests and clears the queue.
  4374. */
  4375. _onDisconnectTimeout() {
  4376. this._abortAllRequests();
  4377. }
  4378. /** PrivateFunction: _abortAllRequests
  4379. * _Private_ helper function that makes sure all pending requests are aborted.
  4380. */
  4381. _abortAllRequests() {
  4382. while (this._requests.length > 0) {
  4383. const req = this._requests.pop();
  4384. req.abort = true;
  4385. req.xhr.abort();
  4386. req.xhr.onreadystatechange = function () {};
  4387. }
  4388. }
  4389. /** PrivateFunction: _onIdle
  4390. * _Private_ handler called by Strophe.Connection._onIdle
  4391. *
  4392. * Sends all queued Requests or polls with empty Request if there are none.
  4393. */
  4394. _onIdle() {
  4395. const data = this._conn._data; // if no requests are in progress, poll
  4396. if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) {
  4397. Strophe.debug("no requests during idle cycle, sending blank request");
  4398. data.push(null);
  4399. }
  4400. if (this._conn.paused) {
  4401. return;
  4402. }
  4403. if (this._requests.length < 2 && data.length > 0) {
  4404. const body = this._buildBody();
  4405. for (let i = 0; i < data.length; i++) {
  4406. if (data[i] !== null) {
  4407. if (data[i] === "restart") {
  4408. body.attrs({
  4409. "to": this._conn.domain,
  4410. "xml:lang": "en",
  4411. "xmpp:restart": "true",
  4412. "xmlns:xmpp": Strophe.NS.BOSH
  4413. });
  4414. } else {
  4415. body.cnode(data[i]).up();
  4416. }
  4417. }
  4418. }
  4419. delete this._conn._data;
  4420. this._conn._data = [];
  4421. this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid")));
  4422. this._throttledRequestHandler();
  4423. }
  4424. if (this._requests.length > 0) {
  4425. const time_elapsed = this._requests[0].age();
  4426. if (this._requests[0].dead !== null) {
  4427. if (this._requests[0].timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
  4428. this._throttledRequestHandler();
  4429. }
  4430. }
  4431. if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {
  4432. Strophe.warn("Request " + this._requests[0].id + " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) + " seconds since last activity");
  4433. this._throttledRequestHandler();
  4434. }
  4435. }
  4436. }
  4437. /** PrivateFunction: _getRequestStatus
  4438. *
  4439. * Returns the HTTP status code from a Strophe.Request
  4440. *
  4441. * Parameters:
  4442. * (Strophe.Request) req - The Strophe.Request instance.
  4443. * (Integer) def - The default value that should be returned if no
  4444. * status value was found.
  4445. */
  4446. static _getRequestStatus(req, def) {
  4447. let reqStatus;
  4448. if (req.xhr.readyState === 4) {
  4449. try {
  4450. reqStatus = req.xhr.status;
  4451. } catch (e) {
  4452. // ignore errors from undefined status attribute. Works
  4453. // around a browser bug
  4454. Strophe.error("Caught an error while retrieving a request's status, " + "reqStatus: " + reqStatus);
  4455. }
  4456. }
  4457. if (typeof reqStatus === "undefined") {
  4458. reqStatus = typeof def === 'number' ? def : 0;
  4459. }
  4460. return reqStatus;
  4461. }
  4462. /** PrivateFunction: _onRequestStateChange
  4463. * _Private_ handler for Strophe.Request state changes.
  4464. *
  4465. * This function is called when the XMLHttpRequest readyState changes.
  4466. * It contains a lot of error handling logic for the many ways that
  4467. * requests can fail, and calls the request callback when requests
  4468. * succeed.
  4469. *
  4470. * Parameters:
  4471. * (Function) func - The handler for the request.
  4472. * (Strophe.Request) req - The request that is changing readyState.
  4473. */
  4474. _onRequestStateChange(func, req) {
  4475. Strophe.debug("request id " + req.id + "." + req.sends + " state changed to " + req.xhr.readyState);
  4476. if (req.abort) {
  4477. req.abort = false;
  4478. return;
  4479. }
  4480. if (req.xhr.readyState !== 4) {
  4481. // The request is not yet complete
  4482. return;
  4483. }
  4484. const reqStatus = Bosh._getRequestStatus(req);
  4485. this.lastResponseHeaders = req.xhr.getAllResponseHeaders();
  4486. if (this._conn.disconnecting && reqStatus >= 400) {
  4487. this._hitError(reqStatus);
  4488. this._callProtocolErrorHandlers(req);
  4489. return;
  4490. }
  4491. const reqIs0 = this._requests[0] === req;
  4492. const reqIs1 = this._requests[1] === req;
  4493. const valid_request = reqStatus > 0 && reqStatus < 500;
  4494. const too_many_retries = req.sends > this._conn.maxRetries;
  4495. if (valid_request || too_many_retries) {
  4496. // remove from internal queue
  4497. this._removeRequest(req);
  4498. Strophe.debug("request id " + req.id + " should now be removed");
  4499. }
  4500. if (reqStatus === 200) {
  4501. // request succeeded
  4502. // if request 1 finished, or request 0 finished and request
  4503. // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
  4504. // restart the other - both will be in the first spot, as the
  4505. // completed request has been removed from the queue already
  4506. if (reqIs1 || reqIs0 && this._requests.length > 0 && this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
  4507. this._restartRequest(0);
  4508. }
  4509. this._conn.nextValidRid(Number(req.rid) + 1);
  4510. Strophe.debug("request id " + req.id + "." + req.sends + " got 200");
  4511. func(req); // call handler
  4512. this.errors = 0;
  4513. } else if (reqStatus === 0 || reqStatus >= 400 && reqStatus < 600 || reqStatus >= 12000) {
  4514. // request failed
  4515. Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened");
  4516. this._hitError(reqStatus);
  4517. this._callProtocolErrorHandlers(req);
  4518. if (reqStatus >= 400 && reqStatus < 500) {
  4519. this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null);
  4520. this._conn._doDisconnect();
  4521. }
  4522. } else {
  4523. Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened");
  4524. }
  4525. if (!valid_request && !too_many_retries) {
  4526. this._throttledRequestHandler();
  4527. } else if (too_many_retries && !this._conn.connected) {
  4528. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "giving-up");
  4529. }
  4530. }
  4531. /** PrivateFunction: _processRequest
  4532. * _Private_ function to process a request in the queue.
  4533. *
  4534. * This function takes requests off the queue and sends them and
  4535. * restarts dead requests.
  4536. *
  4537. * Parameters:
  4538. * (Integer) i - The index of the request in the queue.
  4539. */
  4540. _processRequest(i) {
  4541. let req = this._requests[i];
  4542. const reqStatus = Bosh._getRequestStatus(req, -1); // make sure we limit the number of retries
  4543. if (req.sends > this._conn.maxRetries) {
  4544. this._conn._onDisconnectTimeout();
  4545. return;
  4546. }
  4547. const time_elapsed = req.age();
  4548. const primary_timeout = !isNaN(time_elapsed) && time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait);
  4549. const secondary_timeout = req.dead !== null && req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait);
  4550. const server_error = req.xhr.readyState === 4 && (reqStatus < 1 || reqStatus >= 500);
  4551. if (primary_timeout || secondary_timeout || server_error) {
  4552. if (secondary_timeout) {
  4553. Strophe.error(`Request ${this._requests[i].id} timed out (secondary), restarting`);
  4554. }
  4555. req.abort = true;
  4556. req.xhr.abort(); // setting to null fails on IE6, so set to empty function
  4557. req.xhr.onreadystatechange = function () {};
  4558. this._requests[i] = new Strophe.Request(req.xmlData, req.origFunc, req.rid, req.sends);
  4559. req = this._requests[i];
  4560. }
  4561. if (req.xhr.readyState === 0) {
  4562. Strophe.debug("request id " + req.id + "." + req.sends + " posting");
  4563. try {
  4564. const content_type = this._conn.options.contentType || "text/xml; charset=utf-8";
  4565. req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true);
  4566. if (typeof req.xhr.setRequestHeader !== 'undefined') {
  4567. // IE9 doesn't have setRequestHeader
  4568. req.xhr.setRequestHeader("Content-Type", content_type);
  4569. }
  4570. if (this._conn.options.withCredentials) {
  4571. req.xhr.withCredentials = true;
  4572. }
  4573. } catch (e2) {
  4574. Strophe.error("XHR open failed: " + e2.toString());
  4575. if (!this._conn.connected) {
  4576. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "bad-service");
  4577. }
  4578. this._conn.disconnect();
  4579. return;
  4580. } // Fires the XHR request -- may be invoked immediately
  4581. // or on a gradually expanding retry window for reconnects
  4582. const sendFunc = () => {
  4583. req.date = new Date();
  4584. if (this._conn.options.customHeaders) {
  4585. const headers = this._conn.options.customHeaders;
  4586. for (const header in headers) {
  4587. if (Object.prototype.hasOwnProperty.call(headers, header)) {
  4588. req.xhr.setRequestHeader(header, headers[header]);
  4589. }
  4590. }
  4591. }
  4592. req.xhr.send(req.data);
  4593. }; // Implement progressive backoff for reconnects --
  4594. // First retry (send === 1) should also be instantaneous
  4595. if (req.sends > 1) {
  4596. // Using a cube of the retry number creates a nicely
  4597. // expanding retry window
  4598. const backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000;
  4599. setTimeout(function () {
  4600. // XXX: setTimeout should be called only with function expressions (23974bc1)
  4601. sendFunc();
  4602. }, backoff);
  4603. } else {
  4604. sendFunc();
  4605. }
  4606. req.sends++;
  4607. if (this._conn.xmlOutput !== Strophe.Connection.prototype.xmlOutput) {
  4608. if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {
  4609. this._conn.xmlOutput(req.xmlData.childNodes[0]);
  4610. } else {
  4611. this._conn.xmlOutput(req.xmlData);
  4612. }
  4613. }
  4614. if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) {
  4615. this._conn.rawOutput(req.data);
  4616. }
  4617. } else {
  4618. Strophe.debug("_processRequest: " + (i === 0 ? "first" : "second") + " request has readyState of " + req.xhr.readyState);
  4619. }
  4620. }
  4621. /** PrivateFunction: _removeRequest
  4622. * _Private_ function to remove a request from the queue.
  4623. *
  4624. * Parameters:
  4625. * (Strophe.Request) req - The request to remove.
  4626. */
  4627. _removeRequest(req) {
  4628. Strophe.debug("removing request");
  4629. for (let i = this._requests.length - 1; i >= 0; i--) {
  4630. if (req === this._requests[i]) {
  4631. this._requests.splice(i, 1);
  4632. }
  4633. } // IE6 fails on setting to null, so set to empty function
  4634. req.xhr.onreadystatechange = function () {};
  4635. this._throttledRequestHandler();
  4636. }
  4637. /** PrivateFunction: _restartRequest
  4638. * _Private_ function to restart a request that is presumed dead.
  4639. *
  4640. * Parameters:
  4641. * (Integer) i - The index of the request in the queue.
  4642. */
  4643. _restartRequest(i) {
  4644. const req = this._requests[i];
  4645. if (req.dead === null) {
  4646. req.dead = new Date();
  4647. }
  4648. this._processRequest(i);
  4649. }
  4650. /** PrivateFunction: _reqToData
  4651. * _Private_ function to get a stanza out of a request.
  4652. *
  4653. * Tries to extract a stanza out of a Request Object.
  4654. * When this fails the current connection will be disconnected.
  4655. *
  4656. * Parameters:
  4657. * (Object) req - The Request.
  4658. *
  4659. * Returns:
  4660. * The stanza that was passed.
  4661. */
  4662. _reqToData(req) {
  4663. try {
  4664. return req.getResponse();
  4665. } catch (e) {
  4666. if (e.message !== "parsererror") {
  4667. throw e;
  4668. }
  4669. this._conn.disconnect("strophe-parsererror");
  4670. }
  4671. }
  4672. /** PrivateFunction: _sendTerminate
  4673. * _Private_ function to send initial disconnect sequence.
  4674. *
  4675. * This is the first step in a graceful disconnect. It sends
  4676. * the BOSH server a terminate body and includes an unavailable
  4677. * presence if authentication has completed.
  4678. */
  4679. _sendTerminate(pres) {
  4680. Strophe.debug("_sendTerminate was called");
  4681. const body = this._buildBody().attrs({
  4682. type: "terminate"
  4683. });
  4684. if (pres) {
  4685. body.cnode(pres.tree());
  4686. }
  4687. const req = new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid"));
  4688. this._requests.push(req);
  4689. this._throttledRequestHandler();
  4690. }
  4691. /** PrivateFunction: _send
  4692. * _Private_ part of the Connection.send function for BOSH
  4693. *
  4694. * Just triggers the RequestHandler to send the messages that are in the queue
  4695. */
  4696. _send() {
  4697. clearTimeout(this._conn._idleTimeout);
  4698. this._throttledRequestHandler();
  4699. this._conn._idleTimeout = setTimeout(() => this._conn._onIdle(), 100);
  4700. }
  4701. /** PrivateFunction: _sendRestart
  4702. *
  4703. * Send an xmpp:restart stanza.
  4704. */
  4705. _sendRestart() {
  4706. this._throttledRequestHandler();
  4707. clearTimeout(this._conn._idleTimeout);
  4708. }
  4709. /** PrivateFunction: _throttledRequestHandler
  4710. * _Private_ function to throttle requests to the connection window.
  4711. *
  4712. * This function makes sure we don't send requests so fast that the
  4713. * request ids overflow the connection window in the case that one
  4714. * request died.
  4715. */
  4716. _throttledRequestHandler() {
  4717. if (!this._requests) {
  4718. Strophe.debug("_throttledRequestHandler called with " + "undefined requests");
  4719. } else {
  4720. Strophe.debug("_throttledRequestHandler called with " + this._requests.length + " requests");
  4721. }
  4722. if (!this._requests || this._requests.length === 0) {
  4723. return;
  4724. }
  4725. if (this._requests.length > 0) {
  4726. this._processRequest(0);
  4727. }
  4728. if (this._requests.length > 1 && Math.abs(this._requests[0].rid - this._requests[1].rid) < this.window) {
  4729. this._processRequest(1);
  4730. }
  4731. }
  4732. };
  4733. /** Variable: strip
  4734. *
  4735. * BOSH-Connections will have all stanzas wrapped in a <body> tag when
  4736. * passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.
  4737. * To strip this tag, User code can set <Strophe.Bosh.strip> to "body":
  4738. *
  4739. * > Strophe.Bosh.prototype.strip = "body";
  4740. *
  4741. * This will enable stripping of the body tag in both
  4742. * <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.
  4743. */
  4744. Strophe.Bosh.prototype.strip = null;
  4745. /*
  4746. This program is distributed under the terms of the MIT license.
  4747. Please see the LICENSE file for details.
  4748. Copyright 2006-2008, OGG, LLC
  4749. */
  4750. /** Class: Strophe.WebSocket
  4751. * _Private_ helper class that handles WebSocket Connections
  4752. *
  4753. * The Strophe.WebSocket class is used internally by Strophe.Connection
  4754. * to encapsulate WebSocket sessions. It is not meant to be used from user's code.
  4755. */
  4756. /** File: websocket.js
  4757. * A JavaScript library to enable XMPP over Websocket in Strophejs.
  4758. *
  4759. * This file implements XMPP over WebSockets for Strophejs.
  4760. * If a Connection is established with a Websocket url (ws://...)
  4761. * Strophe will use WebSockets.
  4762. * For more information on XMPP-over-WebSocket see RFC 7395:
  4763. * http://tools.ietf.org/html/rfc7395
  4764. *
  4765. * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)
  4766. */
  4767. Strophe.Websocket = class Websocket {
  4768. /** PrivateConstructor: Strophe.Websocket
  4769. * Create and initialize a Strophe.WebSocket object.
  4770. * Currently only sets the connection Object.
  4771. *
  4772. * Parameters:
  4773. * (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.
  4774. *
  4775. * Returns:
  4776. * A new Strophe.WebSocket object.
  4777. */
  4778. constructor(connection) {
  4779. this._conn = connection;
  4780. this.strip = "wrapper";
  4781. const service = connection.service;
  4782. if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) {
  4783. // If the service is not an absolute URL, assume it is a path and put the absolute
  4784. // URL together from options, current URL and the path.
  4785. let new_service = "";
  4786. if (connection.options.protocol === "ws" && window.location.protocol !== "https:") {
  4787. new_service += "ws";
  4788. } else {
  4789. new_service += "wss";
  4790. }
  4791. new_service += "://" + window.location.host;
  4792. if (service.indexOf("/") !== 0) {
  4793. new_service += window.location.pathname + service;
  4794. } else {
  4795. new_service += service;
  4796. }
  4797. connection.service = new_service;
  4798. }
  4799. }
  4800. /** PrivateFunction: _buildStream
  4801. * _Private_ helper function to generate the <stream> start tag for WebSockets
  4802. *
  4803. * Returns:
  4804. * A Strophe.Builder with a <stream> element.
  4805. */
  4806. _buildStream() {
  4807. return $build("open", {
  4808. "xmlns": Strophe.NS.FRAMING,
  4809. "to": this._conn.domain,
  4810. "version": '1.0'
  4811. });
  4812. }
  4813. /** PrivateFunction: _checkStreamError
  4814. * _Private_ checks a message for stream:error
  4815. *
  4816. * Parameters:
  4817. * (Strophe.Request) bodyWrap - The received stanza.
  4818. * connectstatus - The ConnectStatus that will be set on error.
  4819. * Returns:
  4820. * true if there was a streamerror, false otherwise.
  4821. */
  4822. _checkStreamError(bodyWrap, connectstatus) {
  4823. let errors;
  4824. if (bodyWrap.getElementsByTagNameNS) {
  4825. errors = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "error");
  4826. } else {
  4827. errors = bodyWrap.getElementsByTagName("stream:error");
  4828. }
  4829. if (errors.length === 0) {
  4830. return false;
  4831. }
  4832. const error = errors[0];
  4833. let condition = "";
  4834. let text = "";
  4835. const ns = "urn:ietf:params:xml:ns:xmpp-streams";
  4836. for (let i = 0; i < error.childNodes.length; i++) {
  4837. const e = error.childNodes[i];
  4838. if (e.getAttribute("xmlns") !== ns) {
  4839. break;
  4840. }
  4841. if (e.nodeName === "text") {
  4842. text = e.textContent;
  4843. } else {
  4844. condition = e.nodeName;
  4845. }
  4846. }
  4847. let errorString = "WebSocket stream error: ";
  4848. if (condition) {
  4849. errorString += condition;
  4850. } else {
  4851. errorString += "unknown";
  4852. }
  4853. if (text) {
  4854. errorString += " - " + text;
  4855. }
  4856. Strophe.error(errorString); // close the connection on stream_error
  4857. this._conn._changeConnectStatus(connectstatus, condition);
  4858. this._conn._doDisconnect();
  4859. return true;
  4860. }
  4861. /** PrivateFunction: _reset
  4862. * Reset the connection.
  4863. *
  4864. * This function is called by the reset function of the Strophe Connection.
  4865. * Is not needed by WebSockets.
  4866. */
  4867. _reset() {
  4868. // eslint-disable-line class-methods-use-this
  4869. return;
  4870. }
  4871. /** PrivateFunction: _connect
  4872. * _Private_ function called by Strophe.Connection.connect
  4873. *
  4874. * Creates a WebSocket for a connection and assigns Callbacks to it.
  4875. * Does nothing if there already is a WebSocket.
  4876. */
  4877. _connect() {
  4878. // Ensure that there is no open WebSocket from a previous Connection.
  4879. this._closeSocket();
  4880. this.socket = new WebSocket(this._conn.service, "xmpp");
  4881. this.socket.onopen = () => this._onOpen();
  4882. this.socket.onerror = e => this._onError(e);
  4883. this.socket.onclose = e => this._onClose(e); // Gets replaced with this._onMessage once _onInitialMessage is called
  4884. this.socket.onmessage = message => this._onInitialMessage(message);
  4885. }
  4886. /** PrivateFunction: _connect_cb
  4887. * _Private_ function called by Strophe.Connection._connect_cb
  4888. *
  4889. * checks for stream:error
  4890. *
  4891. * Parameters:
  4892. * (Strophe.Request) bodyWrap - The received stanza.
  4893. */
  4894. _connect_cb(bodyWrap) {
  4895. const error = this._checkStreamError(bodyWrap, Strophe.Status.CONNFAIL);
  4896. if (error) {
  4897. return Strophe.Status.CONNFAIL;
  4898. }
  4899. }
  4900. /** PrivateFunction: _handleStreamStart
  4901. * _Private_ function that checks the opening <open /> tag for errors.
  4902. *
  4903. * Disconnects if there is an error and returns false, true otherwise.
  4904. *
  4905. * Parameters:
  4906. * (Node) message - Stanza containing the <open /> tag.
  4907. */
  4908. _handleStreamStart(message) {
  4909. let error = false; // Check for errors in the <open /> tag
  4910. const ns = message.getAttribute("xmlns");
  4911. if (typeof ns !== "string") {
  4912. error = "Missing xmlns in <open />";
  4913. } else if (ns !== Strophe.NS.FRAMING) {
  4914. error = "Wrong xmlns in <open />: " + ns;
  4915. }
  4916. const ver = message.getAttribute("version");
  4917. if (typeof ver !== "string") {
  4918. error = "Missing version in <open />";
  4919. } else if (ver !== "1.0") {
  4920. error = "Wrong version in <open />: " + ver;
  4921. }
  4922. if (error) {
  4923. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error);
  4924. this._conn._doDisconnect();
  4925. return false;
  4926. }
  4927. return true;
  4928. }
  4929. /** PrivateFunction: _onInitialMessage
  4930. * _Private_ function that handles the first connection messages.
  4931. *
  4932. * On receiving an opening stream tag this callback replaces itself with the real
  4933. * message handler. On receiving a stream error the connection is terminated.
  4934. */
  4935. _onInitialMessage(message) {
  4936. if (message.data.indexOf("<open ") === 0 || message.data.indexOf("<?xml") === 0) {
  4937. // Strip the XML Declaration, if there is one
  4938. const data = message.data.replace(/^(<\?.*?\?>\s*)*/, "");
  4939. if (data === '') return;
  4940. const streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement;
  4941. this._conn.xmlInput(streamStart);
  4942. this._conn.rawInput(message.data); //_handleStreamSteart will check for XML errors and disconnect on error
  4943. if (this._handleStreamStart(streamStart)) {
  4944. //_connect_cb will check for stream:error and disconnect on error
  4945. this._connect_cb(streamStart);
  4946. }
  4947. } else if (message.data.indexOf("<close ") === 0) {
  4948. // <close xmlns="urn:ietf:params:xml:ns:xmpp-framing />
  4949. // Parse the raw string to an XML element
  4950. const parsedMessage = new DOMParser().parseFromString(message.data, "text/xml").documentElement; // Report this input to the raw and xml handlers
  4951. this._conn.xmlInput(parsedMessage);
  4952. this._conn.rawInput(message.data);
  4953. const see_uri = parsedMessage.getAttribute("see-other-uri");
  4954. if (see_uri) {
  4955. const service = this._conn.service; // Valid scenarios: WSS->WSS, WS->ANY
  4956. const isSecureRedirect = service.indexOf("wss:") >= 0 && see_uri.indexOf("wss:") >= 0 || service.indexOf("ws:") >= 0;
  4957. if (isSecureRedirect) {
  4958. this._conn._changeConnectStatus(Strophe.Status.REDIRECT, "Received see-other-uri, resetting connection");
  4959. this._conn.reset();
  4960. this._conn.service = see_uri;
  4961. this._connect();
  4962. }
  4963. } else {
  4964. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Received closing stream");
  4965. this._conn._doDisconnect();
  4966. }
  4967. } else {
  4968. this._replaceMessageHandler();
  4969. const string = this._streamWrap(message.data);
  4970. const elem = new DOMParser().parseFromString(string, "text/xml").documentElement;
  4971. this._conn._connect_cb(elem, null, message.data);
  4972. }
  4973. }
  4974. /** PrivateFunction: _replaceMessageHandler
  4975. *
  4976. * Called by _onInitialMessage in order to replace itself with the general message handler.
  4977. * This method is overridden by Strophe.WorkerWebsocket, which manages a
  4978. * websocket connection via a service worker and doesn't have direct access
  4979. * to the socket.
  4980. */
  4981. _replaceMessageHandler() {
  4982. this.socket.onmessage = m => this._onMessage(m);
  4983. }
  4984. /** PrivateFunction: _disconnect
  4985. * _Private_ function called by Strophe.Connection.disconnect
  4986. *
  4987. * Disconnects and sends a last stanza if one is given
  4988. *
  4989. * Parameters:
  4990. * (Request) pres - This stanza will be sent before disconnecting.
  4991. */
  4992. _disconnect(pres) {
  4993. if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
  4994. if (pres) {
  4995. this._conn.send(pres);
  4996. }
  4997. const close = $build("close", {
  4998. "xmlns": Strophe.NS.FRAMING
  4999. });
  5000. this._conn.xmlOutput(close.tree());
  5001. const closeString = Strophe.serialize(close);
  5002. this._conn.rawOutput(closeString);
  5003. try {
  5004. this.socket.send(closeString);
  5005. } catch (e) {
  5006. Strophe.warn("Couldn't send <close /> tag.");
  5007. }
  5008. }
  5009. setTimeout(() => this._conn._doDisconnect, 0);
  5010. }
  5011. /** PrivateFunction: _doDisconnect
  5012. * _Private_ function to disconnect.
  5013. *
  5014. * Just closes the Socket for WebSockets
  5015. */
  5016. _doDisconnect() {
  5017. Strophe.debug("WebSockets _doDisconnect was called");
  5018. this._closeSocket();
  5019. }
  5020. /** PrivateFunction _streamWrap
  5021. * _Private_ helper function to wrap a stanza in a <stream> tag.
  5022. * This is used so Strophe can process stanzas from WebSockets like BOSH
  5023. */
  5024. _streamWrap(stanza) {
  5025. // eslint-disable-line class-methods-use-this
  5026. return "<wrapper>" + stanza + '</wrapper>';
  5027. }
  5028. /** PrivateFunction: _closeSocket
  5029. * _Private_ function to close the WebSocket.
  5030. *
  5031. * Closes the socket if it is still open and deletes it
  5032. */
  5033. _closeSocket() {
  5034. if (this.socket) {
  5035. try {
  5036. this.socket.onclose = null;
  5037. this.socket.onerror = null;
  5038. this.socket.onmessage = null;
  5039. this.socket.close();
  5040. } catch (e) {
  5041. Strophe.debug(e.message);
  5042. }
  5043. }
  5044. this.socket = null;
  5045. }
  5046. /** PrivateFunction: _emptyQueue
  5047. * _Private_ function to check if the message queue is empty.
  5048. *
  5049. * Returns:
  5050. * True, because WebSocket messages are send immediately after queueing.
  5051. */
  5052. _emptyQueue() {
  5053. // eslint-disable-line class-methods-use-this
  5054. return true;
  5055. }
  5056. /** PrivateFunction: _onClose
  5057. * _Private_ function to handle websockets closing.
  5058. */
  5059. _onClose(e) {
  5060. if (this._conn.connected && !this._conn.disconnecting) {
  5061. Strophe.error("Websocket closed unexpectedly");
  5062. this._conn._doDisconnect();
  5063. } else if (e && e.code === 1006 && !this._conn.connected && this.socket) {
  5064. // in case the onError callback was not called (Safari 10 does not
  5065. // call onerror when the initial connection fails) we need to
  5066. // dispatch a CONNFAIL status update to be consistent with the
  5067. // behavior on other browsers.
  5068. Strophe.error("Websocket closed unexcectedly");
  5069. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected.");
  5070. this._conn._doDisconnect();
  5071. } else {
  5072. Strophe.debug("Websocket closed");
  5073. }
  5074. }
  5075. /** PrivateFunction: _no_auth_received
  5076. *
  5077. * Called on stream start/restart when no stream:features
  5078. * has been received.
  5079. */
  5080. _no_auth_received(callback) {
  5081. Strophe.error("Server did not offer a supported authentication mechanism");
  5082. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.NO_AUTH_MECH);
  5083. if (callback) {
  5084. callback.call(this._conn);
  5085. }
  5086. this._conn._doDisconnect();
  5087. }
  5088. /** PrivateFunction: _onDisconnectTimeout
  5089. * _Private_ timeout handler for handling non-graceful disconnection.
  5090. *
  5091. * This does nothing for WebSockets
  5092. */
  5093. _onDisconnectTimeout() {} // eslint-disable-line class-methods-use-this
  5094. /** PrivateFunction: _abortAllRequests
  5095. * _Private_ helper function that makes sure all pending requests are aborted.
  5096. */
  5097. _abortAllRequests() {} // eslint-disable-line class-methods-use-this
  5098. /** PrivateFunction: _onError
  5099. * _Private_ function to handle websockets errors.
  5100. *
  5101. * Parameters:
  5102. * (Object) error - The websocket error.
  5103. */
  5104. _onError(error) {
  5105. Strophe.error("Websocket error " + JSON.stringify(error));
  5106. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected.");
  5107. this._disconnect();
  5108. }
  5109. /** PrivateFunction: _onIdle
  5110. * _Private_ function called by Strophe.Connection._onIdle
  5111. *
  5112. * sends all queued stanzas
  5113. */
  5114. _onIdle() {
  5115. const data = this._conn._data;
  5116. if (data.length > 0 && !this._conn.paused) {
  5117. for (let i = 0; i < data.length; i++) {
  5118. if (data[i] !== null) {
  5119. let stanza;
  5120. if (data[i] === "restart") {
  5121. stanza = this._buildStream().tree();
  5122. } else {
  5123. stanza = data[i];
  5124. }
  5125. const rawStanza = Strophe.serialize(stanza);
  5126. this._conn.xmlOutput(stanza);
  5127. this._conn.rawOutput(rawStanza);
  5128. this.socket.send(rawStanza);
  5129. }
  5130. }
  5131. this._conn._data = [];
  5132. }
  5133. }
  5134. /** PrivateFunction: _onMessage
  5135. * _Private_ function to handle websockets messages.
  5136. *
  5137. * This function parses each of the messages as if they are full documents.
  5138. * [TODO : We may actually want to use a SAX Push parser].
  5139. *
  5140. * Since all XMPP traffic starts with
  5141. * <stream:stream version='1.0'
  5142. * xml:lang='en'
  5143. * xmlns='jabber:client'
  5144. * xmlns:stream='http://etherx.jabber.org/streams'
  5145. * id='3697395463'
  5146. * from='SERVER'>
  5147. *
  5148. * The first stanza will always fail to be parsed.
  5149. *
  5150. * Additionally, the seconds stanza will always be <stream:features> with
  5151. * the stream NS defined in the previous stanza, so we need to 'force'
  5152. * the inclusion of the NS in this stanza.
  5153. *
  5154. * Parameters:
  5155. * (string) message - The websocket message.
  5156. */
  5157. _onMessage(message) {
  5158. let elem; // check for closing stream
  5159. const close = '<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />';
  5160. if (message.data === close) {
  5161. this._conn.rawInput(close);
  5162. this._conn.xmlInput(message);
  5163. if (!this._conn.disconnecting) {
  5164. this._conn._doDisconnect();
  5165. }
  5166. return;
  5167. } else if (message.data.search("<open ") === 0) {
  5168. // This handles stream restarts
  5169. elem = new DOMParser().parseFromString(message.data, "text/xml").documentElement;
  5170. if (!this._handleStreamStart(elem)) {
  5171. return;
  5172. }
  5173. } else {
  5174. const data = this._streamWrap(message.data);
  5175. elem = new DOMParser().parseFromString(data, "text/xml").documentElement;
  5176. }
  5177. if (this._checkStreamError(elem, Strophe.Status.ERROR)) {
  5178. return;
  5179. } //handle unavailable presence stanza before disconnecting
  5180. if (this._conn.disconnecting && elem.firstChild.nodeName === "presence" && elem.firstChild.getAttribute("type") === "unavailable") {
  5181. this._conn.xmlInput(elem);
  5182. this._conn.rawInput(Strophe.serialize(elem)); // if we are already disconnecting we will ignore the unavailable stanza and
  5183. // wait for the </stream:stream> tag before we close the connection
  5184. return;
  5185. }
  5186. this._conn._dataRecv(elem, message.data);
  5187. }
  5188. /** PrivateFunction: _onOpen
  5189. * _Private_ function to handle websockets connection setup.
  5190. *
  5191. * The opening stream tag is sent here.
  5192. */
  5193. _onOpen() {
  5194. Strophe.debug("Websocket open");
  5195. const start = this._buildStream();
  5196. this._conn.xmlOutput(start.tree());
  5197. const startString = Strophe.serialize(start);
  5198. this._conn.rawOutput(startString);
  5199. this.socket.send(startString);
  5200. }
  5201. /** PrivateFunction: _reqToData
  5202. * _Private_ function to get a stanza out of a request.
  5203. *
  5204. * WebSockets don't use requests, so the passed argument is just returned.
  5205. *
  5206. * Parameters:
  5207. * (Object) stanza - The stanza.
  5208. *
  5209. * Returns:
  5210. * The stanza that was passed.
  5211. */
  5212. _reqToData(stanza) {
  5213. // eslint-disable-line class-methods-use-this
  5214. return stanza;
  5215. }
  5216. /** PrivateFunction: _send
  5217. * _Private_ part of the Connection.send function for WebSocket
  5218. *
  5219. * Just flushes the messages that are in the queue
  5220. */
  5221. _send() {
  5222. this._conn.flush();
  5223. }
  5224. /** PrivateFunction: _sendRestart
  5225. *
  5226. * Send an xmpp:restart stanza.
  5227. */
  5228. _sendRestart() {
  5229. clearTimeout(this._conn._idleTimeout);
  5230. this._conn._onIdle.bind(this._conn)();
  5231. }
  5232. };
  5233. /*
  5234. This program is distributed under the terms of the MIT license.
  5235. Please see the LICENSE file for details.
  5236. Copyright 2020, JC Brand
  5237. */
  5238. const lmap = {};
  5239. lmap['debug'] = Strophe.LogLevel.DEBUG;
  5240. lmap['info'] = Strophe.LogLevel.INFO;
  5241. lmap['warn'] = Strophe.LogLevel.WARN;
  5242. lmap['error'] = Strophe.LogLevel.ERROR;
  5243. lmap['fatal'] = Strophe.LogLevel.FATAL;
  5244. /** Class: Strophe.WorkerWebsocket
  5245. * _Private_ helper class that handles a websocket connection inside a shared worker.
  5246. */
  5247. Strophe.WorkerWebsocket = class WorkerWebsocket extends Strophe.Websocket {
  5248. /** PrivateConstructor: Strophe.WorkerWebsocket
  5249. * Create and initialize a Strophe.WorkerWebsocket object.
  5250. *
  5251. * Parameters:
  5252. * (Strophe.Connection) connection - The Strophe.Connection
  5253. *
  5254. * Returns:
  5255. * A new Strophe.WorkerWebsocket object.
  5256. */
  5257. constructor(connection) {
  5258. super(connection);
  5259. this._conn = connection;
  5260. this.worker = new SharedWorker(this._conn.options.worker, 'Strophe XMPP Connection');
  5261. this.worker.onerror = e => {
  5262. var _console;
  5263. (_console = console) === null || _console === void 0 ? void 0 : _console.error(e);
  5264. Strophe.log(Strophe.LogLevel.ERROR, `Shared Worker Error: ${e}`);
  5265. };
  5266. }
  5267. get socket() {
  5268. return {
  5269. 'send': str => this.worker.port.postMessage(['send', str])
  5270. };
  5271. }
  5272. _connect() {
  5273. this._messageHandler = m => this._onInitialMessage(m);
  5274. this.worker.port.start();
  5275. this.worker.port.onmessage = ev => this._onWorkerMessage(ev);
  5276. this.worker.port.postMessage(['_connect', this._conn.service, this._conn.jid]);
  5277. }
  5278. _attach(callback) {
  5279. this._messageHandler = m => this._onMessage(m);
  5280. this._conn.connect_callback = callback;
  5281. this.worker.port.start();
  5282. this.worker.port.onmessage = ev => this._onWorkerMessage(ev);
  5283. this.worker.port.postMessage(['_attach', this._conn.service]);
  5284. }
  5285. _attachCallback(status, jid) {
  5286. if (status === Strophe.Status.ATTACHED) {
  5287. this._conn.jid = jid;
  5288. this._conn.authenticated = true;
  5289. this._conn.connected = true;
  5290. this._conn.restored = true;
  5291. this._conn._changeConnectStatus(Strophe.Status.ATTACHED);
  5292. } else if (status === Strophe.Status.ATTACHFAIL) {
  5293. this._conn.authenticated = false;
  5294. this._conn.connected = false;
  5295. this._conn.restored = false;
  5296. this._conn._changeConnectStatus(Strophe.Status.ATTACHFAIL);
  5297. }
  5298. }
  5299. _disconnect(readyState, pres) {
  5300. pres && this._conn.send(pres);
  5301. const close = $build("close", {
  5302. "xmlns": Strophe.NS.FRAMING
  5303. });
  5304. this._conn.xmlOutput(close.tree());
  5305. const closeString = Strophe.serialize(close);
  5306. this._conn.rawOutput(closeString);
  5307. this.worker.port.postMessage(['send', closeString]);
  5308. this._conn._doDisconnect();
  5309. }
  5310. _onClose(e) {
  5311. if (this._conn.connected && !this._conn.disconnecting) {
  5312. Strophe.error("Websocket closed unexpectedly");
  5313. this._conn._doDisconnect();
  5314. } else if (e && e.code === 1006 && !this._conn.connected) {
  5315. // in case the onError callback was not called (Safari 10 does not
  5316. // call onerror when the initial connection fails) we need to
  5317. // dispatch a CONNFAIL status update to be consistent with the
  5318. // behavior on other browsers.
  5319. Strophe.error("Websocket closed unexcectedly");
  5320. this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected.");
  5321. this._conn._doDisconnect();
  5322. } else {
  5323. Strophe.debug("Websocket closed");
  5324. }
  5325. }
  5326. _closeSocket() {
  5327. this.worker.port.postMessage(['_closeSocket']);
  5328. }
  5329. /** PrivateFunction: _replaceMessageHandler
  5330. *
  5331. * Called by _onInitialMessage in order to replace itself with the general message handler.
  5332. * This method is overridden by Strophe.WorkerWebsocket, which manages a
  5333. * websocket connection via a service worker and doesn't have direct access
  5334. * to the socket.
  5335. */
  5336. _replaceMessageHandler() {
  5337. this._messageHandler = m => this._onMessage(m);
  5338. }
  5339. /** PrivateFunction: _onWorkerMessage
  5340. * _Private_ function that handles messages received from the service worker
  5341. */
  5342. _onWorkerMessage(ev) {
  5343. const {
  5344. data
  5345. } = ev;
  5346. const method_name = data[0];
  5347. if (method_name === '_onMessage') {
  5348. this._messageHandler(data[1]);
  5349. } else if (method_name in this) {
  5350. try {
  5351. this[method_name].apply(this, ev.data.slice(1));
  5352. } catch (e) {
  5353. Strophe.log(Strophe.LogLevel.ERROR, e);
  5354. }
  5355. } else if (method_name === 'log') {
  5356. const level = data[1];
  5357. const msg = data[2];
  5358. Strophe.log(lmap[level], msg);
  5359. } else {
  5360. Strophe.log(Strophe.LogLevel.ERROR, `Found unhandled service worker message: ${data}`);
  5361. }
  5362. }
  5363. };
  5364. global$1.$build = core.$build;
  5365. global$1.$iq = core.$iq;
  5366. global$1.$msg = core.$msg;
  5367. global$1.$pres = core.$pres;
  5368. global$1.Strophe = core.Strophe;
  5369. const {
  5370. b64_sha1
  5371. } = SHA1;
  5372. exports.$build = $build;
  5373. exports.$iq = $iq;
  5374. exports.$msg = $msg;
  5375. exports.$pres = $pres;
  5376. exports.Strophe = Strophe;
  5377. exports.b64_sha1 = b64_sha1;
  5378. Object.defineProperty(exports, '__esModule', { value: true });
  5379. }));