From bfc61365e0f27a261c477fec030c94ea4f57d248 Mon Sep 17 00:00:00 2001 From: Petr Pchelko Date: Wed, 22 Jan 2014 12:35:43 +0400 Subject: [PATCH] 8030050: Validate fields on DnD class deserialization Reviewed-by: anthony, serb --- .../java/awt/dnd/DragGestureEvent.java | 45 +++++++++-- .../java/awt/dnd/DragGestureRecognizer.java | 15 +++- .../java/awt/dnd/DragSourceContext.java | 34 +++++++- .../BadSerializationTest.java | 75 ++++++++++++++++++ .../awt/dnd/BadSerializaionTest/badAction | Bin 0 -> 1982 bytes .../java/awt/dnd/BadSerializaionTest/good | Bin 0 -> 1982 bytes .../java/awt/dnd/BadSerializaionTest/noEvents | Bin 0 -> 1670 bytes .../awt/dnd/BadSerializaionTest/nullComponent | Bin 0 -> 661 bytes .../dnd/BadSerializaionTest/nullDragSource | Bin 0 -> 1978 bytes .../awt/dnd/BadSerializaionTest/nullOrigin | Bin 0 -> 1936 bytes 10 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/BadSerializationTest.java create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/badAction create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/good create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/noEvents create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/nullComponent create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/nullDragSource create mode 100644 jdk/test/java/awt/dnd/BadSerializaionTest/nullOrigin diff --git a/jdk/src/share/classes/java/awt/dnd/DragGestureEvent.java b/jdk/src/share/classes/java/awt/dnd/DragGestureEvent.java index 3b72ed9afda..fdf6b147389 100644 --- a/jdk/src/share/classes/java/awt/dnd/DragGestureEvent.java +++ b/jdk/src/share/classes/java/awt/dnd/DragGestureEvent.java @@ -36,6 +36,7 @@ import java.awt.event.InputEvent; import java.awt.datatransfer.Transferable; +import java.io.InvalidObjectException; import java.util.EventObject; import java.util.Collections; @@ -329,22 +330,50 @@ public class DragGestureEvent extends EventObject { { ObjectInputStream.GetField f = s.readFields(); - dragSource = (DragSource)f.get("dragSource", null); - component = (Component)f.get("component", null); - origin = (Point)f.get("origin", null); - action = f.get("action", 0); + DragSource newDragSource = (DragSource)f.get("dragSource", null); + if (newDragSource == null) { + throw new InvalidObjectException("null DragSource"); + } + dragSource = newDragSource; + + Component newComponent = (Component)f.get("component", null); + if (newComponent == null) { + throw new InvalidObjectException("null component"); + } + component = newComponent; + + Point newOrigin = (Point)f.get("origin", null); + if (newOrigin == null) { + throw new InvalidObjectException("null origin"); + } + origin = newOrigin; + + int newAction = f.get("action", 0); + if (newAction != DnDConstants.ACTION_COPY && + newAction != DnDConstants.ACTION_MOVE && + newAction != DnDConstants.ACTION_LINK) { + throw new InvalidObjectException("bad action"); + } + action = newAction; + // Pre-1.4 support. 'events' was previously non-transient + List newEvents; try { - events = (List)f.get("events", null); + newEvents = (List)f.get("events", null); } catch (IllegalArgumentException e) { // 1.4-compatible byte stream. 'events' was written explicitly - events = (List)s.readObject(); + newEvents = (List)s.readObject(); } // Implementation assumes 'events' is never null. - if (events == null) { - events = Collections.EMPTY_LIST; + if (newEvents != null && newEvents.isEmpty()) { + // Constructor treats empty events list as invalid value + // Throw exception if serialized list is empty + throw new InvalidObjectException("empty list of events"); + } else if (newEvents == null) { + newEvents = Collections.emptyList(); } + events = newEvents; } /* diff --git a/jdk/src/share/classes/java/awt/dnd/DragGestureRecognizer.java b/jdk/src/share/classes/java/awt/dnd/DragGestureRecognizer.java index 331f085a3d3..87beb46f007 100644 --- a/jdk/src/share/classes/java/awt/dnd/DragGestureRecognizer.java +++ b/jdk/src/share/classes/java/awt/dnd/DragGestureRecognizer.java @@ -29,6 +29,8 @@ import java.awt.event.InputEvent; import java.awt.Component; import java.awt.Point; +import java.io.InvalidObjectException; +import java.util.Collections; import java.util.TooManyListenersException; import java.util.ArrayList; @@ -411,10 +413,21 @@ public abstract class DragGestureRecognizer implements Serializable { * * @since 1.4 */ + @SuppressWarnings("unchecked") private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { - s.defaultReadObject(); + ObjectInputStream.GetField f = s.readFields(); + + DragSource newDragSource = (DragSource)f.get("dragSource", null); + if (newDragSource == null) { + throw new InvalidObjectException("null DragSource"); + } + dragSource = newDragSource; + + component = (Component)f.get("component", null); + sourceActions = f.get("sourceActions", 0) & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK); + events = (ArrayList)f.get("events", new ArrayList<>(1)); dragGestureListener = (DragGestureListener)s.readObject(); } diff --git a/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java b/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java index dd2a4150a85..bf4b4176971 100644 --- a/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java +++ b/jdk/src/share/classes/java/awt/dnd/DragSourceContext.java @@ -37,6 +37,7 @@ import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.peer.DragSourceContextPeer; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.Serializable; @@ -562,7 +563,36 @@ public class DragSourceContext private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { - s.defaultReadObject(); + ObjectInputStream.GetField f = s.readFields(); + + DragGestureEvent newTrigger = (DragGestureEvent)f.get("trigger", null); + if (newTrigger == null) { + throw new InvalidObjectException("Null trigger"); + } + if (newTrigger.getDragSource() == null) { + throw new InvalidObjectException("Null DragSource"); + } + if (newTrigger.getComponent() == null) { + throw new InvalidObjectException("Null trigger component"); + } + + int DGRActions = newTrigger.getSourceAsDragGestureRecognizer().getSourceActions() + & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK); + if (DGRActions == DnDConstants.ACTION_NONE) { + throw new InvalidObjectException("Invalid source actions"); + } + int triggerActions = newTrigger.getDragAction(); + if (triggerActions != DnDConstants.ACTION_COPY && + triggerActions != DnDConstants.ACTION_MOVE && + triggerActions != DnDConstants.ACTION_LINK) { + throw new InvalidObjectException("No drag action"); + } + trigger = newTrigger; + + cursor = (Cursor)f.get("cursor", null); + useCustomCursor = f.get("useCustomCursor", false); + sourceActions = f.get("sourceActions", 0) + & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK); transferable = (Transferable)s.readObject(); listener = (DragSourceListener)s.readObject(); @@ -630,5 +660,5 @@ public class DragSourceContext * * @serial */ - private final int sourceActions; + private int sourceActions; } diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/BadSerializationTest.java b/jdk/test/java/awt/dnd/BadSerializaionTest/BadSerializationTest.java new file mode 100644 index 00000000000..5198b529f7a --- /dev/null +++ b/jdk/test/java/awt/dnd/BadSerializaionTest/BadSerializationTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8030050 + * @summary Validate fields on DnD class deserialization + * @author petr.pchelko@oracle.com + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.util.stream.Stream; + +public class BadSerializationTest { + + private static final String[] badSerialized = new String[] { + "badAction", + "noEvents", + "nullComponent", + "nullDragSource", + "nullOrigin" + }; + + private static final String goodSerialized = "good"; + + public static void main(String[] args) throws Exception { + String testSrc = System.getProperty("test.src") + File.separator; + testReadObject(testSrc + goodSerialized, false); + Stream.of(badSerialized).forEach(file -> testReadObject(testSrc + file, true)); + } + + private static void testReadObject(String filename, boolean expectException) { + Exception exceptionCaught = null; + try (FileInputStream fileInputStream = new FileInputStream(filename); + ObjectInputStream ois = new ObjectInputStream(fileInputStream)) { + ois.readObject(); + } catch (InvalidObjectException e) { + exceptionCaught = e; + } catch (IOException e) { + throw new RuntimeException("FAILED: IOException", e); + } catch (ClassNotFoundException e) { + throw new RuntimeException("FAILED: ClassNotFoundException", e); + } + if (exceptionCaught != null && !expectException) { + throw new RuntimeException("FAILED: UnexpectedException", exceptionCaught); + } + if (exceptionCaught == null && expectException) { + throw new RuntimeException("FAILED: Invalid object was created with no exception"); + } + } +} diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/badAction b/jdk/test/java/awt/dnd/BadSerializaionTest/badAction new file mode 100644 index 0000000000000000000000000000000000000000..d8b58ee48eb957adcfc1eeba826815e9a890be27 GIT binary patch literal 1982 zcmZ`)O^6&t6n^uwJG+~#o7ou5l0+l~qan_q8b6=zS?-8Hk7?XGI8 zs&{%Mu6Xf~=rM-`g$ThP^dKI*d64)A=H$h&C?cqb1wp(h0l`<*J3TvDZ5XDy>b>v1 z`rdnAfAl+yC<(_dkquHIU0w0HUzwMr^Bh$=mULl*a=lr2X4}8>_o)ID>o7_@%>;K~ z+!J9WIG$^G&@lj21gO^fyR!~V_=waLvGk~hnSl)kshS|^5gLvP$vTW{I504}C~#vo zk#NW+7i$(&Y&s4167{t6=J@l6!&lzV10)duOyM;i$gbfKmjhCc&FOblJSlSH=TiGSGl(R?u*}_y>+7iCt7fX#9EvaUaXcS z;i^q#gU>S+2}OcBjJYD_zG_5ucsL`dNhQPZFH?V>XtF}3LIztm^ke7#nOT!O!z17+fQRN2Ft;Asg?+7cwy{t*k9?p5dRH#EKE zV$vOH*d1zqwh%6bXsNJ)RcVrC6itMJXaGb+5MD#`K*RONHi89`s$! zZkdQadMy*#p$NC9d;3STt1!K{VwmD;3TgVbiu@s9v5nW(Fg1vE4mV7sHr6k_+e@$s z6PQ>aJQCp4?DVKbaqJ%2mv-tlGh(`h93>1p@e;R+eVzuQv-fQ?KJg zUjG^9^45*dwk|gww^BE$taQy;!Be{x&#;jj(l@h{M>g#sBb$eIRew%O(sP(XH8V<8 zFt!JgfQR)$)nE?(0t>(FxL*py@$tYBexJOJ4i#*!8c;+Xcwgp{S503lH6* zsYMbc8unYV7ll=96Luy-uz}iV3fGVf1&A2CueRS?c;_wePTs)oLt*Y8So9L+W8gIu zm+=!}FpnVQKzXfffBMB0d1mvQN3HUv^PlEsT$OP;ii@1hG>7gp855Sjw4zpP{(Sn? zd$+EC@<<*^D0yzIwcvG_@KBi6YbXXNT>EVYFXI=aLuD#A#bnM^Ms<*w9xkzE?hjGt zQLJr literal 0 HcmV?d00001 diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/good b/jdk/test/java/awt/dnd/BadSerializaionTest/good new file mode 100644 index 0000000000000000000000000000000000000000..3580da63bb87405729e64dd2e4967eca31f68126 GIT binary patch literal 1982 zcmZ`)O^6&t6n^uwJG+~#$!rW}Ng@)0(GX`)a@!!Tvm2SD*I9O!6=zS?-8Hk7?XGI8 zs&{%Mu6Xf~=rM-`g$ThP^dKI*d64)A=H$h&C?cqb1wp(h0l`<*J3TvDZ5XDy>b>v1 z`rdnAfAl+yC<(_ekquHIU0w0HUzwAn^Bh$=mUMoDa=lr2X4}8>_vr!@>o7_@%>;K~ z+!J9WIG$^G&@lj21gO^fyE6_<_=waLvGk~h>46OfshS|^5gLvP$vTW{I5;p`6u2>y zNH}bhi!}=>Hl2ohiF(?3bNu-u;VbXw0g?y+a(ImgvTHcRrNQYxuKx1zxwB{Tkgvly z30Q}Rn63jwO%u#|Dh(D8-l;aVWV~a-O=Te2RjzHn`{MWKZrv!r$rcir7prAS zxN1|`;B!nxLXn^jW3Gs~uNqMu9?l4AQpqs<%haDEnygT%kiiy|C?{?}{R=Qo4bKZi zt+!yhy@S1hb|kn+dul$lYr(iZGfr5DHOe|`x&@PXFCpm?jR-PUhht3j)3Xq0-N1!p zzE3gg7*mVHTc>^tCPI=lF|8&=n2C__?4`^}NavF%U>?&!&l*f}+O-8x4IcDburHFd zoq3FG;0+P8w80d!366A`uh;63OYqlopiDfEDtlNHJT1W~TY{w8KVkvXz3S}!hNhQX zOu8ctyF+bK6M>L;KIyGt9ceR;A|bK3C(`1&6l+vnlp>;1_iDRsOy7C2RA>(6LEqKv zmWk-2*D{eEig0_nw|_LV3R8P4hAFP5kfv{|$R7e0+jwma2L`dujqdkHpS z0uu{_M*^IhogTF)j@?81(oWrm25*jGbDPGp25Bt~S{VbIYMOP)fw72EY2upq0~je1 zQ5>n^NGmvCmesL-jV)3V<#5sp_$!(PJ_jMUvr69q--b4#;YY)0C}Kq&oYpfc=9Qpp zR3{{!P)Vh>V9s{Q&l2@ShhvAze;&KLecSzE-Hf}0Q`w9fz50Kzm8BW_>&?Q=)a%5M z*MEk&ymjNVt;>zat<+5_D_yfz@YHU_Gi>CB^v&$#kxe_u$mZc))t{A;^c<#8&5TkN zjO`&L;GsSFq=L9|wS4lf`}$Kzbb|CHXnM$+S`Ez5lGnaFe*J6mb^&sAC@N&h!b7)c zYLP^VhW(c81z{E2gq@BMY@qg;!ZjpA0V2lktL^vZ-+9ZslQ*#YP?-A%7QKY|7jD{8go&!=C# zckB8mkL00*lIO-+3too_4~1#HhGKxiwcmE|GJZiiRHkxMOlDnWR0oOa;SyWs{t$H@ z#oC7W@r{dLe*5I-?esGd3ZJzZO78_IcGoCJ*8yK?b{T8dJqqt#YkaUZpY~WeyUFYe z%l7=Y51;t?$WK>p=b?b&>j|#nkotAVGk+CETpzy(8uF{pCWaPMy4+u;OZv~yH1#*_ Hf0FzQUcHBk literal 0 HcmV?d00001 diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/noEvents b/jdk/test/java/awt/dnd/BadSerializaionTest/noEvents new file mode 100644 index 0000000000000000000000000000000000000000..eee6a49949c2f004ba7562890ec9601e8005a725 GIT binary patch literal 1670 zcmZ`)O>7%Q6n_3m9H)fVDHTL*IaC#es|uVVt1R+^dj=6i46_ul8P z{(uFe;qqNJWOX*O^*{#oP0hkrxv`1nTSG4GQR$WA;L%^#OHgjXBJ-_K(u1n62C91^TylvO zQQf7}?%n0S^**S+@!a5@FN*+a3;+du#?$#TJl5mU^*`_b_VulsH;PbfL6t=!lmkrH zgR9jk>5Dcoufl0W6c zFtIW$YQa)ZB{DGWxCPJW1?q4uF#Ov**kqRNb8WDKU8r!$dJzv^gDR&wZ!>e)g|+?! zdx(B4dYzBWR_51*sdDK-{mpGinZX9Fw^uL1bVk|p+yjIjJhPu z4)YIr(1qmzOFNiWha=4LK*)SklWf4Z(l`>nu+i8#tVlj`1emsr#$7lQYu?XqEDZ65 zh*=p5BV2+DBN5nx78Fwa#vas|?{njfO(io5t~m+ky^)TsBQ+4Os zq^TmM(Z1pzAdkG0#Ie#y?v;$(9!HKEJ6gqD+i~-x5A~g&XoK!hoAq7O9Z|#pZ|yN1 za)dk8y;IZr9aufh7^Zk9gQUJ~VEquV+{b5II6I4V8xJT_ANkAP_7)t$GA0%&pG7z| zlOA;_o;yST%7nMM!P^wr+|*dkA>)$JY8aTR~acO@RBgHCCVly9U9|z1; zb!p16LrUWUPFe}SGA;0T4hoZ1wg=qJ9mMlL3yTAl81CV;-Y|)*M&-_hJrH9$AD|5I0Gt9jw559eJ zul{3AdEo^68(OomCk(0?DOHJl&tn0eJ(W); zi2L_zS0DG@dl?IzV);^ZJ?~td2Igq#yFXw4=tuTR2?{MJ8?2Iphf$fhNaNJPQ?A(C z$|*L5U5gQHruMnQwY5M2V&wP3@fTa4e(XOg66_fi=BdG$bo gb+Y}<_wVbQM?byblsB9IEH}Dp=+Y%xO)^dY0aWZLxc~qF literal 0 HcmV?d00001 diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/nullComponent b/jdk/test/java/awt/dnd/BadSerializaionTest/nullComponent new file mode 100644 index 0000000000000000000000000000000000000000..3166bcbcaf13ab9951b779a7b3d14a602b1a030f GIT binary patch literal 661 zcmZWmF=!M)6#cimiCz#fsF)N13$YN~L$M2pXAH7tV+4ajQqAs8&WYKXadsv*CwO9G z5o2kQBAtbzl|}lXqM((P2qD;52sSoKD*w)McVHHV9p=CP-uv&r{slvYf{A6aNg*T3{L`~3He4?zUOB-Kpt80x9WrQlfCa4aqf8c5KX>b*|J zFp?qDO<`0@HH;51DAFiJsuBW+g<>tnH5?g$ZU}ssv{w;okF& zHzz$ffRQFfYr!KJNmp2UcS;zp7vPW-(#UxXrbSidDX`6?D6OGf2d@~;{}K_GM!T4H z)@R;-I=8*#CaUKmV@r&x;u-|)JCtMVVCj{+{^ne}%=eznz5F&^=Br?|KS}fUysQ6s zb@uz%?%G=q`~^6e3SO9;W)XNSTZEw`BbsRN7BAa-&;=LBpK?vTuhzTl`&;`Dw5`y| literal 0 HcmV?d00001 diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/nullDragSource b/jdk/test/java/awt/dnd/BadSerializaionTest/nullDragSource new file mode 100644 index 0000000000000000000000000000000000000000..62a9bb69bc5775d6d65521a78e6c6629e5179a58 GIT binary patch literal 1978 zcmZ`)O^6&t6n^uwJG+~#$!rW}F%b#DXoxc?xor^F*^Nxn>nuCVinFKc?wZ-kc2~7k z)jK^BSG;&g^q50}LWJNCdJqrZJV@dXn3EU7qKKd#76kF41O#7I@AT|swPBd*dau6s z>U-~f{qgTGq9h!y zNH}bhi!}=>Hl2ohk$T#BYy5>H;j16y0g?y+a(ImgvTHcRrNOB`uKe=J*)yl}kgvly z30Q}Rn63jwO%u#|Dh(D8-l;aVWV~a-O=Te2RjzKo_tN)gZ(c9Ji55IeVlB=IFILNv zaMh-=!RMHYgd#y5##|9|Up1mSJdzRAq>^FySExTnG+CiiA%iU_QBK@|`sZPs8lD%3 zT5rL0dk1?1{YY?u_SAgp*Mf0tGl#GcYm{}?bPFc&UP96(8WCiy4o8{lr)MG1yMYVI ze4k>}F{T!Yw@&>QOoSw9Vp>g#FcTr;*-M#|kj^Jjz&xgdo^_byv}+5X8a(K=U|%F@ zJ3BG5fj30V(gst^COFb%zFwLupY+zSj6_WfBb#=Rk8K=mhCY(DjgYwHlbAC9i*X?Aq7lodV?QP*lj0g@T`*q#gr}gm+6rHGc-y4P5YlD F{{k4QhVTFY literal 0 HcmV?d00001 diff --git a/jdk/test/java/awt/dnd/BadSerializaionTest/nullOrigin b/jdk/test/java/awt/dnd/BadSerializaionTest/nullOrigin new file mode 100644 index 0000000000000000000000000000000000000000..e5daa2a136381cf83c31fd08d9ae24064e688ee0 GIT binary patch literal 1936 zcmZ`)PiP!f82|Q9ve~q;-PB@3Tai+zmb#1LEk#^6scbuOLpDU*oZig5-MnVzz45&_ z*;%O-FCMHOduUOpQ2c`)#JdM6>cO78Sb`#gdI$*OMJWh=?@e|#+vvcuZ@%w)zwiHV zKK&ggl!TL)$rh=SzOMS*uP#W^dx0t)OS-s4xxQL>ZpXj-_t^pzn=nZ{%>;L#?1?ZE z9Q7I=b__rb0cwq*bl!n!ACcN3mLAnGH?lz|HRB{zq2Z*EtjD;9!y}_*fr9x&!ciMs ztXWXC;k4Y#)YHy8`t*8l)W(8_f$YKPtofX zQ_IBLq<#mcLz1*HtTsiM>5%d4rNT)_7n3Mp9@D|V8q9Fow+&D&9t=8gAdi_oS)r z*qAg#ASCK%ybbIlZO2h0BsTX{+FX}nk7~wJ1>?B&7m@yyM`4Rk3Qa7 zC9+2mZr}6{RrBjGyT4-?;(7{c=C+3QL%?De)7Efk0xC+x4SRgzS z;MVNUs6}zC4DCz1eH&Z61%|_I2Fn_xy)WLo536=jmesAZF`@^PLcL`^*9W{LQ|F_mw=IO7u3b)c*r^asmr<*I= zH^11v(t5&D-Hfu-Rkwtv_A;KSMsAGXj3kfMw2O?@Ji4d*bt%cfVG7yIFjc|W9>D@U zvM-*L5ZA6%PTzCid>RX#VEGa>Jz`BQ2WDu=8{eJ0@eO&e0J$a<6;{c@L%(Qxkwl4x zgSOa9!ZNmTI~O6?i0w0mYe|L#M2y|nJ0C8-|E_m8Z(t7~F%K0My@c@?cn!r>{6rYc z9dCs3NRz6}-6Wx3gkq&>3i@#nLz-@kq1^T+Z~LK1Uhtp#tww1-r)*}(NiDmrLq zauvS`Jt|WIDJFGS86Lq@2YAFGc+f?YN3pgpeti4VSKmJMWj7`MQ24CNkW4Q^vA;n% zUL92+jV`N$y&i@4ueUzlUQDN^l4VkVY1Qg~`{c==kNx!4ojeqfPCda@98$jtdFHRf fgzF>6YsjxZpO~_kX5{`deIGt!`!N*S|0MYrcdvUa literal 0 HcmV?d00001