diff --git a/scapy/contrib/ibeacon.py b/scapy/contrib/ibeacon.py index af61cbe3d40..0326f915ef3 100644 --- a/scapy/contrib/ibeacon.py +++ b/scapy/contrib/ibeacon.py @@ -102,5 +102,5 @@ class IBeacon_Data(Packet): bind_layers(EIR_Manufacturer_Specific_Data, Apple_BLE_Frame, - company_id=APPLE_MFG) + company_identifier=APPLE_MFG) bind_layers(Apple_BLE_Submessage, IBeacon_Data, subtype=2) diff --git a/scapy/data.py b/scapy/data.py index a1a8e3c8316..b6bff82e9ab 100644 --- a/scapy/data.py +++ b/scapy/data.py @@ -574,6 +574,14 @@ def _process_data(fdesc): return manufdb +@scapy_data_cache("bluetoothids") +def load_bluetoothids(filename=None): + # type: (Optional[str]) -> Dict[int, str] + """Load Bluetooth IDs into the cache""" + from scapy.libs.bluetoothids import DATA + return cast(Dict[int, str], DATA) + + def select_path(directories, filename): # type: (List[str], str) -> Optional[str] """Find filename among several directories""" @@ -613,6 +621,8 @@ def select_path(directories, filename): ) ) +BLUETOOTH_CORE_COMPANY_IDENTIFIERS = load_bluetoothids() + ##################### # knowledge bases # diff --git a/scapy/layers/bluetooth.py b/scapy/layers/bluetooth.py index 6885fb4f607..3c53c04f3e9 100644 --- a/scapy/layers/bluetooth.py +++ b/scapy/layers/bluetooth.py @@ -21,7 +21,8 @@ from scapy.data import ( DLT_BLUETOOTH_HCI_H4, DLT_BLUETOOTH_HCI_H4_WITH_PHDR, - DLT_BLUETOOTH_LINUX_MONITOR + DLT_BLUETOOTH_LINUX_MONITOR, + BLUETOOTH_CORE_COMPANY_IDENTIFIERS ) from scapy.packet import bind_layers, Packet from scapy.fields import ( @@ -266,6 +267,24 @@ class HCI_PHDR_Hdr(Packet): 'extended_features', ] +_bluetooth_core_specification_versions = { + 0x00: '1.0b', + 0x01: '1.1', + 0x02: '1.2', + 0x03: '2.0+EDR', + 0x04: '2.1+EDR', + 0x05: '3.0+HS', + 0x06: '4.0', + 0x07: '4.1', + 0x08: '4.2', + 0x09: '5.0', + 0x0a: '5.1', + 0x0b: '5.2', + 0x0c: '5.3', + 0x0d: '5.4', + 0x0e: '6.0', +} + class HCI_Hdr(Packet): name = "HCI header" @@ -1153,9 +1172,13 @@ class EIR_PeripheralConnectionIntervalRange(EIR_Element): class EIR_Manufacturer_Specific_Data(EIR_Element): name = "EIR Manufacturer Specific Data" + deprecated_fields = { + "company_id": ("company_identifier", "2.6.2"), + } fields_desc = [ # https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers - XLEShortField("company_id", None), + LEShortEnumField("company_identifier", None, + BLUETOOTH_CORE_COMPANY_IDENTIFIERS), ] registered_magic_payloads = {} @@ -2445,10 +2468,10 @@ class HCI_Cmd_Complete_Read_Local_Version_Information(Packet): """ name = 'Read Local Version Information' fields_desc = [ - ByteField('hci_version', 0), + ByteEnumField('hci_version', 0, _bluetooth_core_specification_versions), LEShortField('hci_subversion', 0), - ByteField('lmp_version', 0), - LEShortField('company_identifier', 0), + ByteEnumField('lmp_version', 0, _bluetooth_core_specification_versions), + LEShortEnumField('company_identifier', 0, BLUETOOTH_CORE_COMPANY_IDENTIFIERS), LEShortField('lmp_subversion', 0)] diff --git a/scapy/libs/bluetoothids.py b/scapy/libs/bluetoothids.py new file mode 100644 index 00000000000..f6ca58508a7 --- /dev/null +++ b/scapy/libs/bluetoothids.py @@ -0,0 +1,766 @@ +# SPDX-License-Identifier: GPL-2.0-only +# This file is part of Scapy +# See https://scapy.net/ for more information + +""" +# Bluetooth Core Company Identifiers +# +# This file was generated. Its canonical location is +# https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml +""" + +# To quote Python's get-pip: + +# Hi There! +# +# You may be wondering what this giant blob of binary data here is, you might +# even be worried that we're up to something nefarious (good for you for being +# paranoid!). This is a base85 encoding of a zip file, this zip file contains +# a copy of the Bluetooth SID listing of all company identifiers. + +# This file is automatically generated using +# scapy/tools/generate_bluetooth.py + +import gzip +import json +from base64 import b85decode + +def _d(x: str) -> str: + return { + int(x): y + for x, y in json.loads(gzip.decompress( + b85decode(x.replace("\n", "")) + ).decode()).items() + } + + +DATA = _d(""" +ABzY8*hqM20{@J?TX)+y(k}d0xaMMZvfkLQj+V3UD2bLB9jJ(s9cRrAEx{IA6shLHvGRRCepjIiBp}) +8Su@?~@FcPT6zZwNQ~%$;+PAlzfBj$QU-vSXY2u8sv^+X~vbp}(7Y9$a@uU~a!b@IcB19&W7iT&h@aY +zwo_N!#w{(VCx!E5?o)==XOXS{hM|@QiuUci|cbYka^l*%llapU(*Qx%M243JEj?SGd96!$@5j)e>kk +0nL;@MH0K1Fa;CVOWn^CFW^Wr43eNV6k9r+1524n$I9=|N<|8K?0UU$}pLuP^E0C86Bx$_Vb=Maj!9g +)8PFO+?|W@YT~eeUT!ECtrV=7F&JijS@wapPZS9@)9187dXZhUA*G4{2Lz~ii6yw$+p}S@YSDw$mk%F +&lk5S;RkasT(|?zS$Tu;JeUR}-wT~ji`C<2Lkwytmg#;ELO94mZ27nvgT9b|;g^O-H9~_-L`pi<2c0f +{T8+va2K6Z|oKH#=zjtZ*S>1DSwHl(wG)|;3J#N&{{PbrtZ#i@4b7ygX6&3JQ; +4(7F`BM|OH@*Dnf!tyAxFfEhqxg6lb_z +pDyaw1MK%v|$PB<^78&2d!D<#D5=?heO8=Pr{X*~TYllU~=)RniS!VannJO*1tdd``)7kqPfLDU6@&D +rw$jBHu+v5Rc1;Nn#+U%<+d`3^{d`oQ6Uiod~`WC4V#?ccX>z6NNEMNzCapJfRSAEMG*j5imR-$)>BM +xryYfFn?4@%pZF0Y8lM^iJpz{XY?dNQ=Ie~=j)XqXOT#f*zsbqhsBB70p|u^p=3FhMYP%B?An&Nyg_o +_-=#dlvMHSK1X|^Az9hR!$o)*d?22E}32sf&SDN?rCl==SeF{sO<7dO!RYNlMgSj(W2I~b#c|PEC9W^ +ZOeBcbm{@it@{>)!_yed0taHit-DG|;(bPYju)aaL-h +(#>ojM5cHMI)pbgipL58=x7Yo+n1F0Zdv12ru{fX*~}%AHT+$!Dh;-HsZUAeK*AR8N|Y8jF$Vj6HX{8 +V}s%nN~97qNG1|7mKOe51lZ$TRq_Ai=}_>uDmljDFLsRpyif>z&_Vj0v?MfE_NGS?8bmu(rVw5L}Rf< +ar+6(6a2g)H&;cQw8zq)!RywutgS@-ElN^&=(9Pa+e)nc>DpT?gL|0S%b&GC+DeL{NOBQn&H`q4CBe3 +mpqqsA(LYx3EhW}z=y+3auv+ +?qiwl%ELvQB8=z4q^Ea^e`{us8DU}vy;W5*5on3C$iUBFD7JN>CYpYRDK)Q9p-g64tE)DKqG@ADNb+2 +~i4ZBa99lxEoQStZ=>ot9e48E($?F5Xh|MY97I+1k#BV7#&O@5*bK_&0WO}49dxzpxZs3cnDb%D1>HqMi&dP@u +9$q^htkoHaIT)hcN$dg3v`I=~iur$YDVnog?<;|)7%0`#W#Y}F0p^h|{W} +{;*yPxUb@=t56(YE&5e)=c}Xkotmki6+P$W%_Z64Y(2l^Sd(h-@dKEi4sfBA*!y4JF!Puyp6n>~3^jO +|9NnCBp%#Wsu8y2F6NgSY2#%^}Z@`elH~~r96iL$K7=(a`}=NQ@!_933Dl8neS{<*pyB0tCH;~6&a}E +h)q3fZTG$^(N4k42E}QrD_Q?o$J)&LUI^RAqosw+?X34ziTK{1KTe&QyIMIjZ|K{*aqlbbUh*kV?GIB +@9*w+#;{q^{o5(t=#5*e40LBReqcD@3EESbjvQ3ZPtrGlPEl=Z7x3|``nR|f;yWNNLrgy6({!>AaaEF +Klr|1&omMgQh){3TGWZbWYVlLj6;_%w6R9d)4xXuL&B6^AtLC7TfeDQ^3?79R04tAiKyM%@8^kMu!W5fOoI_ugV!ya1$Sq_sYhIfC?z +$Vz%bBj>k7>qv0L!@^+)i!jG@ZBIw^Lz_yi4Q6%J-`fu`S)hL93i$4z+ha6EBG^xUY?@zpm&HK;j^K@ +QE@jZ3;>LTJ)C@@65=6_Lkdq +5|!nf!cB^QdVN9YSJuRE|FhVIHWhLRB2&vFCvpeUr{zJc!IQ!O&_+##&4pe%5xoqh%&NME4D^Z+IstP +NX&gud<~L_;TK*U@o8U+d&Kjq#fYU@OGJagQONR)Xq}2QfS}Ki3XmT!+e+5Xp-HCX^N|R+pP*k1!f+U +!ut1MQcIVf+Q3cuDr)3p!+l{jHO9fY>WU+!Q9`5e$}{Z56VP}iyu}J9Kw8kL2m`vpvlW$QDSNtTccN4 +YqS3Wv(tPD^4`owXhPM)N3aR~B})Oo&iJK0r-yRKc&&ODLRP`l56$hum^uW +jzWWm}9p>H-;{EVCyDMC)aVzf!4C(r_swFwZ00+GIo_zk^jTN! +=u(X^Vubio~`nrj4>++>@HUpegLZU#!k(0~M(7Eu!AYHKjWWddbmxCuFhn<#n8QYTpRQ8MVoRzBhEF_ +$l)rNJ&s!30&iPjyC#aG&l|I45KC4DIN9Snyv!$r!&*Tepm&M7~+SLXwKmrhel?N+dJ?{%mNqDkD5Jg +1Pj&($a)<{4E+bV5Ha2Cxo+%WCH5|CT%qb&6glpV9KEMH`D=g!IP>nL>wyD~OlNYqy!3=*X@W7cV7F +TnWAdmkY)Wr`Iwi~~$WE}onUID^$);#wJ_P78nl`k3%lGxZM3FLeO$-d+zz6P2ITLVa5I^Xz(2m(Dz) +J(P52=CzLRK^jTH-vTWVb;CC+GhRLU>opQl!`C-j`A1-b=_2n^rP)`GktC%#ed6K}l8SCMO;E;bkyPmrJO4HmB>bdjogYfL}T{n8GxFNJNE+wuxN19G=9fh*Snt;&M1(+}m1`|W91c64u#7Dql +{6dp(wb;ZMmwJF1g<%9keVo5E@9f1HYUa~~eU{}BUNz!Fv~!Q$1uDECn1<$f6Wp0zudc_})_So%jPQ? +pkOvrko42e5)hU|- +j})C~ikxRBPQB%A|Sa8jI%(r73A +x-yy$xPitkZ4-IDysxO1>6XHMuZC*I2 +E`_;&e(;Rjx&^@q*Q`;eULr^OW*7Msh{Rs0w6aja0X?pSsU9XnivLv7BK-6qbGVwl)y9t#c)EAz{on^5Aw?0zhbSw|=j ++q~&{j`5A0tobFZo<3pTDv#jZK}tVKvl`P~r;i{Chjta6`}ta97AKptNwjL}VZ-wj_k1MhG#5ptg;J1 +e0Kwc?2U;z)QyYW*5PGJ^!a{G@dt^1M^J$0t28xjkZ@BO@}Azr?6+g%q8e^6=zi*fJ$D3qQbemSUmXQ +*f>Fg6xl(7eDn0V_=U?WuQrD`=3SmJ)c}l@4Qhb-Js<9vzB6`y`|h*~3$hS75EX+dz1j&tKo{$afEFN +=L-c0v6V+@Y;1$dtcu5e@n)_fTf|WQPdUN>MgYo#2m=fVmLGf3Oj62&CP`@_4N0xiNbQk}aZQvs;>|^o +Bs#T)Y*^J!G9~QJi@EEdY6h3(p%mGjC-?YW0N_9Iaf+a?glqC|>}h#(!AW1l$0RZe{NLl<^jKiptY?L +$GZYI{WCaXx{@M;(UrZ1Rv`V{Aa$FT&4ahr~*GX1j_D$QNVvEq<@EI=gOO4p!b_!DNA0HwzedI)(6p( +J;6tnJim6Et*h>ByXSmgdwp+U7%NS7G#^bb@(ls6Dh@F8M?NK7n4#|*jyy?N_0yF10}UwjMomx*$1Ht +K4Lv{@483GmKhw9fotd*NZU}Bw1EvfdSS^^Qq8F|`sR@a>TdyjP{|4bNOSzPW6R#+Q=~Ld=b53$ZvhT3tdPM(`$On@f@pw%x1c3Vvz;+LXyJRYlNdYr`15;qm~e)B5bjOs>Q +6WI=$GR%FD(>rT*6*k5dXGx2H{|JiPX-Y+O_oF&s~%%N7+B#r$%sA7MQ(aTjO0qeM5l>FLzP75rySj? +-J-+dfo&_88L0WOLPhF?S@%;wDDLyoCN+Jz=+5&-SCJq)G?@fNe~2-chg7M*6Pf!j|EIn@F7h$u-|?{ +NDQkI-5jRc?GhXe5#@wyYm$Dlw83Y4b#TBedKQ4yz&t(}FUp1r7#y@I)T^D)A^?ezHxED!k6l1-MjWLvp2cX*Nf)Ftc|ltsRi$>|S4nl2$aFpNeVt= +2|fw`+Ubre(5`A7a!lfx6nznTM50?}cafW0$bqD*c_-$$4GEbMAUI4whH$6z0MgCyRc+rhkDg?HpkRf +Jx{?uNVyiJ22nNougcyhx8Rs7t+~h(O>e5v%OAr_H~!g;KUud&YkC<%Zbq74AX=~q!gYxKRF|DF!rX? +cd~uEgb+trlx%kLc&^|+RXG=SfUip+66~g=_;7v^a)gmzgCO*NXZXgtgdqE#w}7=8@-a0Ali+)RnV#o +)GTpYBY92;Pxx;iYa^Lwn(%*0novU66y2>zVyMzb>jHwExN9_qkxX=T{$WkQuH(B@BPB?RyD_e84Ig# +t$y@Qv&(w5zUT?ktSHWwJc*_b+4Co2}1?Fk0Gt3R28m8m_jga$`muoxRw24O-M_E)q)VPkkZ^3eoqqu^hzWV{|&@AzuN|12;FuQt)iZhtdWef-l)P2`d?-C%KxuB<6Tm%X4L~Ml%s3hXjZ{BTksOe4SRG;yOHzkj +eNY7lY9Qp1tM5JF)iS5-WFixxzVO4tF36mH80IB&_iYcdA0xLK#qRSM!E@Mac&Ks>UwbJ{(_7|8=XKE +AzlEc5sV`V=RY`lY|zK>-U0My1lU(>S`|I!1K1kH!q-n7APmtf<=PG<2jO=CW&qzq%^@zZVZC^~_~wI +OPPhko)Z^C%WDmEH?}cL@xR#apBz&LwDvCEzuc@5CB{z~RbYt!IwA9{PbL5+z!UeiZ4E50PtrHQ$>&3N8H+2aAd4R}@l#kd3vDf#e7?}KUB6SG$!Plu`AEkjRozWrC=gRC?6l0Qxo?aaSdGqu& +eHPilDJUkkF>meE2GQnXnsf-%sZ?I~!X4IONVjlSCETC$!6v`VONPG3O7tBtSuqlhSVu_}22{I7yDA1%)gp|b(TW6j5r1L6Aa+WD65l3az?>kX|#K&%R+j1l6Omp{U5sz +Z=#?gf)zjL!dX<;~pCOunvTksX3s<1!(Cx{fX4+wqLhvgmQtGJ|z`s->?{)r-iK)7l7mb$V=;!d5VGA +KsZp1fTi>#P{X|un?d4I5{6(cL}E@CLO>Xb1*`3!Be5L +4`{oBQ5}%m!O(6Uf=8kEaOJI+!ElLDifRwlMs9~-(n{n->V)3;{@Nk5cvMRK3AZ_Ie2u0Z^zLXsFHQkK<)3*iF;t3x;nFvfNk{jLh%Is};5dkV-S6 +G|)9V@@FH-oN)%;n6pD;m*yuO?U}RX5G>{ed~7N6 +UGx+S=ML;~XBNH4jrIG4-skI?XV$xR2;RuFfN_y&P6nv8Dhb?pQTA-RdZ>iUTx^7LCU78&%w^9=2V}Y +fs6fX!AQq=NrsOKeVpyb`eP!0o?-x!#m~t9$hhWY$P2md63b$S7d=1I*aqiB7A5QqcEW`GVSzdy;&n% +tl=zv!QYj_czRNh=&C18o7&2H}aML3g4S{{lHqZ)tdxJy{SXZ~2zPuwA(6BbbcgGy7lQbvS!`coJ7)j +oX2+*>^a%L%OJC8enVQ1{(;tD-uDiN@#*k()hkQ&mIX~hArYadK4huo;^CCbG2e)oXu +GbI*Mg!QEq-KF9dXAB0Y_$YT_V+I{M1dkSQVPq(CLPanKUtps&Gzx*Ch0LvVC398~Q1R$)D!lR6ZiIVC|nv^5-oM9>}8hup-n8F +Y8B}e;LZdVU?8y5Jh&fUReqIK7A +2!UaO-@H?#>hP1jhp>YB-qZ30eD82_9hVCcJ;frQQi{c`ot5RjT;3Q~wZ0!nwb_N_LdD%kDw;= +P&DCR9bwMym`=7wa@7FrPM4m~MQCs$4{7nX_#IQ$PH%8>mffI`v21e6%_bidCwhO(<%e$XCpB-9}qyk +)roGPmcUg%+P)~aAQ%aJoOZCs`Cz=XEbBJm +rfKwckj`oci|Qy!MB-ICQY{L?QWQ-xumsGW=uiXRHo+=)`-)qoRXOJ#kFnk!%p2I(l*UxTWQ-Fbt!K!BC__U_w|2Mz%W|cm7#3{Vc{v+r$=cDpTO0f7 +p@eyLx2?r3I$kX6i2n +SQ6Ir6AYlCj|e%rdw5<5mSC)JXmE9^vWVV~qP7V=g~upkRn*bd9x$^#tF-MiIv49-CF}&2=grP7Y=t*Wvtp*oAOQ|a4s4^@8Om@^BZELL&mg?icQuUeR* +X3z#F5!yyC5F3ebHCj-sy1d|k`-XYIPU8He`f>dTLe1kLB_e36M?3Q=Pf)oEf^xIcH9+tQWFX6*I8~{Z`S(OqzP>TMg1 +Uj`caH(8uc@vvrMJIolQn&sQ{z>hXu6T%u%5#)s*4QhpEh^A{d2+u}A=ChK9*X=LFFi!Zm~YYiGV*`YQ9$pYSQFiHTB4UR4c4FyL5ZkVj}1>b{`)w-c)A-m^%hxo`vU*lyTy0pCacQR@o~51z*##DtRB%@+JK^`>#!a +XyuNavA6b?)m}$1XtsI4Yz)iKW2zWJLhb>fgvG+9*CN55;Q9Fq3-pncN!39Wh*xUijYM|9iCInE7U7~ +*B!9s-+?Bgr$J$#2f&gdSqpfop=Wl7&iWjiz`(XFT;vJHQ79pYgV95D|=9F!HwTYDw#6GU)*4}1Y+bz +OFOD_Z)EVP5y#unK6%9+{n*=9Sl-i+cV#~efYdR?>#2B|{79NA{#t+ugj_k4@+&_fzOJ4C&vYx`SVxF +yj;>_ip2fOWTqMlP7``Hqw%pP@}Y`pr&Pqri$)REyBicpNyN##JK6KPEW0n4Y3MLNu>Tojh_jOCoMq#A=6c#4>J8)@War#go(UEEQue{n +LHQ#5JE+uM)Q;_c!I^?^SentEka2-8pD^EvZYxFxjYaS{M*Ulkjo!tN0*5jHs#?-WwgBP)MBkGtgTn* +FzboqRp@!<4qRm1s8$f+tYrk_tXxiD^QZWKK1*r?ek?ymLeYc`VldGPah}v+N>IuAuLuX*Jj|JX{#*f +i@(R4~$ZmCX{Iv?U^YL4*qdJQ7DwI}->uAAnna_iJK1i;}j!&X~j{vNHnn)%ND-->?a=!d< +%mZJQD;f?#8Bxn8Z$aG>oJY<2A1okf~s{&e!EdB8VvWKg`GWemML6S)cuj23IX6EwR=t{SC<;X$_EYc +udwQHc6zj~5d9E5MA(N}1yiCL%XyDuJ>Y`egb(CUsRaaO#i7D{`qDmII-*nfrb1t&BNC0PP28elREMi +)q|T7=o=tCf=@+mMM1dDo!*( +px|EEZRq->ecnOcqZ?f?!F-64Xd4+5fuL +DGdc&j$}2$DVj_*}( +KP70Q#*XvBDCg?e*XO71dCN*YMYNd;w=tQi8nBcEatx!VYLSlqhsnH6C7Iv)xJw)?f`5Bco2&@&#IF@ +NN#8mhI0?fCu2@>0&X++2NG|+2me|PE_5k2yKQRi^?bg?Nu^rPHaHt714I2X$+1P?47SCgj}FVLbJ)V +^PfY@2%M^=Ja5B>dW*F&B0%iRmn4x|P?i^(@hI~>8sQ9=napd!59kv|bGs&sS!}?K*uqZb(Wv9d+B&qWC}R +Up<}a;{9o8Y-vrVhKKhEmIJW8S4&uLidl3SOkpL=y_HUXZgQd3)L;GNU_St<1A5f*YvXsw-U1E%*JZ( +Vlb{qPz)qA25MD2@r~L9vDu{J&D(7Mhz`w?+O+ru)?j%6N;ky387P)W%MDCGrQ4>h9es2yci4?0d`Mcp%MDZJ>~kZnp~+l=bI%(&x=urTS(}8dmOCL1wE5~y$5dJbs(#9(P^?Jx>>xZf +miO{^g^H{jyG*H;T1Uxc9S%G@F4iPaq+o<=O;W-`{vlQQ^6!U`nz{fg%Jy@az&Zd}fz!z75z!uUI3tS?v5x)Q|E^9MHE6g=2(qw<%Gm~^)R)17A3!svFN`mg=%&*YYP~P0AYTCSy|$K;C*rW5n|&PzK +$HPb1>?WjlB|oo9k6G*z^$IkoMO)gOCrHVwjEwOREG69{morxCjMR5cqN%BK$KBmVVzga8a|(YnOhuq +VhL~f-q0tn@)#b=#S>+DN=&jLA_X|_X+L{;waDMvqYNC6H+g+44A@V*|+^KT9k}?;7yV=m!>&GfM?~8 +6p@1sUYi7Z<_J=Lo0^heiL>-nR?GJi3?}orRF9kH2f0boEok|BK0&K=C$dAjPm?RMur3VzCPAD2v`IL|dlM*PUVBCkT$5l8*3mhSGB4AoDE+T0ikbv@CSaBZ0esGakCJnA1K29n&fkV~+-(`F!siZYv_ut_N9r`VStx?N#rnD0#(XCz-D%Ck6g-q!PrsRRL^=X#Jv1hdyOZ8f=X)VHnWC#AVp1u*zOln10L$)8If-R76KmbJgs%sFwLn8U$h7EdH>*xj|S4=EgD +@&-f5?NF0)MrW&HuOSQqE<#FP9XJy0<0w^Q#8lirz_xc7Qkvmv8MUh&+-PClhU;#$2xW3sZV*e&pVx< +hc8-y=LX@gN# +bybtf7{r9Z73hR2d%HmhV)os&hns-I5*I<8s>M51gMb3sTG7>j5n`3m(_6%f(vde(;BoiZaO+3^%`y9-I9~Q`dmQe@)Y +qU>}OFCpt&e9?L74OoRZlK!rv8G^R9kh!v0;S5PNex2oE5Oj=_c9V7@%e!SDN^F3~+4>b +xJK>;%*3_H@y?<9|1vO+h_>7&swpQ>*hdtOj!>DCUU*X^iXfBUpe-85vzDov51PoRM>!4S_ezq&TOdgXAK +F*ywb0-!8CWT_#=vxR9W)lRT~2gn&07r|aYjwhV7vfxxznhb9Qhp81f}wKd60EX%_`c^woHS7!vo +9>@6k~?m5U`-t9`0?gKdVB24RUuK(zdKMh;o}9NP>CjW!qTZ@KWw*kZUcXzVVp`pdwd8Y-ROf*WO9VK +i}Qv~`a{*z)^PE=@r(?;RRbfPLq-4kx&9@6Yd$@>SPAxbvYdw7{UqM?^r4v&Kw*@>eSVXjC@{WvHffP +9FqP>|}YM{1{`?C%BPpw@MfMM%r4donoWKaVh1Hcfo8q6=XtcIT7mcho1gB6{8|OpYy2L{@?LKS}#iT +6Xv^-UDhD5F`eR#Isv!($yU{IB)4NZjh0Rs+NVy=ESU{vJ2=IllbxK^Fad}m=mX_mH%glt1R=nBhBrs +w)<-LqUbvla}3l`&@tMtDi_K#c2rn0qOKHbEkbffSXeuR&m8>AAy4EDCeN$zz4+8iW$Y{tP?U`ObuWR1I#Qk#bsE?1J6W`p +*5I%K>4%I_ego(w~ +x`N|MYIlEuSzc{u5gP|^wr#fe%%bE?CN`J#Ia?cz2HZ(dYlHrJ^EHTx#9yrf(Bh-V;2ht=B^*aTL`)p +3Kk=5qR&)UISg{>AM?v&xvwJSh!#(ZwLalw?aKmyt!^Nbzl4pr@F)vwU{Qz5cF+5_E(h#A}=X^J~fU; +<&!2`_mkyou6IHQD!`3^sLzI=jRylRFB7m)svd5#5pmj%#p1)SscrJhx?Q-8lkb%pPqDVo*rDNcM +|UPkHTz_a_vn0G +5$PWZCJnd|KAT?n&`gxW37Q%vA=kN4}dLRii3>2CKwV6XeH2dL> +fTur*B@g-Qx|e?*+{-bq^``*Wy-eba3~9E8Kuj&^p|VewwbA(n62Q4H}&s4K19(Wm`PSj44i0jn)e31 ++5xXB!X9@RKmm0z#gZQ5mWK9Oy=0(BI2_jc%H3bVs>f$#)x+90CC~)G&Ii3x;96R>QL;a!s)*`>gc`1 +)(oNr+4jmnCs1`M$4p+WmYQQHZ(ENNp>DxME<4XB0tBr>c$O=9S7Q+MkE$i{%9IfxH?%Lcp7x0E#ceJ +!XJ6uF=P9gS|!>9F?KtwF#Cs{bba8?-#gd4sMYbuumnD1r|LACZ;y)*jJSZ+VotOO^piB~~gGR71Uu#+{eJ4~iA?a)Vi{aj +KdEAVUn;#n09XX|sj4b~IetWfLvebO`kx$?z +qZxc(JJfX2QC9dM4FVRNtxRN~nWJMe;DgNiML;T7CPD<6z&=L(~@xCcJv7ZgHpA?XBX4xw +W~%>`oSn+Up{@r*+G01G=td;)2Qvje7Ena&kRkZV3_n^!-J(G;f({dp&pV6tCN3t=(@qvkPlT&eGK29 +3>Hf{qtL1OCyj0r^q=wSX6ga!*R>CGw2Z>M{#Wan68J-`{~nlU;gI9P2!3Tt8#5KX${U(RV)ZMNwwO% +W#a7pX&n99iwlBixPXZ+f-^jK%g6>-+vO^~B`DLE+16QtcFnCjyLMf1onU8Nqa>5urP;?s(r^{0Zw&m +Ey8>-+j~oYcm7u^Ekf$N^`L8-E0ub}fkSHOSF?KABb0)(zm)NZ)07$mW-Kp3e%vUA60$3=(3a^ZM;v& +mPq%Y{P3u3zRXC7z=Equl*>Gnk!T9knhbTN=?!|?}oiAWk-ho?FCsfa)UB(-_ +%Yof(-Y|Y|8L3RctZBP4K3P^1s04cM}gGmK=cV|-``>DQu|o!?&u)WJ}Hx=p0^6aEm`(jgrxDsu5n+9v +HS0S`$l@&jihT*WDBa2zQ{z8l-&Edi0JD(i(K+tGYJF!3W8uPDPUM+ZUSJaw+<<9Z6d! +6bBS^mR)>W#|pu)p&|Iqy&a;38(;Y01&_SmWC;G+h(pQEX`Wpfr7hi&(Z`ut)Vybf{*Asn;ywfot +SHVZf1`HHCJ|80UBrRWd=&3ABSY0i9h9WAl=%F&L_0P1}J%E0_SHl6PX~^Ib3Ljm27|5!HMsP&4fa9o +H+=}&2aEt>?=0IlySW}izNQ-xj9;0Fx8*Oc@?< +3seqa|s)u}O!6GIqj3vY?y1=Z5~!3(gY#>xs)u&v7I(<@347(yd39P|4jo^jt;oW$i+QNj&)O=#o02Q +jdgBRNsb+ngTe>;>VIR2`KvI?X?g7QRxw=xQF)u^j9A0vG09l9q^ +VWQD)vj^K*?YB;4UW65I`ytrQpyiXN-r5Zpk94K6`py(I0-sWm}Qe2z+Mr~dMcvJ6g9S~#+^elY@)aH +7eTaL4!$T}t6PW`}$;evcqWnUuzda%&!U>Q{}J`9!a7Nibt=(Bn7>*nCR>qS(m^P>->$d(7^P^M9y%r +m2GAKfLEHxANPFEIn1t(#}y$kMPD2_MkY*ogtCW=EQZWpVT7$Hh5BW#?5FB2)E=(^SmarfTIl^d45G2 +2U?0fZi_0g&0uU9H$ATFNVkGGdJK{FY*&IS4}hhvt(o8J-Dvvu-_9h8w*p&d;UoTCgYzC^km(idW-!G +gQ3&&A2&kQq9N9S9)@3yyp`tdSdW0&t8fg(FMR};zL2feu!qqg*!u#6AbY6uF7ttDx$|65zRJyzp<+C +s#bB{B%+>xSboR5Y8X)FhZsm^;hI^}tb;1pl&MQ6&xNy_VR2mU2m?h)*m1bue}d!xG^dCu%^bbaRD?A +>rNXI>&h`x|+w&yz8Y{jWz@WL~~vcOpJSEV{XZj`CG*OW4#6UmWh)Ufs~qki}Ui0Wc#c@9z(MJq^Myv +5k=|SL+4>Dia}&GC5yP1XQa12OE5S;9l)@nL95_L|&&<>*w@zYoPR4NCUC=7^eh7e?8+PMC=S-B@%Oq +beT{C^#_Iv&bNZ%BSs7Jd)jt)3NZKI%H$9FgrE#UWb_7FnPqU2(xlz>)ENW3oVoLPwbr}L5%si3`VWI +M}7=-M)wjAQ;&mfcOYUqZxvM2bWSS9+>?wbiGX~$Vj`LC7l`#O@B(W+ +jDJ0icVq8A5NSh3S=HO7z;hWUZ6CDS|FBkbbEU?>;ww(*2Z$TtWpA$dA|gb90uW&HWvTRKa3K03=uh6 +`>PY;1O`3kPJc&D|I7OHsqbYQxtK!yY$4RR%7bJEmAU=;Gu5YW2cRPs8_1h2T891(6bfOIB1B9Ir~kI +6*jb#uHnG5VUcVU5UAG*a%*=tLBpp-FT!#p8NOn!dpFtH?ZQ1_+-8n;hLWU#+xc-O!s?_Adc#CjUcCb +*($VxNyr=tk@Yie%7%56e?+Om*LN6}sHWJJZNA4#&9EpY9gbG +gZhEbF_gE^srPu)~S#be~4K#%l0WyNq|mvgnn7Ez|T{n;-;eCqCyyL71>9D$r}PL?hho*wtAMqsaqzbt=Xz=OW}s_3-KOP`-OA;ebZy^nZYtp) +$V#J-1MptGIoEczL&7dxAKs_6D_={Qo68K)0QcrmH-BY|IY1yGwcJ0#i{yt8%&*uzE|6`enSvsvR+Tz +`M1YlqI}1OLbxH+u}JrrJkN)yOfWtjj-Wq+sV^M)rxx3+V%IB>NOqITk{j!_^sL3+32vgPGsAWbl~)o%zdAf9|louQ1Lz}Voa$fU=D+R%^6)OmQP&2}w!t~19DD13m|B=U2<=i|L$ +=akpgIae>dc$v&Sybzr`hf_>qPx +T4Hd9w*rkeMN@D=d$v-l9!PVRq>MoY{)t~YTnHn!hh6$LJrw1%-;%SC?D0P2^TEB4mR&<2vmA#nm!5a +b<_i9Cpx_zT@0WId`fN-QqCj=*(Iv?nPD3dGNT|RFmxQ+)`Rch-;8wcTxcIeme%8cUjMeL<{mD|Wf{5 +LI56V5j~gLeB}v+ai4$)t4Rr0U|0v($8B;r+NAS%MoQ5A*k_sffPGb{0Is1dR5eyo*0IgEGd6$+*O`s +#i(S8~Q;qDql~GXGnbD^G?n6C8|NIvG56oi~w%MBK=VK9BY9MnKC;JN5~CRBv04g${$QT;&#zH1!0hW +T0TEVh$f;Z=M%St`D}e5VUAY!wg(2U6C^4~X^5RuyhanWMVAw2Fbxbp!s7OHX)yiJM)V_DIQ~8Ft^_?EqeHZdF6Bn(;{hEun>a1EANlic8Vwz}Mz +JuWa#=nwkr(6ad0*dyPT$V#9c?R&L6Lx-;OTXg!801u^rNzRKy$#fqn#ix&%vx;vQ36 +^W0?8t3OM7Yo6qH?M&$sNO2eb)r1ewI-V~)7Z#EZP;?24c!wwKjAxm-UBV(NVrKVAgj2NFL~k&=j49a +VmM*Z*XA;wxh}YfTyz1xE)WbNxVW#pq%3JUOcHa9TrlD4Yz*$=-BZs$3SOg6JAD)?&*(JyUKi@eh3W8 +^$8yr^(qD$Cgc*;M{sqAv}1W{gG=6sBH=XKv*t}mxFVUDT)!=Ju$lAO?KOdI6+*|w&C0t_8(QnTo#+p +}Yuk8&$jdi5V-_|@oe{KUsRdo#bJOF-ow-^aYY9&Zkq3jUP4(qCd%Za&05yv?q7rL+dq-Cq9~VG<0Te5jK+zLA2Zv=wiBj97C5f3-f>LX1I +_Tldm@wkBKGuDp4S6mS0fH2rWjEz9fR+DrlLGV_%;4?$-S&Sl+Y7I77Ea5v{3$oeY@i2xR^)500MoU2 +7HVL#$k<%h{$T{}bTCyXWii!IL6hcy&k!YgjM6_}ZCO=m(b!K~tTH@m8yNtZy3;;bA+2Gr|zI3g+}mf +9O+ZX-TVE{1P2S2rbqV#$28#>EVrd +Vw=`!FnQUXHh+ovz@VHUmcNMycUM3*d=r_iJr=QC-?Ft!e*h)*qwVzPA%+eM1o7Yxb*LI36`jN1S8F> +fH|-hVYNsJf84Ej>Sb`Oqa>8qL-`r)e5^_S9?rL*CB239)8}z)NB +$LKp;kaFG(&x9QNj;-Y~XTv$1-&U*(}!YAC}2J@C7Y{DEAopan*d6vD@w +i+{2R5d(xvKEcN~&IYGEPc6joPb-5BHJ*7pV4^i2JyW}n-4s7qM8Eo^O6AYOS#2NTw_vh4w{| +aHVdi3!uKn!>3tsHj_GZvdxFA;I@L&>3!PetN_;DUggKb#BSmwi7p#6bWAOHYfjz}9E7KE;&32Sq2QQ +n^(jkD}?QR;`}%7!}9l3LYD-e(z7ch5L9`A;FWC)4X<=L5Q`UhKe9;f@Xk9oY*5to-sEFsC+A92{R3@ +IvArw_@p1B*!f7!`O(P@5)n8)yGBFH*~orQ*y0(oz*8Op9s0En`in>k+P4h?giY^N;rK4i~tu(V!R^q%qEOX~jfx%?P4re}_QG0^@d5m7;RY^)oBW`p0{-DLWd2 +LkMK}xET9>C+&WzL!p%3}2Ae2g&;0&~Puocz(%s2`8D~@AOn|x2G +y+p~DsIp8pN~K~+iNy*Ks;Q_n)wog1#QX1AV~?9hNwJ6z@BtA3Cfch|FVP(3!R(;Yjy`eV5EkwAl23B +eC!SATfh#xr`I-HOdgzvpXhY4rWXY`4`Bx!y|{?H-(F-cF+JJre^PPi(-+z-y8LLI^#k +CTbxF3ek$M7KgJ0&=6}a;5S+Rk`{Wc7g{0O|fdzaK7@px0;s2YWfW)lbv1p5QK +!+f+}yIt)$$GRv!M(%)X=iiQZ?c9N}Yrb)txm=U|F2%fJY&LMEYL%-^utR|3e${DNMtFy-%%2dCW5Pa +t48}=}dublZ7u_k{$S+UR*iv{L1i@rn*o<5~cREZ)hP2R&$V|rVFr#k82LqaVDL|F-Sz4^ad(ZpHM`+ +J%Uukk?V@?rFcp8DtYP>&TgqireRAX)J&!NU%osKU5dj&YcyA)S-_!HN0WAPR#j+F-q8W))oJd#ps-c9dwMC2g7cD5?q%<83h~Np8rIX8Q!I66QK4Fp<+?MfPChg)rp*IK +NR@*bpUI6t@@OYPJ?BDR?o~{vi>*uhl|`_a@eUaBfHZex$8S(#Nih}@_ +i)@}3w!snj#^EFmqIAcJZs4KKR!%gZz8+)5wI)^<$);FH@PsUE)QIS<0wQ|1fhErnYw0sh^#JENMROX +nuZ}&wrWWl}ZL&Y)b^kw`a^XPR9IxCYHhR%tTJcb@SQFdhBOZC0;&by +-d&aNC9{tmNh@xcC@faJeE%GZ>xziU0Sqfj@HQR8FBWY(|o(Y5LtmM)USNwSdFP1^@-Fzx-nFx$TgZ0 +Bb36KtSmryg=2c`VK$7Jh4>IH~INTZr>YN$w&)6R;zp0g8q%vd^U(DYg-(&_?VD=dSYjwR_O$=3?L)j +i)%mkJwl1r`^;{3zseiZXWN(Pe`MqtO+)HxKlj(KW0N)4>HhDUy&bo0&@ruG24+EZXoZ5%L~n-Y{s8x +GoA^zRk@~=yD7|scj+X#hrB=cf-BYXk&m*2AbJ%@R#{YT4O0-BYd3qUx#8-nH(Z}MJL)n +V?j)~^HZNPL8jo&#B#4g*SGeo^{lIt*x0zq8{MkCh{%OQ>!L&%+XC9AJ!IrTa35j5|EwVcS8O7xKN2q +XP)?Yd??Lq7n<5u%rV>wRuxjakKt-NvEe!kZ@MDqOcEl!I%D!BDQN8c9ronPmQ$}^5rLti(U@(wdF(Q +3mvZAMS&DncudM37pg@aQ$cVT|?s9PDz#dGzf89uLhKLLg)n4lFvZ_q9ZW!QBR6AAb>{kSc~Oe@F5?n +gsm-&S{C@>ke2XZh=F-hMcOzkNB2bu|e)B4*-HwI?meNwz#J}x>l~1)G4~f{o;pFw#P}GTY0z)nz?iQ +DofJ~BZiyCE3P=4Jk>g?ew7=>OUz<>K`&J`N4Z=4Y&En>-e7NZpP#{Ej&#Z}H;Er&RebC$c38Tq-R}0 +wkjLS4z!J}YOp@h>@so6m!Hj?EUfJSK@{4fcTse!VjAcE^{{2psJ~xzC3LuU3LvAa2)#-JJJQGotp+R +6)=x{UnZ)y7aYK$Luk{^_Z5^LqF9D?(FR&FQn=GYaGD)BgTPCtiY!!zF*pSIb#hw#i4XMZNQlOLjjz7 ++hemD-Pf(^-1>V@46TmmdTV*tW`I?lyVATik7a70F*`$x13dZ+;c$3#N&9qtGR{j|U47z}ANe8{8`1O +Snh34oh!^<8Gs4K_*D$HRT5KDuSsS4j{^7>xO&7FQa@Xx9MxeZOJ|1m*R*apUrz45;uge%VMl)i9H4C +bQ*3CKM8&Y&cyR4?qqGr(0FKz8x+R!g0X@7#QR{{Il&maBcBPMQIe-Wl={D33_rmnq#eEb&PQ%7!aR4 +anjbiQ*u~~v7}`9S+zNgL28lE+M}A4dNi&&Y!rEE-k8L}1S9s+$%9wK7T){V7(X?cuM{Wl1!;VyI6h6 +~pj=QSpha1AfhiJ9LIbA9$^r2qo?(p;URbaiB(Oaj*-Qn*6CvPNCr%Q&$DW=6ocuv&h72bh1e$xE1><1w?n1$$g~Sk;V +0ID>0297v9qG2c8l0;lAwaE9^vLzOBw}+3epou`j;XLgJ`V7rs07*z~S+?Rb4og(h}ZVfJBnN@ncY{A}Y=`5|MGE1zA +hw7~e!h}Da;Wz)IVmR)24=4^g6BLkqR;!p(XCI{_P6eFAcA~Ya)6)W|&@9KfYsi*RDB^u5Bxp+m +b}(CQU9GZn){XHq7;gR5y?VF{7QokJv@dP1(b{&?&Jt0OJeQ6t03VxqLD+vA6~Mpad)1WmHd$Mk~u4;BZBnslj>mz^UUae+fL5Q^7#w? +5aE!aDkNTB})n-tMKsQzgEtFwu0hTYFxsK~0As~~(T;Ch*6I7cvi$@yS6~lR%kj +wl(iP^p8ewA#9ilNBbMTd{ERn|dY?W0}Q|_rg_3lBTEe!!Rj7Gn*4&p{%aCwLf-~?g|BW45BOyn$Xnv3#xtKAvM<6%OtUj*KH(HFW}+AjYd%Kn9?8I!bU7bA6H~&)9ISdVGr^Q=Vf-ifZrWR_p!8aOKVxd`OA!=kl)JL%56j%>TKbU&c|mn>s@3 +!A^B@jxo1V#s5rsFvx%*u#VnT%W5g|?^P-qw@k+z5M@DF>!f-w-kya}eGP3<+*EGc<1SLvU!%w#{Uso}SzzSvxit@gx1e+(t5pMHxvnnY*1+Q`}LF^RA$h36*f_i2X71p2x7-|rbT+)ceNRU! +l$dWK=k9o3iO_4T7>uPyGXz5q=9jA3kiA7h*eeFR&gb3Bt4!4K~q`>QLq2(aji+%RR0dDUgg>45z5?| +lgqQ(#(LQu$KwwcmSd;*N2H`A;k7gxjrqu&U9#IMw@0H>H=E(}qyYDmr3M?nJ{iK1a?(TE@BypgC7Gkrrl&T3I +cY*V +C0HxMYcek?Xy=y}G-E8OR4X(sDe;;-l@CaLKpzw>9;agsJ1su@y{Rw85>^hjVwn9**oxUGAp74mBq+& +zdGT&;*VBlI6SJe_G+%mjUIUTigmAkJ01W{~up!TcxZ!iPM9xU4 +5*2YRWr7uKAEdr}MsH2sPUS7Zux{LAxX;oo9@VqBz(qB9{u=l>m9NWUUAY*J@1fUB$brRc`eGXc1Lt~)*O!s41GXRLO0w0)oz~H{VvtgBl +Xb9m*P*lUmUqE&rY3qdFS*;gKObQQ)rYP@?!BICZ}+n-lpE!v%HDiSlXk>7%>VLH?uNDL<d{(I+)i#Fk6XY +TAiE^*yBI(V=5d2Z!iM6jB7I@ZRFo-u8+LVgAH6o#fVJ7k|Qx9@`_thg7m0C&k+}m9dk{&SKvJM1tTX#3vf-Yg@Yw5 +Et+c*bmLhIzZ%@4PA2l*7rI59_-0LNe%%t8H{m8#XcB#Z&DpNTKIHTwZ6;W-Px^2~EX_P)OiIJJxWuz +w3D=jyt{?8iw|#zi$32sv;c3oB`md#g{-p@1z9&)_)sS094GLj07*hHKO_8`mqLC`P^cqmd)S{nZB|k +Fv|qPUEcm;VMmHsfj!kzt#IqAgqyMAaO#6m(4VMv3j_m-5dJLsoQsg>^04wBsp}Xb0_|aMgF@;!Ia73 +sK9)}JJ|5zG|$CL>8t>RL^<S_DrXc%t^P*fS1kt@pWg(_uk`;U*C1uO)@0Hlsih=OTx3r9@YfcsY;TG+8FaE_K`7AFTSLSQU&ER133^ipGTVSbqThpd2~7A6!gmIPTP2DE=u_+cX;)pj%c-vt4=mG5 +_h;W^=@k+Gm@?-aue$yDtiN=P`NkC}#3bf?mx(g^-YpKVr#}G_u}PnVFn>#lZnKhN%!F>W +-oYB|G5kUwyY-igePXrhs$I|G7s2{YI1vcD?$Hg{NT%~m_bUoV$qfCa*z+Vp5{;IOAo812$t167mp~>y +u`w`~7oUZ!vxAEbGMLaO^B8Oa1=2~iUvv(Q(q6kov+q!!zw?Fm6P&I-CE#?Kwkrz6!)4J>Fim&W2<5a +SJ%0!abAg;*`-qBI&Q@_jVuS;)4O1jI^rmE)iCy$#ySk8k+ +#Y<`<*Lc;-lt$UiXkR~1UwlDp&&0h!r~>CHN9stHo4C`28PgIt#Tms8Hzh)rHElyn*0^1N1!9{-_E^! +sx7)EH++XxjOxE$rG#OAmIrHrGKFxN69l!3SDM_-eF=u44is2tnRy@Gw$3b4tvvtP9ns`|@33As=u8` +6O%|VEuDE?^{fxmg=*&f#r8(Kd)~Z +?`X2_3#48~oaBGBT7!P(@t?cx69tS9z7BP=rrR~Gj9v{(j +8RrH`WvjLjXo0Y;jM4wSYu0?LD3H8PpFVr{3CI!@Mb-R#A01(JJB^*s0766($tGp!gYm8`h&*+BNn3x +&8hiKOBGM0;M$srp9CD2h(tBSS)b6BUM?`t*QEw4Gnw~t09N@eU-&SrB{&0WOj?$yd)}$q<=4qQF?t} +V1@Atg96u*lTCbSRyd#_$Dv*T18oTZZe5@lvRrk@8#&+gn|^ +($Agm?S~pnYrw@^lPlK03;p>#n@Y6GKvfZVAkTxV}jS)o3#?aI}nijw1tdj?xW`Jx4wO|d#<>khcjPgkZ)L7lhl8z_VD!E29e?S?$nnQ{ +ZyB7G_9HnPG8qW!V{E?ecL?n7aRMQz;bt1?H>#RrIeS`_P+aI%pu@Bv^qE~vT%bD`b3G9SeCfinBeLh +l529Mi{O*o$(d<|1RR&wLy5eHk^KuscmUH!sfg1($sqC5d6R25}SsdeRTZUSC;{i%#6%2eX~9(4-fl_ +OQ_+m^h0h!0ivTah{M%e8AfuAAe2Xw-ZjfSbV_U=h5A498DWj6j1j>lvFWfA1ko#6SeK>=%O +Zp<$4*St5|D5Ba+yjthwbtE1Q?Bz1h{gsp`j7Xn9*r|U*n!c;=P!!2+|?uX-?k&G{fBmt*}Wlh$U7a> +KH%`NAX)x%WDXyQ_{ZoMg;Cu}!0t>uQx-#rJA?jLnio$JM|cUHjGNnM*JYx%LR4+|AiUp3N%XyCbOZE +my1@tsT&(^DI_MkRdsd6R7HV>4#dWK*?3Vh~DQ1^QXwsZ~6^9>IHIa(VE(XB+kXEsr`ctc&RI_An=gI +Xa^A4;@pZO%F1im{|CT!%e^V{sVjC3DmL-T7=w?4SULYf!CJRrP(x<9Jg!MszkJXxy +j$7PcYag)75@v)#%(A2Ba7*w3AMxz*U6dG0931%h?FmcJ2l+kGVexhohY#wzUQu-PRzs9HaPjYTcKCB ++@vKh^6ntRf5q}6bMMm}69WGDvRo4*GqUZ}s%-rXX60lWGLg=j%ARp(?S}uI7MpC~^ZMKvBB +YR;qk`=N2OkZhUJ(881evW!o#4D4J^8)Td~LM*_@KWB*}*Cu`Cz?IdcmUavHp*}@Jl3~)}-|>VAVHoi +pj84ds)i15y)Okug)@*9-cK7*=GrQLZc>cEp0nl#;Drlt*l${H +L!^Fn3y8&0rvc<%>HJLmC$_HIU+sKjfEmRT|zdEY1d9a!+eb|wE)dsVuW6whC#-hchiI2bDThmel}ck +lRs!_SE($W>uDe2K#Je}<*_$4*{w;TK}PmlRu6*PCXxY{v%dJD~$kGy4`CPb%&wf6kNeg+0_WJUAZ=`EP2QO4kiV#s@-P&8@n`0d(!D=TA +M7%7-bN8HsW)waAY3lynP$d~o4^(MtBzg0;Gf4|woFrdFqQM{=Thr0ldjgyZ`kHs}f-{4}~%KG7hYj( +*O(yV-pl+}-0m0AmSdlU>gi#P=Xm)+G*nHtc1MVYkBpunDmkinH7u9|ZS_%mj^)GW{OT_6Yf4x~r#hb +o_hJ+;7Lx*%uN5!|4m+zQlv3S{YDQu_9-*nUfW(c;FCPI%q_s%hxfg}QI>1jTg{YU|KwXgaVbvjUI}iyZh$Nsl{_Edc2e@ +bn;8%<*TtTZul9j%PfD54VocLR>;A%-aYm9|mcA%JHE({9yC22Trqt9RlYv{F|34(73~lJo(6kJY&SR=3xatF6e+7$%^<$9hJ;H9I7#!sKq}CFuTj*Tm0F;UZ5Q2R?Dh!Pb3Vpi3`-Pncgo7Kwvrd9{f#+ncBwj-KLGl@N3^Z{H?4_w44_{ubll# +#NWhQPUevcmXnYEEcOu0{zd~owkvS<==)7BS%2nWf8SokM +3zT~)LSG=WTys=nGo0ED~T?K4UHW9yQ5couhL!RG7s^|4FeTFZ88Y}khx4V2$PstiPURQu9e(ooVnWQ*HRE +)HQ?bqk_XN7IPuIc^Au?~8(3U{8~8puQ*J-7mG;k6Z34fbVJ}(CxL2qMr}m`}9Z%mS*V#?VU-sYP6gj +F6i#cPg)mFoEGl{81J(lg$#>T`X12B2km{B6|a!WZtuo1K6TZgz0aFm0K7k>+q87Txxl$ki(+SOc7WU +mW%^ujDV_TyfpZVmqLsS^(tSb1^&Qp4#OB)Azj!PPWi%&ewotI#V>LpnqP3dgG)fn8y7aSa4@mBJ#a1 +@m742yRhTxMBXqQgo~&@!#8r^OG}2*D`#IBc29;BHzop|&^( +f6jM*tkWajQrfWb!}hrzr2>9Fj`CPmKz;_ZHM_TJny_5fOqy~H-;2*RYVYn(B2qKmt_9=cH3aWmA +NdxNpK)j0bFx$EX2q3;I6J>oQ9dAY-I%9SP$^VyB50P@b-4O)`B@;{chAP;W;J@p-m(~rFVcmQQyAzM +e%zG5&_P3~?FG87@k_+#Id$sH_%A&{{9Q0Ez^rj1c_iv1Du?;%|?mky@i~1lNQwTu!xqJ(#m4nny9C>LUSoc^|sT&zY+{ +}R7)3Vy6Ha>2DlJjwSEV_797=|~d$>gYv{;AqKZS@s!?h{dM;9|7KhW&_NoBB3|(wBO({<~@pAlnxrC +nn~rH#bW>I~ilK&d7c)&-jgCxKG5h+LTN~mv5Nu$uD;)VSG_wFL_ozOHRHfu$STbQVT>HpfISe +MkF>($RPabG$gM4eeUimdh_`pc2td5wlQz3Jb$0-JpM-;paEv3oYm6Rn!G=I|=2@~g(&J`WA2|15Up$ +Kz?gM8(b3@=(4SJWzmOKin0<_anx{5EXABZR+{c5l6Q +9Bvy9uimqhMi^znnkRgZ86LTU^!7$X}Z-_?*fmtkCy4{WTv5_q$U0*;|bJJX_(M;kRYQe`E`J;e+08g +FzEz{l5NN%>!`m;~!qruB3f3CmIKY`$*1R|C<10PCy0>_a-MAXw|RLA{cuEHU8?*V5ad?{BibI`ejjU +UzKlygI`e}QITwuQ9<>Ac&A2asYYI^^(DcH8|>hPH>tDCU17|m1nFH_W9qU?X$p$$vqLRcb=XkXz~JL +d{Xd-H3Yh8L&y)USA9#255U5)Lhd9$wR8>}NE@Dy_SyI(sO#*mr(*wBoAf8JH73PEXPETzq(uq7&wQi +Wi3w~|twrHbOhLyT#4M&lwNl^gvKEG9V>pb-44~gApL3Kz`0e-wg@41&KqKVi1M*HgPAW@dX@TgT4DL>El>Li``nP;LbJAom(zlbeCqBUmqwj&V0S%DZi2 +@jLpu}Wa?FZNn5l0GeUi7n~#=HXyMJ=bIJf8Ds0A#1GJ+&r@Qs5~2s*^`o>}Ob$1*Ar}{-~@WDawmS! +vD-}%~gCdg2R{Js8Y_3GrJhWqNeWj@Ws^K+I#*CmuxA@6WK>|i>4!FLvDWAWp4R^waZ08(2|pJ|BSB7 +ETe_d>Er{PeJZzwm%reU)yAuYG+%!ShWnCYY0ex&|9QF0^t3h;8QtauwX|8bBh{mIHd^))Zqw&XknT- +s>o3})(EQ#d$emkv2VkEeSp@yb%!v{J$Uc~3Ex^67n=p#K#0)OG1^rA}k*z2ofa}ViP!=eL@)9)>Tum +0X^SR)xYl48x>$h%;3pndbu@yMrLlpXMHnFcWoB5BWtPZPm=iDchcIEY6%`%piH-P&Xp!6pSzsfB6ZFFzuXUh1xzoKVy!z3)z4c;${Lw~r6(ln6n3E6?g|5d`YAPZG?>Yq28luCgVz`v2VGQi1EJ>D6%UC-BjgXLu0Zm^-lO^N-O-M=S%UINy|J^(olf2M?VD$1im +B@uKlgJ1+s->5E}B@HqYEEsxySKpu5mvO5Ae`u(`?>Fv7x7PHwt7B5=DdGhoOK?dg&tR4LJq)UZ$#<} +wL@PmOxm(N*IYyhG20X~mwor#$%OHQj$o{CT6)9`hcN18!tCl(3}x~WH;tD#&`YCSwH`!s)WX$Yy6c` +C>5NAwuK(B{NdIylV5>>KJOzb(qD)ySNgcj)?Wql!g+1!oq!Yg8;O!m*mSgZnsHP_Q-rI5++sIO*!?r +&-qClv+zDK74~F`NVD30V;j=;EiXqxnqYIUOKflC80MNjy^g9`#y;2YL~!5aSd1>sWWc1E`WwFch)zy +-lxTrmP;}(EZge|xp(atCQ#@T^?=r>r3MC_sw{&n6H8iwq(l70hrWP&T{LDoIOyD@otsv^_Rn#wMoJ= +6A$$uU`s^cN<0l`TBCzIJLA2^k7CY_+YDx&&J1_|8%Do8}(f#C)>92NsIcVv&80?2ki=%uu5CvX3m(` +fwv~xjZVA6-AN>~zHDm_y1PgDDaPkjaOfy^=Y2HBENh=jHyb&aJsXdxB*hX`@6)b1po)6?w*(o^Xnr9 +SGjRh7V})ALy$sq96lZ#%ZGf_ISgcUFA9ywMA^IdjP^y?c2`pPku__&-JS=5k{4Qt%xtA>$=?;5OtgC)fJKk@Y0WX!zWPFK9<_?8!AOsOMTwdL6CC5*lT54hfC!;eqXaVg +ye>aCJ22b$x8Za^9c|$x>d({?(NAWJxK)S&96Et-9sXbkt7^76_5;4vYU&Bj6LA@N-c?8Sfkq#e>55Q +To*rN;8)+N508F2xZ*u1WhNIQ-k>SIKz8Za|#7ky_;Ah)Cxw46X@-)Z|K6)@tE|Tz$!1DHPkO?4JHAh +d^2Mv8p@ysa($%pE4s->tX0o>hP*hv`4GWABviLRvBi*tSTAfl5k;8FacT?uZk8Dg0t-M&&nhzi4whn +9*8KzIXlPjg&vzrLeg_bpvh3qA45$5?$ve^3}nci&8fOsZ|^2`CX*|` +q_G2V9*E3kX?}$;XSeGX38Mzrm_J>pG~Hb$4$N%C3Yslyb`-5dbrP~XMjnkIDxuLvOid&;B37yi0MhG +9*x0GyDRx;w+^W~5USic_r-xaDd$Y=)cRzD`GBPd!{KZ((A_<5SCO$iEjis_L!caJnr2MK5$N|~6o$)lwN{g?9*?&LmEK=wgNA7*l&r!Q^Xrw>574v=a)jlbBv_on|rYb7*Kj9m`XfTk=pQE^yjR2 +1t@-Ogvj4_t(vrg+b1i=_SbsdvrYtU*yXMy1|JD&khY(;Wf4dQYhJl@v$SX2HkVVTliF)fF1JtRb+XOL%|#m9Egs^42{zPy`qE28$}BWypo2%A`EDKGvCMhG-8|T +dLDC~m3yJ6jmut63kJbX9}C94sek)WHlN;+a)9H|+P8(BlfJr?pWf~4J`UaY5qSjhO6Hu_aKZ7f9Iwj +7v!FLvHh;mbOy#R=6$!e_2_WjTgPKjOZPm;W_DJp)v)ie%h|hJ;0G~c>+F9DEQgGC#q|iz+$VFbbt10bE|&r +?Yqq^sgUBBNA=L&-U_Wq_7$t3RCfOSFfzF7FC~ilTnrxW^sMh1OfdeC`)f9d;eCp%1$HT}~p;wt1Zch +Uy_bt4&9lt7mp=(AMeg?rY|vbIl)`-u^H@ysEFxD3RhkYW7-!y?E>|(4*|a(OZASH)Zf+Y`oOTOQz%bEKn*1 +JzFPdgwpYL_)BwkUi5LO398{4*KhbBVl6 +U5bo31~UD>~cc|H>a1UdazvalLE-41c^(s@$aaMHVI9Um0+p_)j_dM=;S^|Nq`??6+Z)*20IO(M5S1x +)q+B8>a9GiX;IX!XH|NmHonoMh{8U1VwzZ2cA2QghR1G;j?L_Dsg%;6ayJH1nL@lkmrI*7~L^6!R*lh +1%rs^w}|)ss~@vVe=R+ftgl*UDXI$*}J{;t%`|v4_^z&^KnfyisqR&R{-7HY+0%Yaqzy~Pw`{yP~57g +bbKY-^PLVuz|L=H)6t#!k2ejbTBoG7<)a7`dxlVR9!hx|H*s}<7Dg`<(;04V2Sm;!?GQP +^@g=EJfIUWkmt=X51J&H*%nJuE(N2AjOhlW} +<^!{`cQsl=tO3k~1H18Z9%lWSRL6T6vF(U!Ei}_o)3MFW&5ZfmVJ$jV>I2z_DHdEpL);EnVd1YxMB^l +;;(B|1i_>&$jVw0OpIht3&}-`7B-p6Za^3e3vC*Pu#mVO-5Tm>drO<5B~T_`BG%`(;Df_Wq`+nH(~5{ +8y@!x^AUzssW-JymfSNA;w5`V^MFB&=Tu41`T>rP-2R-8?;9xt@WaQ&s!-2A`^!1OExLO_z_5$Elyo- +C7_&V;FEV8^R}^%`VWUn$H7PbOarvcDj#fUZJb4{~0e>utO72d(ih%u2(mlzDG*19lyE0Lq$H!b*`yc +6l&1Ct4zWRK%p$X5v?ip^Q5B7JEugjt(&jir7l@=&))cB7tj2;tbV|3tq+p0A3qvYGWiyv^`lhxRPBk +~zG-siNlsYj3;DQen;wt@TpmF5sPa>nfjdl+$pmE|*O*qJ`a-chb2qEJMt)RulOZ~(SWsud;1EEEZSm|`yStqlpoT39oyw<84#V9i>>i%DjtG%id_`gCjVZm8 +(cv+3Ma5+WyL@5giwI@zDr_f*0Q-h%Z_TLsLtFGH{s1DCzMBc?wy5`P4CfcHdDs1D;)-~w^qgBXryUW +F{8nDcbGqAwbP&ry7!d2z@|mHmetnEFo5XIv7honM=_jRyTlSzl=&;^*qhJ~&R(^WMMFy4*!in9H}@7 +bk-?P|8{Bb$+MM132$nQSv&HW^0WqI9 +-TA_8ujcVtae%{JtL1;-zf%O6RuOTbNHHBiwp&bL=-f9QV{e)chT%R|>5;G*6Fa9)sTTVwMq}fMU)b&fd6Vy(00JtY1)p0vWEGnAD +2cX!z+GJfL9N=xr_wUsBY)o#UxhD3aCRFTKu1xT<*|EXlj>GBnHjH9*2~O$pnJ3ELgK}%$$YFAYEJ5i +G2L1CEBZ%GcY!UwOE^I{1$#6Ae&b|Zne$g^%p5Tpz_ufghFS+fHV0LppUpAuEt>5R>??MkyXU7D#`vJ +H9W}FESyZiHZ_c;)|lT_V}wSvTl(djmI15rqt&rmP2l{&>%UF74{$M~*L`cE`@nS%!U +tMEIP4YJdZXYk>M{ryeYN_8-XxgbC*iaokSMhme!q|A0bxvLIxii3RL^A+;#+wUyo+>L-3*82c^BFFN +_x<|CsfL#GgfwaZB|S)6sR9fYF)2)zb-s#)lrXB*DRcwckFI_uO(R??Ghu9y$8n}LMV>438uH&XfKqJ +?W$1%;61C$+j>bGsu#WBm1P%XwpwLPJt;WdbJ|}fLDR|OoACb6S$rFHWGLIr1_SDrc~RB>0`CC2dw0} +!NYyEX2Dp2emNAv5wW|}_;{uT+3Kn)6jg3VBbocCy=#jb2r~mgImEd|uRP$|fJB+J!_b!I2f1 +5W$|75(l^fOutlLz7e`^IlLG4cDL(yb30PdD3g4Wp>^IfPLc_C4t#>g@TyKfqocS~v>Y_5)nD;x#hh| +KDIsovM^#aMGr2sMnU$j%B86V_?rQGq7agZ|6&1lM~q=!6Nt^Nyx*G@3L_GT%2%%vt(uJDY*>zN7fN+ +%;o^d;q!J;?1r<;By~Wnc(Hlt}M;dy1cegBpX_%&OFg63;?>jCok}K$jG6DB~**wwqb0S?0uJ>J$$z>_9?D>D2`dBZKtr&|tuho9L6g%Wn)j4>&=14AW>H}8gTR|t*Ppyueozj6lV=5`TPI7zLB2V=vDI&tU7>tCn;_vjRP%*|yqo2P-H@ +r;m$yzeW%J#TZZ)HXgWzu{FYoO{Xs*?}2}<|(qEt@s{ee?9&7zLZ41bTP^+*oqaMdG4R{|{hSOrEozpb&Ikx@qE)Ru-r60tB#^g<>C!xkfC_d}<*c_>MXk_TM2aVw +x_Q-!3C#GLG+U}xWo>?JLm=L1Z3kD!As02?^jgEBoEkp`&Ku8^w +OnkcLR$(mswUF-NUAcVB6JZ03JKYsIj?pk6#)*sCqrN?g{v!`BPCf8!c$q6S3=47nG~t1QxayzR)F2_ +zO1t6S+DRji@7l*it8ICR%W|QZqq&W0GMXGlNbWDiTZPNAmL8mp_nO>qO4jbXzE5L;O<^0^kKq!XujW19^EXOzHTSc>wz8w +51Au6f82>N`LUqrI-SmULj^jBDKI*Sz^s`0B%8f&-|!kZNgiKE`7pbi!p)SKQ;*sbe#Y?Ab-|EZ`EfbGg{58(fN5U>lu2pc{o)*7x`39lo%N6#m_i&c3n2k +lVT(eI`igyosZxrtx&qN#k?y}5t8LW+1q@GEpV!j6mQ+K>*gy_q*Ig4n;x2AA#l~?MdYonX=er@YoON +h$`$JCj1JrA3z2n;t^ux=7i-p#rNuSzdQ^%;iVXL7nJzJ*SRon5xQJ7#$CW+R6{a~_h)bz!wVmGqzs!~heJFf@qZ{5aoj`oQkXX|^Rwmsc-)}#+Ws-C@OomyHTs-r_!y| +?*G9+KN|;g+I;O+63B=ZoFIrY7;pYL)J7`3l(7k1}5uuamUW7O&a=*y6rb27u8ORu@uiTSZU+R8za|9 +S-QRc`E1UKQ?JU#nec;ULW;)0Ao +l^yO;ewA*+=`ZykZyZi$({Hn%ZvAh^GGxPgp;KfYKirhSZ;5Rh@J!*wRsvwA#3 +5ILQO}lpU{XK5%1nQ!lhfG2)|0Vx}Y=yyIFXOi&P@b>unC?e}udZ9eD-H@8H|fPiKJ>GxvjAVDh+dU? +K;2QcWVa;MerFE$0M)Z--E*;E)ps(kT5ZnCZe1TOScqLGr1eX(i5rAQl<;lB;8K*zZ4bOQzYbM(mQM# +l{W{rQ*3FDA%onpP}8@_8PPyt#T|M>9|P+WG`4? +&b5&Mn&a|RwwE4`LD>=d-H-C3TTHs6UhPvdO#xShsHbx{P~ggwA4mhK!Bb^3rFe?u;>14nk*s$x0~8w +P@W%;ZJo5r{Zl426#2FbWU#-;uiDR;;=53$2HH8wFCl#Il$W%UXdpe0C`a_XF~NU6!&}#jn#ZTvRZyN +|^vB>T|P2E&go8h+x)h%jUB{2Wg6v{sQ@%{uv`|010R4`>R +YuEl;S*v*_?>{0oXiTXbsfw_oN3hjgw%Sz^DPNIk=!whxT9kFm{%iGv?QyOu|L*3}8cfFAnqBWYR1YS +AbRQH>o!*)>L|K0*U}JPvrVld%VuN{K1xS@&}E2_C9@XwK=ygluc~v;-J)swTVslxV?0?v>&40A{fjT +O(i6l%M>8Lm|ifAk<0*Z&+Mm8BVPHUB4Ut1#CHdquXp +Hoq$_TZq?2oF2+Ai(GxP)n&ep1h;{d5$27IN^W#M&c+xy?Dc=KBP9Mh +@oVwdaSoPPW`}~1S+{Vp4~O2QwEbvNxH}2Cm(`Lz+pw{;?yYwb#9#P=W*~5{Q4C*J!haKCm(o*v$4$R +F4mjF_R8jRG#zs9S>u-hhD>emj$EE$BA3N$&X^bzIR*uJA*!8PR?R4UGK;NrA~)b3Pt`o-f8IE)+!NU +j+ktZ&X!KSut52Q*Tf=whp6V%+GJSfIt3GKG@C|-4$upvDc=Ji$89cU%gM%ntF=?=jEoaItF=@Iu>E& +B5Q%_vBl0m>7&-$}?GKPnD1)}KA-IPmw6(Z@8Q)(zR7NM58UF}qNm)y2_Io6HT_+XNu|eaMvrT)_Y1& +@Nf+zI`5LyMCo^5}>Ux_^>jU7#%9`jE*};uvfl2&J?pJBa2iQAZ*p3dHm}Qv`u!v)2a%g&h6I1|?IFp +A%CVG;$6_AG~LcO1wER81uiFHB-8` +HUXcyGF=~9gSbnM0Th`1!IN%PAb^wEd>%}_p)wQWmP7?$z--s*;NVFmA9%oWAt_Y%2Y00Z`Hrj$*uZl +s_Dz->EMS_yhf=XVq?sKr3}4RJK)`X@g78SJsj0HwRy7$E$P#ewrQP4NlPz +K%oQ5-&3Y4e1(nH)IeXPxj<-p +bm|~%916_d{;U@@^Oz0e;M@Gt;dI$QV4amHP9hwD3P?temy>sV+(z9I#K171#JtWfk_*be^TDsqHoM{ +~KeY;y-~pf4!|?ejQ_D$pzViS8UY)Q|vndnZsyq;QXNN=a-CiY|!%_>4t2K#_v1vH*q*KpnM~5xrX*8 +eH@;*sEIt}08?}qWu@%;}z@Tp@z7*WS`sn4@@9H!;7Ll^>bcU0E-`l+x9S1@V0#z^>Mr0W&u|iYM7zC~&yiEq3r`fZ=Lh6L0aYn+6Z})=mweZXa1*w@GOV85d{ryMWz>^|Q_q +43$5wc51|;fxHc>Jyk-Snk_)z_HXao722&RI{>)B$YaKy&^*gdT|I!fTvOpeO{{tG`6EnRilUJhM4g} +oh_?gfR83RIS_JWSP_INOPU`xNRR{#`JPf~kOGfRLcnQmkhr~ze45`XDav(fwUR!#TbvP2ay(w3YwusvJa@$H!L4oDrAnc0EAWpHFsxKy}-C7iBR#6H?!UlD7U(&^+9{-71tlxYrOt +yiZOS^Nm+xX-L9zwg@v@t3}aiGut)9^XQ8$mwZS%znT_aCF?Sbk;A=HdgLs7f)aj%GsJ54-tF3yjww# +5}v8y!+7Rg0Zjc7XmL8$F3v+cKfNatyEfYjb5{jTd_Ftxh$(hXqoN$3ErTaG-Rx-oLqhpKk>7;tFy>i +%4&yLXZVfX@ay9p1^gf^toEczEuXCQ|A(2*e|ATPT +~&&mkc-=#K;=eCYHsPKn?E=6nN94e@^jZ@F%^7}Inr{)ooWm*WB(g)Sz;-WJf5=4izj-sHu9PKUO<&q +=3;4lch|<|kMIkc?kcK6hzFfW77(+;j2!%h*btReLh976NoM8&I4d60Y_)msS)nylKFd>)5bux0pZp> +22bI|iso3j7Y;3Cw5zmd=f78bF^VAmp +*A?K*#nUZz)3694-2uh?pMYT+lrm<^SUtXX%6Lx3^+SqFqbmDRe*{BN$i6E7)yV~`SBQYA5$y*GP?@hoq?+UcdvtGg^Ubj4l38)aGjrnfBFTI5X< +$v|QY-I3ZWDm0PtIa6c4K9;8uP7q^_42S01JF>uF!A9gH@V~KAm+6&rl@Mfk(GABUDM=N>iQ0kr8YzE +R?fq0>2Ti5sA^|iJ%0hL!ADRzv9bUbs<)zve*+;MPOwtYH*I+zLlHiwm1VJyh8!51_{}$!}{#sWR=*K{16d;gSK~X6_0V^G(Ck9$6<<=>Nvm%aLwp5 +yL^0bAp%66HvVN88!b@4-)_dM9H++`+)k&s5Am62V3TzJF-Up9i^e<#SU{iyNBB}Igze +vOtL&g$+R_+MP}OokI!J1>QR_XGKc17c1Q3UOu9R1C=JAiN72 +^2~=$Nhh2rpic=~oE-{20y0fWAf#@SoS~YMvK2nAN94yOvi=^2xdAbyx$wFa*?HrHP26~L!aD=KbTEj +Wmx#?=X7IeY$JriJ9z2OYFXa09w1)|}(m3otEE?rV@JVUaOc!r_gr(k;uS?9G)31RHbiF-M4+fBQEZ( +&2*?CDvU`mxC5~h{-ulnif(^n&mr~mc0ssCDO8xnuSUM2O>TTwK`Fm$cOFH-8|PhOt-xSf)|ii&(vS^ +b~el-|~GMX3Tllqz4Jzs84V{mv~9KYz(8XvUKBZXPFX-@Fb+#5}LJayS}p49YiA6ku%;EX1(YE{h(QMD +Phpo~&lr56U1(@BqtXF*IVWh^3v3dE>e&!+xw9kMNKWgB3lYRt6lfCq8)#ni-*ysNSgq*LP5{3&Xm(> +HZ`0DXEs!@UK?QvRc>BFv&;XWbR*#B)kwXd603=oY}yajhr8Sfo$QFE@V8xNz2+y41}fEbRm4;l +erGv7GwS#L1$U5ij!zOP@+&85vy(IS+JQsP|j)V0kTznD+_J`?(xBEW7=I2NKAdnI^`35`$=Rk9WbGK +TTtav__kMjhfXiC%LaOAbmwVbj&jba85f%6kW&Xs^pTYL&Gd#ahc29^&BHBr20S!D!-^Z1)MmW>m>v_ +k6(jcTu1%>1F$sxZX{I1Hak7p#kBaT>Z|QSQz3NnDw9x +ye`(S_^N>{mC-wMiTl)vZe3q^18TE_WiQ3E-lr&S(c?VMm)M%3KSK +7#Rwp+Hd@5FAuH^Kv;iZ0}Hs=Pw`?d)$XNHhe69{`9_PrwtEJsC^ggm;xAAz#%jpvXA;MgWWs)s9~&n +@+T+fwRJ~?XBTT-aGt0AV=?t{E%)BoXe!b7j#CnBCf!XKBk+ZFs*UTz_D#U)VU&Mk?2w?dyXT?#c%1D +f}bF*zsAPfoPdnyQ8~MRScSMhPZO=(9S<{+4f2t$VeHH;w!jD9W?&WaL4-Yu%zG3|6a=;YN{{1x(pPt +Fdg;ZgCT=dB2ZWSTm~GoWDYnOgKfwuBy71=tfHV4wAatjBH0L1!G5#0FGLqhxT+{fq6Pnx^U(7cN&Dt=H;qHi0YGcH{BeKxr@c(Jw6X6s&4A|Ym3gby8X;<{tN1MX!e~*JS(cV&O#^*&Y +{c8#>*uPXLVdjX6?E*b#hcmo0UYhg%IX>!&kCT?34wi-o$ZFxbh&dgNdXzXqZ-9+MmaB!t%1r)SpYOD +$)C{L@b*U#NX?gw9CKNSz>AW^Bm6Pfb7t!Un9(?;C}|geab=Xj*C$RnD5LQ|b-FIywa<^lcF9WOQ@*Gs=yXPy5@H$Aj`Yn +1pAWn1J`})!677uxDNgsun0Hfy-7B(4N$H7w9G+8#V*sb58{mqLDTN(~~4*x8woMa(xN!Nh^Z(N$w#< +N)7GP3^$57j@9qNi9_-!)4i=``PgEVuS~@TbToUq#97^FK`PzNf@MB^3FqXQwu9p;b6^gDeBK7rfQOM +E9`x`2_u#XQrUTna*-+}E?K)`u5uPEFvmL+ycalfaZg>H)Gbz=|RCMeFz&fYLmQFhGoYSMzm<1xIayF +<)r#ktbOG|F#`#h}+7-tL +6{Wb7WyF=APK@{@%?+M-@_j&0nf$o^Nl-&^|#J_yWQ7{1jxa?*1pg8<)?uz&9^bq80Wghb}6W&sGzo;&HBL8g +!JN4!bV&x&Nw#x0WKMqW#pmL*JwtQJoD#yV*iciBHrVa(j%6Gg2O&-;=C$t4x*$MP2D?<57h1j1yb|^cZ%}hWl) +@HJ+YB)qz4L;i52iztPjJm{ktt){YG8s*sl_K(Y2JL8<6b}G`E9re_z;UBFLj3?AHkBl7rJq*Uu5dPH +q>G|p7RIvs0gj%L~jKjWJ`9hZfn^9uK=D5NRFaX8*TgE7}3N-&8F=nif^MkR|^~r%8ATg(|RjJWA^B3 +H~z?ttD^f;a5yc>HKe%1ooQPfz*NrF=5b;qfJ!+D1~G>h2>5FMP7m8rZn3UGBjdIB!T;o>vZP>iKiwO +83@DLm1)^zo-opnaGE`dvwW7Kwz9OqYrRND1*JJMj7gG1dty`G!=7QM(Rx>{}fp;Jwm6!4qAH=?O3Fr +@{DeUa3AVLQDrj{PZ|Np-R8L}9wRjo?{jsE~%nN!^z7nsQ2hsTcp4mxB)MWSRXJ0r%X@9ll`OL>Om5C%WJ_`wVAlNq|6+ +w0cnOQTtNF>Mk=U_nCr%dT8(@Fw!5$fSb+(&wP>tX8grivlpv$K6t@r_*(ZL4KUj+p8Xy`Z?!Tp!Y0G +`);!#JJoAl)@r2%_;F07bWWWdz>huFtI$>LeE1S#wfocP0I-iku@2wbHUql*xJblx@c!si;^NPi!v+v +!??ca{^u|+4szaRPwq6pLkki5`vIqas+}vZh;5XSv1()u!?S7*>$x(f*?5SrnkDz}+d4&AXG%YlL;z) +$P6VV0ZahilP=Tad=ulzWvevDj{->LJppA6D9mF}hs(Pnjr`Ykathz(!0Vi=KVCyZ{s@{`3Ss4>uNn$ +`x#HZQG|cB^4iw|~_6uG5lh03Ip1uu-Ou=tk&3den_JtXu8e2QMeF@@j(Kc=nSI`Y8K4o(v%yyUN-R` +z;v-lUxSsQCEvb-}%T_b*kMLFa22IYpzkLny;n(G%LiXa$WPkd1V!^Gs##^XF<>0Sg=}W1!LY2wbK2O0;4}u*Nz55*KpNFvu%fp=e6M%UKpVx5 +;@XJT1J`I)pBLf%OjjG7O>Dn~&|$XD8bpgbaox_+1GJ-tkv3^Kn-Xl+0n{3+x +dclv90r$-ExT?(c$N(kupO@hguMCsIox6$P@P2#{l}*2wL2D0BV|=*O*uGN(ZYZ=N+<#1HZG9V;6c$B +@Il2>{(qo-^C*Nw8<+Mf1jmC_as|-YN-@7(*Ggoa&rR3hPM(RaLC0sh8h}&**wp;_;J}`&p5(z%;x{#UATLhp-_q +7VfV+5G5O(0C{{*;2HOp2sr9XJ1lq?c`3dIw7tt`bq)Y4<~sDLXT-VP0^3`E6;;wY#JPnie@W!UsY%9 +$KM743H0G_E#RQypgb$mCyVJOX)ri-1F>w)|Hyaogy=L3bDa_m%gUjC*fo4j;HcXt=G%6y +=Loxb-(!QqxH@}VO)q@RLWgO9CJ$M7uA2R9n+C*Y7L1tJCDcM1?MwpK7Lbo@7xb4jEny5EJo@T8m@||Ktj;m#pSy&P&sPH*SzKY%PDU>-|1 +eDTTi!|tnYCoInGH~wq)@LBFtQP50#rm&i3o|kyW{laCjxV@~`#oOVSHm_fDV +#(_R4sGFfDG8mHeDbl@aaf%v&n7W&&Cx4cLxx`Kfc#d|4;r4Zu2jR2xWE6znYjv(Qd8dlSVSX%&d9MC +%83z5WY{nV%uB$g59Fywi*+#i9JE%VWiX9_Kr2h-Q-?7hvCTx_T8Q;p*V +qlRtpxwb0!E$+611C%%_g_^I)rYuN{juvY?Feq>2l$#fIdBNZET&g!tc)!w(11m%%I#34pVi?)t2>=o +&!6F=wBQLyP%;uqsREK8sV%`+aI1}pzt^Mj}PrDq!N1UsLP`&?J<=f6AHqK|s*w&+3bi<8-mn#3rJQ} +vhb(-~2^JeUWyfI~ThnCuA`zm=#EwpCbZz`%WU8Q{b6RfgIJ@EtNvud|au+gvv8!@iyT)B66kZgDNit +41)1w_M;Zw!cZW^;ET&6nn1ma-faxx-CE)2HE}<-Yx)!@}oApO+temelkE0r +Cipg5***5hx4~63;*NcrI|jaN09e#o(j%iabNLZavth|`es6_Te&s+x3JD-S!~kt^aM81QDc{8@34-q +Zj5T|cBWax?z!t$N!mP^PG-Gm49?*>Fb;r+H8p>`Xm|I*Kbw-=!@q(>uP!Y`60kQutVi@$MQImT-~>R +!k=!kv9wlH5)r26I+tHY~m2!#4f5^pzkye1s?A)UN#IwSwsLsvx-!KEhc=6)VL^j%8 +#m-nXEQWSl7i@e=+5YYNV&yIP1h +MvtkrLKi}r`#79Lz;ag`(hpkTliDCZ4ob7R3ep{YvS+=8Zt4c;vC{=7or(-u&cJxS&f{Ju@tK^6C7Ca +H2@UKWRKghDZdd}x+Pd+O>h~em%RTIdhAwwA3Ef0xsXsgth7yHe;$!v!a+rVCUOM4-l@%k&A|6myFoJ +Sl`<_8v29F(Pp~A&*?+3Dv&!cnlbw119FqpeaGXSwXgWw1Vd#d?05J?c4BWaN_sXu^YA+JXDh+O_{oV +tvLkItE7Aix`V~26xBC#OYwDx{P-POQCL)hD(aib455Di|%mb$vNmTCsG<}}M{41)7|=G+FZCZ)O;s@ +mi(7S|#&|@<{oOJJ!h +`{R0Av+0}krtF{Uzri6g!<-h>53kH0sNhno7*j|rb*5f-n9vjkhD9m8KqXO8{lGUhyw +Rv@{d0P?=!_OH!4jfMa8eIpKsaDA6K9T>DWrI#fJ<@%&U%iIchI3&9{xRO;*(_-2QFmd`Aqgo2y3OwF +1Ep%)H)Z`1%+@7^z)_HnV=wzfZ1kOYgxEOGPFG2{krK%RMw;_#g}z={=E+V8Wj#j{8Q%bDda7gv>mw^ +O+-yl`KgkxsE@#-S((k`Rp=F^o%8=W3%)*Q9LZz6rxYPj4>X)H1PiWhOpZy}6z!%VWD68mntwpOoA|k +~+=PN~gWvW2zY`y=EMs8rtA?VeGPB5Fc|O;ZJ%RO`lw*IjfzsS8g=wUt$&P7lbS76A<1Xx8J?6QpQpG +r>zvZZgf*MU5v{U-+9`9^n--`ed8vm{j_OkMvQEIF;Smv&Bdli<8Rz{u~#;^+e^W%Am{7GQG5HEi1T% +|Mtk?QDAw+~%I?6ZpPZ(fO~2!chrhkLy4HuW{`2BRqJ)-jVYlpH>Yckm=IlU?lufOt9v*MlE@S4hLX+ +m>3wcVphX>s9lXVVN+dt+uqzDa>Y?Xqs^Vzmt&$GJvmr5Mtv7MB>pdYjnPJ0U*_+%)=dm&oI68iTaCP +WETTPCO38{Hy-#IqY#;w&pxUGe7jmNe;0=&n;2?K@Fob_bI=;Wb(rCoZ7dtf~AbUTu`$_50KTMD;Z#B +6ZoWn+!4CCX@)_Xu6Ic^v_0G*<46|*d!e^2o6=fB+tb4E<5o=Uc+}HI}HbN1DSYntO8S(vR-MMyNR^m +_qzKMJML`L7AgJJ$R>ef)oEW7UJEAgPxvy<7KL;)rKMp`O<=Z5C@5Sn5rhBbT;X+KFF)AET +x!%&z%{OjSC^R-&=Wl-2Zq{~yy9_<+LQ0RR +""") diff --git a/scapy/tools/generate_bluetooth.py b/scapy/tools/generate_bluetooth.py new file mode 100644 index 00000000000..9d534a0197c --- /dev/null +++ b/scapy/tools/generate_bluetooth.py @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# This file is part of Scapy +# See https://scapy.net/ for more information +# Copyright (C) Gabriel Potter + +""" +Generate the bluetoothids.py file based on blueooth_sig's public listing +""" + +import yaml +import json +import gzip +import urllib.request + +from base64 import b85encode + +URL = "https://bitbucket.org/bluetooth-SIG/public/raw/main/assigned_numbers/company_identifiers/company_identifiers.yaml" # noqa: E501 + +with urllib.request.urlopen(URL) as stream: + DATA = yaml.safe_load(stream.read()) + +COMPILED = {} + +for company in DATA["company_identifiers"]: + COMPILED[company["value"]] = company["name"] + +# Compress properly +COMPILED = gzip.compress(json.dumps(COMPILED).encode()) +# Encode in Base85 +COMPILED = b85encode(COMPILED).decode() +# Split +COMPILED = "\n".join(COMPILED[i : i + 79] for i in range(0, len(COMPILED), 79)) + "\n" + + +with open("../libs/bluetoothids.py", "r") as inp: + data = inp.read() + +with open("../libs/bluetoothids.py", "w") as out: + ini, sep, _ = data.partition("DATA = _d(\"\"\"") + COMPILED = ini + sep + "\n" + COMPILED + "\"\"\")\n" + print("Written: %s" % out.write(COMPILED)) diff --git a/test/scapy/layers/bluetooth.uts b/test/scapy/layers/bluetooth.uts index 0d67222e63d..edec8d9c9a5 100644 --- a/test/scapy/layers/bluetooth.uts +++ b/test/scapy/layers/bluetooth.uts @@ -532,7 +532,8 @@ scan_resp_raw_data = \ scapy_packet = HCI_Hdr(scan_resp_raw_data) assert raw(scapy_packet[EIR_Manufacturer_Specific_Data].payload) == b'\x00_B31147D2461\xfc\x00\x03\x0c\x00\x00' -assert scapy_packet[EIR_Manufacturer_Specific_Data].company_id == 0x154 +assert scapy_packet[EIR_Manufacturer_Specific_Data].company_identifier == 0x154 +assert scapy_packet[EIR_Manufacturer_Specific_Data].sprintf("%company_identifier%") == "Pebble Technology" = Parse EIR_Manufacturer_Specific_Data with magic @@ -561,13 +562,13 @@ EIR_Manufacturer_Specific_Data.register_magic_payload(ScapyManufacturerPacket2) p = EIR_Hdr(b'\x0b\xff\xff\xffSCAPY!\xab\x12') p.show() -assert p[EIR_Manufacturer_Specific_Data].company_id == 0xffff +assert p[EIR_Manufacturer_Specific_Data].company_identifier == 0xffff assert p[ScapyManufacturerPacket].x == 0xab12 p = EIR_Hdr(b'\x0b\xff\xff\xff!SCAPY\x12\x34') p.show() -assert p[EIR_Manufacturer_Specific_Data].company_id == 0xffff +assert p[EIR_Manufacturer_Specific_Data].company_identifier == 0xffff assert p[ScapyManufacturerPacket2].y == 0x1234 # Test encode @@ -702,7 +703,7 @@ b.show() assert b[HCI_Event_Hdr].len > 0 assert b[EIR_CompleteLocalName].local_name == b"scapy" assert b[HCI_LE_Meta_Advertising_Report].addr == "a1:b2:c3:d4:e5:f6" -assert b[EIR_Manufacturer_Specific_Data].company_id == 0xffff +assert b[EIR_Manufacturer_Specific_Data].company_identifier == 0xffff assert raw(b[EIR_Manufacturer_Specific_Data].payload) == b"ypacs" assert b[EIR_TX_Power_Level].level == 10 assert b[EIR_CompleteList128BitServiceUUIDs].svc_uuids[0] == UUID("01234567-89ab-cdef-1023-456789abcdfe") @@ -739,7 +740,7 @@ a = HCI_Hdr()/HCI_Event_Hdr()/HCI_Event_LE_Meta()/HCI_LE_Meta_Extended_Advertisi rssi = -85, data=[ EIR_Hdr()/EIR_Manufacturer_Specific_Data( - company_id = 0xffff, + company_identifier = 0xffff, ) / Raw(b"scapy\x00\x01\x02\x03\x04") ] ), @@ -758,7 +759,7 @@ assert b[HCI_LE_Meta_Extended_Advertising_Report][0].data_length > 0 assert b[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [0xffff] assert b[EIR_ServiceData16BitUUID].svc_uuid == 0xffff assert raw(b[EIR_ServiceData16BitUUID].payload) == b"scapy\x00\x00\x00" -assert b[EIR_Manufacturer_Specific_Data].company_id == 0xffff +assert b[EIR_Manufacturer_Specific_Data].company_identifier == 0xffff assert raw(b[EIR_Manufacturer_Specific_Data].payload) == b"scapy\x00\x01\x02\x03\x04" diff --git a/tox.ini b/tox.ini index d47bddf8b9d..c3a06ac726a 100644 --- a/tox.ini +++ b/tox.ini @@ -96,7 +96,7 @@ changedir = {toxinidir}/doc/scapy deps = sphinx cryptography commands = - sphinx-apidoc -f --no-toc -d 1 --separate --module-first --templatedir=_templates --output-dir api ../../scapy ../../scapy/modules/voip.py ../../scapy/modules/krack/ ../../scapy/libs/winpcapy.py ../../scapy/libs/ethertypes.py ../../scapy/libs/m*.py ../../scapy/libs/structures.py ../../scapy/libs/test_pyx.py ../../scapy/tools/ ../../scapy/arch/ ../../scapy/contrib/scada/* ../../scapy/layers/msrpce/raw/ ../../scapy/layers/msrpce/all.py ../../scapy/all.py ../../scapy/layers/all.py ../../scapy/compat.py + sphinx-apidoc -f --no-toc -d 1 --separate --module-first --templatedir=_templates --output-dir api ../../scapy ../../scapy/modules/voip.py ../../scapy/modules/krack/ ../../scapy/libs/winpcapy.py ../../scapy/libs/ethertypes.py ../../scapy/libs/bluetoothids.py ../../scapy/libs/m*.py ../../scapy/libs/structures.py ../../scapy/libs/test_pyx.py ../../scapy/tools/ ../../scapy/arch/ ../../scapy/contrib/scada/* ../../scapy/layers/msrpce/raw/ ../../scapy/layers/msrpce/all.py ../../scapy/all.py ../../scapy/layers/all.py ../../scapy/compat.py [testenv:mypy] @@ -136,7 +136,7 @@ description = "Check code for Grammar mistakes" skip_install = true deps = codespell # inet6, dhcp6 and the ipynb files contains french: ignore them -commands = codespell --ignore-words=.config/codespell_ignore.txt --skip="*.pyc,*.png,*.jpg,*.ods,*.raw,*.pdf,*.pcap,*.js,*.html,*.der,*_build*,*inet6.py,*dhcp6.py,*manuf.py,*tcpros.py,*.ipynb,*.svg,*.gif,*.obs,*.gz" scapy/ doc/ test/ .github/ +commands = codespell --ignore-words=.config/codespell_ignore.txt --skip="*.pyc,*.png,*.jpg,*.ods,*.raw,*.pdf,*.pcap,*.js,*.html,*.der,*_build*,*inet6.py,*dhcp6.py,*manuf.py,*tcpros.py,*bluetoothids.py,*.ipynb,*.svg,*.gif,*.obs,*.gz" scapy/ doc/ test/ .github/ [testenv:twine]