nsCookieService.cpp 179 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/Attributes.h"
  6. #include "mozilla/DebugOnly.h"
  7. #include "mozilla/Likely.h"
  8. #include "mozilla/Unused.h"
  9. #include "mozilla/net/CookieServiceChild.h"
  10. #include "mozilla/net/NeckoCommon.h"
  11. #include "nsCookieService.h"
  12. #include "nsContentUtils.h"
  13. #include "nsIServiceManager.h"
  14. #include "nsIIOService.h"
  15. #include "nsIPrefBranch.h"
  16. #include "nsIPrefService.h"
  17. #include "nsIScriptError.h"
  18. #include "nsICookiePermission.h"
  19. #include "nsIURI.h"
  20. #include "nsIURL.h"
  21. #include "nsIChannel.h"
  22. #include "nsIFile.h"
  23. #include "nsIObserverService.h"
  24. #include "nsILineInputStream.h"
  25. #include "nsIEffectiveTLDService.h"
  26. #include "nsIIDNService.h"
  27. #include "mozIThirdPartyUtil.h"
  28. #include "nsTArray.h"
  29. #include "nsCOMArray.h"
  30. #include "nsIMutableArray.h"
  31. #include "nsArrayEnumerator.h"
  32. #include "nsEnumeratorUtils.h"
  33. #include "nsAutoPtr.h"
  34. #include "nsReadableUtils.h"
  35. #include "nsCRT.h"
  36. #include "prprf.h"
  37. #include "nsNetUtil.h"
  38. #include "nsNetCID.h"
  39. #include "nsISimpleEnumerator.h"
  40. #include "nsIInputStream.h"
  41. #include "nsAppDirectoryServiceDefs.h"
  42. #include "nsNetCID.h"
  43. #include "mozilla/storage.h"
  44. #include "mozilla/AutoRestore.h"
  45. #include "mozilla/FileUtils.h"
  46. #include "nsIAppsService.h"
  47. #include "mozIApplication.h"
  48. #include "mozIApplicationClearPrivateDataParams.h"
  49. #include "nsIConsoleService.h"
  50. #include "nsVariant.h"
  51. using namespace mozilla;
  52. using namespace mozilla::net;
  53. // Create key from baseDomain that will access the default cookie namespace.
  54. // TODO: When we figure out what the API will look like for nsICookieManager{2}
  55. // on content processes (see bug 777620), change to use the appropriate app
  56. // namespace. For now those IDLs aren't supported on child processes.
  57. #define DEFAULT_APP_KEY(baseDomain) \
  58. nsCookieKey(baseDomain, NeckoOriginAttributes())
  59. /******************************************************************************
  60. * nsCookieService impl:
  61. * useful types & constants
  62. ******************************************************************************/
  63. static nsCookieService *gCookieService;
  64. // XXX_hack. See bug 178993.
  65. // This is a hack to hide HttpOnly cookies from older browsers
  66. #define HTTP_ONLY_PREFIX "#HttpOnly_"
  67. #define COOKIES_FILE "cookies.sqlite"
  68. #define COOKIES_SCHEMA_VERSION 7
  69. // parameter indexes; see EnsureReadDomain, EnsureReadComplete and
  70. // ReadCookieDBListener::HandleResult
  71. #define IDX_NAME 0
  72. #define IDX_VALUE 1
  73. #define IDX_HOST 2
  74. #define IDX_PATH 3
  75. #define IDX_EXPIRY 4
  76. #define IDX_LAST_ACCESSED 5
  77. #define IDX_CREATION_TIME 6
  78. #define IDX_SECURE 7
  79. #define IDX_HTTPONLY 8
  80. #define IDX_BASE_DOMAIN 9
  81. #define IDX_ORIGIN_ATTRIBUTES 10
  82. static const int64_t kCookiePurgeAge =
  83. int64_t(30 * 24 * 60 * 60) * PR_USEC_PER_SEC; // 30 days in microseconds
  84. #define OLD_COOKIE_FILE_NAME "cookies.txt"
  85. #undef LIMIT
  86. #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
  87. #undef ADD_TEN_PERCENT
  88. #define ADD_TEN_PERCENT(i) static_cast<uint32_t>((i) + (i)/10)
  89. // default limits for the cookie list. these can be tuned by the
  90. // network.cookie.maxNumber and network.cookie.maxPerHost prefs respectively.
  91. static const uint32_t kMaxNumberOfCookies = 3000;
  92. static const uint32_t kMaxCookiesPerHost = 150;
  93. static const uint32_t kMaxBytesPerCookie = 4096;
  94. static const uint32_t kMaxBytesPerPath = 1024;
  95. // pref string constants
  96. static const char kPrefCookieBehavior[] = "network.cookie.cookieBehavior";
  97. static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber";
  98. static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost";
  99. static const char kPrefCookiePurgeAge[] = "network.cookie.purgeAge";
  100. static const char kPrefThirdPartySession[] = "network.cookie.thirdparty.sessionOnly";
  101. static const char kCookieLeaveSecurityAlone[] = "network.cookie.leave-secure-alone";
  102. static void
  103. bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
  104. const nsCookieKey &aKey,
  105. const nsCookie *aCookie);
  106. // struct for temporarily storing cookie attributes during header parsing
  107. struct nsCookieAttributes
  108. {
  109. nsAutoCString name;
  110. nsAutoCString value;
  111. nsAutoCString host;
  112. nsAutoCString path;
  113. nsAutoCString expires;
  114. nsAutoCString maxage;
  115. int64_t expiryTime;
  116. bool isSession;
  117. bool isSecure;
  118. bool isHttpOnly;
  119. };
  120. // stores the nsCookieEntry entryclass and an index into the cookie array
  121. // within that entryclass, for purposes of storing an iteration state that
  122. // points to a certain cookie.
  123. struct nsListIter
  124. {
  125. // default (non-initializing) constructor.
  126. nsListIter() = default;
  127. // explicit constructor to a given iterator state with entryclass 'aEntry'
  128. // and index 'aIndex'.
  129. explicit
  130. nsListIter(nsCookieEntry *aEntry, nsCookieEntry::IndexType aIndex)
  131. : entry(aEntry)
  132. , index(aIndex)
  133. {
  134. }
  135. // get the nsCookie * the iterator currently points to.
  136. nsCookie * Cookie() const
  137. {
  138. return entry->GetCookies()[index];
  139. }
  140. nsCookieEntry *entry;
  141. nsCookieEntry::IndexType index;
  142. };
  143. /******************************************************************************
  144. * Cookie logging handlers
  145. * used for logging in nsCookieService
  146. ******************************************************************************/
  147. // logging handlers
  148. #ifdef MOZ_LOGGING
  149. // in order to do logging, the following environment variables need to be set:
  150. //
  151. // set MOZ_LOG=cookie:3 -- shows rejected cookies
  152. // set MOZ_LOG=cookie:4 -- shows accepted and rejected cookies
  153. // set MOZ_LOG_FILE=cookie.log
  154. //
  155. #include "mozilla/Logging.h"
  156. #endif
  157. // define logging macros for convenience
  158. #define SET_COOKIE true
  159. #define GET_COOKIE false
  160. static LazyLogModule gCookieLog("cookie");
  161. #define COOKIE_LOGFAILURE(a, b, c, d) LogFailure(a, b, c, d)
  162. #define COOKIE_LOGSUCCESS(a, b, c, d, e) LogSuccess(a, b, c, d, e)
  163. #define COOKIE_LOGEVICTED(a, details) \
  164. PR_BEGIN_MACRO \
  165. if (MOZ_LOG_TEST(gCookieLog, LogLevel::Debug)) \
  166. LogEvicted(a, details); \
  167. PR_END_MACRO
  168. #define COOKIE_LOGSTRING(lvl, fmt) \
  169. PR_BEGIN_MACRO \
  170. MOZ_LOG(gCookieLog, lvl, fmt); \
  171. MOZ_LOG(gCookieLog, lvl, ("\n")); \
  172. PR_END_MACRO
  173. static void
  174. LogFailure(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, const char *aReason)
  175. {
  176. // if logging isn't enabled, return now to save cycles
  177. if (!MOZ_LOG_TEST(gCookieLog, LogLevel::Warning))
  178. return;
  179. nsAutoCString spec;
  180. if (aHostURI)
  181. aHostURI->GetAsciiSpec(spec);
  182. MOZ_LOG(gCookieLog, LogLevel::Warning,
  183. ("===== %s =====\n", aSetCookie ? "COOKIE NOT ACCEPTED" : "COOKIE NOT SENT"));
  184. MOZ_LOG(gCookieLog, LogLevel::Warning,("request URL: %s\n", spec.get()));
  185. if (aSetCookie)
  186. MOZ_LOG(gCookieLog, LogLevel::Warning,("cookie string: %s\n", aCookieString));
  187. PRExplodedTime explodedTime;
  188. PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
  189. char timeString[40];
  190. PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
  191. MOZ_LOG(gCookieLog, LogLevel::Warning,("current time: %s", timeString));
  192. MOZ_LOG(gCookieLog, LogLevel::Warning,("rejected because %s\n", aReason));
  193. MOZ_LOG(gCookieLog, LogLevel::Warning,("\n"));
  194. }
  195. static void
  196. LogCookie(nsCookie *aCookie)
  197. {
  198. PRExplodedTime explodedTime;
  199. PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
  200. char timeString[40];
  201. PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
  202. MOZ_LOG(gCookieLog, LogLevel::Debug,("current time: %s", timeString));
  203. if (aCookie) {
  204. MOZ_LOG(gCookieLog, LogLevel::Debug,("----------------\n"));
  205. MOZ_LOG(gCookieLog, LogLevel::Debug,("name: %s\n", aCookie->Name().get()));
  206. MOZ_LOG(gCookieLog, LogLevel::Debug,("value: %s\n", aCookie->Value().get()));
  207. MOZ_LOG(gCookieLog, LogLevel::Debug,("%s: %s\n", aCookie->IsDomain() ? "domain" : "host", aCookie->Host().get()));
  208. MOZ_LOG(gCookieLog, LogLevel::Debug,("path: %s\n", aCookie->Path().get()));
  209. PR_ExplodeTime(aCookie->Expiry() * int64_t(PR_USEC_PER_SEC),
  210. PR_GMTParameters, &explodedTime);
  211. PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
  212. MOZ_LOG(gCookieLog, LogLevel::Debug,
  213. ("expires: %s%s", timeString, aCookie->IsSession() ? " (at end of session)" : ""));
  214. PR_ExplodeTime(aCookie->CreationTime(), PR_GMTParameters, &explodedTime);
  215. PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
  216. MOZ_LOG(gCookieLog, LogLevel::Debug,("created: %s", timeString));
  217. MOZ_LOG(gCookieLog, LogLevel::Debug,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
  218. MOZ_LOG(gCookieLog, LogLevel::Debug,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
  219. }
  220. }
  221. static void
  222. LogSuccess(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCookie *aCookie, bool aReplacing)
  223. {
  224. // if logging isn't enabled, return now to save cycles
  225. if (!MOZ_LOG_TEST(gCookieLog, LogLevel::Debug)) {
  226. return;
  227. }
  228. nsAutoCString spec;
  229. if (aHostURI)
  230. aHostURI->GetAsciiSpec(spec);
  231. MOZ_LOG(gCookieLog, LogLevel::Debug,
  232. ("===== %s =====\n", aSetCookie ? "COOKIE ACCEPTED" : "COOKIE SENT"));
  233. MOZ_LOG(gCookieLog, LogLevel::Debug,("request URL: %s\n", spec.get()));
  234. MOZ_LOG(gCookieLog, LogLevel::Debug,("cookie string: %s\n", aCookieString));
  235. if (aSetCookie)
  236. MOZ_LOG(gCookieLog, LogLevel::Debug,("replaces existing cookie: %s\n", aReplacing ? "true" : "false"));
  237. LogCookie(aCookie);
  238. MOZ_LOG(gCookieLog, LogLevel::Debug,("\n"));
  239. }
  240. static void
  241. LogEvicted(nsCookie *aCookie, const char* details)
  242. {
  243. MOZ_LOG(gCookieLog, LogLevel::Debug,("===== COOKIE EVICTED =====\n"));
  244. MOZ_LOG(gCookieLog, LogLevel::Debug,("%s\n", details));
  245. LogCookie(aCookie);
  246. MOZ_LOG(gCookieLog, LogLevel::Debug,("\n"));
  247. }
  248. // inline wrappers to make passing in nsAFlatCStrings easier
  249. static inline void
  250. LogFailure(bool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, const char *aReason)
  251. {
  252. LogFailure(aSetCookie, aHostURI, aCookieString.get(), aReason);
  253. }
  254. static inline void
  255. LogSuccess(bool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, nsCookie *aCookie, bool aReplacing)
  256. {
  257. LogSuccess(aSetCookie, aHostURI, aCookieString.get(), aCookie, aReplacing);
  258. }
  259. #ifdef DEBUG
  260. #define NS_ASSERT_SUCCESS(res) \
  261. PR_BEGIN_MACRO \
  262. nsresult __rv = res; /* Do not evaluate |res| more than once! */ \
  263. if (NS_FAILED(__rv)) { \
  264. char *msg = PR_smprintf("NS_ASSERT_SUCCESS(%s) failed with result 0x%X", \
  265. #res, __rv); \
  266. NS_ASSERTION(NS_SUCCEEDED(__rv), msg); \
  267. PR_smprintf_free(msg); \
  268. } \
  269. PR_END_MACRO
  270. #else
  271. #define NS_ASSERT_SUCCESS(res) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
  272. #endif
  273. /******************************************************************************
  274. * DBListenerErrorHandler impl:
  275. * Parent class for our async storage listeners that handles the logging of
  276. * errors.
  277. ******************************************************************************/
  278. class DBListenerErrorHandler : public mozIStorageStatementCallback
  279. {
  280. protected:
  281. explicit DBListenerErrorHandler(DBState* dbState) : mDBState(dbState) { }
  282. RefPtr<DBState> mDBState;
  283. virtual const char *GetOpType() = 0;
  284. public:
  285. NS_IMETHOD HandleError(mozIStorageError* aError) override
  286. {
  287. if (MOZ_LOG_TEST(gCookieLog, LogLevel::Warning)) {
  288. int32_t result = -1;
  289. aError->GetResult(&result);
  290. nsAutoCString message;
  291. aError->GetMessage(message);
  292. COOKIE_LOGSTRING(LogLevel::Warning,
  293. ("DBListenerErrorHandler::HandleError(): Error %d occurred while "
  294. "performing operation '%s' with message '%s'; rebuilding database.",
  295. result, GetOpType(), message.get()));
  296. }
  297. // Rebuild the database.
  298. gCookieService->HandleCorruptDB(mDBState);
  299. return NS_OK;
  300. }
  301. };
  302. /******************************************************************************
  303. * InsertCookieDBListener impl:
  304. * mozIStorageStatementCallback used to track asynchronous insertion operations.
  305. ******************************************************************************/
  306. class InsertCookieDBListener final : public DBListenerErrorHandler
  307. {
  308. private:
  309. const char *GetOpType() override { return "INSERT"; }
  310. ~InsertCookieDBListener() = default;
  311. public:
  312. NS_DECL_ISUPPORTS
  313. explicit InsertCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
  314. NS_IMETHOD HandleResult(mozIStorageResultSet*) override
  315. {
  316. NS_NOTREACHED("Unexpected call to InsertCookieDBListener::HandleResult");
  317. return NS_OK;
  318. }
  319. NS_IMETHOD HandleCompletion(uint16_t aReason) override
  320. {
  321. // If we were rebuilding the db and we succeeded, make our corruptFlag say
  322. // so.
  323. if (mDBState->corruptFlag == DBState::REBUILDING &&
  324. aReason == mozIStorageStatementCallback::REASON_FINISHED) {
  325. COOKIE_LOGSTRING(LogLevel::Debug,
  326. ("InsertCookieDBListener::HandleCompletion(): rebuild complete"));
  327. mDBState->corruptFlag = DBState::OK;
  328. }
  329. return NS_OK;
  330. }
  331. };
  332. NS_IMPL_ISUPPORTS(InsertCookieDBListener, mozIStorageStatementCallback)
  333. /******************************************************************************
  334. * UpdateCookieDBListener impl:
  335. * mozIStorageStatementCallback used to track asynchronous update operations.
  336. ******************************************************************************/
  337. class UpdateCookieDBListener final : public DBListenerErrorHandler
  338. {
  339. private:
  340. const char *GetOpType() override { return "UPDATE"; }
  341. ~UpdateCookieDBListener() = default;
  342. public:
  343. NS_DECL_ISUPPORTS
  344. explicit UpdateCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
  345. NS_IMETHOD HandleResult(mozIStorageResultSet*) override
  346. {
  347. NS_NOTREACHED("Unexpected call to UpdateCookieDBListener::HandleResult");
  348. return NS_OK;
  349. }
  350. NS_IMETHOD HandleCompletion(uint16_t aReason) override
  351. {
  352. return NS_OK;
  353. }
  354. };
  355. NS_IMPL_ISUPPORTS(UpdateCookieDBListener, mozIStorageStatementCallback)
  356. /******************************************************************************
  357. * RemoveCookieDBListener impl:
  358. * mozIStorageStatementCallback used to track asynchronous removal operations.
  359. ******************************************************************************/
  360. class RemoveCookieDBListener final : public DBListenerErrorHandler
  361. {
  362. private:
  363. const char *GetOpType() override { return "REMOVE"; }
  364. ~RemoveCookieDBListener() = default;
  365. public:
  366. NS_DECL_ISUPPORTS
  367. explicit RemoveCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
  368. NS_IMETHOD HandleResult(mozIStorageResultSet*) override
  369. {
  370. NS_NOTREACHED("Unexpected call to RemoveCookieDBListener::HandleResult");
  371. return NS_OK;
  372. }
  373. NS_IMETHOD HandleCompletion(uint16_t aReason) override
  374. {
  375. return NS_OK;
  376. }
  377. };
  378. NS_IMPL_ISUPPORTS(RemoveCookieDBListener, mozIStorageStatementCallback)
  379. /******************************************************************************
  380. * ReadCookieDBListener impl:
  381. * mozIStorageStatementCallback used to track asynchronous removal operations.
  382. ******************************************************************************/
  383. class ReadCookieDBListener final : public DBListenerErrorHandler
  384. {
  385. private:
  386. const char *GetOpType() override { return "READ"; }
  387. bool mCanceled;
  388. ~ReadCookieDBListener() = default;
  389. public:
  390. NS_DECL_ISUPPORTS
  391. explicit ReadCookieDBListener(DBState* dbState)
  392. : DBListenerErrorHandler(dbState)
  393. , mCanceled(false)
  394. {
  395. }
  396. void Cancel() { mCanceled = true; }
  397. NS_IMETHOD HandleResult(mozIStorageResultSet *aResult) override
  398. {
  399. nsCOMPtr<mozIStorageRow> row;
  400. while (true) {
  401. DebugOnly<nsresult> rv = aResult->GetNextRow(getter_AddRefs(row));
  402. NS_ASSERT_SUCCESS(rv);
  403. if (!row)
  404. break;
  405. CookieDomainTuple *tuple = mDBState->hostArray.AppendElement();
  406. row->GetUTF8String(IDX_BASE_DOMAIN, tuple->key.mBaseDomain);
  407. nsAutoCString suffix;
  408. row->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix);
  409. DebugOnly<bool> success = tuple->key.mOriginAttributes.PopulateFromSuffix(suffix);
  410. MOZ_ASSERT(success);
  411. tuple->cookie =
  412. gCookieService->GetCookieFromRow(row, tuple->key.mOriginAttributes);
  413. }
  414. return NS_OK;
  415. }
  416. NS_IMETHOD HandleCompletion(uint16_t aReason) override
  417. {
  418. // Process the completion of the read operation. If we have been canceled,
  419. // we cannot assume that the cookieservice still has an open connection
  420. // or that it even refers to the same database, so we must return early.
  421. // Conversely, the cookieservice guarantees that if we have not been
  422. // canceled, the database connection is still alive and we can safely
  423. // operate on it.
  424. if (mCanceled) {
  425. // We may receive a REASON_FINISHED after being canceled;
  426. // tweak the reason accordingly.
  427. aReason = mozIStorageStatementCallback::REASON_CANCELED;
  428. }
  429. switch (aReason) {
  430. case mozIStorageStatementCallback::REASON_FINISHED:
  431. gCookieService->AsyncReadComplete();
  432. break;
  433. case mozIStorageStatementCallback::REASON_CANCELED:
  434. // Nothing more to do here. The partially read data has already been
  435. // thrown away.
  436. COOKIE_LOGSTRING(LogLevel::Debug, ("Read canceled"));
  437. break;
  438. case mozIStorageStatementCallback::REASON_ERROR:
  439. // Nothing more to do here. DBListenerErrorHandler::HandleError()
  440. // can handle it.
  441. COOKIE_LOGSTRING(LogLevel::Debug, ("Read error"));
  442. break;
  443. default:
  444. NS_NOTREACHED("invalid reason");
  445. }
  446. return NS_OK;
  447. }
  448. };
  449. NS_IMPL_ISUPPORTS(ReadCookieDBListener, mozIStorageStatementCallback)
  450. /******************************************************************************
  451. * CloseCookieDBListener imp:
  452. * Static mozIStorageCompletionCallback used to notify when the database is
  453. * successfully closed.
  454. ******************************************************************************/
  455. class CloseCookieDBListener final : public mozIStorageCompletionCallback
  456. {
  457. ~CloseCookieDBListener() = default;
  458. public:
  459. explicit CloseCookieDBListener(DBState* dbState) : mDBState(dbState) { }
  460. RefPtr<DBState> mDBState;
  461. NS_DECL_ISUPPORTS
  462. NS_IMETHOD Complete(nsresult, nsISupports*) override
  463. {
  464. gCookieService->HandleDBClosed(mDBState);
  465. return NS_OK;
  466. }
  467. };
  468. NS_IMPL_ISUPPORTS(CloseCookieDBListener, mozIStorageCompletionCallback)
  469. namespace {
  470. class AppClearDataObserver final : public nsIObserver {
  471. ~AppClearDataObserver() = default;
  472. public:
  473. NS_DECL_ISUPPORTS
  474. // nsIObserver implementation.
  475. NS_IMETHOD
  476. Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override
  477. {
  478. MOZ_ASSERT(!nsCRT::strcmp(aTopic, TOPIC_CLEAR_ORIGIN_DATA));
  479. MOZ_ASSERT(XRE_IsParentProcess());
  480. nsCOMPtr<nsICookieManager2> cookieManager
  481. = do_GetService(NS_COOKIEMANAGER_CONTRACTID);
  482. MOZ_ASSERT(cookieManager);
  483. return cookieManager->RemoveCookiesWithOriginAttributes(nsDependentString(aData), EmptyCString());
  484. }
  485. };
  486. NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
  487. } // namespace
  488. size_t
  489. nsCookieKey::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  490. {
  491. return mBaseDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  492. }
  493. size_t
  494. nsCookieEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  495. {
  496. size_t amount = nsCookieKey::SizeOfExcludingThis(aMallocSizeOf);
  497. amount += mCookies.ShallowSizeOfExcludingThis(aMallocSizeOf);
  498. for (uint32_t i = 0; i < mCookies.Length(); ++i) {
  499. amount += mCookies[i]->SizeOfIncludingThis(aMallocSizeOf);
  500. }
  501. return amount;
  502. }
  503. size_t
  504. CookieDomainTuple::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  505. {
  506. size_t amount = 0;
  507. amount += key.SizeOfExcludingThis(aMallocSizeOf);
  508. amount += cookie->SizeOfIncludingThis(aMallocSizeOf);
  509. return amount;
  510. }
  511. size_t
  512. DBState::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  513. {
  514. size_t amount = 0;
  515. amount += aMallocSizeOf(this);
  516. amount += hostTable.SizeOfExcludingThis(aMallocSizeOf);
  517. amount += hostArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
  518. for (uint32_t i = 0; i < hostArray.Length(); ++i) {
  519. amount += hostArray[i].SizeOfExcludingThis(aMallocSizeOf);
  520. }
  521. amount += readSet.SizeOfExcludingThis(aMallocSizeOf);
  522. return amount;
  523. }
  524. /******************************************************************************
  525. * nsCookieService impl:
  526. * singleton instance ctor/dtor methods
  527. ******************************************************************************/
  528. nsICookieService*
  529. nsCookieService::GetXPCOMSingleton()
  530. {
  531. if (IsNeckoChild())
  532. return CookieServiceChild::GetSingleton();
  533. return GetSingleton();
  534. }
  535. nsCookieService*
  536. nsCookieService::GetSingleton()
  537. {
  538. NS_ASSERTION(!IsNeckoChild(), "not a parent process");
  539. if (gCookieService) {
  540. NS_ADDREF(gCookieService);
  541. return gCookieService;
  542. }
  543. // Create a new singleton nsCookieService.
  544. // We AddRef only once since XPCOM has rules about the ordering of module
  545. // teardowns - by the time our module destructor is called, it's too late to
  546. // Release our members (e.g. nsIObserverService and nsIPrefBranch), since GC
  547. // cycles have already been completed and would result in serious leaks.
  548. // See bug 209571.
  549. gCookieService = new nsCookieService();
  550. if (gCookieService) {
  551. NS_ADDREF(gCookieService);
  552. if (NS_FAILED(gCookieService->Init())) {
  553. NS_RELEASE(gCookieService);
  554. }
  555. }
  556. return gCookieService;
  557. }
  558. /* static */ void
  559. nsCookieService::AppClearDataObserverInit()
  560. {
  561. nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  562. nsCOMPtr<nsIObserver> obs = new AppClearDataObserver();
  563. observerService->AddObserver(obs, TOPIC_CLEAR_ORIGIN_DATA,
  564. /* ownsWeak= */ false);
  565. }
  566. /******************************************************************************
  567. * nsCookieService impl:
  568. * public methods
  569. ******************************************************************************/
  570. NS_IMPL_ISUPPORTS(nsCookieService,
  571. nsICookieService,
  572. nsICookieManager,
  573. nsICookieManager2,
  574. nsIObserver,
  575. nsISupportsWeakReference,
  576. nsIMemoryReporter)
  577. nsCookieService::nsCookieService()
  578. : mDBState(nullptr)
  579. , mCookieBehavior(nsICookieService::BEHAVIOR_ACCEPT)
  580. , mThirdPartySession(false)
  581. , mLeaveSecureAlone(true)
  582. , mMaxNumberOfCookies(kMaxNumberOfCookies)
  583. , mMaxCookiesPerHost(kMaxCookiesPerHost)
  584. , mCookiePurgeAge(kCookiePurgeAge)
  585. {
  586. }
  587. nsresult
  588. nsCookieService::Init()
  589. {
  590. nsresult rv;
  591. mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
  592. NS_ENSURE_SUCCESS(rv, rv);
  593. mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
  594. NS_ENSURE_SUCCESS(rv, rv);
  595. mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
  596. NS_ENSURE_SUCCESS(rv, rv);
  597. // init our pref and observer
  598. nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  599. if (prefBranch) {
  600. prefBranch->AddObserver(kPrefCookieBehavior, this, true);
  601. prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, true);
  602. prefBranch->AddObserver(kPrefMaxCookiesPerHost, this, true);
  603. prefBranch->AddObserver(kPrefCookiePurgeAge, this, true);
  604. prefBranch->AddObserver(kPrefThirdPartySession, this, true);
  605. prefBranch->AddObserver(kCookieLeaveSecurityAlone, this, true);
  606. PrefChanged(prefBranch);
  607. }
  608. mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
  609. NS_ENSURE_SUCCESS(rv, rv);
  610. // Init our default, and possibly private DBStates.
  611. InitDBStates();
  612. RegisterWeakMemoryReporter(this);
  613. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  614. NS_ENSURE_STATE(os);
  615. os->AddObserver(this, "profile-before-change", true);
  616. os->AddObserver(this, "profile-do-change", true);
  617. os->AddObserver(this, "last-pb-context-exited", true);
  618. mPermissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
  619. if (!mPermissionService) {
  620. NS_WARNING("nsICookiePermission implementation not available - some features won't work!");
  621. COOKIE_LOGSTRING(LogLevel::Warning, ("Init(): nsICookiePermission implementation not available"));
  622. }
  623. return NS_OK;
  624. }
  625. void
  626. nsCookieService::InitDBStates()
  627. {
  628. NS_ASSERTION(!mDBState, "already have a DBState");
  629. NS_ASSERTION(!mDefaultDBState, "already have a default DBState");
  630. NS_ASSERTION(!mPrivateDBState, "already have a private DBState");
  631. // Create a new default DBState and set our current one.
  632. mDefaultDBState = new DBState();
  633. mDBState = mDefaultDBState;
  634. mPrivateDBState = new DBState();
  635. // Get our cookie file.
  636. nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  637. getter_AddRefs(mDefaultDBState->cookieFile));
  638. if (NS_FAILED(rv)) {
  639. // We've already set up our DBStates appropriately; nothing more to do.
  640. COOKIE_LOGSTRING(LogLevel::Warning,
  641. ("InitDBStates(): couldn't get cookie file"));
  642. return;
  643. }
  644. mDefaultDBState->cookieFile->AppendNative(NS_LITERAL_CSTRING(COOKIES_FILE));
  645. // Attempt to open and read the database. If TryInitDB() returns RESULT_RETRY,
  646. // do so.
  647. OpenDBResult result = TryInitDB(false);
  648. if (result == RESULT_RETRY) {
  649. // Database may be corrupt. Synchronously close the connection, clean up the
  650. // default DBState, and try again.
  651. COOKIE_LOGSTRING(LogLevel::Warning, ("InitDBStates(): retrying TryInitDB()"));
  652. CleanupCachedStatements();
  653. CleanupDefaultDBConnection();
  654. result = TryInitDB(true);
  655. if (result == RESULT_RETRY) {
  656. // We're done. Change the code to failure so we clean up below.
  657. result = RESULT_FAILURE;
  658. }
  659. }
  660. if (result == RESULT_FAILURE) {
  661. COOKIE_LOGSTRING(LogLevel::Warning,
  662. ("InitDBStates(): TryInitDB() failed, closing connection"));
  663. // Connection failure is unrecoverable. Clean up our connection. We can run
  664. // fine without persistent storage -- e.g. if there's no profile.
  665. CleanupCachedStatements();
  666. CleanupDefaultDBConnection();
  667. }
  668. }
  669. namespace {
  670. class ConvertAppIdToOriginAttrsSQLFunction final : public mozIStorageFunction
  671. {
  672. ~ConvertAppIdToOriginAttrsSQLFunction() = default;
  673. NS_DECL_ISUPPORTS
  674. NS_DECL_MOZISTORAGEFUNCTION
  675. };
  676. NS_IMPL_ISUPPORTS(ConvertAppIdToOriginAttrsSQLFunction, mozIStorageFunction);
  677. NS_IMETHODIMP
  678. ConvertAppIdToOriginAttrsSQLFunction::OnFunctionCall(
  679. mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
  680. {
  681. nsresult rv;
  682. int32_t appId, inIsolatedMozBrowser;
  683. rv = aFunctionArguments->GetInt32(0, &appId);
  684. NS_ENSURE_SUCCESS(rv, rv);
  685. rv = aFunctionArguments->GetInt32(1, &inIsolatedMozBrowser);
  686. NS_ENSURE_SUCCESS(rv, rv);
  687. // Create an originAttributes object by appId and inIsolatedMozBrowser.
  688. // Then create the originSuffix string from this object.
  689. NeckoOriginAttributes attrs(appId, (inIsolatedMozBrowser ? 1 : 0));
  690. nsAutoCString suffix;
  691. attrs.CreateSuffix(suffix);
  692. RefPtr<nsVariant> outVar(new nsVariant());
  693. rv = outVar->SetAsAUTF8String(suffix);
  694. NS_ENSURE_SUCCESS(rv, rv);
  695. outVar.forget(aResult);
  696. return NS_OK;
  697. }
  698. class SetAppIdFromOriginAttributesSQLFunction final : public mozIStorageFunction
  699. {
  700. ~SetAppIdFromOriginAttributesSQLFunction() = default;
  701. NS_DECL_ISUPPORTS
  702. NS_DECL_MOZISTORAGEFUNCTION
  703. };
  704. NS_IMPL_ISUPPORTS(SetAppIdFromOriginAttributesSQLFunction, mozIStorageFunction);
  705. NS_IMETHODIMP
  706. SetAppIdFromOriginAttributesSQLFunction::OnFunctionCall(
  707. mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
  708. {
  709. nsresult rv;
  710. nsAutoCString suffix;
  711. NeckoOriginAttributes attrs;
  712. rv = aFunctionArguments->GetUTF8String(0, suffix);
  713. NS_ENSURE_SUCCESS(rv, rv);
  714. bool success = attrs.PopulateFromSuffix(suffix);
  715. NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
  716. RefPtr<nsVariant> outVar(new nsVariant());
  717. rv = outVar->SetAsInt32(attrs.mAppId);
  718. NS_ENSURE_SUCCESS(rv, rv);
  719. outVar.forget(aResult);
  720. return NS_OK;
  721. }
  722. class SetInBrowserFromOriginAttributesSQLFunction final :
  723. public mozIStorageFunction
  724. {
  725. ~SetInBrowserFromOriginAttributesSQLFunction() = default;
  726. NS_DECL_ISUPPORTS
  727. NS_DECL_MOZISTORAGEFUNCTION
  728. };
  729. NS_IMPL_ISUPPORTS(SetInBrowserFromOriginAttributesSQLFunction,
  730. mozIStorageFunction);
  731. NS_IMETHODIMP
  732. SetInBrowserFromOriginAttributesSQLFunction::OnFunctionCall(
  733. mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
  734. {
  735. nsresult rv;
  736. nsAutoCString suffix;
  737. NeckoOriginAttributes attrs;
  738. rv = aFunctionArguments->GetUTF8String(0, suffix);
  739. NS_ENSURE_SUCCESS(rv, rv);
  740. bool success = attrs.PopulateFromSuffix(suffix);
  741. NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
  742. RefPtr<nsVariant> outVar(new nsVariant());
  743. rv = outVar->SetAsInt32(attrs.mInIsolatedMozBrowser);
  744. NS_ENSURE_SUCCESS(rv, rv);
  745. outVar.forget(aResult);
  746. return NS_OK;
  747. }
  748. } // namespace
  749. /* Attempt to open and read the database. If 'aRecreateDB' is true, try to
  750. * move the existing database file out of the way and create a new one.
  751. *
  752. * @returns RESULT_OK if opening or creating the database succeeded;
  753. * RESULT_RETRY if the database cannot be opened, is corrupt, or some
  754. * other failure occurred that might be resolved by recreating the
  755. * database; or RESULT_FAILED if there was an unrecoverable error and
  756. * we must run without a database.
  757. *
  758. * If RESULT_RETRY or RESULT_FAILED is returned, the caller should perform
  759. * cleanup of the default DBState.
  760. */
  761. OpenDBResult
  762. nsCookieService::TryInitDB(bool aRecreateDB)
  763. {
  764. NS_ASSERTION(!mDefaultDBState->dbConn, "nonnull dbConn");
  765. NS_ASSERTION(!mDefaultDBState->stmtInsert, "nonnull stmtInsert");
  766. NS_ASSERTION(!mDefaultDBState->insertListener, "nonnull insertListener");
  767. NS_ASSERTION(!mDefaultDBState->syncConn, "nonnull syncConn");
  768. // Ditch an existing db, if we've been told to (i.e. it's corrupt). We don't
  769. // want to delete it outright, since it may be useful for debugging purposes,
  770. // so we move it out of the way.
  771. nsresult rv;
  772. if (aRecreateDB) {
  773. nsCOMPtr<nsIFile> backupFile;
  774. mDefaultDBState->cookieFile->Clone(getter_AddRefs(backupFile));
  775. rv = backupFile->MoveToNative(nullptr,
  776. NS_LITERAL_CSTRING(COOKIES_FILE ".bak"));
  777. NS_ENSURE_SUCCESS(rv, RESULT_FAILURE);
  778. }
  779. ReadAheadFile(mDefaultDBState->cookieFile);
  780. // open a connection to the cookie database, and only cache our connection
  781. // and statements upon success. The connection is opened unshared to eliminate
  782. // cache contention between the main and background threads.
  783. rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
  784. getter_AddRefs(mDefaultDBState->dbConn));
  785. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  786. // Set up our listeners.
  787. mDefaultDBState->insertListener = new InsertCookieDBListener(mDefaultDBState);
  788. mDefaultDBState->updateListener = new UpdateCookieDBListener(mDefaultDBState);
  789. mDefaultDBState->removeListener = new RemoveCookieDBListener(mDefaultDBState);
  790. mDefaultDBState->closeListener = new CloseCookieDBListener(mDefaultDBState);
  791. // Grow cookie db in 512KB increments
  792. mDefaultDBState->dbConn->SetGrowthIncrement(512 * 1024, EmptyCString());
  793. bool tableExists = false;
  794. mDefaultDBState->dbConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"),
  795. &tableExists);
  796. if (!tableExists) {
  797. rv = CreateTable();
  798. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  799. } else {
  800. // table already exists; check the schema version before reading
  801. int32_t dbSchemaVersion;
  802. rv = mDefaultDBState->dbConn->GetSchemaVersion(&dbSchemaVersion);
  803. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  804. // Start a transaction for the whole migration block.
  805. mozStorageTransaction transaction(mDefaultDBState->dbConn, true);
  806. switch (dbSchemaVersion) {
  807. // Upgrading.
  808. // Every time you increment the database schema, you need to implement
  809. // the upgrading code from the previous version to the new one. If migration
  810. // fails for any reason, it's a bug -- so we return RESULT_RETRY such that
  811. // the original database will be saved, in the hopes that we might one day
  812. // see it and fix it.
  813. case 1:
  814. {
  815. // Add the lastAccessed column to the table.
  816. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  817. "ALTER TABLE moz_cookies ADD lastAccessed INTEGER"));
  818. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  819. }
  820. // Fall through to the next upgrade.
  821. MOZ_FALLTHROUGH;
  822. case 2:
  823. {
  824. // Add the baseDomain column and index to the table.
  825. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  826. "ALTER TABLE moz_cookies ADD baseDomain TEXT"));
  827. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  828. // Compute the baseDomains for the table. This must be done eagerly
  829. // otherwise we won't be able to synchronously read in individual
  830. // domains on demand.
  831. const int64_t SCHEMA2_IDX_ID = 0;
  832. const int64_t SCHEMA2_IDX_HOST = 1;
  833. nsCOMPtr<mozIStorageStatement> select;
  834. rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
  835. "SELECT id, host FROM moz_cookies"), getter_AddRefs(select));
  836. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  837. nsCOMPtr<mozIStorageStatement> update;
  838. rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
  839. "UPDATE moz_cookies SET baseDomain = :baseDomain WHERE id = :id"),
  840. getter_AddRefs(update));
  841. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  842. nsCString baseDomain, host;
  843. bool hasResult;
  844. while (true) {
  845. rv = select->ExecuteStep(&hasResult);
  846. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  847. if (!hasResult)
  848. break;
  849. int64_t id = select->AsInt64(SCHEMA2_IDX_ID);
  850. select->GetUTF8String(SCHEMA2_IDX_HOST, host);
  851. rv = GetBaseDomainFromHost(host, baseDomain);
  852. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  853. mozStorageStatementScoper scoper(update);
  854. rv = update->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
  855. baseDomain);
  856. NS_ASSERT_SUCCESS(rv);
  857. rv = update->BindInt64ByName(NS_LITERAL_CSTRING("id"),
  858. id);
  859. NS_ASSERT_SUCCESS(rv);
  860. rv = update->ExecuteStep(&hasResult);
  861. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  862. }
  863. // Create an index on baseDomain.
  864. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  865. "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"));
  866. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  867. }
  868. // Fall through to the next upgrade.
  869. MOZ_FALLTHROUGH;
  870. case 3:
  871. {
  872. // Add the creationTime column to the table, and create a unique index
  873. // on (name, host, path). Before we do this, we have to purge the table
  874. // of expired cookies such that we know that the (name, host, path)
  875. // index is truly unique -- otherwise we can't create the index. Note
  876. // that we can't just execute a statement to delete all rows where the
  877. // expiry column is in the past -- doing so would rely on the clock
  878. // (both now and when previous cookies were set) being monotonic.
  879. // Select the whole table, and order by the fields we're interested in.
  880. // This means we can simply do a linear traversal of the results and
  881. // check for duplicates as we go.
  882. const int64_t SCHEMA3_IDX_ID = 0;
  883. const int64_t SCHEMA3_IDX_NAME = 1;
  884. const int64_t SCHEMA3_IDX_HOST = 2;
  885. const int64_t SCHEMA3_IDX_PATH = 3;
  886. nsCOMPtr<mozIStorageStatement> select;
  887. rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
  888. "SELECT id, name, host, path FROM moz_cookies "
  889. "ORDER BY name ASC, host ASC, path ASC, expiry ASC"),
  890. getter_AddRefs(select));
  891. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  892. nsCOMPtr<mozIStorageStatement> deleteExpired;
  893. rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
  894. "DELETE FROM moz_cookies WHERE id = :id"),
  895. getter_AddRefs(deleteExpired));
  896. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  897. // Read the first row.
  898. bool hasResult;
  899. rv = select->ExecuteStep(&hasResult);
  900. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  901. if (hasResult) {
  902. nsCString name1, host1, path1;
  903. int64_t id1 = select->AsInt64(SCHEMA3_IDX_ID);
  904. select->GetUTF8String(SCHEMA3_IDX_NAME, name1);
  905. select->GetUTF8String(SCHEMA3_IDX_HOST, host1);
  906. select->GetUTF8String(SCHEMA3_IDX_PATH, path1);
  907. nsCString name2, host2, path2;
  908. while (true) {
  909. // Read the second row.
  910. rv = select->ExecuteStep(&hasResult);
  911. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  912. if (!hasResult)
  913. break;
  914. int64_t id2 = select->AsInt64(SCHEMA3_IDX_ID);
  915. select->GetUTF8String(SCHEMA3_IDX_NAME, name2);
  916. select->GetUTF8String(SCHEMA3_IDX_HOST, host2);
  917. select->GetUTF8String(SCHEMA3_IDX_PATH, path2);
  918. // If the two rows match in (name, host, path), we know the earlier
  919. // row has an earlier expiry time. Delete it.
  920. if (name1 == name2 && host1 == host2 && path1 == path2) {
  921. mozStorageStatementScoper scoper(deleteExpired);
  922. rv = deleteExpired->BindInt64ByName(NS_LITERAL_CSTRING("id"),
  923. id1);
  924. NS_ASSERT_SUCCESS(rv);
  925. rv = deleteExpired->ExecuteStep(&hasResult);
  926. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  927. }
  928. // Make the second row the first for the next iteration.
  929. name1 = name2;
  930. host1 = host2;
  931. path1 = path2;
  932. id1 = id2;
  933. }
  934. }
  935. // Add the creationTime column to the table.
  936. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  937. "ALTER TABLE moz_cookies ADD creationTime INTEGER"));
  938. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  939. // Copy the id of each row into the new creationTime column.
  940. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  941. "UPDATE moz_cookies SET creationTime = "
  942. "(SELECT id WHERE id = moz_cookies.id)"));
  943. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  944. // Create a unique index on (name, host, path) to allow fast lookup.
  945. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  946. "CREATE UNIQUE INDEX moz_uniqueid "
  947. "ON moz_cookies (name, host, path)"));
  948. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  949. }
  950. // Fall through to the next upgrade.
  951. MOZ_FALLTHROUGH;
  952. case 4:
  953. {
  954. // We need to add appId/inBrowserElement, plus change a constraint on
  955. // the table (unique entries now include appId/inBrowserElement):
  956. // this requires creating a new table and copying the data to it. We
  957. // then rename the new table to the old name.
  958. //
  959. // Why we made this change: appId/inBrowserElement allow "cookie jars"
  960. // for Firefox OS. We create a separate cookie namespace per {appId,
  961. // inBrowserElement}. When upgrading, we convert existing cookies
  962. // (which imply we're on desktop/mobile) to use {0, false}, as that is
  963. // the only namespace used by a non-Firefox-OS implementation.
  964. // Rename existing table
  965. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  966. "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
  967. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  968. // Drop existing index (CreateTable will create new one for new table)
  969. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  970. "DROP INDEX moz_basedomain"));
  971. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  972. // Create new table (with new fields and new unique constraint)
  973. rv = CreateTableForSchemaVersion5();
  974. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  975. // Copy data from old table, using appId/inBrowser=0 for existing rows
  976. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  977. "INSERT INTO moz_cookies "
  978. "(baseDomain, appId, inBrowserElement, name, value, host, path, expiry,"
  979. " lastAccessed, creationTime, isSecure, isHttpOnly) "
  980. "SELECT baseDomain, 0, 0, name, value, host, path, expiry,"
  981. " lastAccessed, creationTime, isSecure, isHttpOnly "
  982. "FROM moz_cookies_old"));
  983. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  984. // Drop old table
  985. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  986. "DROP TABLE moz_cookies_old"));
  987. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  988. COOKIE_LOGSTRING(LogLevel::Debug,
  989. ("Upgraded database to schema version 5"));
  990. }
  991. // Fall through to the next upgrade.
  992. MOZ_FALLTHROUGH;
  993. case 5:
  994. {
  995. // Change in the version: Replace the columns |appId| and
  996. // |inBrowserElement| by a single column |originAttributes|.
  997. //
  998. // Why we made this change: FxOS new security model (NSec) encapsulates
  999. // "appId/inIsolatedMozBrowser" in nsIPrincipal::originAttributes to make
  1000. // it easier to modify the contents of this structure in the future.
  1001. //
  1002. // We do the migration in several steps:
  1003. // 1. Rename the old table.
  1004. // 2. Create a new table.
  1005. // 3. Copy data from the old table to the new table; convert appId and
  1006. // inBrowserElement to originAttributes in the meantime.
  1007. // Rename existing table.
  1008. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1009. "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
  1010. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1011. // Drop existing index (CreateTable will create new one for new table).
  1012. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1013. "DROP INDEX moz_basedomain"));
  1014. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1015. // Create new table with new fields and new unique constraint.
  1016. rv = CreateTableForSchemaVersion6();
  1017. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1018. // Copy data from old table without the two deprecated columns appId and
  1019. // inBrowserElement.
  1020. nsCOMPtr<mozIStorageFunction>
  1021. convertToOriginAttrs(new ConvertAppIdToOriginAttrsSQLFunction());
  1022. NS_ENSURE_TRUE(convertToOriginAttrs, RESULT_RETRY);
  1023. NS_NAMED_LITERAL_CSTRING(convertToOriginAttrsName,
  1024. "CONVERT_TO_ORIGIN_ATTRIBUTES");
  1025. rv = mDefaultDBState->dbConn->CreateFunction(convertToOriginAttrsName,
  1026. 2, convertToOriginAttrs);
  1027. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1028. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1029. "INSERT INTO moz_cookies "
  1030. "(baseDomain, originAttributes, name, value, host, path, expiry,"
  1031. " lastAccessed, creationTime, isSecure, isHttpOnly) "
  1032. "SELECT baseDomain, "
  1033. " CONVERT_TO_ORIGIN_ATTRIBUTES(appId, inBrowserElement),"
  1034. " name, value, host, path, expiry, lastAccessed, creationTime, "
  1035. " isSecure, isHttpOnly "
  1036. "FROM moz_cookies_old"));
  1037. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1038. rv = mDefaultDBState->dbConn->RemoveFunction(convertToOriginAttrsName);
  1039. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1040. // Drop old table
  1041. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1042. "DROP TABLE moz_cookies_old"));
  1043. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1044. COOKIE_LOGSTRING(LogLevel::Debug,
  1045. ("Upgraded database to schema version 6"));
  1046. }
  1047. MOZ_FALLTHROUGH;
  1048. case 6:
  1049. {
  1050. // We made a mistake in schema version 6. We cannot remove expected
  1051. // columns of any version (checked in the default case) from cookie
  1052. // database, because doing this would destroy the possibility of
  1053. // downgrading database.
  1054. //
  1055. // This version simply restores appId and inBrowserElement columns in
  1056. // order to fix downgrading issue even though these two columns are no
  1057. // longer used in the latest schema.
  1058. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1059. "ALTER TABLE moz_cookies ADD appId INTEGER DEFAULT 0;"));
  1060. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1061. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1062. "ALTER TABLE moz_cookies ADD inBrowserElement INTEGER DEFAULT 0;"));
  1063. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1064. // Compute and populate the values of appId and inBrwoserElement from
  1065. // originAttributes.
  1066. nsCOMPtr<mozIStorageFunction>
  1067. setAppId(new SetAppIdFromOriginAttributesSQLFunction());
  1068. NS_ENSURE_TRUE(setAppId, RESULT_RETRY);
  1069. NS_NAMED_LITERAL_CSTRING(setAppIdName, "SET_APP_ID");
  1070. rv = mDefaultDBState->dbConn->CreateFunction(setAppIdName, 1, setAppId);
  1071. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1072. nsCOMPtr<mozIStorageFunction>
  1073. setInBrowser(new SetInBrowserFromOriginAttributesSQLFunction());
  1074. NS_ENSURE_TRUE(setInBrowser, RESULT_RETRY);
  1075. NS_NAMED_LITERAL_CSTRING(setInBrowserName, "SET_IN_BROWSER");
  1076. rv = mDefaultDBState->dbConn->CreateFunction(setInBrowserName, 1,
  1077. setInBrowser);
  1078. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1079. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1080. "UPDATE moz_cookies SET appId = SET_APP_ID(originAttributes), "
  1081. "inBrowserElement = SET_IN_BROWSER(originAttributes);"
  1082. ));
  1083. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1084. rv = mDefaultDBState->dbConn->RemoveFunction(setAppIdName);
  1085. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1086. rv = mDefaultDBState->dbConn->RemoveFunction(setInBrowserName);
  1087. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1088. COOKIE_LOGSTRING(LogLevel::Debug,
  1089. ("Upgraded database to schema version 7"));
  1090. }
  1091. // No more upgrades. Update the schema version.
  1092. rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
  1093. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1094. MOZ_FALLTHROUGH;
  1095. case COOKIES_SCHEMA_VERSION:
  1096. break;
  1097. case 0:
  1098. {
  1099. NS_WARNING("couldn't get schema version!");
  1100. // the table may be usable; someone might've just clobbered the schema
  1101. // version. we can treat this case like a downgrade using the codepath
  1102. // below, by verifying the columns we care about are all there. for now,
  1103. // re-set the schema version in the db, in case the checks succeed (if
  1104. // they don't, we're dropping the table anyway).
  1105. rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
  1106. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1107. }
  1108. // fall through to downgrade check
  1109. MOZ_FALLTHROUGH;
  1110. // downgrading.
  1111. // if columns have been added to the table, we can still use the ones we
  1112. // understand safely. if columns have been deleted or altered, just
  1113. // blow away the table and start from scratch! if you change the way
  1114. // a column is interpreted, make sure you also change its name so this
  1115. // check will catch it.
  1116. default:
  1117. {
  1118. // check if all the expected columns exist
  1119. nsCOMPtr<mozIStorageStatement> stmt;
  1120. rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
  1121. "SELECT "
  1122. "id, "
  1123. "baseDomain, "
  1124. "originAttributes, "
  1125. "name, "
  1126. "value, "
  1127. "host, "
  1128. "path, "
  1129. "expiry, "
  1130. "lastAccessed, "
  1131. "creationTime, "
  1132. "isSecure, "
  1133. "isHttpOnly "
  1134. "FROM moz_cookies"), getter_AddRefs(stmt));
  1135. if (NS_SUCCEEDED(rv))
  1136. break;
  1137. // our columns aren't there - drop the table!
  1138. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1139. "DROP TABLE moz_cookies"));
  1140. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1141. rv = CreateTable();
  1142. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1143. }
  1144. break;
  1145. }
  1146. }
  1147. // make operations on the table asynchronous, for performance
  1148. mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1149. "PRAGMA synchronous = OFF"));
  1150. // Use write-ahead-logging for performance. We cap the autocheckpoint limit at
  1151. // 16 pages (around 500KB).
  1152. mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1153. MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = WAL"));
  1154. mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1155. "PRAGMA wal_autocheckpoint = 16"));
  1156. // cache frequently used statements (for insertion, deletion, and updating)
  1157. rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1158. "INSERT INTO moz_cookies ("
  1159. "baseDomain, "
  1160. "originAttributes, "
  1161. "name, "
  1162. "value, "
  1163. "host, "
  1164. "path, "
  1165. "expiry, "
  1166. "lastAccessed, "
  1167. "creationTime, "
  1168. "isSecure, "
  1169. "isHttpOnly"
  1170. ") VALUES ("
  1171. ":baseDomain, "
  1172. ":originAttributes, "
  1173. ":name, "
  1174. ":value, "
  1175. ":host, "
  1176. ":path, "
  1177. ":expiry, "
  1178. ":lastAccessed, "
  1179. ":creationTime, "
  1180. ":isSecure, "
  1181. ":isHttpOnly"
  1182. ")"),
  1183. getter_AddRefs(mDefaultDBState->stmtInsert));
  1184. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1185. rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1186. "DELETE FROM moz_cookies "
  1187. "WHERE name = :name AND host = :host AND path = :path"),
  1188. getter_AddRefs(mDefaultDBState->stmtDelete));
  1189. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1190. rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1191. "UPDATE moz_cookies SET lastAccessed = :lastAccessed "
  1192. "WHERE name = :name AND host = :host AND path = :path"),
  1193. getter_AddRefs(mDefaultDBState->stmtUpdate));
  1194. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  1195. // if we deleted a corrupt db, don't attempt to import - return now
  1196. if (aRecreateDB)
  1197. return RESULT_OK;
  1198. // check whether to import or just read in the db
  1199. if (tableExists)
  1200. return Read();
  1201. nsCOMPtr<nsIFile> oldCookieFile;
  1202. rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  1203. getter_AddRefs(oldCookieFile));
  1204. if (NS_FAILED(rv)) return RESULT_OK;
  1205. // Import cookies, and clean up the old file regardless of success or failure.
  1206. // Note that we have to switch out our DBState temporarily, in case we're in
  1207. // private browsing mode; otherwise ImportCookies() won't be happy.
  1208. DBState* initialState = mDBState;
  1209. mDBState = mDefaultDBState;
  1210. oldCookieFile->AppendNative(NS_LITERAL_CSTRING(OLD_COOKIE_FILE_NAME));
  1211. ImportCookies(oldCookieFile);
  1212. oldCookieFile->Remove(false);
  1213. mDBState = initialState;
  1214. return RESULT_OK;
  1215. }
  1216. // Sets the schema version and creates the moz_cookies table.
  1217. nsresult
  1218. nsCookieService::CreateTable()
  1219. {
  1220. // Set the schema version, before creating the table.
  1221. nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(
  1222. COOKIES_SCHEMA_VERSION);
  1223. if (NS_FAILED(rv)) return rv;
  1224. // Create the table.
  1225. // We default originAttributes to empty string: this is so if users revert to
  1226. // an older Firefox version that doesn't know about this field, any cookies
  1227. // set will still work once they upgrade back.
  1228. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1229. "CREATE TABLE moz_cookies ("
  1230. "id INTEGER PRIMARY KEY, "
  1231. "baseDomain TEXT, "
  1232. "originAttributes TEXT NOT NULL DEFAULT '', "
  1233. "name TEXT, "
  1234. "value TEXT, "
  1235. "host TEXT, "
  1236. "path TEXT, "
  1237. "expiry INTEGER, "
  1238. "lastAccessed INTEGER, "
  1239. "creationTime INTEGER, "
  1240. "isSecure INTEGER, "
  1241. "isHttpOnly INTEGER, "
  1242. "appId INTEGER DEFAULT 0, "
  1243. "inBrowserElement INTEGER DEFAULT 0, "
  1244. "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
  1245. ")"));
  1246. if (NS_FAILED(rv)) return rv;
  1247. // Create an index on baseDomain.
  1248. return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1249. "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
  1250. "originAttributes)"));
  1251. }
  1252. // Sets the schema version and creates the moz_cookies table.
  1253. nsresult
  1254. nsCookieService::CreateTableForSchemaVersion6()
  1255. {
  1256. // Set the schema version, before creating the table.
  1257. nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(6);
  1258. if (NS_FAILED(rv)) return rv;
  1259. // Create the table.
  1260. // We default originAttributes to empty string: this is so if users revert to
  1261. // an older Firefox version that doesn't know about this field, any cookies
  1262. // set will still work once they upgrade back.
  1263. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1264. "CREATE TABLE moz_cookies ("
  1265. "id INTEGER PRIMARY KEY, "
  1266. "baseDomain TEXT, "
  1267. "originAttributes TEXT NOT NULL DEFAULT '', "
  1268. "name TEXT, "
  1269. "value TEXT, "
  1270. "host TEXT, "
  1271. "path TEXT, "
  1272. "expiry INTEGER, "
  1273. "lastAccessed INTEGER, "
  1274. "creationTime INTEGER, "
  1275. "isSecure INTEGER, "
  1276. "isHttpOnly INTEGER, "
  1277. "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
  1278. ")"));
  1279. if (NS_FAILED(rv)) return rv;
  1280. // Create an index on baseDomain.
  1281. return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1282. "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
  1283. "originAttributes)"));
  1284. }
  1285. // Sets the schema version and creates the moz_cookies table.
  1286. nsresult
  1287. nsCookieService::CreateTableForSchemaVersion5()
  1288. {
  1289. // Set the schema version, before creating the table.
  1290. nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(5);
  1291. if (NS_FAILED(rv)) return rv;
  1292. // Create the table. We default appId/inBrowserElement to 0: this is so if
  1293. // users revert to an older Firefox version that doesn't know about these
  1294. // fields, any cookies set will still work once they upgrade back.
  1295. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1296. "CREATE TABLE moz_cookies ("
  1297. "id INTEGER PRIMARY KEY, "
  1298. "baseDomain TEXT, "
  1299. "appId INTEGER DEFAULT 0, "
  1300. "inBrowserElement INTEGER DEFAULT 0, "
  1301. "name TEXT, "
  1302. "value TEXT, "
  1303. "host TEXT, "
  1304. "path TEXT, "
  1305. "expiry INTEGER, "
  1306. "lastAccessed INTEGER, "
  1307. "creationTime INTEGER, "
  1308. "isSecure INTEGER, "
  1309. "isHttpOnly INTEGER, "
  1310. "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, appId, inBrowserElement)"
  1311. ")"));
  1312. if (NS_FAILED(rv)) return rv;
  1313. // Create an index on baseDomain.
  1314. return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1315. "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
  1316. "appId, "
  1317. "inBrowserElement)"));
  1318. }
  1319. void
  1320. nsCookieService::CloseDBStates()
  1321. {
  1322. // Null out our private and pointer DBStates regardless.
  1323. mPrivateDBState = nullptr;
  1324. mDBState = nullptr;
  1325. // If we don't have a default DBState, we're done.
  1326. if (!mDefaultDBState)
  1327. return;
  1328. // Cleanup cached statements before we can close anything.
  1329. CleanupCachedStatements();
  1330. if (mDefaultDBState->dbConn) {
  1331. // Cancel any pending read. No further results will be received by our
  1332. // read listener.
  1333. if (mDefaultDBState->pendingRead) {
  1334. CancelAsyncRead(true);
  1335. }
  1336. // Asynchronously close the connection. We will null it below.
  1337. mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
  1338. }
  1339. CleanupDefaultDBConnection();
  1340. mDefaultDBState = nullptr;
  1341. }
  1342. // Null out the statements.
  1343. // This must be done before closing the connection.
  1344. void
  1345. nsCookieService::CleanupCachedStatements()
  1346. {
  1347. mDefaultDBState->stmtInsert = nullptr;
  1348. mDefaultDBState->stmtDelete = nullptr;
  1349. mDefaultDBState->stmtUpdate = nullptr;
  1350. }
  1351. // Null out the listeners, and the database connection itself. This
  1352. // will not null out the statements, cancel a pending read or
  1353. // asynchronously close the connection -- these must be done
  1354. // beforehand if necessary.
  1355. void
  1356. nsCookieService::CleanupDefaultDBConnection()
  1357. {
  1358. MOZ_ASSERT(!mDefaultDBState->stmtInsert, "stmtInsert has been cleaned up");
  1359. MOZ_ASSERT(!mDefaultDBState->stmtDelete, "stmtDelete has been cleaned up");
  1360. MOZ_ASSERT(!mDefaultDBState->stmtUpdate, "stmtUpdate has been cleaned up");
  1361. // Null out the database connections. If 'dbConn' has not been used for any
  1362. // asynchronous operations yet, this will synchronously close it; otherwise,
  1363. // it's expected that the caller has performed an AsyncClose prior.
  1364. mDefaultDBState->dbConn = nullptr;
  1365. mDefaultDBState->syncConn = nullptr;
  1366. // Manually null out our listeners. This is necessary because they hold a
  1367. // strong ref to the DBState itself. They'll stay alive until whatever
  1368. // statements are still executing complete.
  1369. mDefaultDBState->readListener = nullptr;
  1370. mDefaultDBState->insertListener = nullptr;
  1371. mDefaultDBState->updateListener = nullptr;
  1372. mDefaultDBState->removeListener = nullptr;
  1373. mDefaultDBState->closeListener = nullptr;
  1374. }
  1375. void
  1376. nsCookieService::HandleDBClosed(DBState* aDBState)
  1377. {
  1378. COOKIE_LOGSTRING(LogLevel::Debug,
  1379. ("HandleDBClosed(): DBState %x closed", aDBState));
  1380. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1381. switch (aDBState->corruptFlag) {
  1382. case DBState::OK: {
  1383. // Database is healthy. Notify of closure.
  1384. if (os) {
  1385. os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
  1386. }
  1387. break;
  1388. }
  1389. case DBState::CLOSING_FOR_REBUILD: {
  1390. // Our close finished. Start the rebuild, and notify of db closure later.
  1391. RebuildCorruptDB(aDBState);
  1392. break;
  1393. }
  1394. case DBState::REBUILDING: {
  1395. // We encountered an error during rebuild, closed the database, and now
  1396. // here we are. We already have a 'cookies.sqlite.bak' from the original
  1397. // dead database; we don't want to overwrite it, so let's move this one to
  1398. // 'cookies.sqlite.bak-rebuild'.
  1399. nsCOMPtr<nsIFile> backupFile;
  1400. aDBState->cookieFile->Clone(getter_AddRefs(backupFile));
  1401. nsresult rv = backupFile->MoveToNative(nullptr,
  1402. NS_LITERAL_CSTRING(COOKIES_FILE ".bak-rebuild"));
  1403. COOKIE_LOGSTRING(LogLevel::Warning,
  1404. ("HandleDBClosed(): DBState %x encountered error rebuilding db; move to "
  1405. "'cookies.sqlite.bak-rebuild' gave rv 0x%x", aDBState, rv));
  1406. if (os) {
  1407. os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
  1408. }
  1409. break;
  1410. }
  1411. }
  1412. }
  1413. void
  1414. nsCookieService::HandleCorruptDB(DBState* aDBState)
  1415. {
  1416. if (mDefaultDBState != aDBState) {
  1417. // We've either closed the state or we've switched profiles. It's getting
  1418. // a bit late to rebuild -- bail instead.
  1419. COOKIE_LOGSTRING(LogLevel::Warning,
  1420. ("HandleCorruptDB(): DBState %x is already closed, aborting", aDBState));
  1421. return;
  1422. }
  1423. COOKIE_LOGSTRING(LogLevel::Debug,
  1424. ("HandleCorruptDB(): DBState %x has corruptFlag %u", aDBState,
  1425. aDBState->corruptFlag));
  1426. // Mark the database corrupt, so the close listener can begin reconstructing
  1427. // it.
  1428. switch (mDefaultDBState->corruptFlag) {
  1429. case DBState::OK: {
  1430. // Move to 'closing' state.
  1431. mDefaultDBState->corruptFlag = DBState::CLOSING_FOR_REBUILD;
  1432. // Cancel any pending read and close the database. If we do have an
  1433. // in-flight read we want to throw away all the results so far -- we have no
  1434. // idea how consistent the database is. Note that we may have already
  1435. // canceled the read but not emptied our readSet; do so now.
  1436. mDefaultDBState->readSet.Clear();
  1437. if (mDefaultDBState->pendingRead) {
  1438. CancelAsyncRead(true);
  1439. mDefaultDBState->syncConn = nullptr;
  1440. }
  1441. CleanupCachedStatements();
  1442. mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
  1443. CleanupDefaultDBConnection();
  1444. break;
  1445. }
  1446. case DBState::CLOSING_FOR_REBUILD: {
  1447. // We had an error while waiting for close completion. That's OK, just
  1448. // ignore it -- we're rebuilding anyway.
  1449. return;
  1450. }
  1451. case DBState::REBUILDING: {
  1452. // We had an error while rebuilding the DB. Game over. Close the database
  1453. // and let the close handler do nothing; then we'll move it out of the way.
  1454. CleanupCachedStatements();
  1455. if (mDefaultDBState->dbConn) {
  1456. mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
  1457. }
  1458. CleanupDefaultDBConnection();
  1459. break;
  1460. }
  1461. }
  1462. }
  1463. void
  1464. nsCookieService::RebuildCorruptDB(DBState* aDBState)
  1465. {
  1466. NS_ASSERTION(!aDBState->dbConn, "shouldn't have an open db connection");
  1467. NS_ASSERTION(aDBState->corruptFlag == DBState::CLOSING_FOR_REBUILD,
  1468. "should be in CLOSING_FOR_REBUILD state");
  1469. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1470. aDBState->corruptFlag = DBState::REBUILDING;
  1471. if (mDefaultDBState != aDBState) {
  1472. // We've either closed the state or we've switched profiles. It's getting
  1473. // a bit late to rebuild -- bail instead. In any case, we were waiting
  1474. // on rebuild completion to notify of the db closure, which won't happen --
  1475. // do so now.
  1476. COOKIE_LOGSTRING(LogLevel::Warning,
  1477. ("RebuildCorruptDB(): DBState %x is stale, aborting", aDBState));
  1478. if (os) {
  1479. os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
  1480. }
  1481. return;
  1482. }
  1483. COOKIE_LOGSTRING(LogLevel::Debug,
  1484. ("RebuildCorruptDB(): creating new database"));
  1485. // The database has been closed, and we're ready to rebuild. Open a
  1486. // connection.
  1487. OpenDBResult result = TryInitDB(true);
  1488. if (result != RESULT_OK) {
  1489. // We're done. Reset our DB connection and statements, and notify of
  1490. // closure.
  1491. COOKIE_LOGSTRING(LogLevel::Warning,
  1492. ("RebuildCorruptDB(): TryInitDB() failed with result %u", result));
  1493. CleanupCachedStatements();
  1494. CleanupDefaultDBConnection();
  1495. mDefaultDBState->corruptFlag = DBState::OK;
  1496. if (os) {
  1497. os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
  1498. }
  1499. return;
  1500. }
  1501. // Notify observers that we're beginning the rebuild.
  1502. if (os) {
  1503. os->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
  1504. }
  1505. // Enumerate the hash, and add cookies to the params array.
  1506. mozIStorageAsyncStatement* stmt = aDBState->stmtInsert;
  1507. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
  1508. stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
  1509. for (auto iter = aDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
  1510. nsCookieEntry* entry = iter.Get();
  1511. const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
  1512. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  1513. nsCookie* cookie = cookies[i];
  1514. if (!cookie->IsSession()) {
  1515. bindCookieParameters(paramsArray, nsCookieKey(entry), cookie);
  1516. }
  1517. }
  1518. }
  1519. // Make sure we've got something to write. If we don't, we're done.
  1520. uint32_t length;
  1521. paramsArray->GetLength(&length);
  1522. if (length == 0) {
  1523. COOKIE_LOGSTRING(LogLevel::Debug,
  1524. ("RebuildCorruptDB(): nothing to write, rebuild complete"));
  1525. mDefaultDBState->corruptFlag = DBState::OK;
  1526. return;
  1527. }
  1528. // Execute the statement. If any errors crop up, we won't try again.
  1529. DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
  1530. NS_ASSERT_SUCCESS(rv);
  1531. nsCOMPtr<mozIStoragePendingStatement> handle;
  1532. rv = stmt->ExecuteAsync(aDBState->insertListener, getter_AddRefs(handle));
  1533. NS_ASSERT_SUCCESS(rv);
  1534. }
  1535. nsCookieService::~nsCookieService()
  1536. {
  1537. CloseDBStates();
  1538. UnregisterWeakMemoryReporter(this);
  1539. gCookieService = nullptr;
  1540. }
  1541. NS_IMETHODIMP
  1542. nsCookieService::Observe(nsISupports *aSubject,
  1543. const char *aTopic,
  1544. const char16_t *aData)
  1545. {
  1546. // check the topic
  1547. if (!strcmp(aTopic, "profile-before-change")) {
  1548. // The profile is about to change,
  1549. // or is going away because the application is shutting down.
  1550. // Close the default DB connection and null out our DBStates before
  1551. // changing.
  1552. CloseDBStates();
  1553. } else if (!strcmp(aTopic, "profile-do-change")) {
  1554. NS_ASSERTION(!mDefaultDBState, "shouldn't have a default DBState");
  1555. NS_ASSERTION(!mPrivateDBState, "shouldn't have a private DBState");
  1556. // the profile has already changed; init the db from the new location.
  1557. // if we are in the private browsing state, however, we do not want to read
  1558. // data into it - we should instead put it into the default state, so it's
  1559. // ready for us if and when we switch back to it.
  1560. InitDBStates();
  1561. } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  1562. nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
  1563. if (prefBranch)
  1564. PrefChanged(prefBranch);
  1565. } else if (!strcmp(aTopic, "last-pb-context-exited")) {
  1566. // Flush all the cookies stored by private browsing contexts
  1567. mPrivateDBState = new DBState();
  1568. }
  1569. return NS_OK;
  1570. }
  1571. NS_IMETHODIMP
  1572. nsCookieService::GetCookieString(nsIURI *aHostURI,
  1573. nsIChannel *aChannel,
  1574. char **aCookie)
  1575. {
  1576. return GetCookieStringCommon(aHostURI, aChannel, false, aCookie);
  1577. }
  1578. NS_IMETHODIMP
  1579. nsCookieService::GetCookieStringFromHttp(nsIURI *aHostURI,
  1580. nsIURI *aFirstURI,
  1581. nsIChannel *aChannel,
  1582. char **aCookie)
  1583. {
  1584. return GetCookieStringCommon(aHostURI, aChannel, true, aCookie);
  1585. }
  1586. nsresult
  1587. nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
  1588. nsIChannel *aChannel,
  1589. bool aHttpBound,
  1590. char** aCookie)
  1591. {
  1592. NS_ENSURE_ARG(aHostURI);
  1593. NS_ENSURE_ARG(aCookie);
  1594. // Determine whether the request is foreign. Failure is acceptable.
  1595. bool isForeign = true;
  1596. mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
  1597. // Get originAttributes.
  1598. NeckoOriginAttributes attrs;
  1599. if (aChannel) {
  1600. NS_GetOriginAttributes(aChannel, attrs);
  1601. }
  1602. bool isPrivate = aChannel && NS_UsePrivateBrowsing(aChannel);
  1603. nsAutoCString result;
  1604. GetCookieStringInternal(aHostURI, isForeign, aHttpBound, attrs,
  1605. isPrivate, result);
  1606. *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
  1607. return NS_OK;
  1608. }
  1609. NS_IMETHODIMP
  1610. nsCookieService::SetCookieString(nsIURI *aHostURI,
  1611. nsIPrompt *aPrompt,
  1612. const char *aCookieHeader,
  1613. nsIChannel *aChannel)
  1614. {
  1615. // The aPrompt argument is deprecated and unused. Avoid introducing new
  1616. // code that uses this argument by warning if the value is non-null.
  1617. MOZ_ASSERT(!aPrompt);
  1618. if (aPrompt) {
  1619. nsCOMPtr<nsIConsoleService> aConsoleService =
  1620. do_GetService("@mozilla.org/consoleservice;1");
  1621. if (aConsoleService) {
  1622. aConsoleService->LogStringMessage(
  1623. u"Non-null prompt ignored by nsCookieService.");
  1624. }
  1625. }
  1626. return SetCookieStringCommon(aHostURI, aCookieHeader, nullptr, aChannel,
  1627. false);
  1628. }
  1629. NS_IMETHODIMP
  1630. nsCookieService::SetCookieStringFromHttp(nsIURI *aHostURI,
  1631. nsIURI *aFirstURI,
  1632. nsIPrompt *aPrompt,
  1633. const char *aCookieHeader,
  1634. const char *aServerTime,
  1635. nsIChannel *aChannel)
  1636. {
  1637. // The aPrompt argument is deprecated and unused. Avoid introducing new
  1638. // code that uses this argument by warning if the value is non-null.
  1639. MOZ_ASSERT(!aPrompt);
  1640. if (aPrompt) {
  1641. nsCOMPtr<nsIConsoleService> aConsoleService =
  1642. do_GetService("@mozilla.org/consoleservice;1");
  1643. if (aConsoleService) {
  1644. aConsoleService->LogStringMessage(
  1645. u"Non-null prompt ignored by nsCookieService.");
  1646. }
  1647. }
  1648. return SetCookieStringCommon(aHostURI, aCookieHeader, aServerTime, aChannel,
  1649. true);
  1650. }
  1651. nsresult
  1652. nsCookieService::SetCookieStringCommon(nsIURI *aHostURI,
  1653. const char *aCookieHeader,
  1654. const char *aServerTime,
  1655. nsIChannel *aChannel,
  1656. bool aFromHttp)
  1657. {
  1658. NS_ENSURE_ARG(aHostURI);
  1659. NS_ENSURE_ARG(aCookieHeader);
  1660. // Determine whether the request is foreign. Failure is acceptable.
  1661. bool isForeign = true;
  1662. mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
  1663. // Get originAttributes.
  1664. NeckoOriginAttributes attrs;
  1665. if (aChannel) {
  1666. NS_GetOriginAttributes(aChannel, attrs);
  1667. }
  1668. bool isPrivate = aChannel && NS_UsePrivateBrowsing(aChannel);
  1669. nsDependentCString cookieString(aCookieHeader);
  1670. nsDependentCString serverTime(aServerTime ? aServerTime : "");
  1671. SetCookieStringInternal(aHostURI, isForeign, cookieString,
  1672. serverTime, aFromHttp, attrs,
  1673. isPrivate, aChannel);
  1674. return NS_OK;
  1675. }
  1676. void
  1677. nsCookieService::SetCookieStringInternal(nsIURI *aHostURI,
  1678. bool aIsForeign,
  1679. nsDependentCString &aCookieHeader,
  1680. const nsCString &aServerTime,
  1681. bool aFromHttp,
  1682. const NeckoOriginAttributes &aOriginAttrs,
  1683. bool aIsPrivate,
  1684. nsIChannel *aChannel)
  1685. {
  1686. NS_ASSERTION(aHostURI, "null host!");
  1687. if (!mDBState) {
  1688. NS_WARNING("No DBState! Profile already closed?");
  1689. return;
  1690. }
  1691. AutoRestore<DBState*> savePrevDBState(mDBState);
  1692. mDBState = aIsPrivate ? mPrivateDBState : mDefaultDBState;
  1693. // get the base domain for the host URI.
  1694. // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
  1695. // file:// URI's (i.e. with an empty host) are allowed, but any other
  1696. // scheme must have a non-empty host. A trailing dot in the host
  1697. // is acceptable.
  1698. bool requireHostMatch;
  1699. nsAutoCString baseDomain;
  1700. nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
  1701. if (NS_FAILED(rv)) {
  1702. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  1703. "couldn't get base domain from URI");
  1704. return;
  1705. }
  1706. nsCookieKey key(baseDomain, aOriginAttrs);
  1707. // check default prefs
  1708. CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, aCookieHeader.get());
  1709. // fire a notification if third party or if cookie was rejected
  1710. // (but not if there was an error)
  1711. switch (cookieStatus) {
  1712. case STATUS_REJECTED:
  1713. NotifyRejected(aHostURI);
  1714. if (aIsForeign) {
  1715. NotifyThirdParty(aHostURI, false, aChannel);
  1716. }
  1717. return; // Stop here
  1718. case STATUS_REJECTED_WITH_ERROR:
  1719. return;
  1720. case STATUS_ACCEPTED: // Fallthrough
  1721. case STATUS_ACCEPT_SESSION:
  1722. if (aIsForeign) {
  1723. NotifyThirdParty(aHostURI, true, aChannel);
  1724. }
  1725. break;
  1726. default:
  1727. break;
  1728. }
  1729. // parse server local time. this is not just done here for efficiency
  1730. // reasons - if there's an error parsing it, and we need to default it
  1731. // to the current time, we must do it here since the current time in
  1732. // SetCookieInternal() will change for each cookie processed (e.g. if the
  1733. // user is prompted).
  1734. PRTime tempServerTime;
  1735. int64_t serverTime;
  1736. PRStatus result = PR_ParseTimeString(aServerTime.get(), true,
  1737. &tempServerTime);
  1738. if (result == PR_SUCCESS) {
  1739. serverTime = tempServerTime / int64_t(PR_USEC_PER_SEC);
  1740. } else {
  1741. serverTime = PR_Now() / PR_USEC_PER_SEC;
  1742. }
  1743. // process each cookie in the header
  1744. while (SetCookieInternal(aHostURI, key, requireHostMatch, cookieStatus,
  1745. aCookieHeader, serverTime, aFromHttp, aChannel)) {
  1746. // document.cookie can only set one cookie at a time
  1747. if (!aFromHttp)
  1748. break;
  1749. }
  1750. }
  1751. // notify observers that a cookie was rejected due to the users' prefs.
  1752. void
  1753. nsCookieService::NotifyRejected(nsIURI *aHostURI)
  1754. {
  1755. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1756. if (os) {
  1757. os->NotifyObservers(aHostURI, "cookie-rejected", nullptr);
  1758. }
  1759. }
  1760. // notify observers that a third-party cookie was accepted/rejected
  1761. // if the cookie issuer is unknown, it defaults to "?"
  1762. void
  1763. nsCookieService::NotifyThirdParty(nsIURI *aHostURI, bool aIsAccepted, nsIChannel *aChannel)
  1764. {
  1765. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1766. if (!os) {
  1767. return;
  1768. }
  1769. const char* topic;
  1770. if (mDBState != mPrivateDBState) {
  1771. // Regular (non-private) browsing
  1772. if (aIsAccepted) {
  1773. topic = "third-party-cookie-accepted";
  1774. } else {
  1775. topic = "third-party-cookie-rejected";
  1776. }
  1777. } else {
  1778. // Private browsing
  1779. if (aIsAccepted) {
  1780. topic = "private-third-party-cookie-accepted";
  1781. } else {
  1782. topic = "private-third-party-cookie-rejected";
  1783. }
  1784. }
  1785. do {
  1786. // Attempt to find the host of aChannel.
  1787. if (!aChannel) {
  1788. break;
  1789. }
  1790. nsCOMPtr<nsIURI> channelURI;
  1791. nsresult rv = aChannel->GetURI(getter_AddRefs(channelURI));
  1792. if (NS_FAILED(rv)) {
  1793. break;
  1794. }
  1795. nsAutoCString referringHost;
  1796. rv = channelURI->GetHost(referringHost);
  1797. if (NS_FAILED(rv)) {
  1798. break;
  1799. }
  1800. nsAutoString referringHostUTF16 = NS_ConvertUTF8toUTF16(referringHost);
  1801. os->NotifyObservers(aHostURI, topic, referringHostUTF16.get());
  1802. return;
  1803. } while (false);
  1804. // This can fail for a number of reasons, in which kind we fallback to "?"
  1805. os->NotifyObservers(aHostURI, topic, u"?");
  1806. }
  1807. // notify observers that the cookie list changed. there are five possible
  1808. // values for aData:
  1809. // "deleted" means a cookie was deleted. aSubject is the deleted cookie.
  1810. // "added" means a cookie was added. aSubject is the added cookie.
  1811. // "changed" means a cookie was altered. aSubject is the new cookie.
  1812. // "cleared" means the entire cookie list was cleared. aSubject is null.
  1813. // "batch-deleted" means a set of cookies was purged. aSubject is the list of
  1814. // cookies.
  1815. void
  1816. nsCookieService::NotifyChanged(nsISupports *aSubject,
  1817. const char16_t *aData)
  1818. {
  1819. const char* topic = mDBState == mPrivateDBState ?
  1820. "private-cookie-changed" : "cookie-changed";
  1821. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1822. if (os) {
  1823. os->NotifyObservers(aSubject, topic, aData);
  1824. }
  1825. }
  1826. already_AddRefed<nsIArray>
  1827. nsCookieService::CreatePurgeList(nsICookie2* aCookie)
  1828. {
  1829. nsCOMPtr<nsIMutableArray> removedList =
  1830. do_CreateInstance(NS_ARRAY_CONTRACTID);
  1831. removedList->AppendElement(aCookie, false);
  1832. return removedList.forget();
  1833. }
  1834. /******************************************************************************
  1835. * nsCookieService:
  1836. * pref observer impl
  1837. ******************************************************************************/
  1838. void
  1839. nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch)
  1840. {
  1841. int32_t val;
  1842. if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieBehavior, &val)))
  1843. mCookieBehavior = (uint8_t) LIMIT(val, 0, 3, 0);
  1844. if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val)))
  1845. mMaxNumberOfCookies = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxNumberOfCookies);
  1846. if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val)))
  1847. mMaxCookiesPerHost = (uint16_t) LIMIT(val, 1, 0xFFFF, kMaxCookiesPerHost);
  1848. if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) {
  1849. mCookiePurgeAge =
  1850. int64_t(LIMIT(val, 0, INT32_MAX, INT32_MAX)) * PR_USEC_PER_SEC;
  1851. }
  1852. bool boolval;
  1853. if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefThirdPartySession, &boolval)))
  1854. mThirdPartySession = boolval;
  1855. if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookieLeaveSecurityAlone, &boolval)))
  1856. mLeaveSecureAlone = boolval;
  1857. }
  1858. /******************************************************************************
  1859. * nsICookieManager impl:
  1860. * nsICookieManager
  1861. ******************************************************************************/
  1862. NS_IMETHODIMP
  1863. nsCookieService::RemoveAll()
  1864. {
  1865. if (!mDBState) {
  1866. NS_WARNING("No DBState! Profile already closed?");
  1867. return NS_ERROR_NOT_AVAILABLE;
  1868. }
  1869. RemoveAllFromMemory();
  1870. // clear the cookie file
  1871. if (mDBState->dbConn) {
  1872. NS_ASSERTION(mDBState == mDefaultDBState, "not in default DB state");
  1873. // Cancel any pending read. No further results will be received by our
  1874. // read listener.
  1875. if (mDefaultDBState->pendingRead) {
  1876. CancelAsyncRead(true);
  1877. }
  1878. nsCOMPtr<mozIStorageAsyncStatement> stmt;
  1879. nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1880. "DELETE FROM moz_cookies"), getter_AddRefs(stmt));
  1881. if (NS_SUCCEEDED(rv)) {
  1882. nsCOMPtr<mozIStoragePendingStatement> handle;
  1883. rv = stmt->ExecuteAsync(mDefaultDBState->removeListener,
  1884. getter_AddRefs(handle));
  1885. NS_ASSERT_SUCCESS(rv);
  1886. } else {
  1887. // Recreate the database.
  1888. COOKIE_LOGSTRING(LogLevel::Debug,
  1889. ("RemoveAll(): corruption detected with rv 0x%x", rv));
  1890. HandleCorruptDB(mDefaultDBState);
  1891. }
  1892. }
  1893. NotifyChanged(nullptr, u"cleared");
  1894. return NS_OK;
  1895. }
  1896. NS_IMETHODIMP
  1897. nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
  1898. {
  1899. if (!mDBState) {
  1900. NS_WARNING("No DBState! Profile already closed?");
  1901. return NS_ERROR_NOT_AVAILABLE;
  1902. }
  1903. EnsureReadComplete();
  1904. nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
  1905. for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
  1906. const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
  1907. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  1908. cookieList.AppendObject(cookies[i]);
  1909. }
  1910. }
  1911. return NS_NewArrayEnumerator(aEnumerator, cookieList);
  1912. }
  1913. static nsresult
  1914. InitializeOriginAttributes(NeckoOriginAttributes* aAttrs,
  1915. JS::HandleValue aOriginAttributes,
  1916. JSContext* aCx,
  1917. uint8_t aArgc,
  1918. const char16_t* aAPI,
  1919. const char16_t* aInterfaceSuffix)
  1920. {
  1921. MOZ_ASSERT(aAttrs);
  1922. MOZ_ASSERT(aCx);
  1923. MOZ_ASSERT(aAPI);
  1924. MOZ_ASSERT(aInterfaceSuffix);
  1925. if (aArgc == 0) {
  1926. const char16_t* params[] = {
  1927. aAPI,
  1928. aInterfaceSuffix
  1929. };
  1930. // This is supposed to be temporary and in 1 or 2 releases we want to
  1931. // have originAttributes param as mandatory. But for now, we don't want to
  1932. // break existing addons, so we write a console message to inform the addon
  1933. // developers about it.
  1934. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1935. NS_LITERAL_CSTRING("Cookie Manager"),
  1936. nullptr,
  1937. nsContentUtils::eNECKO_PROPERTIES,
  1938. "nsICookieManagerAPIDeprecated",
  1939. params, ArrayLength(params));
  1940. } else if (aArgc == 1) {
  1941. if (!aOriginAttributes.isObject() ||
  1942. !aAttrs->Init(aCx, aOriginAttributes)) {
  1943. return NS_ERROR_INVALID_ARG;
  1944. }
  1945. }
  1946. return NS_OK;
  1947. }
  1948. NS_IMETHODIMP
  1949. nsCookieService::Add(const nsACString &aHost,
  1950. const nsACString &aPath,
  1951. const nsACString &aName,
  1952. const nsACString &aValue,
  1953. bool aIsSecure,
  1954. bool aIsHttpOnly,
  1955. bool aIsSession,
  1956. int64_t aExpiry,
  1957. JS::HandleValue aOriginAttributes,
  1958. JSContext* aCx,
  1959. uint8_t aArgc)
  1960. {
  1961. MOZ_ASSERT(aArgc == 0 || aArgc == 1);
  1962. NeckoOriginAttributes attrs;
  1963. nsresult rv = InitializeOriginAttributes(&attrs,
  1964. aOriginAttributes,
  1965. aCx,
  1966. aArgc,
  1967. u"nsICookieManager2.add()",
  1968. u"2");
  1969. NS_ENSURE_SUCCESS(rv, rv);
  1970. return AddNative(aHost, aPath, aName, aValue, aIsSecure, aIsHttpOnly,
  1971. aIsSession, aExpiry, &attrs);
  1972. }
  1973. NS_IMETHODIMP_(nsresult)
  1974. nsCookieService::AddNative(const nsACString &aHost,
  1975. const nsACString &aPath,
  1976. const nsACString &aName,
  1977. const nsACString &aValue,
  1978. bool aIsSecure,
  1979. bool aIsHttpOnly,
  1980. bool aIsSession,
  1981. int64_t aExpiry,
  1982. NeckoOriginAttributes* aOriginAttributes)
  1983. {
  1984. if (NS_WARN_IF(!aOriginAttributes)) {
  1985. return NS_ERROR_FAILURE;
  1986. }
  1987. if (!mDBState) {
  1988. NS_WARNING("No DBState! Profile already closed?");
  1989. return NS_ERROR_NOT_AVAILABLE;
  1990. }
  1991. // first, normalize the hostname, and fail if it contains illegal characters.
  1992. nsAutoCString host(aHost);
  1993. nsresult rv = NormalizeHost(host);
  1994. NS_ENSURE_SUCCESS(rv, rv);
  1995. // get the base domain for the host URI.
  1996. // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
  1997. nsAutoCString baseDomain;
  1998. rv = GetBaseDomainFromHost(host, baseDomain);
  1999. NS_ENSURE_SUCCESS(rv, rv);
  2000. int64_t currentTimeInUsec = PR_Now();
  2001. nsCookieKey key = nsCookieKey(baseDomain, *aOriginAttributes);
  2002. RefPtr<nsCookie> cookie =
  2003. nsCookie::Create(aName, aValue, host, aPath,
  2004. aExpiry,
  2005. currentTimeInUsec,
  2006. nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
  2007. aIsSession,
  2008. aIsSecure,
  2009. aIsHttpOnly,
  2010. key.mOriginAttributes);
  2011. if (!cookie) {
  2012. return NS_ERROR_OUT_OF_MEMORY;
  2013. }
  2014. AddInternal(key, cookie, currentTimeInUsec, nullptr, nullptr, true);
  2015. return NS_OK;
  2016. }
  2017. nsresult
  2018. nsCookieService::Remove(const nsACString& aHost, const NeckoOriginAttributes& aAttrs,
  2019. const nsACString& aName, const nsACString& aPath,
  2020. bool aBlocked)
  2021. {
  2022. if (!mDBState) {
  2023. NS_WARNING("No DBState! Profile already closed?");
  2024. return NS_ERROR_NOT_AVAILABLE;
  2025. }
  2026. // first, normalize the hostname, and fail if it contains illegal characters.
  2027. nsAutoCString host(aHost);
  2028. nsresult rv = NormalizeHost(host);
  2029. NS_ENSURE_SUCCESS(rv, rv);
  2030. nsAutoCString baseDomain;
  2031. rv = GetBaseDomainFromHost(host, baseDomain);
  2032. NS_ENSURE_SUCCESS(rv, rv);
  2033. nsListIter matchIter;
  2034. RefPtr<nsCookie> cookie;
  2035. if (FindCookie(nsCookieKey(baseDomain, aAttrs),
  2036. host,
  2037. PromiseFlatCString(aName),
  2038. PromiseFlatCString(aPath),
  2039. matchIter)) {
  2040. cookie = matchIter.Cookie();
  2041. RemoveCookieFromList(matchIter);
  2042. }
  2043. // check if we need to add the host to the permissions blacklist.
  2044. if (aBlocked && mPermissionService) {
  2045. // strip off the domain dot, if necessary
  2046. if (!host.IsEmpty() && host.First() == '.')
  2047. host.Cut(0, 1);
  2048. host.Insert(NS_LITERAL_CSTRING("http://"), 0);
  2049. nsCOMPtr<nsIURI> uri;
  2050. NS_NewURI(getter_AddRefs(uri), host);
  2051. if (uri)
  2052. mPermissionService->SetAccess(uri, nsICookiePermission::ACCESS_DENY);
  2053. }
  2054. if (cookie) {
  2055. // Everything's done. Notify observers.
  2056. NotifyChanged(cookie, u"deleted");
  2057. }
  2058. return NS_OK;
  2059. }
  2060. NS_IMETHODIMP
  2061. nsCookieService::Remove(const nsACString &aHost,
  2062. const nsACString &aName,
  2063. const nsACString &aPath,
  2064. bool aBlocked,
  2065. JS::HandleValue aOriginAttributes,
  2066. JSContext* aCx,
  2067. uint8_t aArgc)
  2068. {
  2069. MOZ_ASSERT(aArgc == 0 || aArgc == 1);
  2070. NeckoOriginAttributes attrs;
  2071. nsresult rv = InitializeOriginAttributes(&attrs,
  2072. aOriginAttributes,
  2073. aCx,
  2074. aArgc,
  2075. u"nsICookieManager.remove()",
  2076. u"");
  2077. NS_ENSURE_SUCCESS(rv, rv);
  2078. return RemoveNative(aHost, aName, aPath, aBlocked, &attrs);
  2079. }
  2080. NS_IMETHODIMP_(nsresult)
  2081. nsCookieService::RemoveNative(const nsACString &aHost,
  2082. const nsACString &aName,
  2083. const nsACString &aPath,
  2084. bool aBlocked,
  2085. NeckoOriginAttributes* aOriginAttributes)
  2086. {
  2087. if (NS_WARN_IF(!aOriginAttributes)) {
  2088. return NS_ERROR_FAILURE;
  2089. }
  2090. nsresult rv = Remove(aHost, *aOriginAttributes, aName, aPath, aBlocked);
  2091. if (NS_WARN_IF(NS_FAILED(rv))) {
  2092. return rv;
  2093. }
  2094. return NS_OK;
  2095. }
  2096. NS_IMETHODIMP
  2097. nsCookieService::UsePrivateMode(bool aIsPrivate,
  2098. nsIPrivateModeCallback* aCallback)
  2099. {
  2100. if (!aCallback) {
  2101. return NS_ERROR_INVALID_ARG;
  2102. }
  2103. if (!mDBState) {
  2104. NS_WARNING("No DBState! Profile already closed?");
  2105. return NS_ERROR_NOT_AVAILABLE;
  2106. }
  2107. AutoRestore<DBState*> savePrevDBState(mDBState);
  2108. mDBState = aIsPrivate ? mPrivateDBState : mDefaultDBState;
  2109. return aCallback->Callback();
  2110. }
  2111. /******************************************************************************
  2112. * nsCookieService impl:
  2113. * private file I/O functions
  2114. ******************************************************************************/
  2115. // Begin an asynchronous read from the database.
  2116. OpenDBResult
  2117. nsCookieService::Read()
  2118. {
  2119. // Set up a statement for the read. Note that our query specifies that
  2120. // 'baseDomain' not be nullptr -- see below for why.
  2121. nsCOMPtr<mozIStorageAsyncStatement> stmtRead;
  2122. nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  2123. "SELECT "
  2124. "name, "
  2125. "value, "
  2126. "host, "
  2127. "path, "
  2128. "expiry, "
  2129. "lastAccessed, "
  2130. "creationTime, "
  2131. "isSecure, "
  2132. "isHttpOnly, "
  2133. "baseDomain, "
  2134. "originAttributes "
  2135. "FROM moz_cookies "
  2136. "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
  2137. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  2138. // Set up a statement to delete any rows with a nullptr 'baseDomain'
  2139. // column. This takes care of any cookies set by browsers that don't
  2140. // understand the 'baseDomain' column, where the database schema version
  2141. // is from one that does. (This would occur when downgrading.)
  2142. nsCOMPtr<mozIStorageAsyncStatement> stmtDeleteNull;
  2143. rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  2144. "DELETE FROM moz_cookies WHERE baseDomain ISNULL"),
  2145. getter_AddRefs(stmtDeleteNull));
  2146. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  2147. // Start a new connection for sync reads, to reduce contention with the
  2148. // background thread. We need to do this before we kick off write statements,
  2149. // since they can lock the database and prevent connections from being opened.
  2150. rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
  2151. getter_AddRefs(mDefaultDBState->syncConn));
  2152. NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
  2153. // Init our readSet hash and execute the statements. Note that, after this
  2154. // point, we cannot fail without altering the cleanup code in InitDBStates()
  2155. // to handle closing of the now-asynchronous connection.
  2156. mDefaultDBState->hostArray.SetCapacity(kMaxNumberOfCookies);
  2157. mDefaultDBState->readListener = new ReadCookieDBListener(mDefaultDBState);
  2158. rv = stmtRead->ExecuteAsync(mDefaultDBState->readListener,
  2159. getter_AddRefs(mDefaultDBState->pendingRead));
  2160. NS_ASSERT_SUCCESS(rv);
  2161. nsCOMPtr<mozIStoragePendingStatement> handle;
  2162. rv = stmtDeleteNull->ExecuteAsync(mDefaultDBState->removeListener,
  2163. getter_AddRefs(handle));
  2164. NS_ASSERT_SUCCESS(rv);
  2165. return RESULT_OK;
  2166. }
  2167. // Extract data from a single result row and create an nsCookie.
  2168. // This is templated since 'T' is different for sync vs async results.
  2169. template<class T> nsCookie*
  2170. nsCookieService::GetCookieFromRow(T &aRow, const OriginAttributes& aOriginAttributes)
  2171. {
  2172. // Skip reading 'baseDomain' -- up to the caller.
  2173. nsCString name, value, host, path;
  2174. DebugOnly<nsresult> rv = aRow->GetUTF8String(IDX_NAME, name);
  2175. NS_ASSERT_SUCCESS(rv);
  2176. rv = aRow->GetUTF8String(IDX_VALUE, value);
  2177. NS_ASSERT_SUCCESS(rv);
  2178. rv = aRow->GetUTF8String(IDX_HOST, host);
  2179. NS_ASSERT_SUCCESS(rv);
  2180. rv = aRow->GetUTF8String(IDX_PATH, path);
  2181. NS_ASSERT_SUCCESS(rv);
  2182. int64_t expiry = aRow->AsInt64(IDX_EXPIRY);
  2183. int64_t lastAccessed = aRow->AsInt64(IDX_LAST_ACCESSED);
  2184. int64_t creationTime = aRow->AsInt64(IDX_CREATION_TIME);
  2185. bool isSecure = 0 != aRow->AsInt32(IDX_SECURE);
  2186. bool isHttpOnly = 0 != aRow->AsInt32(IDX_HTTPONLY);
  2187. // Create a new nsCookie and assign the data.
  2188. return nsCookie::Create(name, value, host, path,
  2189. expiry,
  2190. lastAccessed,
  2191. creationTime,
  2192. false,
  2193. isSecure,
  2194. isHttpOnly,
  2195. aOriginAttributes);
  2196. }
  2197. void
  2198. nsCookieService::AsyncReadComplete()
  2199. {
  2200. // We may be in the private browsing DB state, with a pending read on the
  2201. // default DB state. (This would occur if we started up in private browsing
  2202. // mode.) As long as we do all our operations on the default state, we're OK.
  2203. NS_ASSERTION(mDefaultDBState, "no default DBState");
  2204. NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
  2205. NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
  2206. // Merge the data read on the background thread with the data synchronously
  2207. // read on the main thread. Note that transactions on the cookie table may
  2208. // have occurred on the main thread since, making the background data stale.
  2209. for (uint32_t i = 0; i < mDefaultDBState->hostArray.Length(); ++i) {
  2210. const CookieDomainTuple &tuple = mDefaultDBState->hostArray[i];
  2211. // Tiebreak: if the given base domain has already been read in, ignore
  2212. // the background data. Note that readSet may contain domains that were
  2213. // queried but found not to be in the db -- that's harmless.
  2214. if (mDefaultDBState->readSet.GetEntry(tuple.key))
  2215. continue;
  2216. AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr, false);
  2217. }
  2218. mDefaultDBState->stmtReadDomain = nullptr;
  2219. mDefaultDBState->pendingRead = nullptr;
  2220. mDefaultDBState->readListener = nullptr;
  2221. mDefaultDBState->syncConn = nullptr;
  2222. mDefaultDBState->hostArray.Clear();
  2223. mDefaultDBState->readSet.Clear();
  2224. COOKIE_LOGSTRING(LogLevel::Debug, ("Read(): %ld cookies read",
  2225. mDefaultDBState->cookieCount));
  2226. nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  2227. if (os) {
  2228. os->NotifyObservers(nullptr, "cookie-db-read", nullptr);
  2229. }
  2230. }
  2231. void
  2232. nsCookieService::CancelAsyncRead(bool aPurgeReadSet)
  2233. {
  2234. // We may be in the private browsing DB state, with a pending read on the
  2235. // default DB state. (This would occur if we started up in private browsing
  2236. // mode.) As long as we do all our operations on the default state, we're OK.
  2237. NS_ASSERTION(mDefaultDBState, "no default DBState");
  2238. NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
  2239. NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
  2240. // Cancel the pending read, kill the read listener, and empty the array
  2241. // of data already read in on the background thread.
  2242. mDefaultDBState->readListener->Cancel();
  2243. DebugOnly<nsresult> rv = mDefaultDBState->pendingRead->Cancel();
  2244. NS_ASSERT_SUCCESS(rv);
  2245. mDefaultDBState->stmtReadDomain = nullptr;
  2246. mDefaultDBState->pendingRead = nullptr;
  2247. mDefaultDBState->readListener = nullptr;
  2248. mDefaultDBState->hostArray.Clear();
  2249. // Only clear the 'readSet' table if we no longer need to know what set of
  2250. // data is already accounted for.
  2251. if (aPurgeReadSet)
  2252. mDefaultDBState->readSet.Clear();
  2253. }
  2254. void
  2255. nsCookieService::EnsureReadDomain(const nsCookieKey &aKey)
  2256. {
  2257. NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
  2258. "not in default db state");
  2259. // Fast path 1: nothing to read, or we've already finished reading.
  2260. if (MOZ_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
  2261. return;
  2262. // Fast path 2: already read in this particular domain.
  2263. if (MOZ_LIKELY(mDefaultDBState->readSet.GetEntry(aKey)))
  2264. return;
  2265. // Read in the data synchronously.
  2266. // see IDX_NAME, etc. for parameter indexes
  2267. nsresult rv;
  2268. if (!mDefaultDBState->stmtReadDomain) {
  2269. // Cache the statement, since it's likely to be used again.
  2270. rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
  2271. "SELECT "
  2272. "name, "
  2273. "value, "
  2274. "host, "
  2275. "path, "
  2276. "expiry, "
  2277. "lastAccessed, "
  2278. "creationTime, "
  2279. "isSecure, "
  2280. "isHttpOnly "
  2281. "FROM moz_cookies "
  2282. "WHERE baseDomain = :baseDomain "
  2283. " AND originAttributes = :originAttributes"),
  2284. getter_AddRefs(mDefaultDBState->stmtReadDomain));
  2285. if (NS_FAILED(rv)) {
  2286. // Recreate the database.
  2287. COOKIE_LOGSTRING(LogLevel::Debug,
  2288. ("EnsureReadDomain(): corruption detected when creating statement "
  2289. "with rv 0x%x", rv));
  2290. HandleCorruptDB(mDefaultDBState);
  2291. return;
  2292. }
  2293. }
  2294. NS_ASSERTION(mDefaultDBState->syncConn, "should have a sync db connection");
  2295. mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain);
  2296. rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
  2297. NS_LITERAL_CSTRING("baseDomain"), aKey.mBaseDomain);
  2298. NS_ASSERT_SUCCESS(rv);
  2299. nsAutoCString suffix;
  2300. aKey.mOriginAttributes.CreateSuffix(suffix);
  2301. rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
  2302. NS_LITERAL_CSTRING("originAttributes"), suffix);
  2303. NS_ASSERT_SUCCESS(rv);
  2304. bool hasResult;
  2305. nsCString name, value, host, path;
  2306. AutoTArray<RefPtr<nsCookie>, kMaxCookiesPerHost> array;
  2307. while (true) {
  2308. rv = mDefaultDBState->stmtReadDomain->ExecuteStep(&hasResult);
  2309. if (NS_FAILED(rv)) {
  2310. // Recreate the database.
  2311. COOKIE_LOGSTRING(LogLevel::Debug,
  2312. ("EnsureReadDomain(): corruption detected when reading result "
  2313. "with rv 0x%x", rv));
  2314. HandleCorruptDB(mDefaultDBState);
  2315. return;
  2316. }
  2317. if (!hasResult)
  2318. break;
  2319. array.AppendElement(GetCookieFromRow(mDefaultDBState->stmtReadDomain,
  2320. aKey.mOriginAttributes));
  2321. }
  2322. // Add the cookies to the table in a single operation. This makes sure that
  2323. // either all the cookies get added, or in the case of corruption, none.
  2324. for (uint32_t i = 0; i < array.Length(); ++i) {
  2325. AddCookieToList(aKey, array[i], mDefaultDBState, nullptr, false);
  2326. }
  2327. // Add it to the hashset of read entries, so we don't read it again.
  2328. mDefaultDBState->readSet.PutEntry(aKey);
  2329. COOKIE_LOGSTRING(LogLevel::Debug,
  2330. ("EnsureReadDomain(): %ld cookies read for base domain %s, "
  2331. " originAttributes = %s", array.Length(), aKey.mBaseDomain.get(),
  2332. suffix.get()));
  2333. }
  2334. void
  2335. nsCookieService::EnsureReadComplete()
  2336. {
  2337. NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
  2338. "not in default db state");
  2339. // Fast path 1: nothing to read, or we've already finished reading.
  2340. if (MOZ_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
  2341. return;
  2342. // Cancel the pending read, so we don't get any more results.
  2343. CancelAsyncRead(false);
  2344. // Read in the data synchronously.
  2345. // see IDX_NAME, etc. for parameter indexes
  2346. nsCOMPtr<mozIStorageStatement> stmt;
  2347. nsresult rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
  2348. "SELECT "
  2349. "name, "
  2350. "value, "
  2351. "host, "
  2352. "path, "
  2353. "expiry, "
  2354. "lastAccessed, "
  2355. "creationTime, "
  2356. "isSecure, "
  2357. "isHttpOnly, "
  2358. "baseDomain, "
  2359. "originAttributes "
  2360. "FROM moz_cookies "
  2361. "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
  2362. if (NS_FAILED(rv)) {
  2363. // Recreate the database.
  2364. COOKIE_LOGSTRING(LogLevel::Debug,
  2365. ("EnsureReadComplete(): corruption detected when creating statement "
  2366. "with rv 0x%x", rv));
  2367. HandleCorruptDB(mDefaultDBState);
  2368. return;
  2369. }
  2370. nsCString baseDomain, name, value, host, path;
  2371. bool hasResult;
  2372. nsTArray<CookieDomainTuple> array(kMaxNumberOfCookies);
  2373. while (true) {
  2374. rv = stmt->ExecuteStep(&hasResult);
  2375. if (NS_FAILED(rv)) {
  2376. // Recreate the database.
  2377. COOKIE_LOGSTRING(LogLevel::Debug,
  2378. ("EnsureReadComplete(): corruption detected when reading result "
  2379. "with rv 0x%x", rv));
  2380. HandleCorruptDB(mDefaultDBState);
  2381. return;
  2382. }
  2383. if (!hasResult)
  2384. break;
  2385. // Make sure we haven't already read the data.
  2386. stmt->GetUTF8String(IDX_BASE_DOMAIN, baseDomain);
  2387. nsAutoCString suffix;
  2388. NeckoOriginAttributes attrs;
  2389. stmt->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix);
  2390. // If PopulateFromSuffix failed we just ignore the OA attributes
  2391. // that we don't support
  2392. Unused << attrs.PopulateFromSuffix(suffix);
  2393. nsCookieKey key(baseDomain, attrs);
  2394. if (mDefaultDBState->readSet.GetEntry(key))
  2395. continue;
  2396. CookieDomainTuple* tuple = array.AppendElement();
  2397. tuple->key = key;
  2398. tuple->cookie = GetCookieFromRow(stmt, attrs);
  2399. }
  2400. // Add the cookies to the table in a single operation. This makes sure that
  2401. // either all the cookies get added, or in the case of corruption, none.
  2402. for (uint32_t i = 0; i < array.Length(); ++i) {
  2403. CookieDomainTuple& tuple = array[i];
  2404. AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr,
  2405. false);
  2406. }
  2407. mDefaultDBState->syncConn = nullptr;
  2408. mDefaultDBState->readSet.Clear();
  2409. COOKIE_LOGSTRING(LogLevel::Debug,
  2410. ("EnsureReadComplete(): %ld cookies read", array.Length()));
  2411. }
  2412. NS_IMETHODIMP
  2413. nsCookieService::ImportCookies(nsIFile *aCookieFile)
  2414. {
  2415. if (!mDBState) {
  2416. NS_WARNING("No DBState! Profile already closed?");
  2417. return NS_ERROR_NOT_AVAILABLE;
  2418. }
  2419. // Make sure we're in the default DB state. We don't want people importing
  2420. // cookies into a private browsing session!
  2421. if (mDBState != mDefaultDBState) {
  2422. NS_WARNING("Trying to import cookies in a private browsing session!");
  2423. return NS_ERROR_NOT_AVAILABLE;
  2424. }
  2425. nsresult rv;
  2426. nsCOMPtr<nsIInputStream> fileInputStream;
  2427. rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), aCookieFile);
  2428. if (NS_FAILED(rv)) return rv;
  2429. nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
  2430. if (NS_FAILED(rv)) return rv;
  2431. // First, ensure we've read in everything from the database, if we have one.
  2432. EnsureReadComplete();
  2433. static const char kTrue[] = "TRUE";
  2434. nsAutoCString buffer, baseDomain;
  2435. bool isMore = true;
  2436. int32_t hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
  2437. nsASingleFragmentCString::char_iterator iter;
  2438. int32_t numInts;
  2439. int64_t expires;
  2440. bool isDomain, isHttpOnly = false;
  2441. uint32_t originalCookieCount = mDefaultDBState->cookieCount;
  2442. int64_t currentTimeInUsec = PR_Now();
  2443. int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
  2444. // we use lastAccessedCounter to keep cookies in recently-used order,
  2445. // so we start by initializing to currentTime (somewhat arbitrary)
  2446. int64_t lastAccessedCounter = currentTimeInUsec;
  2447. /* file format is:
  2448. *
  2449. * host \t isDomain \t path \t secure \t expires \t name \t cookie
  2450. *
  2451. * if this format isn't respected we move onto the next line in the file.
  2452. * isDomain is "TRUE" or "FALSE" (default to "FALSE")
  2453. * isSecure is "TRUE" or "FALSE" (default to "TRUE")
  2454. * expires is a int64_t integer
  2455. * note 1: cookie can contain tabs.
  2456. * note 2: cookies will be stored in order of lastAccessed time:
  2457. * most-recently used come first; least-recently-used come last.
  2458. */
  2459. /*
  2460. * ...but due to bug 178933, we hide HttpOnly cookies from older code
  2461. * in a comment, so they don't expose HttpOnly cookies to JS.
  2462. *
  2463. * The format for HttpOnly cookies is
  2464. *
  2465. * #HttpOnly_host \t isDomain \t path \t secure \t expires \t name \t cookie
  2466. *
  2467. */
  2468. // We will likely be adding a bunch of cookies to the DB, so we use async
  2469. // batching with storage to make this super fast.
  2470. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
  2471. if (originalCookieCount == 0 && mDefaultDBState->dbConn) {
  2472. mDefaultDBState->stmtInsert->NewBindingParamsArray(getter_AddRefs(paramsArray));
  2473. }
  2474. while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
  2475. if (StringBeginsWith(buffer, NS_LITERAL_CSTRING(HTTP_ONLY_PREFIX))) {
  2476. isHttpOnly = true;
  2477. hostIndex = sizeof(HTTP_ONLY_PREFIX) - 1;
  2478. } else if (buffer.IsEmpty() || buffer.First() == '#') {
  2479. continue;
  2480. } else {
  2481. isHttpOnly = false;
  2482. hostIndex = 0;
  2483. }
  2484. // this is a cheap, cheesy way of parsing a tab-delimited line into
  2485. // string indexes, which can be lopped off into substrings. just for
  2486. // purposes of obfuscation, it also checks that each token was found.
  2487. // todo: use iterators?
  2488. if ((isDomainIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 ||
  2489. (pathIndex = buffer.FindChar('\t', isDomainIndex) + 1) == 0 ||
  2490. (secureIndex = buffer.FindChar('\t', pathIndex) + 1) == 0 ||
  2491. (expiresIndex = buffer.FindChar('\t', secureIndex) + 1) == 0 ||
  2492. (nameIndex = buffer.FindChar('\t', expiresIndex) + 1) == 0 ||
  2493. (cookieIndex = buffer.FindChar('\t', nameIndex) + 1) == 0) {
  2494. continue;
  2495. }
  2496. // check the expirytime first - if it's expired, ignore
  2497. // nullstomp the trailing tab, to avoid copying the string
  2498. buffer.BeginWriting(iter);
  2499. *(iter += nameIndex - 1) = char(0);
  2500. numInts = PR_sscanf(buffer.get() + expiresIndex, "%lld", &expires);
  2501. if (numInts != 1 || expires < currentTime) {
  2502. continue;
  2503. }
  2504. isDomain = Substring(buffer, isDomainIndex, pathIndex - isDomainIndex - 1).EqualsLiteral(kTrue);
  2505. const nsASingleFragmentCString &host = Substring(buffer, hostIndex, isDomainIndex - hostIndex - 1);
  2506. // check for bad legacy cookies (domain not starting with a dot, or containing a port),
  2507. // and discard
  2508. if ((isDomain && !host.IsEmpty() && host.First() != '.') ||
  2509. host.Contains(':')) {
  2510. continue;
  2511. }
  2512. // compute the baseDomain from the host
  2513. rv = GetBaseDomainFromHost(host, baseDomain);
  2514. if (NS_FAILED(rv))
  2515. continue;
  2516. // pre-existing cookies have appId=0, inIsolatedMozBrowser=false set by default
  2517. // constructor of NeckoOriginAttributes().
  2518. nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
  2519. // Create a new nsCookie and assign the data. We don't know the cookie
  2520. // creation time, so just use the current time to generate a unique one.
  2521. RefPtr<nsCookie> newCookie =
  2522. nsCookie::Create(Substring(buffer, nameIndex, cookieIndex - nameIndex - 1),
  2523. Substring(buffer, cookieIndex, buffer.Length() - cookieIndex),
  2524. host,
  2525. Substring(buffer, pathIndex, secureIndex - pathIndex - 1),
  2526. expires,
  2527. lastAccessedCounter,
  2528. nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
  2529. false,
  2530. Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue),
  2531. isHttpOnly,
  2532. key.mOriginAttributes);
  2533. if (!newCookie) {
  2534. return NS_ERROR_OUT_OF_MEMORY;
  2535. }
  2536. // trick: preserve the most-recently-used cookie ordering,
  2537. // by successively decrementing the lastAccessed time
  2538. lastAccessedCounter--;
  2539. if (originalCookieCount == 0) {
  2540. AddCookieToList(key, newCookie, mDefaultDBState, paramsArray);
  2541. }
  2542. else {
  2543. AddInternal(key, newCookie, currentTimeInUsec,
  2544. nullptr, nullptr, true);
  2545. }
  2546. }
  2547. // If we need to write to disk, do so now.
  2548. if (paramsArray) {
  2549. uint32_t length;
  2550. paramsArray->GetLength(&length);
  2551. if (length) {
  2552. rv = mDefaultDBState->stmtInsert->BindParameters(paramsArray);
  2553. NS_ASSERT_SUCCESS(rv);
  2554. nsCOMPtr<mozIStoragePendingStatement> handle;
  2555. rv = mDefaultDBState->stmtInsert->ExecuteAsync(
  2556. mDefaultDBState->insertListener, getter_AddRefs(handle));
  2557. NS_ASSERT_SUCCESS(rv);
  2558. }
  2559. }
  2560. COOKIE_LOGSTRING(LogLevel::Debug, ("ImportCookies(): %ld cookies imported",
  2561. mDefaultDBState->cookieCount));
  2562. return NS_OK;
  2563. }
  2564. /******************************************************************************
  2565. * nsCookieService impl:
  2566. * private GetCookie/SetCookie helpers
  2567. ******************************************************************************/
  2568. // helper function for GetCookieList
  2569. static inline bool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
  2570. // Comparator class for sorting cookies before sending to a server.
  2571. class CompareCookiesForSending
  2572. {
  2573. public:
  2574. bool Equals(const nsCookie* aCookie1, const nsCookie* aCookie2) const
  2575. {
  2576. return aCookie1->CreationTime() == aCookie2->CreationTime() &&
  2577. aCookie2->Path().Length() == aCookie1->Path().Length();
  2578. }
  2579. bool LessThan(const nsCookie* aCookie1, const nsCookie* aCookie2) const
  2580. {
  2581. // compare by cookie path length in accordance with RFC2109
  2582. int32_t result = aCookie2->Path().Length() - aCookie1->Path().Length();
  2583. if (result != 0)
  2584. return result < 0;
  2585. // when path lengths match, older cookies should be listed first. this is
  2586. // required for backwards compatibility since some websites erroneously
  2587. // depend on receiving cookies in the order in which they were sent to the
  2588. // browser! see bug 236772.
  2589. return aCookie1->CreationTime() < aCookie2->CreationTime();
  2590. }
  2591. };
  2592. static bool
  2593. DomainMatches(nsCookie* aCookie, const nsACString& aHost) {
  2594. // first, check for an exact host or domain cookie match, e.g. "google.com"
  2595. // or ".google.com"; second a subdomain match, e.g.
  2596. // host = "mail.google.com", cookie domain = ".google.com".
  2597. return aCookie->RawHost() == aHost ||
  2598. (aCookie->IsDomain() && StringEndsWith(aHost, aCookie->Host()));
  2599. }
  2600. static bool
  2601. PathMatches(nsCookie* aCookie, const nsACString& aPath) {
  2602. // calculate cookie path length, excluding trailing '/'
  2603. uint32_t cookiePathLen = aCookie->Path().Length();
  2604. if (cookiePathLen > 0 && aCookie->Path().Last() == '/')
  2605. --cookiePathLen;
  2606. // if the given path is shorter than the cookie path, it doesn't match
  2607. // if the given path doesn't start with the cookie path, it doesn't match.
  2608. if (!StringBeginsWith(aPath, Substring(aCookie->Path(), 0, cookiePathLen)))
  2609. return false;
  2610. // if the given path is longer than the cookie path, and the first char after
  2611. // the cookie path is not a path delimiter, it doesn't match.
  2612. if (aPath.Length() > cookiePathLen &&
  2613. !ispathdelimiter(aPath.CharAt(cookiePathLen))) {
  2614. /*
  2615. * |ispathdelimiter| tests four cases: '/', '?', '#', and ';'.
  2616. * '/' is the "standard" case; the '?' test allows a site at host/abc?def
  2617. * to receive a cookie that has a path attribute of abc. this seems
  2618. * strange but at least one major site (citibank, bug 156725) depends
  2619. * on it. The test for # and ; are put in to proactively avoid problems
  2620. * with other sites - these are the only other chars allowed in the path.
  2621. */
  2622. return false;
  2623. }
  2624. // either the paths match exactly, or the cookie path is a prefix of
  2625. // the given path.
  2626. return true;
  2627. }
  2628. void
  2629. nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
  2630. bool aIsForeign,
  2631. bool aHttpBound,
  2632. const NeckoOriginAttributes aOriginAttrs,
  2633. bool aIsPrivate,
  2634. nsCString &aCookieString)
  2635. {
  2636. NS_ASSERTION(aHostURI, "null host!");
  2637. if (!mDBState) {
  2638. NS_WARNING("No DBState! Profile already closed?");
  2639. return;
  2640. }
  2641. AutoRestore<DBState*> savePrevDBState(mDBState);
  2642. mDBState = aIsPrivate ? mPrivateDBState : mDefaultDBState;
  2643. // get the base domain, host, and path from the URI.
  2644. // e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
  2645. // file:// URI's (i.e. with an empty host) are allowed, but any other
  2646. // scheme must have a non-empty host. A trailing dot in the host
  2647. // is acceptable.
  2648. bool requireHostMatch;
  2649. nsAutoCString baseDomain, hostFromURI, pathFromURI;
  2650. nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
  2651. if (NS_SUCCEEDED(rv))
  2652. rv = aHostURI->GetAsciiHost(hostFromURI);
  2653. if (NS_SUCCEEDED(rv))
  2654. rv = aHostURI->GetPath(pathFromURI);
  2655. if (NS_FAILED(rv)) {
  2656. COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nullptr, "invalid host/path from URI");
  2657. return;
  2658. }
  2659. // check default prefs
  2660. CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, nullptr);
  2661. // for GetCookie(), we don't fire rejection notifications.
  2662. switch (cookieStatus) {
  2663. case STATUS_REJECTED:
  2664. case STATUS_REJECTED_WITH_ERROR:
  2665. return;
  2666. default:
  2667. break;
  2668. }
  2669. // Note: The following permissions logic is mirrored in
  2670. // toolkit/modules/addons/MatchPattern.jsm:MatchPattern.matchesCookie().
  2671. // If it changes, please update that function, or file a bug for someone
  2672. // else to do so.
  2673. // check if aHostURI is using an https secure protocol.
  2674. // if it isn't, then we can't send a secure cookie over the connection.
  2675. // if SchemeIs fails, assume an insecure connection, to be on the safe side
  2676. bool isSecure;
  2677. if (NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) {
  2678. isSecure = false;
  2679. }
  2680. nsCookie *cookie;
  2681. AutoTArray<nsCookie*, 8> foundCookieList;
  2682. int64_t currentTimeInUsec = PR_Now();
  2683. int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
  2684. bool stale = false;
  2685. nsCookieKey key(baseDomain, aOriginAttrs);
  2686. EnsureReadDomain(key);
  2687. // perform the hash lookup
  2688. nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
  2689. if (!entry)
  2690. return;
  2691. // iterate the cookies!
  2692. const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
  2693. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  2694. cookie = cookies[i];
  2695. // check the host, since the base domain lookup is conservative.
  2696. if (!DomainMatches(cookie, hostFromURI))
  2697. continue;
  2698. // if the cookie is secure and the host scheme isn't, we can't send it
  2699. if (cookie->IsSecure() && !isSecure)
  2700. continue;
  2701. // if the cookie is httpOnly and it's not going directly to the HTTP
  2702. // connection, don't send it
  2703. if (cookie->IsHttpOnly() && !aHttpBound)
  2704. continue;
  2705. // if the nsIURI path doesn't match the cookie path, don't send it back
  2706. if (!PathMatches(cookie, pathFromURI))
  2707. continue;
  2708. // check if the cookie has expired
  2709. if (cookie->Expiry() <= currentTime) {
  2710. continue;
  2711. }
  2712. // all checks passed - add to list and check if lastAccessed stamp needs updating
  2713. foundCookieList.AppendElement(cookie);
  2714. if (cookie->IsStale()) {
  2715. stale = true;
  2716. }
  2717. }
  2718. int32_t count = foundCookieList.Length();
  2719. if (count == 0)
  2720. return;
  2721. // update lastAccessed timestamps. we only do this if the timestamp is stale
  2722. // by a certain amount, to avoid thrashing the db during pageload.
  2723. if (stale) {
  2724. // Create an array of parameters to bind to our update statement. Batching
  2725. // is OK here since we're updating cookies with no interleaved operations.
  2726. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
  2727. mozIStorageAsyncStatement* stmt = mDBState->stmtUpdate;
  2728. if (mDBState->dbConn) {
  2729. stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
  2730. }
  2731. for (int32_t i = 0; i < count; ++i) {
  2732. cookie = foundCookieList.ElementAt(i);
  2733. if (cookie->IsStale()) {
  2734. UpdateCookieInList(cookie, currentTimeInUsec, paramsArray);
  2735. }
  2736. }
  2737. // Update the database now if necessary.
  2738. if (paramsArray) {
  2739. uint32_t length;
  2740. paramsArray->GetLength(&length);
  2741. if (length) {
  2742. DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
  2743. NS_ASSERT_SUCCESS(rv);
  2744. nsCOMPtr<mozIStoragePendingStatement> handle;
  2745. rv = stmt->ExecuteAsync(mDBState->updateListener,
  2746. getter_AddRefs(handle));
  2747. NS_ASSERT_SUCCESS(rv);
  2748. }
  2749. }
  2750. }
  2751. // return cookies in order of path length; longest to shortest.
  2752. // this is required per RFC2109. if cookies match in length,
  2753. // then sort by creation time (see bug 236772).
  2754. foundCookieList.Sort(CompareCookiesForSending());
  2755. for (int32_t i = 0; i < count; ++i) {
  2756. cookie = foundCookieList.ElementAt(i);
  2757. // check if we have anything to write
  2758. if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
  2759. // if we've already added a cookie to the return list, append a "; " so
  2760. // that subsequent cookies are delimited in the final list.
  2761. if (!aCookieString.IsEmpty()) {
  2762. aCookieString.AppendLiteral("; ");
  2763. }
  2764. if (!cookie->Name().IsEmpty()) {
  2765. // we have a name and value - write both
  2766. aCookieString += cookie->Name() + NS_LITERAL_CSTRING("=") + cookie->Value();
  2767. } else {
  2768. // just write value
  2769. aCookieString += cookie->Value();
  2770. }
  2771. }
  2772. }
  2773. if (!aCookieString.IsEmpty())
  2774. COOKIE_LOGSUCCESS(GET_COOKIE, aHostURI, aCookieString, nullptr, false);
  2775. }
  2776. // processes a single cookie, and returns true if there are more cookies
  2777. // to be processed
  2778. bool
  2779. nsCookieService::SetCookieInternal(nsIURI *aHostURI,
  2780. const nsCookieKey &aKey,
  2781. bool aRequireHostMatch,
  2782. CookieStatus aStatus,
  2783. nsDependentCString &aCookieHeader,
  2784. int64_t aServerTime,
  2785. bool aFromHttp,
  2786. nsIChannel *aChannel)
  2787. {
  2788. NS_ASSERTION(aHostURI, "null host!");
  2789. // create a stack-based nsCookieAttributes, to store all the
  2790. // attributes parsed from the cookie
  2791. nsCookieAttributes cookieAttributes;
  2792. // init expiryTime such that session cookies won't prematurely expire
  2793. cookieAttributes.expiryTime = INT64_MAX;
  2794. // aCookieHeader is an in/out param to point to the next cookie, if
  2795. // there is one. Save the present value for logging purposes
  2796. nsDependentCString savedCookieHeader(aCookieHeader);
  2797. // newCookie says whether there are multiple cookies in the header;
  2798. // so we can handle them separately.
  2799. bool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
  2800. bool isHTTPS;
  2801. nsresult rv = aHostURI->SchemeIs("https", &isHTTPS);
  2802. int64_t currentTimeInUsec = PR_Now();
  2803. // calculate expiry time of cookie.
  2804. cookieAttributes.isSession = GetExpiry(cookieAttributes, aServerTime,
  2805. currentTimeInUsec / PR_USEC_PER_SEC);
  2806. if (aStatus == STATUS_ACCEPT_SESSION) {
  2807. // force lifetime to session. note that the expiration time, if set above,
  2808. // will still apply.
  2809. cookieAttributes.isSession = true;
  2810. }
  2811. // reject cookie if it's over the size limit, per RFC2109
  2812. if ((cookieAttributes.name.Length() + cookieAttributes.value.Length()) > kMaxBytesPerCookie) {
  2813. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "cookie too big (> 4kb)");
  2814. return newCookie;
  2815. }
  2816. // Note: For now we allow 0x20 (Space) in cookie names.
  2817. // Some websites apparently use cookie names with spaces in them, and the RFC
  2818. // doesn't exactly specify what to do in that case, so it's better to keep
  2819. // wider compatibility.
  2820. const char illegalNameCharacters[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
  2821. 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
  2822. 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
  2823. 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  2824. 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
  2825. 0x1F, /* 0x20, */ 0x00 };
  2826. if (cookieAttributes.name.FindCharInSet(illegalNameCharacters, 0) != -1) {
  2827. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "invalid name character");
  2828. return newCookie;
  2829. }
  2830. // domain & path checks
  2831. if (!CheckDomain(cookieAttributes, aHostURI, aKey.mBaseDomain, aRequireHostMatch)) {
  2832. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the domain tests");
  2833. return newCookie;
  2834. }
  2835. if (!CheckPath(cookieAttributes, aHostURI)) {
  2836. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the path tests");
  2837. return newCookie;
  2838. }
  2839. // magic prefix checks. MUST be run after CheckDomain() and CheckPath()
  2840. if (!CheckPrefixes(cookieAttributes, isHTTPS)) {
  2841. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the prefix tests");
  2842. return newCookie;
  2843. }
  2844. // Reject cookie if value contains an RFC 6265 disallowed character.
  2845. // See RFC 6265 section 4.1.1
  2846. // XXX: For now we allow for web compatibility (see issue #357):
  2847. // 0x20 (Space)
  2848. // 0x22 (DQUOTE)
  2849. // 0x2C (Comma)
  2850. // 0x5C (Backslash)
  2851. //
  2852. // FIXME: Before removing DQUOTE from the exceptions list:
  2853. // DQUOTE *cookie-octet DQUOTE is permitted and would fail if just removed.
  2854. // This needs better checking for first and last character allowing
  2855. // DQUOTE but not in the actual value.
  2856. //
  2857. // This only applies to cookies set via the Set-Cookie header, since
  2858. // document.cookie is defined to be UTF-8.
  2859. const char illegalCharacters[] = {
  2860. 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
  2861. 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
  2862. 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x20, 0x22, */
  2863. /* 0x2C, */ 0x3B, /* 0x5C, */ 0x7F, 0x00 };
  2864. if (aFromHttp && (cookieAttributes.value.FindCharInSet(illegalCharacters, 0) != -1)) {
  2865. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "invalid value character");
  2866. return newCookie;
  2867. }
  2868. // create a new nsCookie and copy attributes
  2869. RefPtr<nsCookie> cookie =
  2870. nsCookie::Create(cookieAttributes.name,
  2871. cookieAttributes.value,
  2872. cookieAttributes.host,
  2873. cookieAttributes.path,
  2874. cookieAttributes.expiryTime,
  2875. currentTimeInUsec,
  2876. nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
  2877. cookieAttributes.isSession,
  2878. cookieAttributes.isSecure,
  2879. cookieAttributes.isHttpOnly,
  2880. aKey.mOriginAttributes);
  2881. if (!cookie)
  2882. return newCookie;
  2883. // check permissions from site permission list, or ask the user,
  2884. // to determine if we can set the cookie
  2885. if (mPermissionService) {
  2886. bool permission;
  2887. mPermissionService->CanSetCookie(aHostURI,
  2888. aChannel,
  2889. static_cast<nsICookie2*>(static_cast<nsCookie*>(cookie)),
  2890. &cookieAttributes.isSession,
  2891. &cookieAttributes.expiryTime,
  2892. &permission);
  2893. if (!permission) {
  2894. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "cookie rejected by permission manager");
  2895. NotifyRejected(aHostURI);
  2896. return newCookie;
  2897. }
  2898. // update isSession and expiry attributes, in case they changed
  2899. cookie->SetIsSession(cookieAttributes.isSession);
  2900. cookie->SetExpiry(cookieAttributes.expiryTime);
  2901. }
  2902. // add the cookie to the list. AddInternal() takes care of logging.
  2903. // we get the current time again here, since it may have changed during prompting
  2904. AddInternal(aKey, cookie, PR_Now(), aHostURI, savedCookieHeader.get(),
  2905. aFromHttp);
  2906. return newCookie;
  2907. }
  2908. // this is a backend function for adding a cookie to the list, via SetCookie.
  2909. // also used in the cookie manager, for profile migration from IE.
  2910. // it either replaces an existing cookie; or adds the cookie to the hashtable,
  2911. // and deletes a cookie (if maximum number of cookies has been
  2912. // reached). also performs list maintenance by removing expired cookies.
  2913. void
  2914. nsCookieService::AddInternal(const nsCookieKey &aKey,
  2915. nsCookie *aCookie,
  2916. int64_t aCurrentTimeInUsec,
  2917. nsIURI *aHostURI,
  2918. const char *aCookieHeader,
  2919. bool aFromHttp)
  2920. {
  2921. int64_t currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
  2922. // if the new cookie is httponly, make sure we're not coming from script
  2923. if (!aFromHttp && aCookie->IsHttpOnly()) {
  2924. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2925. "cookie is httponly; coming from script");
  2926. return;
  2927. }
  2928. bool isSecure = true;
  2929. if (aHostURI && NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) {
  2930. isSecure = false;
  2931. }
  2932. // If the new cookie is non-https and wants to set secure flag,
  2933. // browser have to ignore this new cookie.
  2934. // (draft-ietf-httpbis-cookie-alone section 3.1)
  2935. if (mLeaveSecureAlone && aCookie->IsSecure() && !isSecure) {
  2936. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2937. "non-https cookie can't set secure flag");
  2938. return;
  2939. }
  2940. nsListIter exactIter;
  2941. bool foundCookie = false;
  2942. if (mLeaveSecureAlone) {
  2943. // Step1, call FindSecureCookie(). FindSecureCookie() would
  2944. // find the existing cookie with the security flag and has
  2945. // the same name, host and path of the new cookie, if there is any.
  2946. // Step2, Confirm new cookie's security setting. If any targeted
  2947. // cookie had been found in Step1, then confirm whether the
  2948. // new cookie could modify it. If the new created cookie’s
  2949. // "secure-only-flag" is not set, and the "scheme" component
  2950. // of the "request-uri" does not denote a "secure" protocol,
  2951. // then ignore the new cookie.
  2952. // (draft-ietf-httpbis-cookie-alone section 3.2)
  2953. foundCookie = FindSecureCookie(aKey, aCookie);
  2954. if (foundCookie && !aCookie->IsSecure()) {
  2955. if (!isSecure) {
  2956. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2957. "cookie can't save because older cookie is secure cookie but newer cookie is non-secure cookie");
  2958. return;
  2959. }
  2960. }
  2961. }
  2962. foundCookie = FindCookie(aKey, aCookie->Host(),
  2963. aCookie->Name(), aCookie->Path(), exactIter);
  2964. RefPtr<nsCookie> oldCookie;
  2965. nsCOMPtr<nsIArray> purgedList;
  2966. if (foundCookie) {
  2967. oldCookie = exactIter.Cookie();
  2968. // Check if the old cookie is stale (i.e. has already expired). If so, we
  2969. // need to be careful about the semantics of removing it and adding the new
  2970. // cookie: we want the behavior wrt adding the new cookie to be the same as
  2971. // if it didn't exist, but we still want to fire a removal notification.
  2972. if (oldCookie->Expiry() <= currentTime) {
  2973. if (aCookie->Expiry() <= currentTime) {
  2974. // The new cookie has expired and the old one is stale. Nothing to do.
  2975. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2976. "cookie has already expired");
  2977. return;
  2978. }
  2979. // Remove the stale cookie. We save notification for later, once all list
  2980. // modifications are complete.
  2981. RemoveCookieFromList(exactIter);
  2982. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2983. "stale cookie was purged");
  2984. purgedList = CreatePurgeList(oldCookie);
  2985. // We've done all we need to wrt removing and notifying the stale cookie.
  2986. // From here on out, we pretend pretend it didn't exist, so that we
  2987. // preserve expected notification semantics when adding the new cookie.
  2988. foundCookie = false;
  2989. } else {
  2990. // If the old cookie is httponly, make sure we're not coming from script.
  2991. if (!aFromHttp && oldCookie->IsHttpOnly()) {
  2992. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  2993. "previously stored cookie is httponly; coming from script");
  2994. return;
  2995. }
  2996. // If the new cookie has the same value, expiry date, and isSecure,
  2997. // isSession, and isHttpOnly flags then we can just keep the old one.
  2998. // Only if any of these differ we would want to override the cookie.
  2999. if (oldCookie->Value().Equals(aCookie->Value()) &&
  3000. oldCookie->Expiry() == aCookie->Expiry() &&
  3001. oldCookie->IsSecure() == aCookie->IsSecure() &&
  3002. oldCookie->IsSession() == aCookie->IsSession() &&
  3003. oldCookie->IsHttpOnly() == aCookie->IsHttpOnly() &&
  3004. // We don't want to perform this optimization if the cookie is
  3005. // considered stale, since in this case we would need to update the
  3006. // database.
  3007. !oldCookie->IsStale()) {
  3008. // Update the last access time on the old cookie.
  3009. oldCookie->SetLastAccessed(aCookie->LastAccessed());
  3010. UpdateCookieOldestTime(mDBState, oldCookie);
  3011. return;
  3012. }
  3013. // Remove the old cookie.
  3014. RemoveCookieFromList(exactIter);
  3015. // If the new cookie has expired -- i.e. the intent was simply to delete
  3016. // the old cookie -- then we're done.
  3017. if (aCookie->Expiry() <= currentTime) {
  3018. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  3019. "previously stored cookie was deleted");
  3020. NotifyChanged(oldCookie, u"deleted");
  3021. return;
  3022. }
  3023. // Preserve creation time of cookie for ordering purposes.
  3024. aCookie->SetCreationTime(oldCookie->CreationTime());
  3025. }
  3026. } else {
  3027. // check if cookie has already expired
  3028. if (aCookie->Expiry() <= currentTime) {
  3029. COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
  3030. "cookie has already expired");
  3031. return;
  3032. }
  3033. // check if we have to delete an old cookie.
  3034. nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
  3035. if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) {
  3036. nsListIter iter;
  3037. // Prioritize evicting insecure cookies.
  3038. // (draft-ietf-httpbis-cookie-alone section 3.3)
  3039. mozilla::Maybe<bool> optionalSecurity = mLeaveSecureAlone ? Some(false) : Nothing();
  3040. int64_t oldestCookieTime = FindStaleCookie(entry, currentTime, aHostURI, optionalSecurity, iter);
  3041. if (iter.entry == nullptr) {
  3042. if (aCookie->IsSecure()) {
  3043. // It's valid to evict a secure cookie for another secure cookie.
  3044. oldestCookieTime = FindStaleCookie(entry, currentTime, aHostURI, Some(true), iter);
  3045. } else {
  3046. COOKIE_LOGEVICTED(aCookie,
  3047. "Too many cookies for this domain and the new cookie is not a secure cookie");
  3048. return;
  3049. }
  3050. }
  3051. MOZ_ASSERT(iter.entry);
  3052. oldCookie = iter.Cookie();
  3053. // remove the oldest cookie from the domain
  3054. RemoveCookieFromList(iter);
  3055. COOKIE_LOGEVICTED(oldCookie, "Too many cookies for this domain");
  3056. purgedList = CreatePurgeList(oldCookie);
  3057. } else if (mDBState->cookieCount >= ADD_TEN_PERCENT(mMaxNumberOfCookies)) {
  3058. int64_t maxAge = aCurrentTimeInUsec - mDBState->cookieOldestTime;
  3059. int64_t purgeAge = ADD_TEN_PERCENT(mCookiePurgeAge);
  3060. if (maxAge >= purgeAge) {
  3061. // we're over both size and age limits by 10%; time to purge the table!
  3062. // do this by:
  3063. // 1) removing expired cookies;
  3064. // 2) evicting the balance of old cookies until we reach the size limit.
  3065. // note that the cookieOldestTime indicator can be pessimistic - if it's
  3066. // older than the actual oldest cookie, we'll just purge more eagerly.
  3067. purgedList = PurgeCookies(aCurrentTimeInUsec);
  3068. }
  3069. }
  3070. }
  3071. // Add the cookie to the db. We do not supply a params array for batching
  3072. // because this might result in removals and additions being out of order.
  3073. AddCookieToList(aKey, aCookie, mDBState, nullptr);
  3074. COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie);
  3075. // Now that list mutations are complete, notify observers. We do it here
  3076. // because observers may themselves attempt to mutate the list.
  3077. if (purgedList) {
  3078. NotifyChanged(purgedList, u"batch-deleted");
  3079. }
  3080. NotifyChanged(aCookie, foundCookie ? u"changed" : u"added");
  3081. }
  3082. /******************************************************************************
  3083. * nsCookieService impl:
  3084. * private cookie header parsing functions
  3085. ******************************************************************************/
  3086. // The following comment block elucidates the function of ParseAttributes.
  3087. /******************************************************************************
  3088. ** Augmented BNF, modified from RFC2109 Section 4.2.2 and RFC2616 Section 2.1
  3089. ** please note: this BNF deviates from both specifications, and reflects this
  3090. ** implementation. <bnf> indicates a reference to the defined grammar "bnf".
  3091. ** Differences from RFC2109/2616 and explanations:
  3092. 1. implied *LWS
  3093. The grammar described by this specification is word-based. Except
  3094. where noted otherwise, linear white space (<LWS>) can be included
  3095. between any two adjacent words (token or quoted-string), and
  3096. between adjacent words and separators, without changing the
  3097. interpretation of a field.
  3098. <LWS> according to spec is SP|HT|CR|LF, but here, we allow only SP | HT.
  3099. 2. We use CR | LF as cookie separators, not ',' per spec, since ',' is in
  3100. common use inside values.
  3101. 3. tokens and values have looser restrictions on allowed characters than
  3102. spec. This is also due to certain characters being in common use inside
  3103. values. We allow only '=' to separate token/value pairs, and ';' to
  3104. terminate tokens or values. <LWS> is allowed within tokens and values
  3105. (see bug 206022).
  3106. 4. where appropriate, full <OCTET>s are allowed, where the spec dictates to
  3107. reject control chars or non-ASCII chars. This is erring on the loose
  3108. side, since there's probably no good reason to enforce this strictness.
  3109. 5. cookie <NAME> is optional, where spec requires it. This is a fairly
  3110. trivial case, but allows the flexibility of setting only a cookie <VALUE>
  3111. with a blank <NAME> and is required by some sites (see bug 169091).
  3112. 6. Attribute "HttpOnly", not covered in the RFCs, is supported
  3113. (see bug 178993).
  3114. ** Begin BNF:
  3115. token = 1*<any allowed-chars except separators>
  3116. value = 1*<any allowed-chars except value-sep>
  3117. separators = ";" | "="
  3118. value-sep = ";"
  3119. cookie-sep = CR | LF
  3120. allowed-chars = <any OCTET except NUL or cookie-sep>
  3121. OCTET = <any 8-bit sequence of data>
  3122. LWS = SP | HT
  3123. NUL = <US-ASCII NUL, null control character (0)>
  3124. CR = <US-ASCII CR, carriage return (13)>
  3125. LF = <US-ASCII LF, linefeed (10)>
  3126. SP = <US-ASCII SP, space (32)>
  3127. HT = <US-ASCII HT, horizontal-tab (9)>
  3128. set-cookie = "Set-Cookie:" cookies
  3129. cookies = cookie *( cookie-sep cookie )
  3130. cookie = [NAME "="] VALUE *(";" cookie-av) ; cookie NAME/VALUE must come first
  3131. NAME = token ; cookie name
  3132. VALUE = value ; cookie value
  3133. cookie-av = token ["=" value]
  3134. valid values for cookie-av (checked post-parsing) are:
  3135. cookie-av = "Path" "=" value
  3136. | "Domain" "=" value
  3137. | "Expires" "=" value
  3138. | "Max-Age" "=" value
  3139. | "Comment" "=" value
  3140. | "Version" "=" value
  3141. | "Secure"
  3142. | "HttpOnly"
  3143. ******************************************************************************/
  3144. // helper functions for GetTokenValue
  3145. static inline bool iswhitespace (char c) { return c == ' ' || c == '\t'; }
  3146. static inline bool isterminator (char c) { return c == '\n' || c == '\r'; }
  3147. static inline bool isvalueseparator (char c) { return isterminator(c) || c == ';'; }
  3148. static inline bool istokenseparator (char c) { return isvalueseparator(c) || c == '='; }
  3149. // Parse a single token/value pair.
  3150. // Returns true if a cookie terminator is found, so caller can parse new cookie.
  3151. bool
  3152. nsCookieService::GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter,
  3153. nsASingleFragmentCString::const_char_iterator &aEndIter,
  3154. nsDependentCSubstring &aTokenString,
  3155. nsDependentCSubstring &aTokenValue,
  3156. bool &aEqualsFound)
  3157. {
  3158. nsASingleFragmentCString::const_char_iterator start, lastSpace;
  3159. // initialize value string to clear garbage
  3160. aTokenValue.Rebind(aIter, aIter);
  3161. // find <token>, including any <LWS> between the end-of-token and the
  3162. // token separator. we'll remove trailing <LWS> next
  3163. while (aIter != aEndIter && iswhitespace(*aIter))
  3164. ++aIter;
  3165. start = aIter;
  3166. while (aIter != aEndIter && !istokenseparator(*aIter))
  3167. ++aIter;
  3168. // remove trailing <LWS>; first check we're not at the beginning
  3169. lastSpace = aIter;
  3170. if (lastSpace != start) {
  3171. while (--lastSpace != start && iswhitespace(*lastSpace))
  3172. continue;
  3173. ++lastSpace;
  3174. }
  3175. aTokenString.Rebind(start, lastSpace);
  3176. aEqualsFound = (*aIter == '=');
  3177. if (aEqualsFound) {
  3178. // find <value>
  3179. while (++aIter != aEndIter && iswhitespace(*aIter))
  3180. continue;
  3181. start = aIter;
  3182. // process <token>
  3183. // just look for ';' to terminate ('=' allowed)
  3184. while (aIter != aEndIter && !isvalueseparator(*aIter))
  3185. ++aIter;
  3186. // remove trailing <LWS>; first check we're not at the beginning
  3187. if (aIter != start) {
  3188. lastSpace = aIter;
  3189. while (--lastSpace != start && iswhitespace(*lastSpace))
  3190. continue;
  3191. aTokenValue.Rebind(start, ++lastSpace);
  3192. }
  3193. }
  3194. // aIter is on ';', or terminator, or EOS
  3195. if (aIter != aEndIter) {
  3196. // if on terminator, increment past & return true to process new cookie
  3197. if (isterminator(*aIter)) {
  3198. ++aIter;
  3199. return true;
  3200. }
  3201. // fall-through: aIter is on ';', increment and return false
  3202. ++aIter;
  3203. }
  3204. return false;
  3205. }
  3206. // Parses attributes from cookie header. expires/max-age attributes aren't folded into the
  3207. // cookie struct here, because we don't know which one to use until we've parsed the header.
  3208. bool
  3209. nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
  3210. nsCookieAttributes &aCookieAttributes)
  3211. {
  3212. static const char kPath[] = "path";
  3213. static const char kDomain[] = "domain";
  3214. static const char kExpires[] = "expires";
  3215. static const char kMaxage[] = "max-age";
  3216. static const char kSecure[] = "secure";
  3217. static const char kHttpOnly[] = "httponly";
  3218. nsASingleFragmentCString::const_char_iterator tempBegin, tempEnd;
  3219. nsASingleFragmentCString::const_char_iterator cookieStart, cookieEnd;
  3220. aCookieHeader.BeginReading(cookieStart);
  3221. aCookieHeader.EndReading(cookieEnd);
  3222. aCookieAttributes.isSecure = false;
  3223. aCookieAttributes.isHttpOnly = false;
  3224. nsDependentCSubstring tokenString(cookieStart, cookieStart);
  3225. nsDependentCSubstring tokenValue (cookieStart, cookieStart);
  3226. bool newCookie, equalsFound;
  3227. // extract cookie <NAME> & <VALUE> (first attribute), and copy the strings.
  3228. // if we find multiple cookies, return for processing
  3229. // note: if there's no '=', we assume token is <VALUE>. this is required by
  3230. // some sites (see bug 169091).
  3231. // XXX fix the parser to parse according to <VALUE> grammar for this case
  3232. newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound);
  3233. if (equalsFound) {
  3234. aCookieAttributes.name = tokenString;
  3235. aCookieAttributes.value = tokenValue;
  3236. } else {
  3237. aCookieAttributes.value = tokenString;
  3238. }
  3239. // extract remaining attributes
  3240. while (cookieStart != cookieEnd && !newCookie) {
  3241. newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound);
  3242. if (!tokenValue.IsEmpty()) {
  3243. tokenValue.BeginReading(tempBegin);
  3244. tokenValue.EndReading(tempEnd);
  3245. }
  3246. // decide which attribute we have, and copy the string
  3247. if (tokenString.LowerCaseEqualsLiteral(kPath))
  3248. aCookieAttributes.path = tokenValue;
  3249. else if (tokenString.LowerCaseEqualsLiteral(kDomain))
  3250. aCookieAttributes.host = tokenValue;
  3251. else if (tokenString.LowerCaseEqualsLiteral(kExpires))
  3252. aCookieAttributes.expires = tokenValue;
  3253. else if (tokenString.LowerCaseEqualsLiteral(kMaxage))
  3254. aCookieAttributes.maxage = tokenValue;
  3255. // ignore any tokenValue for isSecure; just set the boolean
  3256. else if (tokenString.LowerCaseEqualsLiteral(kSecure))
  3257. aCookieAttributes.isSecure = true;
  3258. // ignore any tokenValue for isHttpOnly (see bug 178993);
  3259. // just set the boolean
  3260. else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly))
  3261. aCookieAttributes.isHttpOnly = true;
  3262. }
  3263. // rebind aCookieHeader, in case we need to process another cookie
  3264. aCookieHeader.Rebind(cookieStart, cookieEnd);
  3265. return newCookie;
  3266. }
  3267. /******************************************************************************
  3268. * nsCookieService impl:
  3269. * private domain & permission compliance enforcement functions
  3270. ******************************************************************************/
  3271. // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
  3272. // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
  3273. // dot may be present. If aHostURI is an IP address, an alias such as
  3274. // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
  3275. // be the exact host, and aRequireHostMatch will be true to indicate that
  3276. // substring matches should not be performed.
  3277. nsresult
  3278. nsCookieService::GetBaseDomain(nsIURI *aHostURI,
  3279. nsCString &aBaseDomain,
  3280. bool &aRequireHostMatch)
  3281. {
  3282. // get the base domain. this will fail if the host contains a leading dot,
  3283. // more than one trailing dot, or is otherwise malformed.
  3284. nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
  3285. aRequireHostMatch = rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
  3286. rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
  3287. if (aRequireHostMatch) {
  3288. // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
  3289. // such as 'co.uk', or the empty string. use the host as a key in such
  3290. // cases.
  3291. rv = aHostURI->GetAsciiHost(aBaseDomain);
  3292. }
  3293. NS_ENSURE_SUCCESS(rv, rv);
  3294. // aHost (and thus aBaseDomain) may be the string '.'. If so, fail.
  3295. if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
  3296. return NS_ERROR_INVALID_ARG;
  3297. // block any URIs without a host that aren't file:// URIs.
  3298. if (aBaseDomain.IsEmpty()) {
  3299. bool isFileURI = false;
  3300. aHostURI->SchemeIs("file", &isFileURI);
  3301. if (!isFileURI)
  3302. return NS_ERROR_INVALID_ARG;
  3303. }
  3304. return NS_OK;
  3305. }
  3306. // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
  3307. // "bbc.co.uk". This is done differently than GetBaseDomain(): it is assumed
  3308. // that aHost is already normalized, and it may contain a leading dot
  3309. // (indicating that it represents a domain). A trailing dot may be present.
  3310. // If aHost is an IP address, an alias such as 'localhost', an eTLD such as
  3311. // 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
  3312. // leading dot will be treated as an error.
  3313. nsresult
  3314. nsCookieService::GetBaseDomainFromHost(const nsACString &aHost,
  3315. nsCString &aBaseDomain)
  3316. {
  3317. // aHost must not be the string '.'.
  3318. if (aHost.Length() == 1 && aHost.Last() == '.')
  3319. return NS_ERROR_INVALID_ARG;
  3320. // aHost may contain a leading dot; if so, strip it now.
  3321. bool domain = !aHost.IsEmpty() && aHost.First() == '.';
  3322. // get the base domain. this will fail if the host contains a leading dot,
  3323. // more than one trailing dot, or is otherwise malformed.
  3324. nsresult rv = mTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0, aBaseDomain);
  3325. if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
  3326. rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
  3327. // aHost is either an IP address, an alias such as 'localhost', an eTLD
  3328. // such as 'co.uk', or the empty string. use the host as a key in such
  3329. // cases; however, we reject any such hosts with a leading dot, since it
  3330. // doesn't make sense for them to be domain cookies.
  3331. if (domain)
  3332. return NS_ERROR_INVALID_ARG;
  3333. aBaseDomain = aHost;
  3334. return NS_OK;
  3335. }
  3336. return rv;
  3337. }
  3338. // Normalizes the given hostname, component by component. ASCII/ACE
  3339. // components are lower-cased, and UTF-8 components are normalized per
  3340. // RFC 3454 and converted to ACE.
  3341. nsresult
  3342. nsCookieService::NormalizeHost(nsCString &aHost)
  3343. {
  3344. if (!IsASCII(aHost)) {
  3345. nsAutoCString host;
  3346. nsresult rv = mIDNService->ConvertUTF8toACE(aHost, host);
  3347. if (NS_FAILED(rv))
  3348. return rv;
  3349. aHost = host;
  3350. }
  3351. ToLowerCase(aHost);
  3352. return NS_OK;
  3353. }
  3354. // returns true if 'a' is equal to or a subdomain of 'b',
  3355. // assuming no leading dots are present.
  3356. static inline bool IsSubdomainOf(const nsCString &a, const nsCString &b)
  3357. {
  3358. if (a == b)
  3359. return true;
  3360. if (a.Length() > b.Length())
  3361. return a[a.Length() - b.Length() - 1] == '.' && StringEndsWith(a, b);
  3362. return false;
  3363. }
  3364. CookieStatus
  3365. nsCookieService::CheckPrefs(nsIURI *aHostURI,
  3366. bool aIsForeign,
  3367. const char *aCookieHeader)
  3368. {
  3369. nsresult rv;
  3370. // don't let ftp sites get/set cookies (could be a security issue)
  3371. bool ftp;
  3372. if (NS_SUCCEEDED(aHostURI->SchemeIs("ftp", &ftp)) && ftp) {
  3373. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "ftp sites cannot read cookies");
  3374. return STATUS_REJECTED_WITH_ERROR;
  3375. }
  3376. // check the permission list first; if we find an entry, it overrides
  3377. // default prefs. see bug 184059.
  3378. if (mPermissionService) {
  3379. nsCookieAccess access;
  3380. // Not passing an nsIChannel here is probably OK; our implementation
  3381. // doesn't do anything with it anyway.
  3382. rv = mPermissionService->CanAccess(aHostURI, nullptr, &access);
  3383. // if we found an entry, use it
  3384. if (NS_SUCCEEDED(rv)) {
  3385. switch (access) {
  3386. case nsICookiePermission::ACCESS_DENY:
  3387. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
  3388. aCookieHeader, "cookies are blocked for this site");
  3389. return STATUS_REJECTED;
  3390. case nsICookiePermission::ACCESS_ALLOW:
  3391. return STATUS_ACCEPTED;
  3392. case nsICookiePermission::ACCESS_ALLOW_FIRST_PARTY_ONLY:
  3393. if (aIsForeign) {
  3394. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
  3395. aCookieHeader, "third party cookies are blocked "
  3396. "for this site");
  3397. return STATUS_REJECTED;
  3398. }
  3399. return STATUS_ACCEPTED;
  3400. case nsICookiePermission::ACCESS_LIMIT_THIRD_PARTY:
  3401. if (!aIsForeign)
  3402. return STATUS_ACCEPTED;
  3403. uint32_t priorCookieCount = 0;
  3404. nsAutoCString hostFromURI;
  3405. aHostURI->GetHost(hostFromURI);
  3406. CountCookiesFromHost(hostFromURI, &priorCookieCount);
  3407. if (priorCookieCount == 0) {
  3408. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI,
  3409. aCookieHeader, "third party cookies are blocked "
  3410. "for this site");
  3411. return STATUS_REJECTED;
  3412. }
  3413. return STATUS_ACCEPTED;
  3414. }
  3415. }
  3416. }
  3417. // check default prefs
  3418. if (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
  3419. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled");
  3420. return STATUS_REJECTED;
  3421. }
  3422. // check if cookie is foreign
  3423. if (aIsForeign) {
  3424. if (mCookieBehavior == nsICookieService::BEHAVIOR_ACCEPT && mThirdPartySession)
  3425. return STATUS_ACCEPT_SESSION;
  3426. if (mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN) {
  3427. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
  3428. return STATUS_REJECTED;
  3429. }
  3430. if (mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
  3431. uint32_t priorCookieCount = 0;
  3432. nsAutoCString hostFromURI;
  3433. aHostURI->GetHost(hostFromURI);
  3434. CountCookiesFromHost(hostFromURI, &priorCookieCount);
  3435. if (priorCookieCount == 0) {
  3436. COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
  3437. return STATUS_REJECTED;
  3438. }
  3439. if (mThirdPartySession)
  3440. return STATUS_ACCEPT_SESSION;
  3441. }
  3442. }
  3443. // if nothing has complained, accept cookie
  3444. return STATUS_ACCEPTED;
  3445. }
  3446. // processes domain attribute, and returns true if host has permission to set for this domain.
  3447. bool
  3448. nsCookieService::CheckDomain(nsCookieAttributes &aCookieAttributes,
  3449. nsIURI *aHostURI,
  3450. const nsCString &aBaseDomain,
  3451. bool aRequireHostMatch)
  3452. {
  3453. // Note: The logic in this function is mirrored in
  3454. // toolkit/components/extensions/ext-cookies.js:checkSetCookiePermissions().
  3455. // If it changes, please update that function, or file a bug for someone
  3456. // else to do so.
  3457. // get host from aHostURI
  3458. nsAutoCString hostFromURI;
  3459. aHostURI->GetAsciiHost(hostFromURI);
  3460. // if a domain is given, check the host has permission
  3461. if (!aCookieAttributes.host.IsEmpty()) {
  3462. // Tolerate leading '.' characters, but not if it's otherwise an empty host.
  3463. if (aCookieAttributes.host.Length() > 1 &&
  3464. aCookieAttributes.host.First() == '.') {
  3465. aCookieAttributes.host.Cut(0, 1);
  3466. }
  3467. // switch to lowercase now, to avoid case-insensitive compares everywhere
  3468. ToLowerCase(aCookieAttributes.host);
  3469. // check whether the host is either an IP address, an alias such as
  3470. // 'localhost', an eTLD such as 'co.uk', or the empty string. in these
  3471. // cases, require an exact string match for the domain, and leave the cookie
  3472. // as a non-domain one. bug 105917 originally noted the requirement to deal
  3473. // with IP addresses.
  3474. if (aRequireHostMatch)
  3475. return hostFromURI.Equals(aCookieAttributes.host);
  3476. // ensure the proposed domain is derived from the base domain; and also
  3477. // that the host domain is derived from the proposed domain (per RFC2109).
  3478. if (IsSubdomainOf(aCookieAttributes.host, aBaseDomain) &&
  3479. IsSubdomainOf(hostFromURI, aCookieAttributes.host)) {
  3480. // prepend a dot to indicate a domain cookie
  3481. aCookieAttributes.host.Insert(NS_LITERAL_CSTRING("."), 0);
  3482. return true;
  3483. }
  3484. /*
  3485. * note: RFC2109 section 4.3.2 requires that we check the following:
  3486. * that the portion of host not in domain does not contain a dot.
  3487. * this prevents hosts of the form x.y.co.nz from setting cookies in the
  3488. * entire .co.nz domain. however, it's only a only a partial solution and
  3489. * it breaks sites (IE doesn't enforce it), so we don't perform this check.
  3490. */
  3491. return false;
  3492. }
  3493. // no domain specified, use hostFromURI
  3494. aCookieAttributes.host = hostFromURI;
  3495. return true;
  3496. }
  3497. nsCString
  3498. GetPathFromURI(nsIURI* aHostURI)
  3499. {
  3500. // strip down everything after the last slash to get the path,
  3501. // ignoring slashes in the query string part.
  3502. // if we can QI to nsIURL, that'll take care of the query string portion.
  3503. // otherwise, it's not an nsIURL and can't have a query string, so just find the last slash.
  3504. nsAutoCString path;
  3505. nsCOMPtr<nsIURL> hostURL = do_QueryInterface(aHostURI);
  3506. if (hostURL) {
  3507. hostURL->GetDirectory(path);
  3508. } else {
  3509. aHostURI->GetPath(path);
  3510. int32_t slash = path.RFindChar('/');
  3511. if (slash != kNotFound) {
  3512. path.Truncate(slash + 1);
  3513. }
  3514. }
  3515. return path;
  3516. }
  3517. bool
  3518. nsCookieService::CheckPath(nsCookieAttributes &aCookieAttributes,
  3519. nsIURI *aHostURI)
  3520. {
  3521. // if a path is given, check the host has permission
  3522. if (aCookieAttributes.path.IsEmpty() || aCookieAttributes.path.First() != '/') {
  3523. aCookieAttributes.path = GetPathFromURI(aHostURI);
  3524. #if 0
  3525. } else {
  3526. /**
  3527. * The following test is part of the RFC2109 spec. Loosely speaking, it says that a site
  3528. * cannot set a cookie for a path that it is not on. See bug 155083. However this patch
  3529. * broke several sites -- nordea (bug 155768) and citibank (bug 156725). So this test has
  3530. * been disabled, unless we can evangelize these sites.
  3531. */
  3532. // get path from aHostURI
  3533. nsAutoCString pathFromURI;
  3534. if (NS_FAILED(aHostURI->GetPath(pathFromURI)) ||
  3535. !StringBeginsWith(pathFromURI, aCookieAttributes.path)) {
  3536. return false;
  3537. }
  3538. #endif
  3539. }
  3540. if (aCookieAttributes.path.Length() > kMaxBytesPerPath ||
  3541. aCookieAttributes.path.Contains('\t'))
  3542. return false;
  3543. return true;
  3544. }
  3545. // CheckPrefixes
  3546. //
  3547. // Reject cookies whose name starts with the magic prefixes from
  3548. // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00
  3549. // if they do not meet the criteria required by the prefix.
  3550. //
  3551. // Must not be called until after CheckDomain() and CheckPath() have
  3552. // regularized and validated the nsCookieAttributes values!
  3553. bool
  3554. nsCookieService::CheckPrefixes(nsCookieAttributes &aCookieAttributes,
  3555. bool aSecureRequest)
  3556. {
  3557. static const char kSecure[] = "__Secure-";
  3558. static const char kHost[] = "__Host-";
  3559. static const int kSecureLen = sizeof( kSecure ) - 1;
  3560. static const int kHostLen = sizeof( kHost ) - 1;
  3561. bool isSecure = strncmp( aCookieAttributes.name.get(), kSecure, kSecureLen ) == 0;
  3562. bool isHost = strncmp( aCookieAttributes.name.get(), kHost, kHostLen ) == 0;
  3563. if ( !isSecure && !isHost ) {
  3564. // not one of the magic prefixes: carry on
  3565. return true;
  3566. }
  3567. if ( !aSecureRequest || !aCookieAttributes.isSecure ) {
  3568. // the magic prefixes may only be used from a secure request and
  3569. // the secure attribute must be set on the cookie
  3570. return false;
  3571. }
  3572. if ( isHost ) {
  3573. // The host prefix requires that the path is "/" and that the cookie
  3574. // had no domain attribute. CheckDomain() and CheckPath() MUST be run
  3575. // first to make sure invalid attributes are rejected and to regularlize
  3576. // them. In particular all explicit domain attributes result in a host
  3577. // that starts with a dot, and if the host doesn't start with a dot it
  3578. // correctly matches the true host.
  3579. if ( aCookieAttributes.host[0] == '.' ||
  3580. !aCookieAttributes.path.EqualsLiteral( "/" )) {
  3581. return false;
  3582. }
  3583. }
  3584. return true;
  3585. }
  3586. bool
  3587. nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes,
  3588. int64_t aServerTime,
  3589. int64_t aCurrentTime)
  3590. {
  3591. /* Determine when the cookie should expire. This is done by taking the difference between
  3592. * the server time and the time the server wants the cookie to expire, and adding that
  3593. * difference to the client time. This localizes the client time regardless of whether or
  3594. * not the TZ environment variable was set on the client.
  3595. *
  3596. * Note: We need to consider accounting for network lag here, per RFC.
  3597. */
  3598. // check for max-age attribute first; this overrides expires attribute
  3599. if (!aCookieAttributes.maxage.IsEmpty()) {
  3600. // obtain numeric value of maxageAttribute
  3601. int64_t maxage;
  3602. int32_t numInts = PR_sscanf(aCookieAttributes.maxage.get(), "%lld", &maxage);
  3603. // default to session cookie if the conversion failed
  3604. if (numInts != 1) {
  3605. return true;
  3606. }
  3607. // if this addition overflows, expiryTime will be less than currentTime
  3608. // and the cookie will be expired - that's okay.
  3609. aCookieAttributes.expiryTime = aCurrentTime + maxage;
  3610. // check for expires attribute
  3611. } else if (!aCookieAttributes.expires.IsEmpty()) {
  3612. PRTime expires;
  3613. // parse expiry time
  3614. if (PR_ParseTimeString(aCookieAttributes.expires.get(), true, &expires) != PR_SUCCESS) {
  3615. return true;
  3616. }
  3617. // If set-cookie used absolute time to set expiration, and it can't use
  3618. // client time to set expiration.
  3619. // Because if current time be set in the future, but the cookie expire
  3620. // time be set less than current time and more than server time.
  3621. // The cookie item have to be used to the expired cookie.
  3622. aCookieAttributes.expiryTime = expires / int64_t(PR_USEC_PER_SEC);
  3623. // default to session cookie if no attributes found
  3624. } else {
  3625. return true;
  3626. }
  3627. return false;
  3628. }
  3629. /******************************************************************************
  3630. * nsCookieService impl:
  3631. * private cookielist management functions
  3632. ******************************************************************************/
  3633. void
  3634. nsCookieService::RemoveAllFromMemory()
  3635. {
  3636. // clearing the hashtable will call each nsCookieEntry's dtor,
  3637. // which releases all their respective children.
  3638. mDBState->hostTable.Clear();
  3639. mDBState->cookieCount = 0;
  3640. mDBState->cookieOldestTime = INT64_MAX;
  3641. }
  3642. // comparator class for lastaccessed times of cookies.
  3643. class CompareCookiesByAge {
  3644. public:
  3645. bool Equals(const nsListIter &a, const nsListIter &b) const
  3646. {
  3647. return a.Cookie()->LastAccessed() == b.Cookie()->LastAccessed() &&
  3648. a.Cookie()->CreationTime() == b.Cookie()->CreationTime();
  3649. }
  3650. bool LessThan(const nsListIter &a, const nsListIter &b) const
  3651. {
  3652. // compare by lastAccessed time, and tiebreak by creationTime.
  3653. int64_t result = a.Cookie()->LastAccessed() - b.Cookie()->LastAccessed();
  3654. if (result != 0)
  3655. return result < 0;
  3656. return a.Cookie()->CreationTime() < b.Cookie()->CreationTime();
  3657. }
  3658. };
  3659. // comparator class for sorting cookies by entry and index.
  3660. class CompareCookiesByIndex {
  3661. public:
  3662. bool Equals(const nsListIter &a, const nsListIter &b) const
  3663. {
  3664. NS_ASSERTION(a.entry != b.entry || a.index != b.index,
  3665. "cookie indexes should never be equal");
  3666. return false;
  3667. }
  3668. bool LessThan(const nsListIter &a, const nsListIter &b) const
  3669. {
  3670. // compare by entryclass pointer, then by index.
  3671. if (a.entry != b.entry)
  3672. return a.entry < b.entry;
  3673. return a.index < b.index;
  3674. }
  3675. };
  3676. // purges expired and old cookies in a batch operation.
  3677. already_AddRefed<nsIArray>
  3678. nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
  3679. {
  3680. NS_ASSERTION(mDBState->hostTable.Count() > 0, "table is empty");
  3681. EnsureReadComplete();
  3682. uint32_t initialCookieCount = mDBState->cookieCount;
  3683. COOKIE_LOGSTRING(LogLevel::Debug,
  3684. ("PurgeCookies(): beginning purge with %ld cookies and %lld oldest age",
  3685. mDBState->cookieCount, aCurrentTimeInUsec - mDBState->cookieOldestTime));
  3686. typedef nsTArray<nsListIter> PurgeList;
  3687. PurgeList purgeList(kMaxNumberOfCookies);
  3688. nsCOMPtr<nsIMutableArray> removedList = do_CreateInstance(NS_ARRAY_CONTRACTID);
  3689. // Create a params array to batch the removals. This is OK here because
  3690. // all the removals are in order, and there are no interleaved additions.
  3691. mozIStorageAsyncStatement *stmt = mDBState->stmtDelete;
  3692. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
  3693. if (mDBState->dbConn) {
  3694. stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
  3695. }
  3696. int64_t currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
  3697. int64_t purgeTime = aCurrentTimeInUsec - mCookiePurgeAge;
  3698. int64_t oldestTime = INT64_MAX;
  3699. for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
  3700. nsCookieEntry* entry = iter.Get();
  3701. const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
  3702. auto length = cookies.Length();
  3703. for (nsCookieEntry::IndexType i = 0; i < length; ) {
  3704. nsListIter iter(entry, i);
  3705. nsCookie* cookie = cookies[i];
  3706. // check if the cookie has expired
  3707. if (cookie->Expiry() <= currentTime) {
  3708. removedList->AppendElement(cookie, false);
  3709. COOKIE_LOGEVICTED(cookie, "Cookie expired");
  3710. // remove from list; do not increment our iterator unless we're the last
  3711. // in the list already.
  3712. gCookieService->RemoveCookieFromList(iter, paramsArray);
  3713. if (i == --length) {
  3714. break;
  3715. }
  3716. } else {
  3717. // check if the cookie is over the age limit
  3718. if (cookie->LastAccessed() <= purgeTime) {
  3719. purgeList.AppendElement(iter);
  3720. } else if (cookie->LastAccessed() < oldestTime) {
  3721. // reset our indicator
  3722. oldestTime = cookie->LastAccessed();
  3723. }
  3724. ++i;
  3725. }
  3726. MOZ_ASSERT(length == cookies.Length());
  3727. }
  3728. }
  3729. uint32_t postExpiryCookieCount = mDBState->cookieCount;
  3730. // now we have a list of iterators for cookies over the age limit.
  3731. // sort them by age, and then we'll see how many to remove...
  3732. purgeList.Sort(CompareCookiesByAge());
  3733. // only remove old cookies until we reach the max cookie limit, no more.
  3734. uint32_t excess = mDBState->cookieCount > mMaxNumberOfCookies ?
  3735. mDBState->cookieCount - mMaxNumberOfCookies : 0;
  3736. if (purgeList.Length() > excess) {
  3737. // We're not purging everything in the list, so update our indicator.
  3738. oldestTime = purgeList[excess].Cookie()->LastAccessed();
  3739. purgeList.SetLength(excess);
  3740. }
  3741. // sort the list again, this time grouping cookies with a common entryclass
  3742. // together, and with ascending index. this allows us to iterate backwards
  3743. // over the list removing cookies, without having to adjust indexes as we go.
  3744. purgeList.Sort(CompareCookiesByIndex());
  3745. for (PurgeList::index_type i = purgeList.Length(); i--; ) {
  3746. nsCookie *cookie = purgeList[i].Cookie();
  3747. removedList->AppendElement(cookie, false);
  3748. COOKIE_LOGEVICTED(cookie, "Cookie too old");
  3749. RemoveCookieFromList(purgeList[i], paramsArray);
  3750. }
  3751. // Update the database if we have entries to purge.
  3752. if (paramsArray) {
  3753. uint32_t length;
  3754. paramsArray->GetLength(&length);
  3755. if (length) {
  3756. DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
  3757. NS_ASSERT_SUCCESS(rv);
  3758. nsCOMPtr<mozIStoragePendingStatement> handle;
  3759. rv = stmt->ExecuteAsync(mDBState->removeListener, getter_AddRefs(handle));
  3760. NS_ASSERT_SUCCESS(rv);
  3761. }
  3762. }
  3763. // reset the oldest time indicator
  3764. mDBState->cookieOldestTime = oldestTime;
  3765. COOKIE_LOGSTRING(LogLevel::Debug,
  3766. ("PurgeCookies(): %ld expired; %ld purged; %ld remain; %lld oldest age",
  3767. initialCookieCount - postExpiryCookieCount,
  3768. postExpiryCookieCount - mDBState->cookieCount,
  3769. mDBState->cookieCount,
  3770. aCurrentTimeInUsec - mDBState->cookieOldestTime));
  3771. return removedList.forget();
  3772. }
  3773. // find whether a given cookie has been previously set. this is provided by the
  3774. // nsICookieManager2 interface.
  3775. NS_IMETHODIMP
  3776. nsCookieService::CookieExists(nsICookie2* aCookie,
  3777. JS::HandleValue aOriginAttributes,
  3778. JSContext* aCx,
  3779. uint8_t aArgc,
  3780. bool* aFoundCookie)
  3781. {
  3782. NS_ENSURE_ARG_POINTER(aCookie);
  3783. NS_ENSURE_ARG_POINTER(aCx);
  3784. NS_ENSURE_ARG_POINTER(aFoundCookie);
  3785. MOZ_ASSERT(aArgc == 0 || aArgc == 1);
  3786. NeckoOriginAttributes attrs;
  3787. nsresult rv = InitializeOriginAttributes(&attrs,
  3788. aOriginAttributes,
  3789. aCx,
  3790. aArgc,
  3791. u"nsICookieManager2.cookieExists()",
  3792. u"2");
  3793. NS_ENSURE_SUCCESS(rv, rv);
  3794. return CookieExistsNative(aCookie, &attrs, aFoundCookie);
  3795. }
  3796. NS_IMETHODIMP_(nsresult)
  3797. nsCookieService::CookieExistsNative(nsICookie2* aCookie,
  3798. NeckoOriginAttributes* aOriginAttributes,
  3799. bool* aFoundCookie)
  3800. {
  3801. NS_ENSURE_ARG_POINTER(aCookie);
  3802. NS_ENSURE_ARG_POINTER(aOriginAttributes);
  3803. NS_ENSURE_ARG_POINTER(aFoundCookie);
  3804. if (!mDBState) {
  3805. NS_WARNING("No DBState! Profile already closed?");
  3806. return NS_ERROR_NOT_AVAILABLE;
  3807. }
  3808. nsAutoCString host, name, path;
  3809. nsresult rv = aCookie->GetHost(host);
  3810. NS_ENSURE_SUCCESS(rv, rv);
  3811. rv = aCookie->GetName(name);
  3812. NS_ENSURE_SUCCESS(rv, rv);
  3813. rv = aCookie->GetPath(path);
  3814. NS_ENSURE_SUCCESS(rv, rv);
  3815. nsAutoCString baseDomain;
  3816. rv = GetBaseDomainFromHost(host, baseDomain);
  3817. NS_ENSURE_SUCCESS(rv, rv);
  3818. nsListIter iter;
  3819. *aFoundCookie = FindCookie(nsCookieKey(baseDomain, *aOriginAttributes),
  3820. host, name, path, iter);
  3821. return NS_OK;
  3822. }
  3823. // For a given base domain, find either an expired cookie or the oldest cookie
  3824. // by lastAccessed time.
  3825. int64_t
  3826. nsCookieService::FindStaleCookie(nsCookieEntry *aEntry,
  3827. int64_t aCurrentTime,
  3828. nsIURI* aSource,
  3829. mozilla::Maybe<bool> aIsSecure,
  3830. nsListIter &aIter)
  3831. {
  3832. aIter.entry = nullptr;
  3833. bool requireHostMatch = true;
  3834. nsAutoCString baseDomain, sourceHost, sourcePath;
  3835. if (aSource) {
  3836. GetBaseDomain(aSource, baseDomain, requireHostMatch);
  3837. aSource->GetAsciiHost(sourceHost);
  3838. sourcePath = GetPathFromURI(aSource);
  3839. }
  3840. const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
  3841. int64_t oldestNonMatchingSessionCookieTime = 0;
  3842. nsListIter oldestNonMatchingSessionCookie;
  3843. oldestNonMatchingSessionCookie.entry = nullptr;
  3844. int64_t oldestSessionCookieTime = 0;
  3845. nsListIter oldestSessionCookie;
  3846. oldestSessionCookie.entry = nullptr;
  3847. int64_t oldestNonMatchingNonSessionCookieTime = 0;
  3848. nsListIter oldestNonMatchingNonSessionCookie;
  3849. oldestNonMatchingNonSessionCookie.entry = nullptr;
  3850. int64_t oldestCookieTime = 0;
  3851. nsListIter oldestCookie;
  3852. oldestCookie.entry = nullptr;
  3853. int64_t actualOldestCookieTime = cookies.Length() ? cookies[0]->LastAccessed() : 0;
  3854. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  3855. nsCookie *cookie = cookies[i];
  3856. // If we found an expired cookie, we're done.
  3857. if (cookie->Expiry() <= aCurrentTime) {
  3858. aIter.entry = aEntry;
  3859. aIter.index = i;
  3860. return -1;
  3861. }
  3862. int64_t lastAccessed = cookie->LastAccessed();
  3863. // Record the age of the oldest cookie that is stored for this host.
  3864. // oldestCookieTime is the age of the oldest cookie with a matching
  3865. // secure flag, which may be more recent than an older cookie with
  3866. // a non-matching secure flag.
  3867. if (actualOldestCookieTime > lastAccessed) {
  3868. actualOldestCookieTime = lastAccessed;
  3869. }
  3870. if (aIsSecure.isSome() && !aIsSecure.value()) {
  3871. // We want to look for the oldest non-secure cookie first time through,
  3872. // then find the oldest secure cookie the second time we are called.
  3873. if (cookie->IsSecure()) {
  3874. continue;
  3875. }
  3876. }
  3877. // Update our various records of oldest cookies fitting several restrictions:
  3878. // * session cookies
  3879. // * non-session cookies
  3880. // * cookies with paths and domains that don't match the cookie triggering this purge
  3881. // This cookie is a candidate for eviction if we have no information about
  3882. // the source request, or if it is not a path or domain match against the
  3883. // source request.
  3884. bool isPrimaryEvictionCandidate = true;
  3885. if (aSource) {
  3886. isPrimaryEvictionCandidate = !PathMatches(cookie, sourcePath) || !DomainMatches(cookie, sourceHost);
  3887. }
  3888. if (cookie->IsSession()) {
  3889. if (!oldestSessionCookie.entry || oldestSessionCookieTime > lastAccessed) {
  3890. oldestSessionCookieTime = lastAccessed;
  3891. oldestSessionCookie.entry = aEntry;
  3892. oldestSessionCookie.index = i;
  3893. }
  3894. if (isPrimaryEvictionCandidate &&
  3895. (!oldestNonMatchingSessionCookie.entry ||
  3896. oldestNonMatchingSessionCookieTime > lastAccessed)) {
  3897. oldestNonMatchingSessionCookieTime = lastAccessed;
  3898. oldestNonMatchingSessionCookie.entry = aEntry;
  3899. oldestNonMatchingSessionCookie.index = i;
  3900. }
  3901. } else if (isPrimaryEvictionCandidate &&
  3902. (!oldestNonMatchingNonSessionCookie.entry ||
  3903. oldestNonMatchingNonSessionCookieTime > lastAccessed)) {
  3904. oldestNonMatchingNonSessionCookieTime = lastAccessed;
  3905. oldestNonMatchingNonSessionCookie.entry = aEntry;
  3906. oldestNonMatchingNonSessionCookie.index = i;
  3907. }
  3908. // Check if we've found the oldest cookie so far.
  3909. if (!oldestCookie.entry || oldestCookieTime > lastAccessed) {
  3910. oldestCookieTime = lastAccessed;
  3911. oldestCookie.entry = aEntry;
  3912. oldestCookie.index = i;
  3913. }
  3914. }
  3915. // Prefer to evict the oldest session cookies with a non-matching path/domain,
  3916. // followed by the oldest session cookie with a matching path/domain,
  3917. // followed by the oldest non-session cookie with a non-matching path/domain,
  3918. // resorting to the oldest non-session cookie with a matching path/domain.
  3919. if (oldestNonMatchingSessionCookie.entry) {
  3920. aIter = oldestNonMatchingSessionCookie;
  3921. } else if (oldestSessionCookie.entry) {
  3922. aIter = oldestSessionCookie;
  3923. } else if (oldestNonMatchingNonSessionCookie.entry) {
  3924. aIter = oldestNonMatchingNonSessionCookie;
  3925. } else {
  3926. aIter = oldestCookie;
  3927. }
  3928. return actualOldestCookieTime;
  3929. }
  3930. // count the number of cookies stored by a particular host. this is provided by the
  3931. // nsICookieManager2 interface.
  3932. NS_IMETHODIMP
  3933. nsCookieService::CountCookiesFromHost(const nsACString &aHost,
  3934. uint32_t *aCountFromHost)
  3935. {
  3936. if (!mDBState) {
  3937. NS_WARNING("No DBState! Profile already closed?");
  3938. return NS_ERROR_NOT_AVAILABLE;
  3939. }
  3940. // first, normalize the hostname, and fail if it contains illegal characters.
  3941. nsAutoCString host(aHost);
  3942. nsresult rv = NormalizeHost(host);
  3943. NS_ENSURE_SUCCESS(rv, rv);
  3944. nsAutoCString baseDomain;
  3945. rv = GetBaseDomainFromHost(host, baseDomain);
  3946. NS_ENSURE_SUCCESS(rv, rv);
  3947. nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
  3948. EnsureReadDomain(key);
  3949. // Return a count of all cookies, including expired.
  3950. nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
  3951. *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
  3952. return NS_OK;
  3953. }
  3954. // get an enumerator of cookies stored by a particular host. this is provided by the
  3955. // nsICookieManager2 interface.
  3956. NS_IMETHODIMP
  3957. nsCookieService::GetCookiesFromHost(const nsACString &aHost,
  3958. JS::HandleValue aOriginAttributes,
  3959. JSContext* aCx,
  3960. uint8_t aArgc,
  3961. nsISimpleEnumerator **aEnumerator)
  3962. {
  3963. MOZ_ASSERT(aArgc == 0 || aArgc == 1);
  3964. if (!mDBState) {
  3965. NS_WARNING("No DBState! Profile already closed?");
  3966. return NS_ERROR_NOT_AVAILABLE;
  3967. }
  3968. // first, normalize the hostname, and fail if it contains illegal characters.
  3969. nsAutoCString host(aHost);
  3970. nsresult rv = NormalizeHost(host);
  3971. NS_ENSURE_SUCCESS(rv, rv);
  3972. nsAutoCString baseDomain;
  3973. rv = GetBaseDomainFromHost(host, baseDomain);
  3974. NS_ENSURE_SUCCESS(rv, rv);
  3975. NeckoOriginAttributes attrs;
  3976. rv = InitializeOriginAttributes(&attrs,
  3977. aOriginAttributes,
  3978. aCx,
  3979. aArgc,
  3980. u"nsICookieManager2.getCookiesFromHost()",
  3981. u"2");
  3982. NS_ENSURE_SUCCESS(rv, rv);
  3983. nsCookieKey key = nsCookieKey(baseDomain, attrs);
  3984. EnsureReadDomain(key);
  3985. nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
  3986. if (!entry)
  3987. return NS_NewEmptyEnumerator(aEnumerator);
  3988. nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
  3989. const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
  3990. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  3991. cookieList.AppendObject(cookies[i]);
  3992. }
  3993. return NS_NewArrayEnumerator(aEnumerator, cookieList);
  3994. }
  3995. NS_IMETHODIMP
  3996. nsCookieService::GetCookiesWithOriginAttributes(const nsAString& aPattern,
  3997. const nsACString& aHost,
  3998. nsISimpleEnumerator **aEnumerator)
  3999. {
  4000. mozilla::OriginAttributesPattern pattern;
  4001. if (!pattern.Init(aPattern)) {
  4002. return NS_ERROR_INVALID_ARG;
  4003. }
  4004. nsAutoCString host(aHost);
  4005. nsresult rv = NormalizeHost(host);
  4006. NS_ENSURE_SUCCESS(rv, rv);
  4007. nsAutoCString baseDomain;
  4008. rv = GetBaseDomainFromHost(host, baseDomain);
  4009. NS_ENSURE_SUCCESS(rv, rv);
  4010. return GetCookiesWithOriginAttributes(pattern, baseDomain, aEnumerator);
  4011. }
  4012. nsresult
  4013. nsCookieService::GetCookiesWithOriginAttributes(
  4014. const mozilla::OriginAttributesPattern& aPattern,
  4015. const nsCString& aBaseDomain,
  4016. nsISimpleEnumerator **aEnumerator)
  4017. {
  4018. if (!mDBState) {
  4019. NS_WARNING("No DBState! Profile already closed?");
  4020. return NS_ERROR_NOT_AVAILABLE;
  4021. }
  4022. if (aPattern.mAppId.WasPassed() && aPattern.mAppId.Value() == NECKO_UNKNOWN_APP_ID) {
  4023. return NS_ERROR_INVALID_ARG;
  4024. }
  4025. nsCOMArray<nsICookie> cookies;
  4026. for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
  4027. nsCookieEntry* entry = iter.Get();
  4028. if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
  4029. continue;
  4030. }
  4031. if (!aPattern.Matches(entry->mOriginAttributes)) {
  4032. continue;
  4033. }
  4034. const nsCookieEntry::ArrayType& entryCookies = entry->GetCookies();
  4035. for (nsCookieEntry::IndexType i = 0; i < entryCookies.Length(); ++i) {
  4036. cookies.AppendObject(entryCookies[i]);
  4037. }
  4038. }
  4039. return NS_NewArrayEnumerator(aEnumerator, cookies);
  4040. }
  4041. NS_IMETHODIMP
  4042. nsCookieService::RemoveCookiesWithOriginAttributes(const nsAString& aPattern,
  4043. const nsACString& aHost)
  4044. {
  4045. MOZ_ASSERT(XRE_IsParentProcess());
  4046. mozilla::OriginAttributesPattern pattern;
  4047. if (!pattern.Init(aPattern)) {
  4048. return NS_ERROR_INVALID_ARG;
  4049. }
  4050. nsAutoCString host(aHost);
  4051. nsresult rv = NormalizeHost(host);
  4052. NS_ENSURE_SUCCESS(rv, rv);
  4053. nsAutoCString baseDomain;
  4054. rv = GetBaseDomainFromHost(host, baseDomain);
  4055. NS_ENSURE_SUCCESS(rv, rv);
  4056. return RemoveCookiesWithOriginAttributes(pattern, baseDomain);
  4057. }
  4058. nsresult
  4059. nsCookieService::RemoveCookiesWithOriginAttributes(
  4060. const mozilla::OriginAttributesPattern& aPattern,
  4061. const nsCString& aBaseDomain)
  4062. {
  4063. if (!mDBState) {
  4064. NS_WARNING("No DBState! Profile already close?");
  4065. return NS_ERROR_NOT_AVAILABLE;
  4066. }
  4067. // Iterate the hash table of nsCookieEntry.
  4068. for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
  4069. nsCookieEntry* entry = iter.Get();
  4070. if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
  4071. continue;
  4072. }
  4073. if (!aPattern.Matches(entry->mOriginAttributes)) {
  4074. continue;
  4075. }
  4076. // Pattern matches. Delete all cookies within this nsCookieEntry.
  4077. uint32_t cookiesCount = entry->GetCookies().Length();
  4078. for (nsCookieEntry::IndexType i = 0 ; i < cookiesCount; ++i) {
  4079. // Remove the first cookie from the list.
  4080. nsListIter iter(entry, 0);
  4081. RefPtr<nsCookie> cookie = iter.Cookie();
  4082. // Remove the cookie.
  4083. RemoveCookieFromList(iter);
  4084. if (cookie) {
  4085. NotifyChanged(cookie, u"deleted");
  4086. }
  4087. }
  4088. }
  4089. return NS_OK;
  4090. }
  4091. // find an secure cookie specified by host and name
  4092. bool
  4093. nsCookieService::FindSecureCookie(const nsCookieKey &aKey,
  4094. nsCookie *aCookie)
  4095. {
  4096. EnsureReadDomain(aKey);
  4097. nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
  4098. if (!entry)
  4099. return false;
  4100. const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
  4101. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  4102. nsCookie *cookie = cookies[i];
  4103. // isn't a match if insecure or a different name
  4104. if (!cookie->IsSecure() || !aCookie->Name().Equals(cookie->Name()))
  4105. continue;
  4106. // The host must "domain-match" an existing cookie or vice-versa
  4107. if (DomainMatches(cookie, aCookie->Host()) ||
  4108. DomainMatches(aCookie, cookie->Host())) {
  4109. // If the path of new cookie and the path of existing cookie
  4110. // aren't "/", then this situation needs to compare paths to
  4111. // ensure only that a newly-created non-secure cookie does not
  4112. // overlay an existing secure cookie.
  4113. if (PathMatches(cookie, aCookie->Path())) {
  4114. return true;
  4115. }
  4116. }
  4117. }
  4118. return false;
  4119. }
  4120. // find an exact cookie specified by host, name, and path that hasn't expired.
  4121. bool
  4122. nsCookieService::FindCookie(const nsCookieKey &aKey,
  4123. const nsAFlatCString &aHost,
  4124. const nsAFlatCString &aName,
  4125. const nsAFlatCString &aPath,
  4126. nsListIter &aIter)
  4127. {
  4128. EnsureReadDomain(aKey);
  4129. nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
  4130. if (!entry)
  4131. return false;
  4132. const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
  4133. for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
  4134. nsCookie *cookie = cookies[i];
  4135. if (aHost.Equals(cookie->Host()) &&
  4136. aPath.Equals(cookie->Path()) &&
  4137. aName.Equals(cookie->Name())) {
  4138. aIter = nsListIter(entry, i);
  4139. return true;
  4140. }
  4141. }
  4142. return false;
  4143. }
  4144. // remove a cookie from the hashtable, and update the iterator state.
  4145. void
  4146. nsCookieService::RemoveCookieFromList(const nsListIter &aIter,
  4147. mozIStorageBindingParamsArray *aParamsArray)
  4148. {
  4149. // if it's a non-session cookie, remove it from the db
  4150. if (!aIter.Cookie()->IsSession() && mDBState->dbConn) {
  4151. // Use the asynchronous binding methods to ensure that we do not acquire
  4152. // the database lock.
  4153. mozIStorageAsyncStatement *stmt = mDBState->stmtDelete;
  4154. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray(aParamsArray);
  4155. if (!paramsArray) {
  4156. stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
  4157. }
  4158. nsCOMPtr<mozIStorageBindingParams> params;
  4159. paramsArray->NewBindingParams(getter_AddRefs(params));
  4160. DebugOnly<nsresult> rv =
  4161. params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
  4162. aIter.Cookie()->Name());
  4163. NS_ASSERT_SUCCESS(rv);
  4164. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
  4165. aIter.Cookie()->Host());
  4166. NS_ASSERT_SUCCESS(rv);
  4167. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
  4168. aIter.Cookie()->Path());
  4169. NS_ASSERT_SUCCESS(rv);
  4170. rv = paramsArray->AddParams(params);
  4171. NS_ASSERT_SUCCESS(rv);
  4172. // If we weren't given a params array, we'll need to remove it ourselves.
  4173. if (!aParamsArray) {
  4174. rv = stmt->BindParameters(paramsArray);
  4175. NS_ASSERT_SUCCESS(rv);
  4176. nsCOMPtr<mozIStoragePendingStatement> handle;
  4177. rv = stmt->ExecuteAsync(mDBState->removeListener, getter_AddRefs(handle));
  4178. NS_ASSERT_SUCCESS(rv);
  4179. }
  4180. }
  4181. if (aIter.entry->GetCookies().Length() == 1) {
  4182. // we're removing the last element in the array - so just remove the entry
  4183. // from the hash. note that the entryclass' dtor will take care of
  4184. // releasing this last element for us!
  4185. mDBState->hostTable.RawRemoveEntry(aIter.entry);
  4186. } else {
  4187. // just remove the element from the list
  4188. aIter.entry->GetCookies().RemoveElementAt(aIter.index);
  4189. }
  4190. --mDBState->cookieCount;
  4191. }
  4192. void
  4193. bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
  4194. const nsCookieKey &aKey,
  4195. const nsCookie *aCookie)
  4196. {
  4197. NS_ASSERTION(aParamsArray, "Null params array passed to bindCookieParameters!");
  4198. NS_ASSERTION(aCookie, "Null cookie passed to bindCookieParameters!");
  4199. // Use the asynchronous binding methods to ensure that we do not acquire the
  4200. // database lock.
  4201. nsCOMPtr<mozIStorageBindingParams> params;
  4202. DebugOnly<nsresult> rv =
  4203. aParamsArray->NewBindingParams(getter_AddRefs(params));
  4204. NS_ASSERT_SUCCESS(rv);
  4205. // Bind our values to params
  4206. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
  4207. aKey.mBaseDomain);
  4208. NS_ASSERT_SUCCESS(rv);
  4209. nsAutoCString suffix;
  4210. aKey.mOriginAttributes.CreateSuffix(suffix);
  4211. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"),
  4212. suffix);
  4213. NS_ASSERT_SUCCESS(rv);
  4214. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
  4215. aCookie->Name());
  4216. NS_ASSERT_SUCCESS(rv);
  4217. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
  4218. aCookie->Value());
  4219. NS_ASSERT_SUCCESS(rv);
  4220. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
  4221. aCookie->Host());
  4222. NS_ASSERT_SUCCESS(rv);
  4223. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
  4224. aCookie->Path());
  4225. NS_ASSERT_SUCCESS(rv);
  4226. rv = params->BindInt64ByName(NS_LITERAL_CSTRING("expiry"),
  4227. aCookie->Expiry());
  4228. NS_ASSERT_SUCCESS(rv);
  4229. rv = params->BindInt64ByName(NS_LITERAL_CSTRING("lastAccessed"),
  4230. aCookie->LastAccessed());
  4231. NS_ASSERT_SUCCESS(rv);
  4232. rv = params->BindInt64ByName(NS_LITERAL_CSTRING("creationTime"),
  4233. aCookie->CreationTime());
  4234. NS_ASSERT_SUCCESS(rv);
  4235. rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isSecure"),
  4236. aCookie->IsSecure());
  4237. NS_ASSERT_SUCCESS(rv);
  4238. rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isHttpOnly"),
  4239. aCookie->IsHttpOnly());
  4240. NS_ASSERT_SUCCESS(rv);
  4241. // Bind the params to the array.
  4242. rv = aParamsArray->AddParams(params);
  4243. NS_ASSERT_SUCCESS(rv);
  4244. }
  4245. void
  4246. nsCookieService::UpdateCookieOldestTime(DBState* aDBState,
  4247. nsCookie* aCookie)
  4248. {
  4249. if (aCookie->LastAccessed() < aDBState->cookieOldestTime) {
  4250. aDBState->cookieOldestTime = aCookie->LastAccessed();
  4251. }
  4252. }
  4253. void
  4254. nsCookieService::AddCookieToList(const nsCookieKey &aKey,
  4255. nsCookie *aCookie,
  4256. DBState *aDBState,
  4257. mozIStorageBindingParamsArray *aParamsArray,
  4258. bool aWriteToDB)
  4259. {
  4260. NS_ASSERTION(!(aDBState->dbConn && !aWriteToDB && aParamsArray),
  4261. "Not writing to the DB but have a params array?");
  4262. NS_ASSERTION(!(!aDBState->dbConn && aParamsArray),
  4263. "Do not have a DB connection but have a params array?");
  4264. nsCookieEntry *entry = aDBState->hostTable.PutEntry(aKey);
  4265. NS_ASSERTION(entry, "can't insert element into a null entry!");
  4266. entry->GetCookies().AppendElement(aCookie);
  4267. ++aDBState->cookieCount;
  4268. // keep track of the oldest cookie, for when it comes time to purge
  4269. UpdateCookieOldestTime(aDBState, aCookie);
  4270. // if it's a non-session cookie and hasn't just been read from the db, write it out.
  4271. if (aWriteToDB && !aCookie->IsSession() && aDBState->dbConn) {
  4272. mozIStorageAsyncStatement *stmt = aDBState->stmtInsert;
  4273. nsCOMPtr<mozIStorageBindingParamsArray> paramsArray(aParamsArray);
  4274. if (!paramsArray) {
  4275. stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
  4276. }
  4277. bindCookieParameters(paramsArray, aKey, aCookie);
  4278. // If we were supplied an array to store parameters, we shouldn't call
  4279. // executeAsync - someone up the stack will do this for us.
  4280. if (!aParamsArray) {
  4281. DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
  4282. NS_ASSERT_SUCCESS(rv);
  4283. nsCOMPtr<mozIStoragePendingStatement> handle;
  4284. rv = stmt->ExecuteAsync(mDBState->insertListener, getter_AddRefs(handle));
  4285. NS_ASSERT_SUCCESS(rv);
  4286. }
  4287. }
  4288. }
  4289. void
  4290. nsCookieService::UpdateCookieInList(nsCookie *aCookie,
  4291. int64_t aLastAccessed,
  4292. mozIStorageBindingParamsArray *aParamsArray)
  4293. {
  4294. NS_ASSERTION(aCookie, "Passing a null cookie to UpdateCookieInList!");
  4295. // udpate the lastAccessed timestamp
  4296. aCookie->SetLastAccessed(aLastAccessed);
  4297. // if it's a non-session cookie, update it in the db too
  4298. if (!aCookie->IsSession() && aParamsArray) {
  4299. // Create our params holder.
  4300. nsCOMPtr<mozIStorageBindingParams> params;
  4301. aParamsArray->NewBindingParams(getter_AddRefs(params));
  4302. // Bind our parameters.
  4303. DebugOnly<nsresult> rv =
  4304. params->BindInt64ByName(NS_LITERAL_CSTRING("lastAccessed"),
  4305. aLastAccessed);
  4306. NS_ASSERT_SUCCESS(rv);
  4307. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
  4308. aCookie->Name());
  4309. NS_ASSERT_SUCCESS(rv);
  4310. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
  4311. aCookie->Host());
  4312. NS_ASSERT_SUCCESS(rv);
  4313. rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
  4314. aCookie->Path());
  4315. NS_ASSERT_SUCCESS(rv);
  4316. // Add our bound parameters to the array.
  4317. rv = aParamsArray->AddParams(params);
  4318. NS_ASSERT_SUCCESS(rv);
  4319. }
  4320. }
  4321. size_t
  4322. nsCookieService::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  4323. {
  4324. size_t n = aMallocSizeOf(this);
  4325. if (mDefaultDBState) {
  4326. n += mDefaultDBState->SizeOfIncludingThis(aMallocSizeOf);
  4327. }
  4328. if (mPrivateDBState) {
  4329. n += mPrivateDBState->SizeOfIncludingThis(aMallocSizeOf);
  4330. }
  4331. return n;
  4332. }
  4333. MOZ_DEFINE_MALLOC_SIZE_OF(CookieServiceMallocSizeOf)
  4334. NS_IMETHODIMP
  4335. nsCookieService::CollectReports(nsIHandleReportCallback* aHandleReport,
  4336. nsISupports* aData, bool aAnonymize)
  4337. {
  4338. MOZ_COLLECT_REPORT(
  4339. "explicit/cookie-service", KIND_HEAP, UNITS_BYTES,
  4340. SizeOfIncludingThis(CookieServiceMallocSizeOf),
  4341. "Memory used by the cookie service.");
  4342. return NS_OK;
  4343. }