rovided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type null $last_used Null. * @type null $last_ip Null. * } * @param string $new_password The generated application password in plain text. * @param array $args { * Arguments used to create the application password. * * @type string $name The name of the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * } */ do_action( 'wp_create_application_password', $user_id, $new_item, $new_password, $args ); return array( $new_password, $new_item ); } /** * Gets a user's application passwords. * * @since 5.6.0 * * @param int $user_id User ID. * @return array { * The list of application passwords. * * @type array ...$0 { * @type string $uuid The unique identifier for the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type int|null $last_used The Unix timestamp of the GMT date the application password was last used. * @type string|null $last_ip The IP address the application password was last used by. * } * } */ public static function get_user_application_passwords( $user_id ) { $passwords = get_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, true ); if ( ! is_array( $passwords ) ) { return array(); } $save = false; foreach ( $passwords as $i => $password ) { if ( ! isset( $password['uuid'] ) ) { $passwords[ $i ]['uuid'] = wp_generate_uuid4(); $save = true; } } if ( $save ) { static::set_user_application_passwords( $user_id, $passwords ); } return $passwords; } /** * Gets a user's application password with the given UUID. * * @since 5.6.0 * * @param int $user_id User ID. * @param string $uuid The password's UUID. * @return array|null { * The application password if found, null otherwise. * * @type string $uuid The unique identifier for the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type int|null $last_used The Unix timestamp of the GMT date the application password was last used. * @type string|null $last_ip The IP address the application password was last used by. * } */ public static function get_user_application_password( $user_id, $uuid ) { $passwords = static::get_user_application_passwords( $user_id ); foreach ( $passwords as $password ) { if ( $password['uuid'] === $uuid ) { return $password; } } return null; } /** * Checks if an application password with the given name exists for this user. * * @since 5.7.0 * * @param int $user_id User ID. * @param string $name Application name. * @return bool Whether the provided application name exists. */ public static function application_name_exists_for_user( $user_id, $name ) { $passwords = static::get_user_application_passwords( $user_id ); foreach ( $passwords as $password ) { if ( strtolower( $password['name'] ) === strtolower( $name ) ) { return true; } } return false; } /** * Updates an application password. * * @since 5.6.0 * @since 6.8.0 The actual password should now be hashed using wp_fast_hash(). * * @param int $user_id User ID. * @param string $uuid The password's UUID. * @param array $update { * Information about the application password to update. * * @type string $uuid The unique identifier for the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type int|null $last_used The Unix timestamp of the GMT date the application password was last used. * @type string|null $last_ip The IP address the application password was last used by. * } * @return true|WP_Error True if successful, otherwise a WP_Error instance is returned on error. */ public static function update_application_password( $user_id, $uuid, $update = array() ) { $passwords = static::get_user_application_passwords( $user_id ); foreach ( $passwords as &$item ) { if ( $item['uuid'] !== $uuid ) { continue; } if ( ! empty( $update['name'] ) ) { $update['name'] = sanitize_text_field( $update['name'] ); } $save = false; if ( ! empty( $update['name'] ) && $item['name'] !== $update['name'] ) { $item['name'] = $update['name']; $save = true; } if ( $save ) { $saved = static::set_user_application_passwords( $user_id, $passwords ); if ( ! $saved ) { return new WP_Error( 'db_error', __( 'Could not save application password.' ) ); } } /** * Fires when an application password is updated. * * @since 5.6.0 * @since 6.8.0 The password is now hashed using wp_fast_hash() instead of phpass. * Existing passwords may still be hashed using phpass. * * @param int $user_id The user ID. * @param array $item { * The updated application password details. * * @type string $uuid The unique identifier for the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type int|null $last_used The Unix timestamp of the GMT date the application password was last used. * @type string|null $last_ip The IP address the application password was last used by. * } * @param array $update The information to update. */ do_action( 'wp_update_application_password', $user_id, $item, $update ); return true; } return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) ); } /** * Records that an application password has been used. * * @since 5.6.0 * * @param int $user_id User ID. * @param string $uuid The password's UUID. * @return true|WP_Error True if the usage was recorded, a WP_Error if an error occurs. */ public static function record_application_password_usage( $user_id, $uuid ) { $passwords = static::get_user_application_passwords( $user_id ); foreach ( $passwords as &$password ) { if ( $password['uuid'] !== $uuid ) { continue; } // Only record activity once a day. if ( $password['last_used'] + DAY_IN_SECONDS > time() ) { return true; } $password['last_used'] = time(); $password['last_ip'] = $_SERVER['REMOTE_ADDR']; $saved = static::set_user_application_passwords( $user_id, $passwords ); if ( ! $saved ) { return new WP_Error( 'db_error', __( 'Could not save application password.' ) ); } return true; } // Specified application password not found! return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) ); } /** * Deletes an application password. * * @since 5.6.0 * * @param int $user_id User ID. * @param string $uuid The password's UUID. * @return true|WP_Error Whether the password was successfully found and deleted, a WP_Error otherwise. */ public static function delete_application_password( $user_id, $uuid ) { $passwords = static::get_user_application_passwords( $user_id ); foreach ( $passwords as $key => $item ) { if ( $item['uuid'] === $uuid ) { unset( $passwords[ $key ] ); $saved = static::set_user_application_passwords( $user_id, $passwords ); if ( ! $saved ) { return new WP_Error( 'db_error', __( 'Could not delete application password.' ) ); } /** * Fires when an application password is deleted. * * @since 5.6.0 * * @param int $user_id The user ID. * @param array $item The data about the application password. */ do_action( 'wp_delete_application_password', $user_id, $item ); return true; } } return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) ); } /** * Deletes all application passwords for the given user. * * @since 5.6.0 * * @param int $user_id User ID. * @return int|WP_Error The number of passwords that were deleted or a WP_Error on failure. */ public static function delete_all_application_passwords( $user_id ) { $passwords = static::get_user_application_passwords( $user_id ); if ( $passwords ) { $saved = static::set_user_application_passwords( $user_id, array() ); if ( ! $saved ) { return new WP_Error( 'db_error', __( 'Could not delete application passwords.' ) ); } foreach ( $passwords as $item ) { /** This action is documented in wp-includes/class-wp-application-passwords.php */ do_action( 'wp_delete_application_password', $user_id, $item ); } return count( $passwords ); } return 0; } /** * Sets a user's application passwords. * * @since 5.6.0 * * @param int $user_id User ID. * @param array $passwords { * The list of application passwords. * * @type array ...$0 { * @type string $uuid The unique identifier for the application password. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $name The name of the application password. * @type string $password A one-way hash of the password. * @type int $created Unix timestamp of when the password was created. * @type int|null $last_used The Unix timestamp of the GMT date the application password was last used. * @type string|null $last_ip The IP address the application password was last used by. * } * } * @return int|bool User meta ID if the key didn't exist (ie. this is the first time that an application password * has been saved for the user), true on successful update, false on failure or if the value passed * is the same as the one that is already in the database. */ protected static function set_user_application_passwords( $user_id, $passwords ) { return update_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, $passwords ); } /** * Sanitizes and then splits a password into smaller chunks. * * @since 5.6.0 * * @param string $raw_password The raw application password. * @return string The chunked password. */ public static function chunk_password( #[\SensitiveParameter] $raw_password ) { $raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password ); return trim( chunk_split( $raw_password, 4, ' ' ) ); } /** * Hashes a plaintext application password. * * @since 6.8.0 * * @param string $password Plaintext password. * @return string Hashed password. */ public static function hash_password( #[\SensitiveParameter] string $password ): string { return wp_fast_hash( $password ); } /** * Checks a plaintext application password against a hashed password. * * @since 6.8.0 * * @param string $password Plaintext password. * @param string $hash Hash of the password to check against. * @return bool Whether the password matches the hashed password. */ public static function check_password( #[\SensitiveParameter] string $password, string $hash ): bool { if ( ! str_starts_with( $hash, '$generic$' ) ) { /* * If the hash doesn't start with `$generic$`, it is a hash created with `wp_hash_password()`. * This is the case for application passwords created before 6.8.0. */ return wp_check_password( $password, $hash ); } return wp_verify_fast_hash( $password, $hash ); } } ۖF(lMxg-Kre,}ڵ@ IBl\u~80Og͙O?_2 0$XU>eI$"#####"3#3<|ó/.l 2l/x;5@n>y͜<9_k^ƙϗa4 ƹ&#yżK\j #ذygN8s׍ҋ1y,^h5~?1~!o͌PJl hG 2.ؓV6,⇳1j|"؛F2(6Wmt K@u ՐY~@m-l0H%a ORӋǛV]o4e A?\^<__?!^3ܴV V#I,`kHLe"GnAxo9VdO"I ؠcx?d}\~55 -Y`L:­z?f aID]FaZO<_ (we 㶲Y}26"t} a#)H.}<3 [4C{W5  ǟ &|FLgo(fۖx#aI$9T SX^0_7ڟ?b1zO/>o?)W/wFIj<!K|~bHO>qxۗc&Z 9PP< xus +_ۘ`bohW<cf0C7kAB\clߛf8&s:4p+Zڮ QxQ:8M&~j7`QkMsŗx ~ lpkZ4Mj-H0X`]/tASXu]B^ʈ337-|Es)tm@tm7ގN w xelm:i<+~mYk^+ KsY$M0T l^MxtCȘ>p J֥`F#wmFYhfs@s.mhPO9q=VNBx g-v?NS-uFKd"Jmw‾;O[Ny}8n[Զ{zڮ8)nͦ=zYo' -zll(PhNnM{cδ|A&  `5tEG<Т!״=l錏MĈR!_cra,o28A@%\n6HQ`.͝՚Q]R"e D5R6>{ qieoqp@weOK;(3p|$L7:_ڻ4Nh6E./xs`g8Y2ΔE+ ڬSյ OA+aZ:7*btCcmyrڭy(;D\mAO}p 0e{d% L~(@uEiDc!JKUP6& ZaiYw(tìU:,6b;8&YtIr'h(N%$W^MPh|#~@+x湅oG 91fnr~Xt e2&d}O~{Εr͹Rp+a9%H# 0OQFfz4ka#,{UxeSu3 .NA:cf g<'m=Mý#D;rk4PRrqQy(㺠C;^6Y~:0MSeN,^Y0d|83/,@#|U2f< W"/J<9hC'ީWلMfF5vv^.8YfZ'B$-gSY]!Ge*(/NCzb7 {ʠ:>}7anynn*q4|HCbB;"++u2TN a.X3ӘinM<>XA.R靎DՠS7VeY|X|w{ EE._*}7 tk5诓!';ל*o.Q+ۖޛZd;NNKq@0tiFbs~hl/f~6R i:H )ǐd3sʥ㭚aDeL#DXPvBs1kM5>TȾHic;Yr}9f<[jo]4jhn6ܭPО9z.D\uɊ ԛMyku⳼e)Nʮ,5=ʮ$`4@|a)DPZIp&D{8N>WYbCZG".'_e =a͋`ψu:^1dS]0/u+G^p:66 ZU}i-1u%Dv gwb=LQG&vB{+幩\%;~S&V\Zq7K>_l^.4 }Fxe|N)ղSy*^HLydR56#V:fM*M x>ShVH:3K9eDyrV}STof)/Sྫྷ] YSnKt3_V!02FWEB`E8w E†i!VN0 +laְ7VG$o4)=OWY#MnnX/+ . `:~oqӽ3~7NPY{2b zv` 갔\u|E Lw/-rw\hE(ku?Ի򮴾 fP>8)ͮ PNNQ;7нԣg{JLXͲR7 2ơUj%l,Mۋ Znn3ce񶵖0ս!g7v.[W(Rsdl3<ډ7x#j2K6ݮ~F&n4(;l v\M,5+=SLDiŎ P-Sı&m"KFoP7OkT2MkUu9V%A= *4tJR٫r@/Jd<Iqt"1-jl)Fe(g,6qtm | Omrp3n'6t?s{[ - !t_BM p҆fEqX1n[1rX="wwSnn,Y¹ZGࠎDzf+VpoD^N*Wis5˃&;frO Zm1 <5pHm5|^YX&:uuǺQݶ%ؽ9.S(rO]%)ZЋit<:L/NZ%k>e'"PV/AutC2FJ/_B8d?$Hޅ,&xzsā\&h5pz>q> \M.<}@(Fbts5sKJ {ve:Sb( beu]* ċ߬3Js|D}T~'"}u+y/ >й^%P]-DC*@ %x hV;:rǎ8"Ju72k2;ގs>9ٿ {: "m(ySjP+p/bxʼ@9wDRhGpVșN^!Y+jq-s̵Z`1wTfO}yj#OqR<&t 8Jjؾu(U,, —/qBG ?x3q6g(6yiQ7Jlْ}3'ty\uQ9#)/5*߀$BQKf>3x a;R(/ /KŘ'u fS;"]#'Iм{~)wBj/xlb6zKʫ^X*\lw=>)_6v\e],BVsk2.Z+nҽG0 A9L ҈KoMY o[E~os|Oh$MlLP U*#a$[/;k x7zjurqk زHv+%wy(Cv5$ [GUnJ@rSK̍wS2ݽi{!ENlJL&ٯl=ELjm9WmC)qZr$?N0ɲu#붒XVWx.St:4dx-A*6ɩg+Cf\/6VP<ganZ/ Q3ϔe-@907)eD:έ+0RhBvw(N;s{-8؋^Hd} X2` {g"9jB[ʣKEQ n6 jq]8>(-›E%$Pa4k7D]>Vp>-='H5Ck$m̽G9* & `'QI:jzGČQy5*d롦tA%ty&3EdȓecO`S)uD!KyQ,PnEJ q$=50z_jO{6j/HTf'z%Ȥz(tiԱ by:O)>IO֘ȧ0),w("{[BЯ/_ O,HK,Oj IR5⌾'bؾ+f䫂r3T.l6_$\XEN_jQ!:x}{ɸkz;y3R- (eQ2~|k㈮djE2rּ+6?薔kbLs?I}'({}@۹1f9pVsBsʋp/ U*c*R|B$e=P6* ;^EY}=2Zw[9 %(5 xuFj;4F+ z]X B!, mU܎ً3`NVvNK +{' H;G сv6>N =JAP λ5%P]`Z,S$6g^5,jh 5\l#C1b )]XaaW!Ѐ{!!(^K;3h,cֈu_9qWrt VWG:}5}ˊ4Lo'CHˢl=ІFeu5F7{]}imX&0KG]|02?dUS?3 5̪(DVV@,f\ Pmr՞}%C)ZEm].)rE2kt)}i7ID [,%6`EW*%U5c$j+a*> [MRlc=:Qϰ"%8/xt;j2u+󐀮*sZ˦xahĂ}Yٿ)ߔm'uqe7g7DtJ[?y "N.ȻN󬠜RYlltjVƶfYOYe rJBQpJ~Wb,$ua([9udZXDºe k`{N}`­ѽpDTGLv4X/Nj_w2Sj^ν <}8s'h +Xh/vqYRu05stkn/jW5mW_,j[ev )R9Y e~)^˦Ȩ7 [!fm[O}]w D}^g۪Qctbn=~5מaKm創^yϼ׵(DWTc#*n^}lDWC9TTk?؋AF`:TQA "սeTvP}s5H^ͨ^e]tyĞЈXZ,g!Ap״AZ'd=v-4ؼc](PYB!A엎Dn6_,X #@22Kywv/C5Sr7dtYL]"B!X%S*)KE!'md TZETԖN&8df17sdčW}O%ˮ;8cgz_h9߀oXx >o>}6߁Gne OLNw ̾eIvw1XzRć?.?Ff!4 e_7N;,s`.zݢI&KcYGz;H7ZY^eϤ❞0)m"X n#r6 1 M8eS+d#!;DD!`( ŀPK Ԓ JzUy| Z|iM%17P?#y PxD 2qaPgOھbL`0ƻN9t)s= ^@7|nbP-{s㖯*~@QrC"h>APV? 8rOE/Ÿ=,,ل!RkUk/&*lp#r$k2 py 8"@vPTOC/"&1TEi)UgT頨\;*e?G?FxxS_ |+4AM'| Pظ1 @_ t A٢6LchvߺRR _E\ _؛.L:f#A;~z\AAy f9}]NJD)AD=[}9EOES7slh++Z" a4AA)~y)X `+D<0"/)F7[O`SNWy+M {I~d'hn{oU9tJ0gY RA/F{ ڟZLp:>h{,` *xgk>VW@Ivɹ-5pҧH3_x`b'q? ]iKI(s 8AŞg/pgyܾ8HDMչ@OADubCUpe>@qA4uRga9sGmW9 5gL80aJ؊ 6x;L!w]4e鿈MC`e~4ngie ۙ~gl[N jWn{Q U ɪU⏒˔|;K`jlgķӆ(72?rTaTN_Fk0<V9ϓ`OԚk*QUXg{N ܎ZΗ_G['AMlNcUMU)*Ŵ}MMԠG5nV:(XN*s5b3'댃n7: \y 8fMt-gF,F{=׭+ rtq ,}f8ڠxi^ԍ!v0{0>޷,}832zgtfYݹiL,O $?{Kp[\/V2%?>~dt8D= Ou>}%t>s0!}Xwut  @ݟH8X*WB 0 |2Yg =X  >Oy>:P, Ӱa@_F}S!wVٱ }͂^v4upCdmF@zM0!9@j҃|#43bY 5!3B#bـ%]\u RT8*/.up![(zOQyϲp`;b6̎'(-C[hn6?4QXX.qpAB^*dF%ώpu`U:+ks ӉGA ӄ*b6̱Xu݈bP]T?Lf#3!:P Q7[qf Kmq8_͍R/.^#CDjv;ӑ\Ȋ4P\ "wg+oQ:$Q\ɢF52*ز%8[39+kŷXZ,ϖkU™%>7͊.tBWwtp8Qwڦi$a箸x@|/[\&^<,ľ  Jz)z@d&\EII1eġ /C {R}shiAE, -{QDsp=|0zm;XpEMQȧӀcŞfDXn4Bphմ(=BphROid[his, S| SgO* WRTPOTkE%7?-󠲬svŕb#1 r3Ze[`*ֈ`hٱfpK,}K<]F0=N۴s~sskL=R{1D2;Mٝp1Nxƿ=IAn5*yxM={J Z]D stІ](ꮂ%cuVIr-ꉢzYh4wcx@!J8CJ'D}xi0,}Kv+ [,@7+Q Z0@/cxAoFf- 85NO$hP.?8\_tVծn)eY`ts4G?@S"{JAALA*1GF=%?lU]f ϹgόcȚQgE؟"uT(JC=Ms{NGAãCpN1)y^a,[jEĮ!t")>UNMϠtM䧗Xh+fj4I&MEV.;Tn ~/5 H*tӄg1'UICH{NLZDU4>Z,^%[& mq%H* }\  B!!+o<N3^F ;f©z[pƑ[9o[m/z"W7{5vNB|IŖ\m~.Z%CM  cޱ2 k, 2ĹٌSbuS!zuM4 ěpHٔG$ƨ5ĥQ %r BLnm_(9@5~m~ŗ 4˧zTnڮ"=E@YJ }3~EF լ/6{WYL-V?Q{t&G+wlꑥCG|97&xקּe!L;K{e{$T|~yN}{רpVa{x٫ă't|  ??rc,/i=q.sܔo8mbD%Z)#h^o }"L>PX+)\/t}I$ກę7oƸNFq5 Z yŸqӺ*|@9n!L[|*jLuӉq[(fB^}0R_s#ZƸS y޴ܦ1jљMg-;.A@ex1Nsr oA{0NYj?{S&8Ϛw0rIPD{P'C(8%LZw5HVaP6TQ5?p}pC&2 )nR .B/iDƁ\irehh"<5-#05TDę tl0 zewTȰU;h˔_R%Azhi&x7u ުKDQgs"_+i:j)aJfyQw6Y])4/3MFž̀S.ZUCgOqm@ԙ&YƃězWtnSS,N?ɏVhf&am+FfC1:61ef`k`]t!g"tD71 /ɘهlrĂ#\ٕb<+i4Dh)I&P'eP*G*V؎U\]25ubEUY=,\]?c 8rZqa)ȭ&m/^ gnE0Ё:DgDRN_~-Y~5"ctYS~^}1zWfZP2HA4I;jy_@dҍjyIeWot )],xAGJlIµr ?oEpb|@MJ5q,vt=;j5U`|-xN( cL AG#B0JS9drڵv4eSW>f 8=_0f1.mWFlO7״Ojr[^&9#&L1k,r/F"/͸mbw-RkmfOkSڑ/@ < 2zJa'nAw"fqh2A!``@*S+wHu1t xyH?U4uCD-ѥa-T碰v#^I!~a~ q`ڟ'<{73;g!]5wĮ;xGD@9}jGƚEi1vѥxvD IFA0zyXo&Nn^XYsgsUyh:2#εAqMD HIQV7< (<@Ap l1jD|&pf</8>cГy"*)P49*Z'B2J_,=NWG߁gf2mّuEHII7[ epNF t,SVaSb3n(񲉺q#jܴ0,;Ry4;n) -l`&^ie5ۃE 3Apo';TV|mOs!ѮQntx,N:<| M,%ۏF^&`N)U믩a.Ι)r/(s5SlAUA~WIТXZu4s qIWߺQޞ i£s/ps|I$݂/ԤWiL}Y)X0Zfo^'5:u,g6Jr|siiA0h^mB-0ozy47nnn(4";Ɂ2ZltZ=&$k`AF09B c="#Ea:hu6@L HxFKt2{ylk#M^?Q f!('I@t>fEӛċ7mST)X 1hC1*8Q܎ek5u>_)7M-poDJc#ؤ9~XD(H˔us5ߖRMy p)ZK魥xhclneyM9ay5EE!d5tX5A#Ա˜$Nl.1NJ/w0g$h=9,%UAC~D& 2߰Fý~5M9)Ez* f/[6ZG 4I[7y~F/lT|G1#' "ԍB o]ztt<:y'GVçt\gsw/غT5idӫm1n!sB;hf 2jVV֭dfAh6A!4ā١Ag+iznu?2;O/Fwhf ڲfjb2ҼQZЊw%c NU_7Wn$wb/A㋩4h@›񍛭Ti9Ryx t k@t[4y7\p砢zvuw]].K檬YrV;T.t&5tzҖZ!dh5ɛ&x{ᣵJW2B0[h@+s:[5na;c}@ +wPm$ss7}8ѓs4AՄi__=kfq' h=hd&pJKgt*`i3f*wgyV-L_0\;:$c|Etq$PN q@ dȉ0(A2EXs/>]y=x.ur8s6>U>in0V`xtxlxx__3za2c4cΦ=д2 ^,]