strophe-1.2.8.js 203 KB

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