From 975b73eb369d5b1c555653dcab7953a4631eeffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20P=C3=82RIS?= Date: Sun, 17 Jun 2018 22:27:47 +0200 Subject: [PATCH 01/39] Fixed deb package --- phoenicis-dist/src/deb/control/control | 2 +- phoenicis-dist/src/scripts/phoenicis-cli | 5 ++++- phoenicis-dist/src/scripts/phoenicis-javafx | 5 ++++- phoenicis-dist/src/scripts/phoenicis.sh | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/phoenicis-dist/src/deb/control/control b/phoenicis-dist/src/deb/control/control index f731eef5f69..3754bc776de 100644 --- a/phoenicis-dist/src/deb/control/control +++ b/phoenicis-dist/src/deb/control/control @@ -10,4 +10,4 @@ Description: This program is a front-end for wine. Copyright 2011 PlayOnLinux team Maintainer: PlayOnLinux Packaging Homepage: www.phoenicis.org -Depends: default-jre | java8-runtime, openjfx, unzip, wget, xterm | x-terminal-emulator, imagemagick, cabextract, icoutils, p7zip-full, curl, libxext6:i386 \ No newline at end of file +Depends: default-jre | java8-runtime, openjfx, unzip, wget, xterm | x-terminal-emulator, imagemagick, cabextract, icoutils, p7zip-full, curl, libxext6:i386, wine:i386 \ No newline at end of file diff --git a/phoenicis-dist/src/scripts/phoenicis-cli b/phoenicis-dist/src/scripts/phoenicis-cli index 5fb3f4d8dca..c6e8e7126bf 100755 --- a/phoenicis-dist/src/scripts/phoenicis-cli +++ b/phoenicis-dist/src/scripts/phoenicis-cli @@ -1,4 +1,7 @@ #!/usr/bin/env bash -CLASSPATH=${CLASSPATH}:/usr/local/lib/phoenicis/* +POL_HOME="$(dirname "$0")" +cd "$POL_HOME" +POL_HOME="$PWD" +CLASSPATH=${CLASSPATH}:$POL_HOME/lib/* java -classpath "$CLASSPATH" org.phoenicis.cli.PhoenicisCLI "$@" diff --git a/phoenicis-dist/src/scripts/phoenicis-javafx b/phoenicis-dist/src/scripts/phoenicis-javafx index f7331bfaf76..19fb8f281dc 100755 --- a/phoenicis-dist/src/scripts/phoenicis-javafx +++ b/phoenicis-dist/src/scripts/phoenicis-javafx @@ -1,4 +1,7 @@ #!/usr/bin/env bash -CLASSPATH=${CLASSPATH}:/usr/local/lib/phoenicis/* +POL_HOME="$(dirname "$0")" +cd "$POL_HOME" +POL_HOME="$PWD" +CLASSPATH=${CLASSPATH}:$POL_HOME/lib/* java -classpath "$CLASSPATH" org.phoenicis.javafx.JavaFXApplication "$@" diff --git a/phoenicis-dist/src/scripts/phoenicis.sh b/phoenicis-dist/src/scripts/phoenicis.sh index 971b6c037d2..19fb8f281dc 100755 --- a/phoenicis-dist/src/scripts/phoenicis.sh +++ b/phoenicis-dist/src/scripts/phoenicis.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash -POL_HOME=$(dirname $0) +POL_HOME="$(dirname "$0")" +cd "$POL_HOME" +POL_HOME="$PWD" CLASSPATH=${CLASSPATH}:$POL_HOME/lib/* java -classpath "$CLASSPATH" org.phoenicis.javafx.JavaFXApplication "$@" From 41ba39feaf129a791a0e0dd05858cd590bcd2bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20P=C3=82RIS?= Date: Sun, 17 Jun 2018 22:32:08 +0200 Subject: [PATCH 02/39] Fixed deb package --- phoenicis-dist/src/deb/control/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoenicis-dist/src/deb/control/control b/phoenicis-dist/src/deb/control/control index 3754bc776de..f731eef5f69 100644 --- a/phoenicis-dist/src/deb/control/control +++ b/phoenicis-dist/src/deb/control/control @@ -10,4 +10,4 @@ Description: This program is a front-end for wine. Copyright 2011 PlayOnLinux team Maintainer: PlayOnLinux Packaging Homepage: www.phoenicis.org -Depends: default-jre | java8-runtime, openjfx, unzip, wget, xterm | x-terminal-emulator, imagemagick, cabextract, icoutils, p7zip-full, curl, libxext6:i386, wine:i386 \ No newline at end of file +Depends: default-jre | java8-runtime, openjfx, unzip, wget, xterm | x-terminal-emulator, imagemagick, cabextract, icoutils, p7zip-full, curl, libxext6:i386 \ No newline at end of file From 03affc4f83f8cf8961b076fcb2b4c91bb5a4d9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20P=C3=82RIS?= Date: Sun, 17 Jun 2018 22:38:21 +0200 Subject: [PATCH 03/39] Fixed deb package --- phoenicis-dist/src/scripts/phoenicis-cli | 5 +---- phoenicis-dist/src/scripts/phoenicis-javafx | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/phoenicis-dist/src/scripts/phoenicis-cli b/phoenicis-dist/src/scripts/phoenicis-cli index c6e8e7126bf..41ca2d5a2a3 100755 --- a/phoenicis-dist/src/scripts/phoenicis-cli +++ b/phoenicis-dist/src/scripts/phoenicis-cli @@ -1,7 +1,4 @@ #!/usr/bin/env bash -POL_HOME="$(dirname "$0")" -cd "$POL_HOME" -POL_HOME="$PWD" -CLASSPATH=${CLASSPATH}:$POL_HOME/lib/* +CLASSPATH=/usr/local/lib/phoenicis/*:${CLASSPATH} java -classpath "$CLASSPATH" org.phoenicis.cli.PhoenicisCLI "$@" diff --git a/phoenicis-dist/src/scripts/phoenicis-javafx b/phoenicis-dist/src/scripts/phoenicis-javafx index 19fb8f281dc..48f2a992bb2 100755 --- a/phoenicis-dist/src/scripts/phoenicis-javafx +++ b/phoenicis-dist/src/scripts/phoenicis-javafx @@ -1,7 +1,4 @@ #!/usr/bin/env bash -POL_HOME="$(dirname "$0")" -cd "$POL_HOME" -POL_HOME="$PWD" -CLASSPATH=${CLASSPATH}:$POL_HOME/lib/* +CLASSPATH=/usr/local/lib/phoenicis/*:${CLASSPATH} java -classpath "$CLASSPATH" org.phoenicis.javafx.JavaFXApplication "$@" From 750a9a1646c81e8fbc296852b13850850d65c2f6 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 25 Nov 2018 20:31:18 +0100 Subject: [PATCH 04/39] - OSX: Build instruction - Proposal for a new icon --- README.md | 12 ++++++ phoenicis-blue.svg | 39 ++++++++++++++++++ phoenicis-cli/pom.xml | 2 +- phoenicis-configuration/pom.xml | 2 +- phoenicis-containers/pom.xml | 2 +- phoenicis-dist/pom.xml | 2 +- phoenicis-engines/pom.xml | 2 +- phoenicis-entities/pom.xml | 2 +- phoenicis-javafx/pom.xml | 14 ++++++- .../package/macosx/Phoenicis PlayOnMac.icns | Bin 0 -> 179527 bytes .../src/main/deploy/package/macosx/icon.png | Bin 0 -> 67628 bytes .../deploy/package/macosx/phoenicis-blue.svg | 39 ++++++++++++++++++ phoenicis-library/pom.xml | 2 +- phoenicis-multithreading/pom.xml | 2 +- phoenicis-repository/pom.xml | 2 +- phoenicis-scripts/pom.xml | 2 +- phoenicis-settings/pom.xml | 2 +- phoenicis-tests/pom.xml | 2 +- phoenicis-tools/pom.xml | 2 +- phoenicis-win32/pom.xml | 2 +- pom.xml | 9 ++-- 21 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 phoenicis-blue.svg create mode 100644 phoenicis-javafx/src/main/deploy/package/macosx/Phoenicis PlayOnMac.icns create mode 100644 phoenicis-javafx/src/main/deploy/package/macosx/icon.png create mode 100644 phoenicis-javafx/src/main/deploy/package/macosx/phoenicis-blue.svg diff --git a/README.md b/README.md index 277b8b4f4cb..85d4b3a4da4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Supported engines: ------------ ## Build and Run +### Linux Build ``` mvn clean package @@ -26,6 +27,17 @@ unzip phoenicis-dist.zip ./phoenicis-dist/phoenicis.sh ``` +### OSX +Build +``` +mvn clean package install +``` +Run +``` +cd phoenicis-javafx +mvn jfx:native +``` + For more details (e.g. regarding dependencies) consider the [documentation](https://phoenicisorg.github.io/phoenicis/). ## Scripts diff --git a/phoenicis-blue.svg b/phoenicis-blue.svg new file mode 100644 index 00000000000..f8a25f01a74 --- /dev/null +++ b/phoenicis-blue.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/phoenicis-cli/pom.xml b/phoenicis-cli/pom.xml index 27b531a036c..6ab75a05bc3 100644 --- a/phoenicis-cli/pom.xml +++ b/phoenicis-cli/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-cli diff --git a/phoenicis-configuration/pom.xml b/phoenicis-configuration/pom.xml index f4b1882e9cb..dcb35c3f0d1 100644 --- a/phoenicis-configuration/pom.xml +++ b/phoenicis-configuration/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-configuration diff --git a/phoenicis-containers/pom.xml b/phoenicis-containers/pom.xml index 2759988b736..3460d94c9fa 100644 --- a/phoenicis-containers/pom.xml +++ b/phoenicis-containers/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-containers diff --git a/phoenicis-dist/pom.xml b/phoenicis-dist/pom.xml index e574f517c5e..9f3f4abd4b7 100644 --- a/phoenicis-dist/pom.xml +++ b/phoenicis-dist/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-dist diff --git a/phoenicis-engines/pom.xml b/phoenicis-engines/pom.xml index 94a5a30c453..9717f01e99c 100644 --- a/phoenicis-engines/pom.xml +++ b/phoenicis-engines/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-engines diff --git a/phoenicis-entities/pom.xml b/phoenicis-entities/pom.xml index cddd165eb14..99a3d4497d7 100644 --- a/phoenicis-entities/pom.xml +++ b/phoenicis-entities/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-entities diff --git a/phoenicis-javafx/pom.xml b/phoenicis-javafx/pom.xml index 835e74212a0..2dcb01254c1 100644 --- a/phoenicis-javafx/pom.xml +++ b/phoenicis-javafx/pom.xml @@ -21,11 +21,11 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-javafx - ${project.basedir}/../settings + ${project.basedir}/../ 4.0.12-alpha @@ -143,6 +143,16 @@ org.phoenicis.javafx.JavaFXApplication + + com.zenjava + javafx-maven-plugin + 8.8.3 + + Phoenicis PlayOnMac + Phoenicis + org.phoenicis.javafx.JavaFXApplication + + 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 literal 0 HcmV?d00001 diff --git a/phoenicis-javafx/src/main/deploy/package/macosx/icon.png b/phoenicis-javafx/src/main/deploy/package/macosx/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..85611fed0fbd70c4dc3dce7bd6c41a593df605b2 GIT binary patch literal 67628 zcmb4JWmj9z*G+=Ey97$n;!xZv?nR1gixig@C%C)2Q)q!gf#MR1OL2FX;%*^%`2D}Z zGcWGkweFoWd-mGrjGT$mR9C>kq{IXO060pD@7@CdpqEz=03G$Ey7roR1OU;ll-^0} zcr6{TXxQd_p$@!&D=ocNoAUULW-zb3XmBLm=0kq9@#%e22pL}3s`a8)RY`D2H1C4u z`NT3cX*)((cV^FKxJW2&nor{9M@)C(y~dwLIsD!!Gv())T=d*!i*wogL3sXXqG^d| zcX#&Fq+ofBRD!dz-i{`Ww(x#AOffy-S;Kb)hTT971pr>hKR~H3} z>B*|x(_9I~XlM542nCW&BvA9d#gb_o3ChZH+?Oth#eM)D_t8`L*ctbJS*hWhQDK?K zK0DFzh+-+xu`y^)IJ&1S*doWZ;6i;hB0bdy9UX0)s&$rQcPk#ZBs1-|$nB*t?&G~o z0)l6-@2%Ne>5!99=o3B}_fh2bS{(Oqeq<0t~8K|j7}pJ--ZLNk@{ zPzLoSjsdQ6Qcg(QJ+Q!n@QEjsdPkMPlj4<{#7C8u_s5_q^)-gGSy%SEopS zDr0WFr*I~c)K7=Ih1}V9a`3jZCtz8(`r)G6(JzEZC{X%i;yFWrrZS+*S9;2nYjd^K$w9Mp;7SPSS z5Fzo}1a)*Y|9qlGhYjOer*hV!J#J*MBotG?Fhigy7~nl>Md>?%l>y|dAK<@V7FhH6 zg*46sTmK`M4;H$8m_*kX*NJ6#zlW#TEo$b&3+~FOt>wEN4J}R zJ~|42vV)MHU9<-RK1Bprop)5tUazNw7sk!Af?)lUl7DFPeJ2j91E2q{wG1fguDZ5= z{#|XU3>En8$gi6sU`LePh{&YWCQXmPi^StDO-!epx;8=Bmgw25*jIu^WbyGe6Dfam zllV!+pf9j}1=nkMF5boX|2iHUJ03kA&GU^g>$(%%|5l$`QCjP6v-hMXA&j<==ezGV z{_s3@1{)J5mdmp`q!k&ignl_-A?Br3sL=HyYO6qG88&MwBS5mts<<9q8&aIpZeSHbs!n)#^yn^qtH zm54PB!iD&K)rlg&PX})6V$d7SE&|ofAUR?=lJ-r(5`V#@TenXNjOvAE^uidT(}^dhaPViFp^w()$0F3;12G70})$ zt#=HQ>g*~}Pj{?LV>J4_ldq=hHs8wCiYmbN}Y<{OM8nUhLV} zuhVq@dkBq>iLu!k(p|}2*wge=@AE(Z2+Mv8#f8U?Ds1MWiDD3oL+UC^Q_xwl1dXw} zLSS)4u!z@yTCxnYS2e{6_?em{=e;cHgu5iR+^JK(54#dUz| zx)0)>_R-&z?JR>KuIGKiS=3q1A>F}MBr{gGu%@szNw>5M*FQ7lg1u{ioR+uaz1sWr zn}hrG5YNlEG3!cY3Rm0d;rp`_IM4%K&xlWEvxV{bh;zdn*l{0f3tqSzd`a1AVVw71 zpL-&J`AUM1CfssE81ugKu>%r+)PZ_qKT6n11O{!a|AWh>Rt+JPH4}q}58v%pH4n3kneuo0PgAbDv zX2`MHfLQHXFZVdHh9-l2#JrO58fj{$xp4`1QgfDwVwsz;23_#o*%ue)dodbdbg^6a z`8Z110dz|^{cjDbcoLQYUoV zEsLQdb?^Gy)COGnw~O4_F$ADN)f593+#nPWWR~d5lFb6#Xz`u`*(iSzW!2Zy^v~Lx zp*kM%!;PLkJLM3^35Tz5IkC6*=dObuBy*Y?W%Q08r3B~?FP|F=bIdD}a+}Md!dmvw zU&k`Ipxsk%n1YDqKD})j&nsc)ErM4daJndVm#tI5ze|xYe9?{~b}j^$J`T-MUSJ0h z-~_0rP^4YJHO~5J(eS#a*nQuim3q^$F=(ljW3dy_3=$}quc+dTBF#?_6taA-!$Ih{ zU@e5++?Lp_>D%FB_ z)>p7@n^7|Oa6lDzB!R5=J`dO>3ews}AN5#fzI(y*PsVw}o$;P@AwHhzm?8j7<^Oj9 zQo~p=(w}arvb$3!-MkiG5ISDp<_!|AXZ(}%vznfMr zFz~KYZ;q~5XO8Cw8f_o@a)K|8SH2WS^e#(RFANY}EO9H=rNquACyqg97Iy#k^>P3k z6Ia@emE?oGr#3mIKd;>w;H&q{NuDS8EG4}=GSpvKuCVU!RQE%LhE>2z7y%26OCmXV z>=QkwE;`FUwW@>Z1Jw3J>mhNPO{|oK5(Qu>6w+-_pAFTusSOOU^|GiN9p`-R@uN zXhu|W9>-}&6hBEy7Lx5UY{)}tP*{IJd!DDe#S5vWI=Nx3eCreXGV?Zw07zwgowXWy z*yKGvLIlan?54xN>1~BpW%Ju^f~7>Fq&=j815XXc93ZqfPPcXKYI4x z^mOM6F(1dc^V0u0%Pic+c;S%pMw5~}H9yHUiV>E~U$Gg;676V?dQV&eBu?9Rquzh- zUO4z(6K{w_3ZZeJdbgnYvTPOZcqp*i6{`KpL zw`>~Jwizf?Abwcs26d3=9NNw=e-0nz&2#-%%kfYOvTM9m76|YsQY@;b&d=exDwT=C z@KZ02Vgm?1S13A}06eN2f zNsDUszlO{`2)8M=%Auc@^KXv`|51iu}Xp5U|pkuqT5!K}w@l_t5XeUY=Nreu?O$FCgK zNw)2Q#tnH`G?-Fawr$wfIpq}nx)7FRzzX$8Vi|UBdnj@Zpm6|24e`R&OEn=ToR^|r6RpKxMngPaVIPEoZTm#u^8 zVe0WMlhT4ilcS;lwq!5c}uZGB)miz-;fcJCFF$ zNSxAm#Y*h-mjHV6p{?C*^oVTTT)+7A3Mmr@Ft!oUDJM3U92gzlV@Vz^cil$hg&9#Z zP0~}~I7S{qz=w*{9})%v-<611L5}sBNKXGB<3uf|`*Yl~h1%Y`C|#>uBwi z%#SU``}@>ajyB7+CsoFAoiaRXxxjNIXqO$mBptD92fGUe`Zgk~j1fpevab(cZ}ffg zK3Gw`^Sp`Jd46Ag^@eyl3w+MUPQ3&v%GsaYZR{43J{S6l2V7z-EOrZtMcBC31^{#} z6I;k{@FK8T6FrL zf^>9}RUfu%S1?CWBqHbMjUyi^82O*1FGZ)B*3rJB4@SMY4{y_gS1qT#vEo zP6$qH3}m#41^*h1nH0X*HcnuFQV3%o;LaFu6ieUl9v{B5U3tf7K&HV_`Tr~k4 zJH~8b>@5|JrMw=7sN!6^whF_$a^~_}_fs+_od{^317Q4NZ~L#<9j80eqpJzd%J-`m z)>x|UjQSBy8yd}cZMsKbY`L-u36@H3Ep}Ow(=Sy1~)^thQij1Y71Q^AnCTQjRnN1(u zG@6Fp&*ltH?EL~HL$|M2|59_7;Rc03y=@xL983a3ItuYtC~1j*DWF40?*1WHly+Po z_+2E20`LXDJy3k>JI5PZmOH`t9iSWiog6;uz-eQFi4aa?^z@&h*QJpqjE)z)Z;a)8 z>?T0cT8N~+yx-}2zKM;ram5Inggf8PD8e6bzdL{}Q9lYfY%RBA+svfKILf*eRw>lQ z^N(UV z?|3$xDCuiIdw#fS*4ZaJvh)~bDBbAv@w$60tk03dZkhNuD6ceH>jS% zj`DWBwR80`>uEC6`T>bN!q9T-E*yWi@h)sv#}VMWN>$y0Y3QcpHHX(zSR+$uC?T-o zEgB)M88ZbWSMh2r8LZOTws}%+YYEwkYWi_bE&j2IYvLFh=Y_}KO$_~hLV?~UgWqst z%EJx5Yhn@BWHiZyHyJ!NTucf!Ctfm7mUr4@_7;pfn5?A4ZbHaa^l1OGGDn@AEp!L` z@{c6&<8@n9zqtF8=#9VXB=R$kEeub*MNE)30-rv%6x;0EVF$aN_>SvLN76ydp+3b);qptC)-uL(qbzW)6(juKUpKIvQ zMR35ipvMdM7s-zH3rN~G)co%E^7hwu;O|}L zjn^IVwqQHkZ>K3F4E;+IQI19$jlq%S)r5XA~BC&{(zm0R3z` zobbV4UWb|?Fh5^LKHG`3ZGq}LCMZW2E{=Zy+ z@okCYb9zf>NXhDQmrKbv7&AXifWT47NjU59%s&?Ab0jalQuzY|3&wR}LRUr6_>Yt6 zs6kDnZCkw!1>xOM%+yBg#5deZ2H`t@O40%zWV;!Y1;LB&Q6n@kLWk%i=C^*AylkVz zdy@gzG1HM!8l8ZZFyAjA9qY~uv`+LYBy48fIJ^EcH3}T6_t8rS^=C6IBakirE;(we z^@kDeC4}R@j6?#JzVoX)QK^5T?`Ku^f$Hg$L>cSb#ec~bbXIkh z=4gg%dS^eay*GXDsqS+OUuE__4A5M)9Ng19P)3VEmF5UbFU zz{A)JLCKa|90mN7d;CL|k#%{Q>;ewIDn}>LW%Xc+W4RyU1&)xX5g+b%eU(}}*DQDw z7^239HsvUT87W_^&&5rpP%TEHNRNEYqv*6%kW<8+zv7YTUAs7V6|Z&A0yW{7esVA3 z%Mw{Vqot!FN9tuI|28z+q5o``v@m5FME_ZdAOCe|5bGGFn}$rbB#AtP>fbO5H|({x z_4lOAa7G$(cdk6X_R81PK+JMNS~(`PMdVXk{oblTYY(QtJa5*t6L8~VNC-6-hB0a* z;O-xkE{)>tDjxXcS?cHEIepYSHSR*RPtQe<&70a*fWYg%fT!OFt6c2|%t~Bx7XB39 z1JwV?_SVQ-gttRf33fzf<)NH7gQcMN^hV-{@G5{cHn&lO6itw8tqLRck?|_efe;sT zuD}`OK7*l|#o7FRRVpvqNzeI4ue7u zbBSuH=FxC^G?}G7`9$BiZZY^ndA6PVYnYzgHT3Q*dkn+MGH1@6**+oQJ`Dl>D`Ir0 z#dkBI93O83vw-qKgRd&43Q$5E^9L68iXSBosd(Poo$Z`~4|?o0F{PqUo(Mcked-e#}XH0k7n&ZS7o$p z%@cARJHbTf66bVl1tLJU>F9Gow1sW{>8^Tboi&m9{zyT`IK&JN#PTFV^Z}v0DSOiN zevh@=c`iWEs|jX(OYE5phSo`4$_#9Jm?VaFGaCKoPrP^d^($#vuI*9A$pigZPd#wB z0pD*Y{i2J^3+XPN_c`a8f%?=6-0I=@WQlhtLJm)6C5H>OtxpTE9N`LVoPBPUq%p7Q z+_NmlI|^ZcFmHDmjtBI=yK-h9(^8<*0!vG3X-Jz1t6{GISJ0THjF5GmJ{KV71LtY@ z;km~jR)r&5ugko!3=MBDWEU+R?ecPeV7Oqe-$V=dSFC@gK#JHZmVZ7pAf=m@JoCtD zxDL<6QPgU-LCRvlm$TA|N4c>en8)ue&#EhekD{|8Rx~ zPEsLaPSRNZvPwdIzKtLM{I-UvfK#3q=s-hSy7l8-iI#n1*V{GEkPz8`1ylFa_kmXS z(>(tog7#pyx!Tpa(~>08*%>My<8>Z?(aGCCN52b^dqwqmfo5tEys0;EMBqG-=)mHT zT;`qMz=Z`X=&+AVs%PsdLE%>>B8=%u4F!`2P!bixZQJ_2z8e-B7t3J1{*3>Y;NpFh z%*5v<#%xEd-wmlO6W3d$hDJeCQ*(bBS67`p%U4G{nx;?vJ*_=C+xSGAoZGG*{WPqx z=?Pu;bL;r_{gau&kW4k3vAfRw_I>Yyd4&4edI0V!nFCRSBdJbJV|R4r>_0LVoLleF z_zo_m@sn-w6t{7`@+H`sM0&=yhbynuB`O>1!!*rl76hkG$EZBY?7?i_1WT@LU*SD} zP&VERFj~;MZ}5LcXk_dK1k(pk9>#m}&=g zZS^w%v3i{8)!{Dlelb|Bhi<%}F`B&Nb;bw^F=h!q0b&h-%3fEnnbnj6vw29S{ug^$ z=KAJgdNd-KP1Vb4qZ*(3(XCi;%;0k5=qA}An|23uTf z!hAJAQ?6j(R*f(ArO!qzfVNYm-*&8LqcPG&3xSyP-S$_hqKewpGfEd$@;Z;p^02-9bS;BlK|$SF$&&4}!Y;e| zEKGq>I~uEiz2}}R)j0puJr7`lWwXw{VsyWDTA?7XY!SUc`A;Vh{{9mFnctDQZyLbQ zNrTnNj7XD(jr{b=j=h-3d{Xan-Sso_-MTs>gz4aITnF(%mNI-B-&8#A{Sn#kE;GB# zk-4V9o;De`ujbj)uigM6u*H#(irZG(30aQ(^~+>o^}jJ#g4F_GbDPr9-W1Q~7b~FC z`ZU-!OUOyT`hBGnBP2gy&VqYHQ_P0*LHkSfkeOY=$Uh@n#o0MYT|36WoXF=BI4nw? z4ClKt36!ty>bl~uvJ^qVa^!yb46i;9IHRi^T8x7B2GfFaobEwTC=DiT=6^LQ?NduN z4F8lw0*_~cl-;NS@{pF%$`84;Sk=RbT>c(O^LjKALXQBpAO}5AV_<>KhzD$!Lnl%C^lY)ve!j}@>bdH75TMdzS^GT?j`_%QFUy`; zb+|Jwfl!q;#gJbh+WYj04yi$h<7^f|ard@EoSC#hNf243Lg3cw(z8Tpm>XFpcLD71JVLLK#dnQ{ z?TPO}_PobeTXsW!OG<&5XtiNh;{pB!ZKlh|K%P*TC+NUKdQO(=>aCz4@LEzh^geUU za2dI`Tl857{DRo^tP35MyDkADmFDStQ+RfQkiUCP4Hq$1hI{Dvo>p5JUQ&Y2OmjGy zKHl8Af4(g7E137HgjCCY>cUVh#az*8KA_f*CyQufBIl~%4xs!*a4eUL5Nl>B)NGf% zfDB%>zhVg=3>)Z{C{lX3C2dNUWTVM#%D>5k#j(P9zQpzDrQ2P8;6!%MiF4GvR2`pas16H1``{;x|?keRbrvG&QD)GQVhN=}-oU?oAl3XlB zl&MN$9!K+yd!qXkfUEWSUkpz`Cr3V|#bII-aON=RClc0{&pADNI4EB(Wk}NOywr-D zbsb^PO!iy0S{&OnDwY{lx^iQ++Ul}Q^80~C%lW&YF7K9vdUos}hN_D!TLKR^B0xgu z)zn&nIrZE27dN3jw>p0e{IfWP__8toi@f+-3Z(n$HNOZ)!tuW(k%9JSNGEfzlr4Vl z;8)(<#4`t+A1`Lj_fZ`n?)u;A_I{WaUGN*>nmD-k-9=S)I<*7}8J-X407`TNF<^Jf zJAgcjjg28h$c9ZV`jT|YP=Tk~Gm)LQC8R**Mc-gpbxN7$GnyT0RrZP?>9JtbY}sOL)xAEz>lueMVzohRSc zua0vcbaXx%I6i3WY1q?Y|EMtFLD3;zaB8k|pt~*AUT57t#A1%1Fmv^FE)hRZ2r!Kt z*aphsApID}+m7f2k5aAyCbsUtC))>`;@fko5HZ&?4MD!1LQ0>W%>do!HU{J7N}(GetK@Cj{9jB5TW=K}zkAi}z6D zUNdaQY}OgZ`%I`4GPJ(0wy=A`SA9QDB4&p%u)9_*J+CS%G=XmAWjA3(-k=`_Pz-|% z|FAL{n)vqc{=KS|^Rm#%?+d})737sR4wmYS;|8=>lKjDf4-Yb0ve1N9au{R4f^e;E z-D-^|dstHb>KG$LOqh}c)BjC5V!J5QIqF9_him{UKX1h9gr9mAsG>VNWNgjQ+;R1*k1A zvprt;&2>%HKTqV_b%$-fxYNl7v3nV^6#9c5Pb0(cp8)+~3gw?uNjVKis<)6+l<|Ck3!b!fa?-Ee8Y zJ&9NEfq@E+u@m2k*vJxlQH~q^ep?%zq{{S+10{rI=IAf+c%Eqx3~&Cdh*64W+xKlm zvG5`!!!jQtF4NY9zp>vkd7!O=w#iRfQYL*DYjdV1Mo!KZ4u11zUbG~3fb=JbGEp@B z-us1F5|V8d3F}N7fy8(M8D-QV#a)WLvPQ~eo-3cG!Ky4J;x#x>RrG@OHzTt* z8U}o4I5xrW_beQ|zw+?u=;`^%Fz9)dnq>kL6Qyu{=Z~Hw=Hkb1(r!+Ed-@-D z`gba{rI8RGH08V2t&cH?8x9zZ`wfgfb@E-&R?=m(p~>rtqJ&SbshNcfm2>iyHxm(i>upr&6Q_DCPK+Fl;1wdI%DJ7vvZl7Iz=c+i*|G6-mY zgs|i`x|ow3e@^myl{LPYdE>nLfuk6!xXpy=k%h01uM zkdK99z8WMdm|eF>H~ku-w^Sw~DTQ2Be;r%q`~nju2LDZ(%OO_4am0MS#gB z#3Nb8>|lCg!aH_$oFxbx$mt+5$24EfsqB+Q;=6`e+a@Vk@&OXKAh$10iL#>FkVgK6 z;Wk=T86!~4ky8)dKn*fVUz`0WgHkiDja%!(bqD!_zKl7qYhofn#q4GGd2zO%RCHQq zP&BN%R)W0w=SLZ0QrDMFBq-2BS(Zt7UKT%RFyzO98BC#`c!(r4#)|?wgjeknC1GoY zg^V78WyaO(O^K?N&TKIB)O9-xGVrZ1@c1)3;aoMdcOEBkyTw#;M`TxYw;dGT&8~vTED7W@+PkB4k^1|uAcTSP@y6QHDQ#F*~JpMFY7(f0$YJRDXxbzUzM#TrV*1ADbj60J1uN!tz z5hnnOX5^xv0s49Rb=^}CcJRCuURGVVjrp##H{1+F8!ciTiFu0~H{8mV^O3fCKNNfq zYsdU!-i@hYOhlPKQemyfd9w)7-!L|aBi)80&D1Q0d3TUk@Z=e!Ji)QWF zN~MZ?+#Da{2Gunu78ytQeefnWKLJq#xPDzHb&n3f*3s0}A+zHeP6WF%4@u|W=q|>) z>J+`s$hrx>nVzm2Mk$$h;h?iyBe3E%qB%YA!aw%y$Ibm?IUHxlp-~$vM@y<5SS}kc ze%!#sbwsyyw2X1M0bH{T?)Jsj)|SnW`|x(QI6>Vst=vVN!J>b$P-k+b5qCbM{JzD zk(Nwth^d)O`3DnnatZ}K=bdnc-Ba*|6=^`xkTPal-7qAX?=fN#?! zcyWXHGOqVQ+(bM7%uvuk(u>K{!_t4!+(3K+7G%zxDL-y44$0Anr`zmnU%4#XMFej= z&W2q!jxbYp&rX;%>1vu6naRqQCtqFORbrM_cuVnvSbrbY`q)6e)+C4){?5}(CbGCE z;(vu(8#lK?lkZZWW{^IA1*+@& zU_1^HV@-vnr<2;>IDdkTp!WP|g!_vFy{$cQP@9qT5Lpc~6nzt6LsU`kdRHfyS8zug zW=Tmd^YBr_d_KIX0=n>Fr?yz5K_?b>V5%m2TcmC3?Pd$Cg4Mso%kQ5f8g(!Aos^?3 z$PYgKI?}i{0j%}ZpUo;7X-MsANs+IFTLpr>JJyJi#BC7!TVWi?3U*&uU0_s{7{i=+ zgm9O@W}>`Ys9+W6M-vCEo+6CJQ>TcKypgX)F2h#HQYnyk{K2TGjWi{H`X(k8bDxt{ zaCPq@)v3Rqq)B!dfEd;~VP0FSpD%7uKC!L2HeCrNy3Y_?@oRkb@k>QSQ-c`NO@rfG zY56UJC@tX};|P`Rk{@~qdIw@El0{ zbTRQyyMP7%*n4vQFz=g)tZ$dYM{ZUSb-Z}`PQgh@)T%WxgQvitaZLHr`(FcYK&HM5 zgUDlu+gZBXm_-h6X#S<6e~BA!v{BXhpZCMV?+Pk^`FdS!iyQ{i7J>sQ)dTTzYNc*0 zAd*NOKd2F)68gmtYf-~m*O?j~ZrtN$r{8WQsB;s^HNB8=#bA)O6W3y~kaMxsLFZ$I zJ>LIyo2N<%h~TFJ$acG)u9aCYWNY&SXuaBGvNw@j*c`-`*ycOax$M+4E3w_jQjkdW z7B>==BcLJ&KMJyIrM_4|_A9ZUWJRYI#9!Za>h|+-FykBf$679EPcia@_V-+}Y9H?{ zIVKF>W0K=?`RNEUph9)uTrDs^3h;Yy+W~6n64hfFLeDl&(maG3L|+nBnr^MheX17i z9~DT(C-ZpCzzc@z{(z1`-j`guS3q=kLlb-)$c5zpGF((7=|$^4m`yWvcgu#Ks{Csn zOOxmC7}9X}W)vB^PJxjgRS7q(bLi@^T# zO=TvUlseMfNi&8*oE>g*fswmm1sU-v=)TzJD3>_Ij``U;jFpT;7_QrJ%fQ*m?Q>g@ zbI|*4H0?-lSEaGs8J{ zEXmCSj{V91f%&xU@}ICJpLDp(J2m)2`X$9~Pdd%svF>myTln#^mjkggXy9M3xgQcJvs9FL+|R41B>vRw(Z1y)e=iFO?VE)kaNIgh>p84&xmu%CCq zi68J7%sjRi@9Y=K|9TF&*8V*y;k&St+TH-$f&*mYDc*dLjc;vdCoOd&GbfSw%PB=Y zjA+}ak&-0W+Pt4%qmoLF`EgL{63p$Y1zqp#ytnU@W9lIgHk_BWm*7yB{tX}?G$?o* z=``bkUE(Conwp>`GOc&7v!erJIsDh}?IaOb=_`=-r>)JHHmG25{i~jquoyy`qscA9 z`op9Z^;zJrNq)-3bROiHJO$Pl2bRjVPgX;1=XdB!Ef5|{Xebe^g>cUS0!nrE>h+4fr z%#0)!s8BI5llvI|QaI5D^NTFPPXRRK^j@PBzi|5X?!5+Jpcy8qzmp zApJ}i<}&c73b^v!^xdz|&ae}P_})g&iK{JJH_n?X82-e%=bAOl3YxV9e+m$5Q=0$K!`WbSdET^s z=vfLr(C0m~rQ8wq+w3tM)98pCEIHGu?eglU`buh0dbGR&#vOW2l%DKzk2Ur`vgNxD zuM+{LY%rY{6Nez|ZwfFgznjhe>-y#&XhFNe`vsLXmf#y2bH}zDn|T=UO=sqMOuav5 z!a!~G1P(d(%GXY@=giqAi`2<92}uYRTA3VGv_YSPLViM}pO4)*Ab30F(!!OwqjR5q z`zF(qh}otLyq$Z47iTwyEF@ikH@ewebJcZh;_ae4OOfG9qhKvE{b@;3L?n+B(!_?m z6x}E#%Bd?&#ZeN!9jupYS-rea99V)LDHWRfD(4-D&5#(M=k;bs#$8>K|N32MD4A*c z9qpxzI^<4J5I5v?%aG`PcD`>{)_SO<;+Xip=)T|j!(2}-lRpU+B*9T+E>byvvGo{h zILdbe{^MU_I;jwLD0a)9A+R6v3_()_hJQ?j;o!XM+iM8F3eLU;AR}?@96!dC6X|yE zKPg;ks=HyE(rRoGQi$=D8bgmq9Le}T_z3?@kuGv(GZU;CE(Qm%vI{SZ{T)p&v|A4- zt_#9V!Dj1i63*91kpta&VO9xP{I-cE4OeI+Q%nB<>-c*Z7mM?2!V5hQO~YmVpUXh} zD7|opbSP@c6fY>yY*w@^*`3R&8Bj_H zQGt%AgywYyO#?z1ev4{Mb}8Zb7#mjWo-$yVAGO@m;ED(BRjmt+}T=VCFNg?;l6M9s@(dI$V`P%;aYG@OeY=_eW&(gDye_ykjSejaufb41N? zbzlZ@v2eNdBC#t5kD|-(YrG960kMYkRoEo>+xj)dMCC1i0UCV_e}{%*+wajjk6eKx1SM|wxSVTtzCWBGS8J1K_b+z!NbaX9iXl1Ao&PVjo%X#C0MC^(Gh;TZtNd*f6HjXdR)>o zws6U1E59bJ7^%cJAwovM1V!0}rIW=1zL%chZIau4PyKk&AS!?X8|7-#x66i8#p;6- zJnf&oZ*o*0Zr%7PJ01x$It8AoNCWMIlqqTu=su!k0w@>RQ&%^Vo^fVX;aOh%|1jmW zcB={PzN~o-6jxecO+kr?j!4^|<;24<`@hza1ZEuA7tBR+-owl3wCLKul#c1l8NUgX z(`->uwl56+nY|{Y6QUni<#jI$|0!WSQg0Xbl|p%-)nWA(*@UVuk$I8@oZ$-mgP_)o zhnS(Y>>C1AuMlZaE2x1~Z;ZCT#YKNN8@KaL@Dv(D%_Rf(MVySEg&2zD$?^T(*zcVB zVdnH@P3!|z2Xw8yllIr!8(9Kk`CW5d()tx88tNY8867PH#`;1kE6ZT3)l9?kc680} z5044<;6GE^p)w{G_UT1?_}i%G=Mz!Ru$D*7-{`lKm~==sK?6aRoUS4`*x2NI2rvl9 zJ}CUcP;j{RU=D7k>h%H0K^_k`1MT7Y0spcDJS^`4^s}wpOo;InLnV~RdvB>CWmw1= z33_d?=P62Qd}l;}f6qCEkWuW*{_IhF*x==$KtH;~SyPo?=57CsJ*KTzv57}!L4T_W zTZf_fxf>W2d>Wb9%9DeMcZ}7NUG{QeQih~*5fTG>FcVQ=EQC=)Yj+Ug+Ko4w#@|Q) zlHbcZ;09%d=le`EN>p+>k^vI+<*7F-3JmW~Un$}Pr3m*DRFHM|G2d0<1dJ#nD~QMb zD%=r*_u2jw57BS(&eqTNWYe-?iT+wo;x$X_DOON*d5O{ftx^>2cyPv?b=`n)Obc|M zqqoLkITx+|%M(4#EAQ0QowL$h7x#9g4wOg=$Pm@L$D+qZAUuDeM+6lKk)=vx*QJ}@ z!;xY&=WyzA;$R$Iit>-^`l0d+_YffMAEALA#$@#R{F=Q~4#uDl)D>H0A-_yqp5rpC zNPi5>MhRdEFA9*NZVCR?cU@VqiQk_VDlBdwbroF|U~xl>eS|3qTQ-(nbupGq){$U3 zi9L?~{q+UJ;kH}dF^HaMi9!y-rM$!&*<0A?8VSs?0HX8Vb9K&3UcuLuD*3|7zHV5w zQ=y4=6x*~98(Y(MDfDNq+_%*mhJMEE35{I3C1xXEM)7an0V1^)QeU79%`Xra(yUoe ze>ip(E9eK31U^69-Xn@K>DBmDmL4D%J#K9KdKJx#p_pafF!8@$iY>{0Rrzk=Zp>q; zQnIHMmw3&i-}exQ(61OZkZ<}kUqdx}gcZ)^F0%tN!{~~;3Y!(Czn?e57qi0g)42B> z%yCnr((om|YFdB73&ZSM?eq4dBTv|@!Fg}+n2rIerZIRGV0_C7>EB-U`*^yuuz^U( zqxu$uFP8Kbex@=;bVG^-x{7D>u3-fu~pB{WXiERmRJXJ&}P0TMwx zyC*F<=Kz*>p6oWDKD+|?D>gVCUiwzhSxC%ypdu)cSlB2cV9X}ux(ImRK?Ci z+&v{x4}pqv*i`B<`AKLW-9<^wQBlnyjLP-Z|Thw^fXx}c<-ou-{<~1=+sYW z88bqg8JoyHFDr<6mLu< z;y{=hGbiSp9X`9>itK6}o8Bm;YvSW4Z|bShGfn1W1m=Wc;wIy3$*Xe3d_qzW3`LSs z#Lay?-GQ0M9_Cc|F&{!O6^dWg7h?smeYR;0CR4I><814Vqfr7Afp;1w9hmKp4ZfW{ zOwypRVEOcuO72}f4cm(J-E;Qu;XvS4&fi&_&uskBb0SSnG#UQvPLnsgdNU0l&Miz$ zQ7EGt^FC<8`O40I$+-XpJd7}rOXVA>u{S7k@h6cgKBp@Oy@OGXmsY#_5n@`uven9UY6+_zP}b^gxI+eZ!*_$*K&G zD{hhqpFWT5YWLxH1%)#!n<%qJam334> zi{N1IQKJ*9BF;Db_;FEah?H?rtMq)ovUcyop7(FG^x@1mvs6Lm9E`>L+TTF5PRZ`a6n^e9M`gRMi;j5bEe;A@ z-?~hF)r#{5V_=BMpt0Gkj<1^(yAms9Rz%E&bkLPAiyA`R8UI;pSFG6Fs67QUaC;tZ z8Hh3>5LQLI_kh;4S1H1^bFt58qK8FMbmA(k4gB4aq1I=uZOE~kSPBPF+<$leB&B-# z-W(Sn82+gg&ge8o&<^tmw+J@cmNixU)u#4Pu~TBXyy0&d$OhIEmQ3A4}mhtfzsb@(AY4 zA`Y(#=e|ui`r+GcEl4Wdoq7TLa6QSq6*vd6NT8x?lZaGyH(-L8==b5&SaZ_`IvSHC zgY5$Gpz|>ud-rd<@bNSKk}{eq|{sT8$R)B2Y|hdHm8xWq4tx8{@l}zd#44XFpO6^OW*oCjR!aPbx&0#cYO@Kns+2Ljlm5*OWic?=7Z0 zk`xTs0Q?wAfOh8pD}Y1*6o1#hlI9?SFc>7JJ#hF?-7A|uS1Uca_vf|KXQ%30?%Eiz z#l(CjHiYhCZM;MPV|*7Das8|5;>8cjM0;;UsG_4~C+TjOvBKre`M_RfV6vyWN7zBz zJx%D_@8j82cn2Xhx0JO2tQG^hbzPR2ssFj994c)IQZ7kq6TA?(T*t;g@5^ZX6GG6% zW&~d_%5+5rkf}9hLYOCR%CXXr!z=~dDQ<)SQ1LAZSnr!&H=@lenR9MOr1?Eb*}Q-- zz6)!h0(kevY;hFz`V|rV+jkg#=jASM&I8Zh-&0GNH*ZJE?BCum=D7q^&57r4DaL8D zs8All-nbomL;rJ|K{>P~y-tMW1Ovu^_kig-ng6F@Le~MUGJ^kFQHS~dJnM+Iw}S_q*K>QVy8t|h z54m$gfWN0b-wi7O;sMf1J1L(exd9S^D|9OTyH|$!|5V=~gDz&#FPyGBu({G11ozO^ zya|}iPvdv^yQ;p4ShBlNgd*x&($<|EKp*@fC^g57@m*L0_rTpr0r6$I8aKd}j0Q1- z`^;VI5nPWw`qq&emdv&z_aBxx@LBo1$meArmm>(!ngZafHWUEAnBkDU0eOVVfF+Pm zQYr%u0eiG?1BAojX=wcO6Ql1xFbs%EfX$v)Nb51U-?H#^Mws{&AJU@ka8-nha%hK=)a@X2iZw7?1 zVV4cG!)pZpdBqI%XM#$`TJYOyOJiS+sQ@_Wyv|$W6_sthlvDA@Ia5xy8*X#Meg6O6 z`R@wgA>b=OOcGp}&_GzE_!^mh0j7D9@+Df zkQK6rBxK*n%ggqF)8GBgeQgG@zVq(#-hKCdGrwU5c=z0M?m6H7eMbeY|5x{viKgyB zVKns&jEKL%14sT~k^-1tWOIeZcrg$o~Hv zI1LCbXz%rir#>uH1(0391H{8F;~J!}RFwx~K&F7sfH}Apw%Zhj_pYOsVE~LBMPQ&% zLa2rpx#&q~Z;N)XWl#X3--A#U;)J6u!e_$8KfH54K9gtAg!wV2=(D`$fD;>C{t$rr zel`I4^xwA^Qcgi$X~k(M0lR@qtnU8*Inb)r{XdB0iqaF^=!u^$=oVjO8*pPigBWvoXL7dQzop6KftrWc|{QrpfIsrHj=XR&dWf8;Za2au>BeX zRJ5LeaZ=gp5k?cNaAf2co5}}c@SPpy?SZy6=B=S~;xlb^R`ES3pUN7Q3xb9;hXEir z+kl@ba@4Bs|BHb~fDw(0LHGYQSFM_mqF5dF0Ora$gV{=z&Mn?ofl@%j-;V<3zV*4H z!uXb_EJ0ehw;V#pp^P0x0GNXPGh>Ad&7qog?$4lH1?(0v(Qkn>N}wv@gzy?)`bRIr zoYk3ToNq$qx4iy2Kw>YFTUTX>DK_NQJ2L7hue8cMHEjMMFy6}U|2=xS{~zD#rj}(V za8s!n_kmKMcND$2=`J_p{iyZ6lU&WRB%^ztC!r*8`to#+i}9oQ_V2@eu+dp3^rRz| zo#@4pi)Tl_1J;0Rfjkvc8vnfXX$i8<`E5@$Or~5p=32sS;X$@Q7d(OD>IH|z+liG-*3{7oMGQJau|jRHU;$LHm>_2`C6~3a`o=`X zi$eT3cR{ugR_q1+BV!Dlv4;-YYzhuq`ABsz+&S{HWGNXzTHaM>prIaTeQwR@S^t`9g+Dn)>{1LQ@dC&;c(%F0;+eXn^t$CZIW# zv7`7FOojr0^HiR-Y}-3&`8RT(#mZ^2}Q0OoITBSzh;nS| z4V<@jp>n`fYXJ-qC1>WiL#x{QnHK@(WP2+#knaNT4DX20gWCFr(Dt-cOnS|}YwuS8 z-b<-rlOg)bLz@FWk-WI{4=p#W(GrnQx^-A=IZC(4kVL6fF}>`mi0>lk9J+-t?@^KduRM+duD@3DQ{=q%5N3kZ_Ry1xwU9-spy6r)2kd1xG3BefC)gn#_~V9 zO`1DT!P3aEWG1MeQ9KOo9mH0-a0&76gLoEdYSVczWT^er5L)M!Q6QTD^tlIbs6nKQVLbh+F zE=`qA>II)7zryb&$D^UcGG7qxR&c|JvhY5`yWg=UQ#5z=i=ko7VSrJ{#CTvrxR?KZ zPp|U7oT_$ljpF`a=&&4OA#`H^#R5Nb_AG_*^E;37?}K<21zbE_aWdo`@rhlzjm*|I zd;XX^J7S?;+~~4EDjEMq$l+>~rD5zS&a;1h1BSHiNWC6bDZ2_@^=dMoYqNEPTbGIy zOX3V9p4}z;D_BYwqIKpOerSVK2SC#D1IG~hz3-?*Hl<~)1#aj&*JjaG2!=tIc#O0)PR2t;=;hrd zLY4~Zd;Cl!6!pW5mFCrT3j>J?)?u_ro$;m<>l^g~oM-5 zb^cZbAl?+n`)9Kb z3P2sZFc#6G5Lju@AYIW0<%7OhTf>R0!a;_v03h1esk=`1#3ksy{Stxm2&lH8%ttO4G8=1Qm7k=bDQJpmb2 zov@l*SSw}hDE^Cu{0>N5s})+Q=tPgAh(ZD2ezFTV-xqfsr9y3NWy@)_-K|6hd@A@v zNcRjb^GO%SB(M3-sp%rm!@JA!N`ySh{oV%T0VthJGt}o;7m#0+W7YbU1C9b~feGQN z09=kqGXAea`5)X!iso%yO9Pu1_17+z&x-~g}jSvF`g7VRxT7NzwW@6B;89%>R@s2*-?X$629d2qGdxCDmf zhgfctMI7`(L^v%CWP*1=;0~bYN{XgUX$D6vvw52I@<;E_sE@FTgIM5%Bsox*< zsV;d%^ObxSECXcN76ipyoX0v)NdU$j!nK$TJb>mN<;K=fvjNg`9~U80{g5)Y?GXc9 zjkVYS0i%^xBR+lQD@h{89_t2W2h|g|I^dRy7$TW zz0?|muQ}l*9Ywg(PrG3#+_b_O8T(8z-j<~>uMP4|Ya-w%uo9RMZXSS3fpqpQ))@a! zC}@O$R5r&UXn@uQ-k%@-o#aX}mYPtve$RVZ((}W??@{8i?O)F}~fep#a!m-MD5?Y;zl3_Qy-U zp4(Zd8U-QWD->7>MRMK${8szyx6^Uvq{c(lKo)u8SX!T2Bc>~fkP9N>{865LRn#Or z-a)Mjz-b@_xHO!Ezk(m(1>3d8|9em`x$QEqU!Wu=}s z3D27o@1nIJOP(R0_%NfMW8%F+vL1d{^vz%rxL@4JssL6?Ak@M&Q|D`6C<=h~5C9u4 zz3@qqxDtXf!q<%Z=6VRA{e{i6OpOd#tx2xpSQs-b=6%Q743sR(D?KZ}QW^lI$ecIw zgLNs@;uLt|SX#Va$t7?^%GsqnTa{Qr`KCi2KnvvU54~P**dzd608GbA4FW<7aQ!bW zRc3Nq8f3iu@Q-hEf6YQIZ0PYH7MbTUMhKT=(5*#ht#tA^pBYeFmm|jOh+sWQRVI(E8;g5^sg~8nUZPy&G z61|eQ9D1S1JhC~@zkUI)^Hd0VcU?Dm;#fj5B$MaVXIUPx!2Hd*><0ijrxk;sX;1(z z2&eo1pT|p$0747u+j>zqnHm|4M1kt!LOI^K7AiNnqyoU0Mdek&AiDmODb3XZ=6IlrrP7-+#+FN*2auB@g}`h@5>jV?`R}8|0i;j6mK# z3tSv73cy5QJK)oz*?0!L!d21YkN-ykcp&D!bA%;CU%#)vNlcc19u*2U%#cq8Bon`O zDLfCMUl+JNQ|SJL8`RM2R9}Rm0i}oI+7S#zEk2AM_6A(zHo0TzYEV|=)p7c%IC7leA{mWlQD4~X^X|35`Z7{lg>1WW?{xGJ5Q@8`>! zy)`af_Idn`>v)F{-@uZ;Gh%y2J+jIr0p}V&xX`0$~lb+z!}KfLg2D+DF0JD06zhGfwLMW z#rVjxAGrm!!$<&QiAktub+XLmd{)4l2^C}l0Y4MZo3_k>y8h!L2x6^nVo3{n^plZ? zc70UgbpHq0q6GN~#t{1;uo46NN@C6twFl{K`7BRc4~F+ss;RU^=o<1I&O-fra%n4L z(ne8cRi~0@r>5X-uFA z?UYcOb}+JC0*)I)%@*Qdyu@*FNypT9Y)MbMtbMU8YnLS?S+=ar)?(ROjO2yW&->2R zOs1`@d+)hVy6?U-^Bw;qpL5T7=lu3%d3tsv&uH#ly-K02QJ7MqM(5Ckn!~^hv}Q>5 zj`r^>^-p6DmZh)a-@e@&4O0N*%*Fq^9+?-K&-uG30gGpe=^r^~2;(z${8Ho3z(>Lm z0QgT})@DP~czhUs zXDtBCfFFYE!Vm!XOWOdzbCc6%U+oFf=HliOe)xz$()Id$vun-iK`9Jrt<@=n-bt|<;bK;p%$AKwX^}Q7o&{a z`NZGT#v*FppOi+Lh(Vwm0&bVq;jV{?K1m?KE$FMwF}+Q2XO!!XF_-JNzPYf|oF(`F z{~H@J+Q{4~h};{6uZh3yKYRjyXB`N50lWo1zSJ&&Pl6g%D6-hl85$+LmeWSQWc;vl zq5>{)4!-AHY8%wdTll79@SOScO>vW#hdF;7uToa)H-cX72jUVSNQKN7igOYkezVe4 zcaN9^4b?&*<^aJbTkEC*Ah;BeDwS{B?;u74x6>h=Th!j??cgT|T8Vx?pRhnwtS>aT zDRG#8c);&TTCx9h++F28xR;7N(Jo?`NE;lRFU|tGc36DCnKMezzTU`d7r(O<04{;@ zC9McJGBWa8Q&Ur)K>#=%L;&E^3ILQ;8X@FJIwloRX4H9t)D#Q|ad17aqcJOI*Y6m? z*ZEVVZHPcv&PtN<6Thl8?FdDQas}WE$HZ7Tr?o^Lx2@IvGsX0v@H;9xhiTANId@kg zgEaQ@?JZR*%$3MG{|L6ueGAQ(6Xu(*mpPXt zT7X$g`fr-YXMd5$N8~G}xO`oFck|{bC5!T#R+>ozgpcyVViI9FEBuMdM;=U26}BPf z-GIiYvlenA4ct65nyC<>_d9SOC_tCFBK0o+1Z{LL*%2_aga-W~*oN$`<>4Y7twvAHdx?^5v(KPwZzgYVg58l)ivT)kBO|7*av zfOQ5y*Wekm(g{k77eX+;{fx?+6liXgI6)IF_c;P5iLejb zx#^8Pgzvu9BIuEL8qsXd(e0TN{8z5vzxhT%%a8(Ao%4z$R-)(~gWsLE&jk1YJP58? zD*ylW;9r#iU|9jMq-{X{5y$isC9o$kGz9_8H;8^GO1)oy@A3@vNbY^YQu*Gk*pEXXeZqMP492 zSKdBIXF%&fz*&$Eu3stv;0CZ8SY-g5r83g4(k}A_1&L%w4gBcYn6>lB1ZZ3+q849m z8GrA7UM>^GC61U`|Iy0|2&}+7Odul6FeK;3-XIuq3l+8vnM901#}%N5sWXox5CCH4 z@f|gCLHQhFX3NuKkvJ#rIE=Y~!brV|NIMl%B{+`S??v3V56xZC+61KeETuey^{NR0AePkU za64GOCMJQeIRSTx_orI=-^7Z?U7CBtp#e0Ao)EHpsQ!peJP4_O%;%a$N$bsBpw<9Q zfMfx6=8Y`ibF>Do7T*gX6QCdb&Z1`mT)1%IDxwc>VsXn1fb*uG62M>OLMbq3Z8X89 zmWiFTy@gWq=tJHtzpa~jZv)INss8;TyVN=Ia+RO^Ab~n7WbO^k+2J0_B~A2*m{`C= zLfPxN9rAHw9*)}GlK}9KyN}4lB*~ z-6Og7c+XgJ)S1-h5R-mlXDwQ~=5+mjS8c6UtkIQ5Bg)VH$~4iuQHAL0eZ)7ZB(S{z zFbyC6gW1{Hs}|e*|50!sIB!(~m`KRh-VrANFgvo~u_*U^cjfT~{qdEZ!&Jh`=l{5$ z`z!qYD>?1PcATeT*#UwI@7{=nza|!z^Al0{!{_s8Akrv?6!1WJnot%7f=|@$9*jH7 z{0Ihq(%-qsb?}3oha^ZO^ZWZemZaBmJ0$Q0J+Mxp)g6*{=e*lcfqN}0TK8!y0b1p> zQP!XhuFqZAtSlpffl2I(G;8Mq6V7b&&Z**0%mSPb+GS_npf4#X}T3!&n%70PVsdxb94b2&H z9;DFZnSVRb5_r|N%TC+izj&ntVwn}GyN3WkwYPfT)!u5cT0OF*GO5pnZzmtW*49ck zU+%iPn0`T(^+LIKOzWklvTIlhhTkzs%CHLmNXT(do4)aSshjr7de>imA`0KEe) z2GIjR=_%`?4IU1avr1G5W1%rXK;932bx4uKuIA_O0L4AZOoy}_`+%m-p>vqUYx!=? z9f~t_2s-YR0<*28E9gQ&axbLAES>X=#J#%(0f4kG-vU!cLq9bcMEH|#@4O#UaC6PJ z;w~|vAV<&XNeNbWBB+ZghG^fnIeY&y7PIZV6Y3J!3dht6sdM@VxTZ-23l@@odPekQ zf#zgXBrTbvcaaDDj-CKVt$PB*Q}DqqaKmB?05^g%+Z4bNQX$g!9MOS9UFzdpc| zWx=j&%1=H}-#3@H>)HzNB_907tTqMoSq`LfWhGEVrV9UWR;bQ$xue<8Nl_8aL5}(p zW9AVoHOrhmCp!=TlwW<;CljC;ldMZ=$)%a;J*0uvdm7ZuFu;KjKdHU)U!hmh9D zg7_k|K$aiNLH`Vxxp(b<{ec!&f@e|NDOnesI~H|_uCM#=hCosQK@qrdu?2ungKluy zIt6fh<(%m!%5co|dFJN@@4MATDOX#`qjNt$e=A9vX#T*O=ahAvR@qR5fH0k>e4F=e zI%xi@`b3aIfcA{mD*=NwlK$S^vO1HJw=HIJ#-9efv$WgXAwe{`_KoXrUaVz*dg!HN1huu~b zgSj0ydQcwL_~MUq%oag+Y&Aijy#ZN0cHlzz;|=+q-T`MrY{U@-G`koCQ63g;)E$dHL_@#+>OuHlAgO?3 z1Oa}AV_Y=t|7ws1rfr)5^`0I#D|t+*AoV#9lsx-VGd?xz+Ryl{UzB^&G}6+uNYyD+ zzH>lB0YtIJe)A2i3owJQoaz4(0*)(EIiR783bHYDo~)Cs_f7)92A={z+;zJtcGTSb zOh%IIS9gLa>+GNdMO7&4rL0!-6*OBITp0IFu78Y_&pJij%kOSE+6T^(T~wyIf!ptp zfK<|cN&(Mq8j?NqhKn_#1@2L<_@8Z4ATUiGfLktJytp{E09fp!NF4Sx0H)8I#-8Ch z$xr5=h(nD0YnYa)z3-aw?!gC3`iSc@;_vkLMAb@T31~>eMyOCkI6^>p&XxTxM+o>D zLclv!$3;+r8i$UJD816zZ!$sxz)x@i@ca4t&DT?yiaC;Fzq$)qva)r-1cMqU$EGNv z(`r^a%s+&5MQ{F}(Ym_=V*WE&PF~=*|Kjsl!937s&PRgtDVM3bxM(Pnc5rTu)PQ3R zZaO{;AGEIp06)G5d}Ogw0Ivm)f)9Z4i;2l;lh3mI|yJ> zP#V$iuGd-Ntn&tDY=ZfMv;-&}2#KBGvP||9mUE4X@ZTE`46KqRAh521?;So#fsz8v zFJhZyxC!PBA@_48sv1Y6I|4vv==XUm;7%&%T>|!p!C8VhKl%K12ST=I-nwY$GkfYO z^b-j4*T`!blu!*r_d?%P@1*#sw11bKCc7OMgVsqE=r=29>QgNo8$Rmr;qQWL7OM!* zb>JB=1B?&w(bH!9+@yKCtdoey_(jf23Cv0Ae@BX++ni)fw=khz%zJuZuGfJu&?uU+y?ddp|FISM<9ri=o^xC}*(t?>kjC&TteeS&`b3&CKKMX3+k-$Il@s%M=f&|K1BPCGlqzuIO{gMNpGh;W=sZnVuxf|- zhU0A0z_RoVECG~VuIN$zb3j_4f|}STNUHmW0C0ea@Fw!$y`M$9$^h4(S!aZKkNQpS z;I6+EYgN>GP!;~rstE+mcHUjMz&%HE&NKfWYg4p;kXbM2 zx7t5~0+j+wv`(7lNgcrVsDaZWK4@tHm<2C_k1du9;0CY@T(J4@k?{#~A?t}+Rq#iu zK*Z#3Ov+gvg7j1u;7Ff{91qRYS}S?Tn+rOWjuYgdtf<=QBJu0HSSuuFoT+SudpKhX zzy!q*lv??nEfr#hNp#-`;5#48H0Sqz^6gp`uL@$c^^Yr#*$?i;Mzaw;t>aydowUKbl+nc28^c8(=N+B#h)X59pmbXPioq0Px)2dhtO^0pJ4K z{|@l+#c~0B0%WNQk;R7(lLPtG+lQ1&la`)J$W*G{1_HA@40Y4`+Z{D}XkOM@0q!Tr z2FW}TEl{N$KfkX@g2plkQ&`TKZ;^)dYR*xeT@bB<$%GJb)IQfF$9=mA0bt$UMx{1- z9BNGUS5%io00`yY_zlDiA0o4+if;{Is4Lx*Sk3N;*U^`YD_pdLyQkk|Z*6bT)eYtH z_};cEhbDWYGid)RVSWJ%g4;n#rT(9@Rw_Yf{PgZx_@K1_a1rbWpI9slz$d{WaLMMw zPmu%p=++7&O*NH}E}~+-^HzmS>0Zq41mNkxFo&o0xNNu(supO5Vpl9=OVRr@(S8Sq^E@SQRv<1hMenHw1tTl{X&o(FPWwQSK^YJwmrH&R*Yj z*H81!yN7yZC9`dZjSwNXmYOK(orY$wx^K`*x(?Q;IM+P;wQBZ5D{|GoVDDbOO+wge z+$E==10L#&hz9D6o%K=)w*rdbi4WTFQIzpZpaR^uSOUPOKsUH-^Wj6I=gfmYE0LKg zEw#X7&LH)r$h7v2duFuH^3X6tds>eNEpz5N0-Daus|nf@5ZoGi)T{?Wan=$72;ieQ z$`SdrZ3or_OzMsJ@#ciNP(u~~_O7%nuLFw*L6wC%!_d8d;Tf5jv5slUYaXEH zMw#Xg+wTg4CAU6@cGwNLC(Z`#&Xe%->*^l!rvyB zrptI3dSEr(!^9BeGl#b|d1zXb%3d;OCt#@RT3yd*JYWtjIS0aZ!1>Ol1$!5>_#q~@ zhTgG2e8k@;N_v^~{t^!a0Dg};e6!x&Ap7c0inw!rp?g2e!PEGhvFunky1|@|rQ;y` z)ZAg(r-C>ic%_II-5nC*5-^mu$NpK*^Zq3qZi=;fb}{WLJ4R>ETxY#h1O?0xt_R_R zmM(w~!7%uA!UcediHUd_;NM5un9|}F=o=n4UwS?}6{SC2dV)Ym2dwejqnpTfrKLdt zF(qZVUsD7nZh0t_W|yF$xB#lAiC}&GwS#j)KnOI(47oxchjCYTjo7q?)&Io|D+?9` zI?Q)XL4X8m`ig@frB0eBXBVzLapsR~DL01^HZ6wSB0I~v$Qmhtk?Ed>$x5hySCRdb z+0dr~R|y!hLay3c$=eWJ^7^JEoHRQf|Gd1Prx3DOFLn)#p_Ro0A~HZ`szDF@{+EkLH}Ytf=AE_ke;$0JM00pqbA@x( z3Yx-Xm%t&HKwis$`TJcpRK1Ehg22-L)+-dvo@pMYymC&{fK9&y`C#AsANJ10xvTQb z6KkZNGHx2h1VptZc#oAo-CX0egNrC?@kg$xo5)2 zA$YBCMypu4{7Fg-%VQ^?p89r^&gYaMvIN4T=o_5uKX#UUA$eRzto(<>_x*?aqz^4Y zz&QZG_`$)!zX&`2{{?6&^glKWK1=tbrmmwBE@aw{MAGKxkm~^Xyrj6^t?gC`B?s5{ zbFqY}D5&u|eZ<5AjucjAF>zWQcm$hqv&NP0%A~#r@b%wS`(*_Ny~w9fiRDys47g`P z?lVFF%4**qz*SCSr+KdgcsFIh>&yt4SXjgY-Se|r@jwoTsqz!y{mkCX6+nLjxu9V> zAzgix4=&2@2)cLmoBLm>4AgS@t?l~>N0#!wdhUoN^dIf;k9p_Nhjs?FuCGB~2pa%= z5xNWFvS|Z+&a3F?l>)~$Y!CyMnj004@$DgpJI8NRrn9RQFM?{|Qw z+OGnT02zxam0s^BSglh8C=x~np!{PD*{YCI#x3t7o*pc8wV`@H$(^O1l)ryIV06R? zE7b=|EXDgm2xqea9wVBh{|cJAr4KDb07C%aKIn$90ljuFs74wDiBX@9DW zXsp?w3Q(EexO){3kLsd}nQE`8QK$a6)JvHalW>XCcYyMwz;h^lV{L(iYpm%b>gLNO z*b@%`4EWA(m`khfl=uPw@zPYT1OaD?QNK*s;0`=x+$$jz4`)v2_1?6tsZN&l_c+$nu%nFDYUdLs0RfIMt0^o)y1fWI&9014_)jClIS^My>{ z+T{hjG!SHAeme&%@EUZuQNXODLoU+@FL^&`=psS?5bZHZP?Y5AV_1+Heg(N!mhGlNSkZ`=_lBhZ zR+i92$=(BS5qb)W1OU9~0st&6Xyb4qAUT{PnI=OBCz6x4Ns$L^z9RvDKli}R8FVfD zR!MOvu$`)tj`~8dsLS4JEbs?bRT3V-f<*y|M^Vw7m^Vo-{9GxJSYwaxvBLY0pHYQ{ zlums2S6~3Z-`DT>eO%7OB{F)B@QPgX(h5+Y_favnm?kpxo{X%1R@SP)d2^gkAgXU-gkBDcq!fkHDaGQ~-N~>IS$<~S(UXM-ptYw@ z0vo=^W6kvC-=?HLKVGCAT_*@zG-&_Y?-%?1Ga)sbZf`|eQ(5~VDdecGS){~2$7X45DDxWWs7;FINO z(}$m|XGWSimLeN?gVI9)1_szo6gARsqlN_0R#LKMX!Z^Wj$ZW1NW zUvKi2yfXj9cV)ft1pq$1Iq&n$?@Jr%-Wb`sWoh2wFX>Mm4%3P|<>s~rhxQ($DJGmp zro2Fx=Ho|rAwL+++oZ%t_-Xl0`nN(ClpF_M6A8}L{?jsBuJ2JnhnA*&gEXb%7| z%vXl)0l0AC!VmyhV+#OI@WRi$*I*i9#6}7F|E4qrI&3xSJ>NRzrlsDjdx*%c&QtDWu z-$%T!;7@j-MnIHk0$j9*BHI5sbeGFloS6i<>zF71CjnEuW+=OSqx_>kw?^@6A9Cij zenJ;MI^;PU=_`+uDYMg>T zIsgDq3~Iz>2*;ly$20t;OZE znb7h}9svm}!^3dD(?oc|^Jq-7&3o_qya7s_A_JN#ivs;#YmA5{z?VnY)%qTri_4d8 zaV4xbn!SH3JWYM40cG=<>r{y*<(5pF+~R3}T6}j4;#`QojOQ*<&ji|C#{(qmnHf6{ zc>lIys{p(BVgGJ;B+CRUEQS%O%=gEfQ3LWu<@_a*9HAVv-W33N6$3q70Kh2Vn{YF{ z30VPv4YVDhWwasNc>hyQ9HV&N*p4Gbey@`eAdrxm_lJ#pU zm=eGvaQu@%AT`^^`v&HR0LM3tH=yOfXO;`9-7?<;D_7Q8RC`K^Xw%l!NwNC5*n_y^ zi&$&;yaZ%mX;yn32m3!Wq*Ar#7!?)`vM9~Bj>|b6pPD6a5NiP}_4vq+cOUOxQPL?7 z6YFxq5ALBH1}X@w#}Lzb5SgZZG~(s^m4yByH=@ov?gjw79=Znr#eXxjjt5x+fYaXN z>uQBoC(j-wL&TbM+MFZ&#WzUnlLk)a=JR-jg2-Ad61qXjvV4T z4M35FSiSeypG}lI$ndLmmHQNqk`k87Kf?1ltoY&Q9WNA-AiF~#>99qDpY5bgcqRa1 zLLg4|)NtA2+)d32#aLGJ5PquB8DVWBUcO&Jk^_&gua!P@kOPp8fgXAY;AZG89ySPh zLFriEX(p6ad$*2y0Innnwr^os6j3(AQZ0S{P{I#t9Iwtr%gXf77SBvjy^^L{Hsr1Nr3~$OiH6MzfaY~F;iQh)nK!u6w z$sJnP+RG2FuGBuYZBM+N*%~BgA}>a*|4$*wr^nK(r4McNsnN&lp_`-d0IUaT3_@N2 z(t?ln4d9lnfdO(iB{$6;a$#m8n6fyCZth2e2)S*Ebvsdt#0Lc`00GY485cMTB9|7v zwO&DMubSl~7!V*?a2L&yE(Zdk?E&$F=P=8?g8J(ZNV*%imTN{yVh`dqNC|OGhEQBt z-t-Oinlk6-dX1@#la>Dw&br{2K+{`QUDs-dD<%8pw!J7i#kPI!Uwh)+#lOELZ1aoP zssKe@3Z*FdGx|%OhBo>$5K5veU!nNH8eM)98WwH#Dmss-DVME>V)na@21L3$XL?`V zFX>5pdRlr70AN6R5&amHh5*1i=;qLK0HAai9)RDzRSN@#5_qzcmr(o&Nn*i8xZ10* zP=;Kl_V?vrlYmvsgs{WTl7tw;i>ojXu;S+3Cqdi`QWBKvjx9zIE4OM!;^--)CK+?a{By%=Bh%toPkc_Z5;B z$BS3)`NaNKK1mt?ja-#a-LEvxAwF7<)m>oLVR-C=Y`^1e0NkIyy4`!NVvm~ONGZnY zVjq!o`XTF~y|GDpb@^@hCW_S)%5pN5u(A6j?T5=e$s5Sg8kPHj;xzY6YLfnY5tH&#FQq)W-gE?@$-s4z z6u2<2O^I^c-z5)t=Pc&?4-E?0ekm57NEa5S;-yOp+nK<&MOo4lShL}SYm(68{;Hgq zPe;Wn!Z0*7x+*>zYwD4ZG$0@h5&)P*cKtD*@!T}RD7VbWl%R6V8!517Gn!@LUt!_O z^-O3<8R(p)NUeUO__e+VsPb5iC#R#gUtx%NU4y2>;q%!oOf)QnC+_zx_Y-LSWBG0u zo;-cWXEU31PgiSYFOcY4;Z0j#C-7+Qo6IErm%6bIedr_v@EQ~e0C?3B04V8U4rPG1 z%B_>U#3qz`@TFLr>Tt=9ms^wnZ+&kBA9K4)<;r$-_4b{UGT`1?qf=gR&y-ZGh!=~b z^jLS)uF~tO$tNd5vkvZATCVj%;zbyHV>nCwK0wFHvQ8-zcyjrAlxck@mKUChQ1<9o zP~uPFiuF6{c`+Ux0DyCzuuq7iUp<|WgfoH1hj6vu2KY6en#j&E&yzet$*A0w$_|C# z;(6_(cLSjq@>wdREK#LxSJMz5fP5IOn}a zfeRur}iYkE^?%@DT}OwK6aaPu#!1 z`6j=?s-{8(%iT>wqUSIGkc&>%%st47iqTndw*WJpHDzR&dnYqV|Dgw60f1Ma@cRHj zuRvA+-~=!Hd}bpI5CTnFGD=zk)+kujGT*(Zj6bu#_e9)%cP%PKC>?h?dKUq#hC{fA zL*dIrqx8r*K)BR;IgLFMYKeBC6xDNnHeL~wkCY`m4~r+_w-U!dd**)jDKh|8Uu6ubkLE31VuIi^q&EaqX&7G0NDS zn~=#oYXaZn9~YMi?9lK=3MQV{`N2PF9rH4qwI2me!s7?PHkvK4%pTd{gmM z>vphqc)%Qv`b#6qNl?VgT4eZ=+=Tw46)@g8O8{V!@>f9P!Uh22pyirAWCH;DPcsat z*_*6HvaF;flm)T+7`3?dm&cn)xch)Vb9a9ZfMUk3E@GM-q{DO$+cFFSLg7scgy7eK zMX1rb^@tMl66pf7Nw{RzcMGh19ZUR{VIbg^C)`gw5zhff^n0ix1h8)?7OsTPyB3$i zFSpP9@{A1P?M+zP*AD;~t^=rTgbx|c_5xNM-j6*}{8*d%Zz?pXh0Z|{`QWz&8HFPL zf1CV_x$uRS#{gnLoxky}V3F0_j@x`i=`QaF%QqXqB))~l2Y7%HP@()3SILcl2+B1Z z$%~4)Si8>3zbq^FT6!)46!IaClUoBg(*2W~r2iJin$d^W0N`V2DHQrNz_HLmTL5s9 z7has#0t1rU1HdMi0DCmO<`W4pT}MwV?(4#uWTR`tm+U;~eB^F<$04%g1p-3l(QrKX z2Uvh4mys~p=dyW6jOK}0RsdCIHnO3r&$jf?}PyyKMw#vPDV3s z?d!L0iN_0c0svg?0RdUO3Y7ir(|G{^@&4j(RA*K1L!}PMwL8?kkGzQZf=_QKB-bx6 z^z${9;?ejBcmm(mHW(woX@XX21T-!9kERdgP62 z*OrQsoUniZD*^#w^Gd>v3~T1hSIPyx51^E27XneW)=P`pl_)qqHFFrigbD=UO(;U` z%hNMxP}HJt?I&6E2`XFgggAZAznhmw2qYJN`g~XWeMj|6j!Nh@RCm!;@LaqBsP=w3 zXW(JNK`1&Oir-%O{}3+&WtK7m9Lf>_D?B|;-h(qIuOVT&E6WQ%R{$WbsLd4sm>UHE z_!5)~U9?!N<{z&p-icyMasXfi%)M!wuM#LdxOM-7gr^ioUVy*Ha$&$v7=v}>T`(XZ zGJ%W*NJqRHy;C>+%oFUwWv)a)xzE2v2*ZR0Lfl8;@EZU?eVhjchOJd3WQUk2Us4Ryj_l<$P?HJa-}MzREd69(*cD zX_5y(KqJ|8hxoOoyu%s*4C!O&QD{uqIRImzhoK8L3qHjQzqz&3(}v*?Br~Nz-=h=W zM}1c-<|F;*<+`6R)6H|fJ}*!8vl z=a*M-5Bp|aof8oB0|yd9i{wHs!CFJeWmk$0C^Yhg)GSMo+SKqt*W6K z8hYJHLnNKQu(}uNrmO0G#;u@nZX;>gY;Q+FOWkH!Whfg~A0t4dZQ|~fjwK?H{ zfO0U3T6+myRLe>+Kp>&_=U)N2hN=JN?HyKyNr^W*obtdDo_mU zT9dnGkQ{@8bMY*?q{S4MzJPlyq$D`C&HV)ylyOr=$xA&`plX4DK%#jBR4qUPmrAw> z{YdGs-UQv70su@${6Fr_JxJ^7yyH}@iHS9){iD-SR6v*zwWKv|Y>a8zm}yImCT*%o zrmdOWNSXu`wI$V7L*gw~jNniuKqgi8euZ83#${PxmtFP(%d!i?vg`$x-Gyb@1zA!2 z_49t`&|x|ge1GpbzsvicnP<#Q*!OqNb1u*A`#evFPc-+C9(9CPTHHV8sqoA^6f?Y7 zk)`!tvx@ZC&|B>-G{wX$V1Bzz+2E?L01H=09+2;37xT-@c6`OuYTVEAc%}m0I1@& zmab*{f0O%I0u>jTi$kl&ikIQ+$dV~N??&sugsCJ;yptd zxk&Iw#ursxr0G;pC5`cJ9;MxHg_Cv8G)0MehQ}|Np+Odh#akLHQmCqN{}IVUD-QsJ zHvM-%mj$W-jE|3BHZd`Ao2>#cK?}o<-k}5oOq?oxiGwH?|4)nCE(MZ7Q*Kp!%fWCD2-@(OR34nDh0;_)*d_ffpbEg*vu6`HfE%FGmM#mgXYd4;Q=-8|0hFJ<^k_c${)KXxUrLy^^o)=ulZahe ze-n*6pFZ2gq<6K+S`QM$M9wrIvR%Y66IO0sB>M%=^x)5c01J|%-r%jNS8w*%loZa} zaY$}+DJf|z;_ zeL8lU!Z!sH{1NZF_UjhjqQ?N!b`G3W+v%X*g{25$unkVu%)O>oMQbwsDhPizOKzBV zG>xf1Z}{P3C%n^VtQCOMjO(B&fe!dT1vkU*@xd|ZJ)21n44)*HvsRki%(SL_(OiHw zr!N7r!9t-5UWX35{Wo>lNrx#XeXl=^R5)6rc1bAQ^spYfsCX^@@z~-9q>cc{>R+j* z{wY;ei*P9Iv;M#k<%H{{+0S5(k)Lg2C9kB?oSy;iLdq(k3zqjkk5e+K6EYAZ>sfs znv5`Bud<$((wh1I^>}gUZNI7Cm`xms5~88F#K(zqmXbsKHKD`vEZ1PacG9T;Ipxtn zr&K9lE9k$lcRY1xFzm>K7v7*lgZ44--@#6?# zzHK{q?d_`CHA5IT$UzqhKo4~7lrRP#x(3<>owu3z(c>q* z`(NH*a@^CKIE;EQ*R}M$Uz(k&fZ>+qZ|uLzqbU5r3uSwBIl@3WQ;SG6@o-A26??j2<$exoAGc*Sv*K!rF&5Jm>ctqhEMPYH)DzF@B10q_1)B*DAnJ` z;eMfr2r<0?P%3@;LrL#L!B95DlJy;c>1yS6W-m=1e?0hNJP_c4YUyg(Cm|X%=H-=| zPAT2+!O6Zd_hc)93Iu$&2~DNB%#hrBVA)3G;Fzrfa2{%gt_e#4_#LPQde>&+$4*df zY{eD>0?AKnnkhj{V+Sn1_Zz*(C$M6+aQO3_d=~_!<9$dmV+!JhYLL zc*otMqF;-=D`C`n069kPv$f^&EqKLn9@@;rX3Jq2YG0ISj;$$~;%Oy7r z7bNqD&0g?e3k>xSOE=PQzFn4_QlY{fDg?EIZ-0Aboi2UYJi1v}iBbU+{ft$(lHp&( zdrF9hePsR=GTN;Y*g*q1t5MjVENt=SzEH&joo|%e*u6El{Bx*>UrsHG4#IyEQpy7! z>XiA7*R~LUFL1#V6qsD^ku|2`Uy_4VYeF!k=KE<)l6#N7QSS|n+A08&!>>ah4$A}Z zA!r$N&Sv5xqo=&53U0ZG!F@)Fh-akvVl$Gp$bp?~;|M7OnE7vI;nZ^IgRqM+HQ z8_>Ro09rY%Y#RW+73#r`%bpmIfF7(bW63>-cZOVT)jWFA|&$&#ooubS3(@W62yTzy~2c+k;dSH@Ee zkJumuSt_YKmm|w_W(@X(IcP3`q;$^irO(0BCA*{uoC((>pz_VsIF~{)BOLw+84g-q z!otUp&^1CZl0Ca1{Fig4z*7X4#}25KYLiV0p|Mu8X0tc3BAsD*L!K&ZRgta`yBaFx zmb6QqTjt{8T&i{kUPNE`qpy%=YZTLTmlt}EmRGyz1SmQD1oXkM6o4!F*!Q7H+kOD~ z4R6YIK67Dyx)Xv%Kr}U8NoI_mp7fsM-(od8`#ujBnH819Fpqd)4?GWYq5c4b@GT&O z2@Aw)e=b%8mej~2(0Fh-OpE~OievKpFb6%R0k2tT_0RpRTya)EV@bsx?}qs(av|@L zg#Saq3V-gMLU8{|uKb_VREB-f9lY6MKZ4}PjED0?_b=T$C44{D^-zyud{6KO}dlhUQy!#7kvOGp?L|p92N%P3g}DFS;$L~RR6ecZ(o8D zMtG$+8BiakKsQOKB;y+H=pUt?d?kn4-5hW&$wf#rlnCnNpBGSz(z9}>-nc;CfY|L5 zF;rRNfV>1|q)3wrDuprF7h1yBD-IYiZzS06#=7-cD?(t_sq?c#-y7x&!o0-$MMZ}M zoofYkbLUaOgw5XPpD7pbjo^>%d&2#ZGKAGHK0!7Am zZvud(6j>p;x4pApau9iFLy1N}_o4t?5sCspDd5YXPeErO&xX4ChDawXfGN2d zK>%AQ30tO4U`eJLQCQ@FxnR!bAfEnPcB68FqZs2nww3MuaAFv=wi>!lg1q?wKOr5$ByD&AvctVQ59fjkFZ7-P zeLv{1=L5<(-Q|Y#yK`Ttl;UM^9)qWa+Ba_&g5sb5yH)_skqGWFo)T5vyA5p53`e{| z;XH*~`L z^i!pZ>0|h$A5d^X}&Xk1SCM;hc6%DaCTjT)< za}Tf#1@h`6P1eznBEE!J^Owm~LCF@c`Bq%t58Yd=ek$WtFz+k?f32pGfkD_@D z*eU==hDW_GEvb@5G$RwpJY)|dNRo6Z>)+M#Km+j6-#eOzh*HZcNaICVCKoS<;)wrQ z(4zU=uprw!!tvR+3y&{Kiqeeclhv@cwO6TOK@HH)0*vyo0i4VZf*%ri_n>^y-Orcz z=5Z{)sq9locSec|N-*IOCmt9TjKQ-;cez?+rvOZi-I)_zqyTX*)NkW4Ja?Q6?L$-} zf5#!fqw6#;$jKTxi)32tOewTexw~J0Y3^u7$cdTI-~XZ-`Ddp9AcNn7dLdN-3{wHP z25N@R+bm#^YJlHcRcF%ZGJ~dH2m|zuCZAJ(dQ!R0(;QmJ=E0LSVPe2hmdMq`r-d=~ z`?p>ED96kjASAfvMV|#JEseO3!8QbzUX?>Fh0pNVqI|esj~?ComQ*Y2!ACwk?U$P* zkiy_O=YS?k8)%t*Oy|V5A46@|Q3+8LYVnl&7;BFq>!;XI0nNnfR96G#Mfjko#>S2# zs)JzW=W3mL6Y+YbRIwD#klqY4pL8e5yMOsro#daT0`M->09_Nd0&q1{3cX{o02Kpz zGQY_HqH;5WX2{i5Xhmhj2QR7ek(I%g!sFd8aTtqKO6aJ(0GU@8>GrOp3leN?>AnpRsL`chSthsqc+2T`E(h= zCW{9_#d2A%%kAfz!Qb?o$4m@M0u8z(0_suw8o+ zB!8zvfVx2ZD<9mdf?r`%kHBSrxTsKTo)O=-!OTyd8Lb_wSZ6Bg(M2PzZ{vmFt#Hb} z=?M=HN+>C?DFq@?4nhBycv6ltgCy@()a;S`vs3^qiU8Ru03U!JfhKJhFiH`G!iG0t zh&0ZOAr33)JEgwR*T(vySWE*zpls*!0R)BHX`9Lg$7HBQGCac7-YiRSs!;@DwV&|L zpvHx$Cr4h7nd6~~ieJRixxwMybB@Sx*dQU3>^L+$?tO3dR(Sw|OgHhmj3CxBn}d%q$Gz_h z7$$e^XNd_KjNfCAt1M@ zVT{X;gJ<76BV2q&tQ3J^YQC4Z^?HkPnv@J)cm61e*QcOZbp#=qvQIcVHC3TC2Zx&p#=mQgT+J?4**S+A}h)SpVlF7-DJl zYfkV_cbJA$#J~5-3!~yU@fqr|@ROnz?FZ-DH4kW5RHjg0svz2*m9$FXHeQKLAz{Q0 z9vMOYSt|f%pgGWGVS@k@^as$WZ7SdZsStC2zTQ*JHG^on9=Za`{kD022Nt?qUkC!% z^Wvf>tk?o|J+lyAfi}-M#=JsJ%NU^;qrD|{kDmF(-oNKH$-;Iu zs@nUhO<&G>t@j=wltop@&2p6|J9ZsDPLK9Y`djyW)10K_Ajne!sXc^6iNRo$q*&c| z?HOlP_Y1z$RH@Tz?HN(uiMS1lgsC{v9C*o_;F=ep-E;70b;+PfQ5go%Sz&%JbL0YY zj!<7Ea?id701CtjjPc(e9UYw#`d|Vdf%dvc1^mHkQUL|J%FY;?uK(sxN4r9;G<~Aw zN!=RsKC9TK`?>L(Qcf15{SV=ui{l{vhnM0s4O@Ef2s9EFp@zVZvH($K8Qw-u%tfH7 zuj6JfLt(+9bC0^gfeWhX@>3j#JbcF}*^N*~5e%&tbkBso2?*3i~l-Y}tm*NmWat@ve z?m;HW1eTRg=3kB<5J_Id~61> zVwHmB?AWYLy!p<`I+bKjcF-^fd_#rSHuM~HRa72;E1`LiRVv^(NXXWMM|sQz9LYH15bH3%jL=G@yWY<3VOspI z*To8Ae*7*)Xz~-lI~D1P|B!%G+J6)LMLNohZIzB1CiZd6WbRc zmH*P^a=p@4iwR8~7#vfxp>!B1r6~H%;%89zf|AYx^`=-8L*6f={AQXwkeqAl z9`wfT0f3Vj;NOIua0D$?ybIk9dn=a z;{xsH@(rEZQ)1`>tES5!vje5HL5eArajV5!7jvRMOo?~+ z*a^>8W*WKs85;9l9*F{Q>eQ)3I^c~=)8NbyZqypRB0v;9C)Rv`s)5Vo6j7CDnq z+*Q`TM#|G&i%SJSEoeIAbsjOGzBC(n5Y=x5r4)V!$20a>;jd8(*Z;2G;>yxAx!=I&$~NxnUv z)1V$Wme&7yr~@JcAi}`}u7--BcWiqCyjI-~gB174Na6yLGM1|c-M@=WZG(l-Hr8!$ z^EBySpzS3LG}km7Ak9@(k#r&nVWGB-A`sf1YoD^jOI+*1bof+gR4_*Z(f^)NF{BrkRk2kP;|O3Mn(#U>hhYB!K}F zL#82SDd2zsFWN21+K?^DvSitoENi#qMV2L5i#OSpjD(48j4`*L_nn_+T#YrlPrv)_ zd-py4X3oGL*1Ny`oZmU$^F3+KgQ+z_e!Fd{)g5r^uhsXw82>eRzEY|>#x;OgD_%zx z>^+Uu&vPj>KY%!BdN%+t-KENe!>8VL0@{SW&#CKHzBy+(qVvgJ1bb&8CC%-aG4mUG z0MX`;oSAsk0n%@;mwQqJ;2b(P3$e@quxA7eWdi&bIcqbZVK|XDwsbQ=0;R>Ra9Fg- z3!8c;41UW(DdSSrZ$+-J8Io^3S?DY)+pSl1P#d(dptO(aV*kM(lv<}3i+}5`xfGDI zu(3Dn6ft7XG9YG#-)9*B4-xwvbYFvAKs~q(toPT50dm*!ad`+-a`}#Bo0Rq?IAKUd;hrO8T)|!`Wl?)7D1fuo4XQm5Tmr;KN#$xFYapLjYO2dQw4X~14${chfs_u<0|ccke;@W*%K%`X zgjH@APYDV#&2~0Qt&Z^5M1o z>1%4bgoX}sWBQcJ1-&eJ@>^x#FppXnKh~akBxZWpb$^Rn4p1DaUq$SA!ixC3W9VrPpU7=+w(LEkG|M<>I*VX+@Mb99 z?~7Lp<4*Nk0By>fHPHt77`Tse$yuMS=|M^H&Lm^?MTxSuN{=jR?zMUd zoCRD=%B~E+_;&px=DyFa*@vF91^~WK2%!kMJSi5Mp#*-~k`h?VNG>w1$7Qswq+hG^(zhE#6)?-X@A&GW&x(N$J zaP4&=+zsaf#7k~?AXIe_g&x*-i~*p!5-yEO)rP@RW zzh*4pg}1K?R?336Gi7yy?dKS9nQj*T2RdIEsfn~vh8i6LS@TwB*|asyJRMG7kTO<@6%zTEzDIfphqQuApHsWf8WnJCI_<1$2Y-zzqcVG_=F zP{b+DuQ32XXuCCp(Q&mrv_a0U_EpUe}ibfFp-^#DJQN?FB8 zQ}xc}6?>e2#^~n^Y9Gz==giBK^1r3Kta$!S)`#|h!9>Nq%oYH+Ncs&uXDcB9;LQR6 z#zF%GcnO{P{7D+^1H_qtXmCehNLsjRizCn!8!p~u7IGD1{Sh(YoqdB=2=|PdPO5O_ zqO?Mj3$(Q)Z+s***b802 zSBCM8Y4^$iaIFBzeb-v@cM~4h43zH=mzGHJj%v*7pz|MuGGtVFKcCu<2n>uH@G^92 zPUERj2n({D8ZvWoG)~Q-55k^TUVx7wyO8r1mW@gzzFOAme8#v*XUD(j5PS}I@O`VQY%VOh zd6T#9gJ&R5Mu1;^M~TUueG4QitG2YqO^g-p1JxM#$qQu~qZF_Ab0~$05hYK`dt(>M z5i{f86ITyoZKcS6lU3&$>CXO>M*^c~qWeMKgD2KD@JjrDVFz&$Jf2f8?|}DnDUhEq z1&?OcE1zHS={RYJhyFw+$Q~J1fXTEUnQLFp_=uW32ZAN& zG3m26%6Ce?S-$?yBRi0fCB;(1GWkm+6**@yI1waN?>QjrFjxz1f5_&52jGxVE$zG& z`&}yWxo;XHz)vHFF+d@^*7@)B?E$DI;GUioWujN{7m2l^tRrItuvRzzu1G0SW4lcS z_@obE82~86I=S}UzRnh`u9~m`z#f1_Fk56*qhr=7Rr{Q4AIsHPz21ADqQBoGy%Gad zya{pAgjuL0WU?r^S~=wA)$i-Q#N&iK{buI%lr?VQj~jP8Utt{P<)0|`yYyMj-UHHa zmIlB%WCijUv1R~#1bF~CYa8R}K0F`=hUJtvvPc)1Krw069kvv>`i<*Mj=O(jkT*at zl$)L{p{Dn)c*qn`0R9MIHVAS+wioJ!%UC^+ud)YV!>bT6&R7-Q$OH%r2QW3{0myc5 z0I*)vxZffJU?SsSzflgh%xUT#7+u;}bj*f)$*Xx|zX^+?bl6QO%6N+PQ+x=7{|*^8 z;#CND?(FAQd?nA|_DA_!gMD-ByI~eB7`ZDF>VChTySV$vfEV%qv&enON0MTxnbN@5 zA*U?|2vNcb;)Ed(RvM_&1QJ=qX@DS$RMpPb)s|h`Me(5`6b6^MPh*&6#=$EO4)|<} zz-$Jf*b@(AD@vuK)G39MHp;s)a}kQz!+GQPcx`3iZP6XY+-zk%G%VTbUdCjtuFw94|mr z40ww8l*?7lS<<}ud4@m5H~$uI%_|ta_5fc2KPj-Z-kEK&2LdV@^M5X{5CM>_ZsdV| z8u>bvcs~?t20%!rAic;3HiJA)WxyBKH*;YyfuTz_frRmrGnxibObKrEe!J8*VxT20 z2jDp`MQJ4S+rExNN)k(-oZ$kH<#h@Rh!Qry?JP0+=AHq?$x+t0<26Qa=1l;)6uHn} z2Jue}d$eKz&{r!Z>+(1CZ-13p>pBgvrFT#U4jzE8`_Fn)Lm*Si>q56L91C(^Gd9om z7W-NI3N(DY0R41cH|#v%Jdjc?@G0{t@%h|p?dLMLtfTggZENZ|?))oWAsK)R6YxGq zkG)i~Lwe0N{*UtSLarDZ8cK?}M#nBk^62RM);NGRZ~(ZHb-6A~Ea8v{HTttZRyb|A zeQgo#)zJ6e$*Ybyseaq%vFPGwEh)rP3^QD_v+KZM;B&Bh2TnRq7c@IxMp-kj-tAMx zvvmQ>b27>UA#mN)`N?j7PyhDejP0(O0BbvsicyX}gxh!h%}pWnKZc%ZB0Bmp;82hD zo_>Shl7XDx(5-O=yvZoW*q4#~-KRq-&@u$0aaN`LZ$(+JYwq>LYuC{~L<#ds!h-1* z;|Z_`Ly_TZYwPnO{%=tM&>t_rN0CPmYaBpN|Df~TrwTQJi6~h9QkrB2sA!N)gWLtB zdqS_r&jaW&uU22h7y^1B@){tQc&Ch;V7qEhJFmgfPh1nQU9zWhHIp^^_hsc5k_72O zUXtI4Qg{C?{w>9Tw#y~&qCt^+So!6~sF8&8tzV|sjDW@T&Bxc)YacTvujWOD@m8pO z&jg*08fobp6u=JSn0ZP!hLZ2+Q}g;69vu%6sv(`%ixS{6k{2ddNdX#fU_#1`7y-P(VS)GN3MIw_5=_K#f9-{Ha{z8-de5uv1d=4) zA7~E}V^GG=&3H$yW}TTMjnVgehHsK7qIm!FC9NTxiK_G2wj)EQC_`PN^g%POL=@?- zN7AdE{z3crztOALBOgwR#Rf4!5}lih^x5J7P7Dn@S=;t%0uA6Q@kEzu3h8H|^uT5> z5YGCIg2tYZ^3^3TS7Q{$HG^>hGy~+6iHo?D5~htxcf!pbNE%@jr?3_n0bcA^6N^no zf#yk(8c&W;;h&tp+V$^Dxp%ej3U0X>-fZg8H`yCu^_PDS|3t2;92&r)lr{T{`LLS@ z_KIA{52kGsND*T;uql0{lu~PxlUjT0(|U}-ms!XiFK>_@^CJF_PMwcf9FbOOp zM!;7v0-jsnDz~#&FV|fMPY}D^MI3vbT-d}T^{pt46Xl6;Q2`hP9JL;1!5ZqFcLst~ zi1?|}1n+uPMt~RdD5WtMpkWF$&x>;#q-@>(?Nt6Pm%U=LZrbac+T@LyN#Da-Gx`W< z5rwy3)WdVgUpGkhiHQCdP@0r~Z*a(@MCHu-3*>qihTsj0^Q8!;bFP@vsGKXlt8YoMfzW;zRoG+%NF_nzSjk)Mb6u% z16J+n(?xLQg4Bc;4zlE{mvL;0w~(f{1HSnJDGn}meJ@&L6Ce^=93J^npdID#i`mSSQS`#HH?)Xp6TZKDXms>Q) ztv*aczVJ|nVyp94ZczxMDYh!W$q1(GFs~^<@q!A8z5Ef!&q#6gC_-+d>CM&Ui%McYEMS(Y%GwcL3@mF5oAJzRNSjm zMhAwUUi^`0tPxb-A(Ef8kkL1JFH0oSS4#gI@Pz*NUE~(z(xf1?!NI|h5ip$&Tc-oQ zHTWjP`v%HWzu)({u2clw7(WC|7+iju2jKJToBfE@>q0j9}K_>DD&5(F%lp){#T#!I=yZoF-2a`zR^~0EQflig`|6 z!+yzfp+%RZ@^zs9Yg3A35s{D_|9FmC21YT4o>| zLS0Pv5{m=T@5{vqtDtz}Giy4P_sh!-1aP`@1KIGcNwMcmm#|f@7i~uXeY)!8S}rm=-&BNhBxeg zCY^qPefeMi(*Zw(%i@c4{G=CQfc1@?Se)8)lQLS&C2`gLsesPP1&s62#jX5w;(_E9 z_bL{DKh?}`Bvim9!E16Q7uN;Y0Uw1+TL3)b-~i}S0T1R2tOez%^7RRC_)Z(VtZbK* z9sTlS4m5V+)XMM}uaR!Yi?UJxGC$V{Oaq~ZTFWq&eW;5)0r+)?^K&o=*N?dPjd#oE zv#c`%7K6&5sSsqnsWbIPWt+lDc>1LDeJ+J9&@o;Ivu=}j?iiwdQ_|Gs40+@JKMBL` zH6eYP6tqU?KaK^no&TP<4Fhx@I_CV{;#~8>TRkbGWQGAD%|huyGpg5a6gbWmNA4r# ztPTKS4nV0Tki*>eQW;hAvSbWQL8fuJsn7n-kuFtUaS)YcINyI}9cgA&crZGkt@SO> z9AoK}FdMVE*nJr@3XE(HS4JT~K>&m0S|eZf?1I(W6Bw;vHcmKEXiAR+CC^@{W=|9; zoW}l?__;UPmomHvzrgj8McUp*vGn0?50PPMkG>nF(8wV?B6!e9?_87c_w0Crej;lv z&*?hkiTnRNvYm(Y$CHBC==^2KlgN7>gaJTRR`FVxv_8ic8ArUlll5vy-r63`c&cy% zgRl*9xhqDwa`RpagoIep-juiEihEW{LGc<^zx|q@=?LG|Ui$iDIe@y#l>SEUzAwN* zVzo-oq7RWCx4!WYU^U8j9~`e+ig({exP%aZFKaQy^sQ{?0YCZuA?pz*zZC54tUa$< z?FIAN_hm2g4H+32Kr)2zP6@!eqH?dmh5&Dc$v7WnFeI5ZO7pv#5Tlu!gui3Q^JYDo zxy?E1f&1U+&Bu|;l7iT7(nin(&jE__GB1fTrr1K7aM7UQo^y&Fh)cd z+K8*Qkm4gtakt(HGugF|=IHX3cfe<3fbSuz^z!>Z_Rc)Gs{6X*&omRKsb@?vF*ew> zv)FZN>RO;TbTFB8)=G{kXY*9J_$0qr0OkU$#(fg}(} zh(#a?2?->2gV+~IA>bC<@ayM(=PH94gWh}h_kO%5-81tY{z32F-*SKF{LcBFbMC_{ z@Z5s+QkJKw-=0WM2~2HV^Gl@w5;#VU4RW2D^zVoU!0s3<|D~#3Ip7T92GJHQwNQ(WYtP84KDnvtip}vu1k@BX!wC=9- z8113*j|w?*|5t9_Eq!IF`rqfkkVq;@RaKRj4miwK7y!45+$-Tmi~godjn6mzOELPM0jFL8Vy=*!O63xU)1X@Ei86*3Htd4bwJ4Da zSIX@kyc|WeEbdQMLn@=Lg5D?Lijeo3z#L-`ItaVGp>Wg>A&k z-##H9g}PjQ9m-IyoAEduh4%oDCfS-7$uQ$PP|EMfQ1M3#ivN^YbN}-jYd!th(a{l+ zq;MGEXI8=hxK$?3ulI|h-KW7BKM23d$Q%ef%zl34KF$)j}(XBA^rfu-{1z3RNz zW8bxJwWj~1g$?2%Fi#*nN27%F(xk$5Hr@Xd<^E4)@tPNad$4AH56%MhsP1WXI z`lvWCLqrE1=t=ir{hrv%(JiZFeVLs}!;1e>ZKIsjwisIdtMQySxT3)9fMy>fQVGm#N>$#&6 z`i*7#vHeVWP$m~|G?lIU=w7KZys6N^P&|bDUs>v1uKe}S9`xsCtY^jFLvG%E5cfaL ze*)^cbD$2r>45$3h79<}U?O-Em>@Wcd*zkIjZ$W8(m|hg5(6|}x`B}f_xPMO%H2;D zft@d}Ky_P}>YU%h&5oOvvvjRKL*OIaj=nW*nKCR+oU%f$RO)0pH)1K@adyWerTb`> zvK;!8Su5Hxbmk=LyVCqoAuS;(s?F?W2%PfP;I)zBcX2+jUNUOnuFy%dS?QK|AY5cM zSnJ{cLTRJ8D5JtNH7@jBuBI(*lzs}T<52!@f@cBl{{bML8^d#FoI-+z0eZ2J8h0Ep zcRf3A&~LoBjev1-SBa2R*0?8N(-rsR!DCeOBXx{YziXA7%+1T$!R6nM6eA^VOQn%* z9mG%Ydm}C&KU#o(>$*oWRl~kNnNrc>KR-JW`%a1(C26(V-{GFq3KJ3DQn4rh{hfSA z>^oJO{8d?#vO0+Er_#5i5F0LkWkay9L**X;A4eZG?&v~4S#ke+#q&Nz0iYp~fg+we z10$dooQvBs^8Y<>^n@8ZyBe!2j6202i_OyfeTA%w>C0P9chFQp_caF8gB=5g%5Bw< za1^t5$`9bSOar1NBAQuHYOwGR&@Z5j8)c@|_Z|L7hAQp#7jyq*$tH}23^U4p{1-1o zCMAh06c4u|DB3!*N5;XQAl{Bq>!px7SbuqfGKrJtDwI*(w%_!SUT93}D1JOnc(6Lm zKfY8BHz-GQH!_U*$Bmo7qiHLom(Wi^@iwRdxc>)`cQ1Bi95T+n zE~BqgRRl$-O(EF>FJaQ625-;$dpJA?144SJtfAAK?CG^(0C?{I3E+K^fh3eO9D-Zp zlQzNtOpezf5K>mL(IyTNKR5t%n|*wad2m_mxylBiV3G``loC895EJ!%uvhu!HFx3pXJK#r^)E}% zM@P_0R^0!+=*hc0eHj^O!r+F2O7M2TiBhiRuS*(yceY$uAp)R}H1!4YrJpa}q;FjuD*Nb1a^OsxCRhWwYDiF>hd_XVzc?n zUzFgk)|!N8rUmqcoc^b5qd8)S{P#Af07D}KQ5e|O;9&-PHt1yal55$}x!-*BsXU_v z85bfT6broJu`J~QKTL_inuJA;> zF7E##X0L-91bjEm0cB^E=Y0v0;q8-W@!H%dBr}bArLSCxcwEIYHrY%=>veGTVmyTKD3WM_DsHv zp*ThLDyA&mOxkCG<`%eGM|m0kolb=?{B0C1a^89x&V_Q*|BM(lYr&Yp^UWqkfghlM z&_gzq|9N!cPTc>u(E2wrV1EY z^Q@zE?}?Nn`jKfNKF24O9l?0QvoRsKi9J$;{y`7fFaX{HQ^21@2C|^|y$*q20%vTH z0k`izf@`qY$Z8K^7vrQnJf=12Zsk&Dr&7!fiu)M%?OrPFmB|QjId>QV#PSO$!@E@q zJ)VR7n^P+Szc*`0k)b>){dJg2$>OFx=JR+hu1C?@7(b*rJu{E$g7HWf412;L&BYNcoEqTLT12%KJJ_N;B|tBvV>k1Ia+t-Ej-8ULwn?F7|iG&^pLgEzh4A@ z6dA};S6A29A#f!)XQMLU^ok~PqnO{BAS;Az_{!{4&&{Wwe|@T@3rhK-;HcJCEJpl| z4(E+Sn6}J?N*jYb7Bi`F)`~XR=X0b`1H6a02{WoLT@DyGJ)I--^}Yee&HU>2ulfv~ z^tF#sR6_SB^i;oXLN3bR<$6i^>-LQDPo<(he3QqN9r5Xj3w#4CJPQ*C8C7P+vL};KQWcUGH|C#1$Erxk~AZDF2l4aLt1t`A>Y4w~p7X-y6%I zKd%yv&y%Ioirztw*(&`z8eAP248lOKV&ESFuY>bJD+3-re$q@TSSxpz4zLgjLtiZR z?uiR@OKabMBBflUS#-tw_%r#wa&a?f>`6WAA6{InSbK~W52c(xM4blaKJIrIHYUwH zmAghQh~G^h+Mxi`ZG1lpSv-IndeiQsCKiEgN?rO*bbicrxIRDzQW{whmGtO8mNIUC zZvB+QuBU_J0*!(FG3d`HSKspFg|z)W-tFt5!0!LO=)@0tB>Bi-5e9q>cn!Q|BPDP{ z$3DnHc|PeSBSZpm0|(6r4(JaOXZ+@rrSzIl8E=5vvc6Q_WNv|6$2eThnOMW$N0*zLyrOv~UU#{@~%9Rji+^4c+>gZ{|Hbm)szeJ(_1N_0Q9PUa5FAh$pdxdy5 zDOuSN%+{z@L6jn;^jqBNw~E*m-~0C6${Y6_eOHC@?7X!Y0BM28_yLsn7vMZBqb+Z= z0yjbmjeX>NO?Y3W7#3~ZrIgKNmqxrQPt2_oA4lP(@T_wO-~0JQ_9}A_ePiSH@997A zWp4kF{|1pTcn`xttqn3@cWUNJ2`(`eJj!#C! zkFr;*yJ3`~hC25u1yM$&v%QN!CNI|X6caLnI80wCGy{D41nR?-QAHvCk*~-FvbEIP}q0)xcT0U<$c7f&|-B54pSXSbY`2U z8%h%jE7`jV(wCn8@e8FKUCk5wQinHWM3wxFQdEfMjZeZy6rtApLYH_0sPsOI+{7*(vF_RsaO3s& z``x&~-)r6Zy{Kc^lZzXu?VqE4<8ZIa*ukSBmwJ+lsvyFPpzC3&xtX=Yd&D}1=gbv? zzMs+!*1Ggk*x@cV^dtqvjhgm3i?RkRHN4AP55f{RDaC z2wL3Ow#L_kaJt7j3|>iq4&0ym6=b+)o!686J-3{w~Od1$aZwCTZM%HQ6F# z(^gQ!KiB)7KzOcPS!9?8US2HyVk7krc;vt5!22VES(u2cz#UA)evtt!ln0(y+pbcL zFx5j`F{CT4?B*Y3QH8G*65AFr@RLz!l0(h(GT#VrGe0~>itp1unMV$3*g(YE)xOr0#VGK*nQk%=RK4{_%LaPI}i7mC`45WZ+~*G6r8K?bcE(W zu78DIl1bF=xb=1V^;enOC(PAaus^~xED10I+)s!My<+qBKUJxJp+q8Q&YT&7C3FuH zbJCUv*NU4VmLUJ_jU za-Bz|D5$Wfzws4B)WE}`?ub=e_FYs2sG017H2q$j>#K20sn{G%xnmJTsaMbY=$TIE z`ZkOX$}%UF>wo`=Q(gfi2&s$ANL3`1@*ni)Mv_2!q+rcfYqx(Ce>eK>GpA3V9uf)V z4_tt2!94J`4KKist|N-s5StXiYea}E4GuLO_~y=Ma&bkkPP$BiHJt}#pj_w2*j<6= zkvm^t${Ke^#|q!!wF)kc`8|p8>vlP}uHzlq5R-`8Upso1uXJJup$u~A7nmi@d!sU7 zUR>M=QPd-~r8(St;wWI`)hX>US)``D%Y6T}D)SM~`=sw9JSUO`eR|`;SIW)KE<3k> zU%%yoYa^k&!vJ^>xK~{QEqMV7*KQ?_=2>xRA(8_!0#HN{;4)QxEpJVSD#j&CUGA?dwv$B;~e-SrKyc6=kgy%z&0k35SEq}L} zlij_xV*hV|zXI=xgd&lgJ$u$O0)~TS;2bakA*=17spW&-l1)bVw?C@C&8JDtMsuY1 zOwzbxf1I-#r6z^wY(BU}(>`~uNv~-$(a8LD+bJmI#n5AWM-;to!9B`65T`(~d&V2D zS4YZ~TfMp$_9w3_ZGvwg1IwxB2_>t7Kc|zRoptNJwPN`5ZXO2xd62C5&a@?F zmw)@WfOB9881ALOhjNiHDenajfj5B(0w!5rfRgncSaF%2Yc0h8vv)4QSypEr|8!P5 zRYVA|fOYC>+wFB*s@>Vv*6D6rdtKY=ww+!SV~X8cYwI2Cy13QWwd+`G?g1`|o++1th#{g~=Cdg7fsGMG zuYLrtw6q(91L5ClPV$LookTU!ehf|?XPRk|fsM>>T~9;c>%45fVo86(OU zO5lZN74*A^{_^ko-v=>fo}aBoMLTF)97|>rULo_J_ib-Yc^00MLX|oyK`}m}eh!7S z(1yrKO=YC~hyC79SV&*|G__oH|32RStHC6X_SZ?@M!@?(vS%&%u;{Yhmcb7AL1NBW z+YSi|V$&kr_}U*|&|w|_UIA^~)XJscY^?!`97UY1+=$w}hfOS5ShI)n%0iS}&Op`~ z7kr*r8WGldw{Gxw9|kjCZr54rBp54?Mq#7W^ZQ6dAt(pqBFWWzLAtor(R31mZ% z4sFspvG(r#NpSQSXUQ2W&>Q~U{!#oJz!gW29*sCTza~MqvT(k50ot+RDz@zAFrPi_ zE!#v#bOUl*Dl+L6FUPZ9TOIB0nX^W>NSkYbl7u~RF{DW?{rAv+`yEm5j!wxM(5;gB z5EEZX0^l))8~FFZ+)9;C@I6Xx>zWV8Z1>AJ38`4S{pvZaBVm#nI44fWjn!{@@dmgD zWJJ1~e@O75-_ABKZrx+reZ2jHp8)O5jHd0dOy>Ih5auv?^yJaj~2@wyCg=jNd zWiCUJ4|t4sDEUxcniwLkhfh5?7sJES`Y|^6^HuhD^2|GNDJA`F{#p_`^R(7(FNXu; z!L<+1Gd1W5^o1YFzdM@w|3jDgKe&p3NuYA56#+eEfrs1L&9mj3b(re_b4X~66S8Fl zplHNeXJ15KBUg9SRc>YO#IK3XKZlFH&h#gTJ5P$YVASoI16tRyN;X`Ik+DQM0RtSL zya?AkTx3eOG{>w~m{zo2&$F@Tcs~k>{NT@Jrhyy@+wywnTju^kPx;UC<~;28aEQ(t z{dWFRS}Q#f)f;}?{$B(W+uPeCP6jBQot-11b(3%V;04eN-O=1)zWKzW=#7pI>X6XD z01&Q4+2pO+Vd5cY-|6DnjjAMJEVkw?gX2HWLy`SuMZFA$0gO$bznW?QFsM0=CT#E{ z-1>|yrt$P$8maJkTkEFxL=vfpiEIj$4j4Nd%vE>iq5R$64GYi%(g)vu@&)OKp(}o& z8@$bT-Q;fngOs+mHqQ$%0jwNqF2J$q-?sJ+8awPTAGmX-_tG6g04XT`eqqLZdbsa0 z@sNEa%uUUGNjGgv^OllEnZ$InFR9yWCQqBITloOi^xS_Q5H7tv!#rHPK_Qa2&R@Uq?42|Th3bXr-PRpU9GiJu~)k+{^uHKps`+XZCkaLr37Hrt=>4)fL(U;8s zG;41*Er(3IEjk4L*!CCw3z zu%^tSUI3cKR4A#eZ&tuoS_+rxvjhEpFsN~1K&ynXA5rq=^r8(ICRtV#A_fygL-{I8 z6p@l8VV?BxuQMOJZ;n&`VMBBeq-3oy`wsa=`P&W7pu7GZToiFKcwv7;f+}kv%Y>i?0`2D!r)eJV_d3cfKpmXcs6Agq)L;g&+7@p4Euc= zGN?Sf^5s3!3x4PqPfvj*DF5-E{&F((gusiyH(AuTWL^1G7WliOwQSlf%5<_FLV$4~ ziT5=TcinBcdG{RdG`3~!&Vx|rmT=ofsacpaN<|9yo`N+}^nFS8ss^o#h2OIj?Jemm z;MQ+9{mK05CQ3OA8Gqno(lb6YeXd!yWuLJr3u|_p>mHqN#_3(TJsLW&Umhu@MYL~a zf6DxS6Wjn?$3MTk09aE=EN&in*N+#~NutN*T?fpUyv-}iJ!Ljjk7 z8Q@)?Vm0a(ct`e1qfHF>VK{^Uyf5LO|EtI>sNQA0PXOdLiXA3d_|0%&NwNO~1;3~F z>~?uV9>61VX>vx8W0A7`Eb;DTb^W^9l^<|DN*V7((7yv2gHJB4RYiiW<~|S2Tkr8S z@N9QxgbvZOEvN5CZ!CxKK1T)^Uf7{y)hS1v#Yj8E%#G@EOP9}>fJ z2oZXeFJv}i#Iq@f+E2jtwnm9}7Urr9|9^U@&=eC_?@OL8-=c&&)+MlU@cq>FFEw@h z`se>og`41tU(F13EYb1k{cmPG7DUwr&n(|6?-K+{!+sBj6`!3}sB<62KR;vtTfm3- z=L;fE0&N#O#Se<$o?6c%N83&2N{{YUNoEcq!B{Y6YNnY$2qz`yMcmGIv%f1EUNxWp z?_!iQMnKT>MUis*7OUPVngI18R0_O89eSQV7tlBq|G#VYYV!t+cYVn*ZsK?6SIPa) zxC301Omq3Ka!pQUgSCDUa4zVb^Sk5uZV!eKI_EQ1)V+uC&zG{lv)~rj@F!q7b?TI7 z1WW?upv$jNK+|h)n4dgb%BJ=X&rCDcAtaDugxmf(^XT){QjWIdO_JcAD)p$PrHq1} zD+)Co`rajd-M$qAzy9|Nln@uh^O++ak96Gao5hRJmoPq!a}?tU@V#p+x5DSK>-7$6 zxgR%^`!P=Md${kr@PT(G{4A}E^Zs?6`#v`OSqdf*`;RyYSiJHI@U`Q}ki7u!;Rbk# z7LXsmFW);{9Kr)_YHnoI%NLmER&O^JMI<~oJ-tlIIpBF}j#vF=mdT;yuQzEt=p_P{ zYb-%M|7xC3{zHuBKReAQADE+3$)MH(qYpRyY~_F0ApPki$KsxXDlx{f4#R%Wg%zK; zKOYX5zt#L7^M~~381fZY_Gi}!7z46=@&X92w;nZ5E!pHHh&Y4{J=XnKVG3N$P1t5a zV8ObbQj932py#M$z`JHunxnn6;B7x~+T2&L){ML@J&@<)S)h$@Nx$ps>Eao*7*-ep z1ho#A5{kGNZiJSO6UK&+(;$O`c@6u$7E&l-^Zyoa)H%<2?5311!rSMBtq%zDN0)sB>+}!S*-J-NeOFNYg9_%R#^dlj#y`$5V);5t$r5hdYK8LU%35-t3 zpge0Px6r?;6|F6)YCK^6^|2DU-;;uz=g%{!J%Dh!gwL(a~`M2Eae?A}ak11+*OTLIJRtNvUKwga=q1AGyT71DzkY>omL^E6l5{$9t{6mjX^n_x-tir-+yO5&r)=Gx3-31q8AN zndTCVklQj>TIu%pCe;jZD-ie3;hnm@6<%bTKP|4-x$a}le;24g`Ckc1UBpTJya0a( zJl^G_pc|MWLbaid`&-OUpDm3ZWSI^jf^KST{BJz5$ZTk8F_uJ4XnyU50&Y(W^ml_I zhW59}l=Z@UU%U4W>f#qGL*CD4VpPd1Rz#^`AEF$DJ7=xJKnZ64moe|j(+kbgy1lAG z2wHM08)#*m&vU)GxN{(^XTdFLWjfE%6FT30KsVpj!QUG&0$k!>0vG|Kz!RVgn4y9f zD_2p~w!P-shYOt%5YCNH``|420V>Qvi{0VYG#)hn77YaiIk)k+q@P?;+bc!wl*|=U z!ahDz^ZAlTx7$xYg6dr@=Ia<;7#~5*S$J&GMl$lxn4pFH0$NW$?>*D)|L~px5dIYC z9OpcbUhX>mKo|G}9_UdKCkasz;DrMI5p43q3(#W(9DyhzZ&kgSM9h)oABS*3x7I6v zJ<~kCXp=3X808&g6AJhsc?di|u(^%I6`q{6@?7JF>Hx~s=BzQ7OiAH>H!Rqf+1Y&7qm@~p4VyLd{f`Az=nKD+Im#lV_Yv`*t1?dM#% z#o>K`S0LY%Y}{qu?dmcC3iIf~VBClSmrKcII?s1|D1^|vyLa{~o!^|}e!Biof}2qO zBO*=`t7R|-KoT!019T193jp{l!id_YSIt)+^BS9^lMLh#Dx?4o@f-fQ(5$0L!{U6?zK9?e`Gx`5XRFqH~+``)}v@(BZlMQ&Ij&4Gj$uCy5sdxDb4b zm(<{AD4>mYkxMo&;R$47@cqoS5- z$I95wfR7^1_^7s1wLh$sR&8yqN)?fZ3fNZg0SZ;1RiKIpBs2+xAR#2d1d;$rfW!m{ zF%U=~0n$LYNk~GTH=qPafZ={!XHCxZ7D+Yte&^8dvbVD)j^gp2+;jHc-`;zz-3J&B zoYL6}sBdmlvsM+Nyd*D~i9j$!5L-lj0GIX!a99l|SDw`M2NbpeAPQvd!9VmBF8;k! zR_F@Gyv{vS5>zIx+kV?Ec^L?w$9E@D+#zB+1m2JR)CIbz~1TxH9TUSI0!>e23L3YlH>B-+ZRyN z`h^-7o6A17?j!)uAvx*`)|aY4D>~4qhRj-J)A2$Duk$FVgrd=K<0%4=`}T5^)xWItnN}lG0Xos8zXOoLtXCnf~GT zQW&uH-49Ra7_;PWC-Y z09Qehci4hVE#-f=6Z^Tlx(J`^}Lc`G&FT`&$GrWEbhnFP=QVi=Gd{I1g!J5ndGBxg!; z0Z;Baeor?e!B}2U(Iahc(3{IbEdP@xNwJ^jBD2)q`ZgC@Gm1$KdBANB?&NiC~&}pIQ4N>3Cr|u zCjkiD+IsX)qNP#M;QOz#e4pX-wG89ovr=&A9Am< zET)9?{S6QPPk)?2KYUa#-`@}TfT6(U9x>ySfGdGFHMIf)_5~bKdAqCB{eMjmCt~hU zAcbEN>?JquBmtam|MAonOae3nY2L%?;woj^ysQ#l8}}FTjKO0cq^X_fVF2v#HmP4B zXoou2V5=IgE%CZB@y~@xxF_NJoBi%0flHwO4bb`5IrTpoxY8qLzO}cvTS-8FAdT6b z#w4KC*RGPc>_H!Pu{aa+V1H-a<-fkH#?>Ss1*g#a-(fo-)O+9%+w6?uz)g1SZS(## zFaXZelPCS~#_=&ciG`57u!Ru6Wgqt_IBiWwkE^kZbI>=IzQ4imELb9oRO04x)#9Vh zzwS+cDL{WKZis~sHSkh=x%UN919hRksa4HMFS69;BndE>8Gtpq5S^)aH3?W(TFtF< z2(%%q0*;B6-CF-%NXp-5#S4#E*ir*Kbq@s|sB2PhCFDy2V76mnFMVfq zvvSASN2eyiFK4ad5FCc#f-a*&-+=3gElWc?;0}8@FC+#)BxfYfH7r-+`~?1eNFqiv z)3;NF&tc2 zlLVA|8`MiN*>butc&Ml!sGZM7X4z|R-J%Fm?)JBHGMEIw$p!P-xxp?bw~W`^fOf|H z)}Z0Yh!7b7i`DWi6<;l*$L1jT^Rot~Oph-H5P=nco|^~XNZ8D4ZK|l}o`4XB3>n}tXvN(EftFNQscwIROOAqOUyNIl zO9^2&Y-2gLdHy{`hW-qHFQ1r`M*j*wtFiCT>-d39z%4@gy9lhKqvJC4c^<%*?*;t2 znE{_+GWCIX6D0{SJP8;cnMI{Ix9ki8_wL$OCLi`X+TJeDdE%oCN&#aZ0F?)k0EEN< z7&>dU+E&#Vs4~c*KzzO+lN56OSz+KVLE0!WIo5_vj2nAl2l%q6BGYD z`PU%uKjicIF7t?8G}OR8z%V=j$22DalnY+7twIf+v`n0ixk&((9-qO9yu_`U0LljY zS$P;8v!S=;YM#jP%u#W$w5Ek~1qiqyF#v`y%%<9(ZCIp4@th!f|4dL2jG3IA#eF($ zNTyg(LAybcaf5^ZAXJ+&wsF0$6T!a&coq}?()YiZtfi&JN&@=gA(*OJDwt?)YgY-I z%B(48NdnAH0#JI)aFZRt;8=OM9ewuPUTD!ctnkiT95Bf=zazVf6}Yt^LN9a`si0R!w^8k*avQGLV9vNOQoB!F7un6RgMoJ+>3b#L<*>eXfWHof#4(*a*x zv{7Z2dKs*P6Y1f#kO0i5qM&8Ru?T2-%ICr8=CciP_uC1U_z?#loi=aFQT6iT9K3hY z68sGg{@)uLts*mvb?^2M?E5dX{6E^k()YiJlYmQr!9bDbB!HzVZ$q=1@o53JrO5!2 z!On=^-@Q=oTY?h!X#_F1RteX%eyLuI--INDN#r-C14e*bj~)FXB>>ms^c<5zRsnI*=6mo6Yqhh09yd)y@w(!L`mUsvAK2|qSt%<-~F_ZYj>TtUHC4* zE|B1FFl^sAacQpFTixK)$-fI216<(|6SK_{f3WsG}$JVOak6YDzG*Fzjiqggl~dX0_1=9vuVj(_VayP@4uJ7 zqgUc`Rjmtq{qv!I_?r*YW#G^3;E(SvLXBIrUhS@`qrZgT)D1r{)AI9Y&YbaxiJea* z=!batk^w)UbOG0;MnLI-LuyP^wm7OV*&pyDP?T^oN3Y%;QJebqW9rQno1s|Pa}v;( z2}(49zYqHUZWtSFCmX$SK6vXwl79!(?7yb+J`2c@B<0J z&A7G<{fYJ13b+Kg0}o1`&PqUF60oDPR=uz=Q(YyJ!C}!7xN%$zOYWJa)oxJSI1?}t zJ^t%@D9huY1^#^~_ZJl1?-5i+^7qw|&gkS0h1k4uu2jBG25gO&_#GaxmVPlH_-j4+ z<9fNkAn^TV=uhl_8)zYy;-UEou-mC5U|U6vdTMUE>bokr`CZafDe1&-fH!`Icbsvgikeq8}1&Nxk@DzRB*+ugX{n%8Y80C z+rm5-+>sA}LSR(RZqWUl+v{J9eF@J)o$#jdi)68n`N5xiQ?|knIvD)xfT6&*JYr(K zF-sm259CuoyAw%(4-ft3@`LKvvr;7q2%l;IX9EUKj8iW}Wpm3Pg0*8}L8a9#>JRhQ zvhRMOf??n8EsE1pK6h-j?}@I{lp%g_`bulqPgeSdLGXWcb}Id!&#B-K!!?Hi-}Z=! z_0|#-6Rk?XRd|kG0lEO?L=sT2$7{6%WF{c|m;zM5dpoIs(zH&=2BY7OszWN1fD3T1 zqzsOU*+At&Cu|>fBm8-3o*FnIPD=ja5&T*Cgx_;0_!FJLE0+ImYHIR`i3MfHjvcd- zfPTPq;4B|52igHe=z%@AV67wp;W#8XK8Bq7$PK$xtA0*A`|Xe^2JLT!z5UNEFzFbI zr9)dQ882$vY(Xv!(!l%!5?LRuR~>j!Vmln=m&Vj#6mKxiL~f}HvtO) zqpeOfI!S6{hPI{-HqlO+cBZZEm^7WlXj+WMnpCYcQQK&10^y+n1m#tu_&`8; zEuyf1@@9E12n!1fR!yuUCO!TBGyKD1MG*F`@c-wV`R1;UF3x!Gx#yn8cLd(o2*Bo= z1*UK4GE{Hw*7(^FVKrT0|2Q2WqU`L8(tNa$?AmO zK*z-^6p~&-{#%kXl3rsTMd6PQc^Xc*QE6fEtX%eEUG2CO0d_ z(FFAYGuK4Z>gX&gKkudyfG6+;#^&gks#hHr%pw@}gum-37PMBkaGC<+h9%E$Jod_C zhtk#$^Voi=y!^ou*x?0ul#R0p1U?oD8i!525Sy@4Wjeso>Y~upLV9rZZZVX`j|gzf z+k#E;6pXY$*9h7MbM39H%kQmJz=Y+ypxkzd3L9L*QZ6iRba6lbthIZ!K}B-$8*>jM zqO*s>(u&C8bg91l?f71Q*kPILFSs*syWOAdl?<-)z7x>*xz*K4an>@L^ZH(ydS;rm zz;1dDQ}r?`nz=P@IH`c(v=X|1Wd!e;k@jzYJ1~-Ba;sQ@@HW7b#cTIjY3}-1y`Nw5 z{05!qH+Ovu#lucHU7enGOhwqg7W@%fv_;}qaK{S+{jez)V{P>MM)&yaa(B@&TQ&V) zOM*xTn>@kb;3>9q{+Oy+WF@0}i zyq3a}i{Ietem@LKq?AHCb-26w+b{343AQ!>FMmIaKrl*l0`A4ed>U*~6z*cWySm9* z)}W~dN(j{)jsUM;dpVqbzU%0)6!)TXDv&Hq8U=zZ*)iXxt+;mg|x4`yIYm*)6)p;xcGkxioU;K)I|cugU@1 zwpr5(kTyY+MFSpvb3f+bm-BEO)lmRX!Jl_i&$8OzV0r#LyOVwsl8RwSQrT~oHosT) zq(k7R$wS&#+2!A=*;k}p#|H%kSptFBGZNn4U~wY;hr zS@RfNwBhaH0YlC1%QRHLX+TZEZPx;@)9@>d->q}~d(avE+mdZ8ZZugc`^~Ty`Tu!S zLJn0nwkj6*;qPsPUDYD2pR?I)7J*MIf>Y3D z^~?8CsCbAK`30DxzZl{@2<)*{=RlUW5^71lMO5iEv=2J@? zG#Ea*+QXIj5-3Ijg_E?}i96;8kn#et_Q8|imp$K@4fCbhNaL+G(T z$5G;$nnBz1BC{)4Zg&X9fILg!iOmVzf_O93v|Zqyete(Qp_ru@6`KAiKIS?8us4b; z{h390NG(LpU;6w7V^%g;Qw)4elMUugt&W_Bt3W>PFG;zg9B8h(0PeAm&8@nnUO*;B z?0$b!0$=Wfd?y$1y?v;FuH9s8aduS;E&Y2k1{%qXe)A~%h0(@%E0xx_=*50Y343`8 zt7bPv;ZHE;BLUg~p+~@z$_`B5!C?ii$dhO3yRSuwXpnhY1$l(-UTf8W)%q2X7k+l< z5mxjY%CMh;y0-tD(~tH(?(2txAQ!($#C>M3ji#uKbN!Y5)H(p!;CvbEBQTT`fWtEz zA*~}=Nj2L$+>};qr=@@UQ0f9qtu4Tue^wII z(R2M%ib4tjqC^t%H^+WryRqo6i7Y+_d%||b2Rx3;|6#fO1x73kyc?VeCV-@h`eAZ+ zb&;*wK`%$7$&1ZI9Rl;;*iQ#-b#x8;ZXkbC0Q{fS9ozvvfY-lV{sLnIoBS?t3O4+DZ2l_>50<|2aE|oi z8kX#wxIzrM843fJy>r;}CC{Jnx*tdjfPa6{TYZwhJxBpWWR~OQFPFbrqDM=%9^jBa z?1iWOP+MUVf4 z&dyGYKwwJL*VkKc0Q_(mp3t-cK;=#V$Rj%oP~h{&sFO7L#rKH~{R%ON2CU@f%KgBY zY+ADY5MT88{hM9{53JrzuSRE4L`E4ceJ5E8{tUi1zKm{<7sFC1ucCoE`W5;qcL4Ii zCvo}v6%-U$1On3oPQX1l6tlqi-h=XAkQ9;&eCp1(QFvOB*6Ss5OUFldj_r> zK+0e$CjWh7)o#rxl>K1fJ@FmXb%Y$Zp+H!Dp`WKcgMB*+&cWp`0zU$QCvYh4#v%C@ z7>+}t9T-r3fnZxlC*_pY^J@T_?!94OF_Z=}_&SI3=OpZJ$UV^4-lxg=W}K_VxmRB3 z=gCjOQ2d^UQ}8a;28~ows%dFlcNETtdEroKqxer6F3i} znj>(&s~t|(^6zH{)bcXy*q^|u_$-zF0>NNyf;cE&1b+tB=uUtS3Itj4>kNhp1)??p zVPkm>3WZ}k^24d0--o605FeaxAHMpT4d5^Ev+hHhQ!amjU?^}potk{G9}dr>THZ`` zT|jsL+5n#OotRrri-HpMP60^^2!&(f;_qMKADdOewLV?Gd-d!2{&YNM9`Chd7J)!8 z4CO!^o^OIXaiDs9(W2woDNtl@q7D19Y1&F%H$b8RLg8qC`A>t+#~X3kRA_gQtJ8hs z`Bu#y{G4~e>G&*~{sMs)QUa5~mvE>$u#&HcXaM&IC!eWgl`t3>X%7<$N2vt#B=9HO z4$$HJN^0)w>-QzQ@H1E8;qnzwLjD4Qmz)5N6L7!+aKK9RgY6^T09FSpZ)l;A6dM;4 z!QhA^AQawVdY;3%ukWRQ9?hpR%6&jOc{0d9Ux1E1z6tKR|3EM3OO50IovchB@nY zNOhy%S=y&`vA(|;>y74pGVc^Y5Cc0=a6GsWJnBpibSX(t+X-*~;D9`mUaC!F^f6+7 z07<%*q_wJ z5dP-{0)YgAAYue40q5XSjshRWrS!X)7vQu!bcT3UgJ0$%9(na|a_JEu$+Kkfe+BpH znf#rI^ZT5$`dy{|CO;sAu49K} z&e|O&L5Ye0N&atwUlVPouiPrTK0K)R3UPkdv-|D0#A--p8v6`4qs$3{@a90FFN5=X z0hg-;{;MJ|n~DHQo{`{3<1H2ZJ3nlZKu5QucfaGFxtr*>089bd*oPpz9ufePaB=T3 zv$vhzfS7m$I=a<@c`A3CTuDWMB%=!cE1t-aUuABVt$Uj#5Hy0{k>KZktlzBF;N4&n zj0-0E5k$Ns2;$P7i%UEK>msnXzY^eX#ejAclyZzAeJrO;#yy@xMS!H>xAvD$&z4`T z*d%3pn#B*nA9e`-7{&UJfG>d)VN9IM+J1tF-*Uku@D6aB(;E;A!lF^jW-A`Lw5%-* z4Uj`w@ay@XmR*MXa6kueIZFCsmEmt#fqbI7z>7J)mmuOC$B!RZZvc1&I2#wd13Ya} zT%@zLu*Nnk8+@{$phB*nv(`L_Y$!wy3Bf;U)@qr#x?DqJfey9!off#;v^$D+W1R=4 zK={+@Cy2q88-VQH3qUhsxDforRsv!~^$4_~tKgmkt+JwYx7@y{P?A&ik>m(sGVbYY zk(~_QGXD+vQ&A<_Y5O%d28yurKEz7+p9DAIUAPT#{tFrFCx}6eP$*zhuwZ6F?4TaW}e;`wq6rt5=2Ezkn+*|vjre~`;w0DIh((mlY zfVKxPfc@j( zh42WNE`kqWU*oV0B*xTMr@r=1sjTaXxRHmoLx0 zu?zA27CpZ>$59kscWK{ktkqcWH}{?cdnYF!ihx=$YEAirT~dwefhFn{C_s#rUIEf| zYhQCzzKx{5`KV&4*mqE^eT1)YPQ4}g!9M(-aJ`}UZ`T;>Bk84b^^9CoK|p1I95A5|>R_HnGfJhnszJR{9<%nH!vEjk zCh!N~CAi0Q_Y(y1nezjNK@k`YK7U;P1cD%l{|n)8*t8V>0(hO3AJFZkM8TfVpbY$M`9_)Wl;s&nr7}P| zgnp_+=r8?L==-Ax{T}aG{9QQn%dlQ&?kQHr`3Zs;kg~yx@W5PeJvd#S^9V#&2Go6< zzWIGs2EK-Ntd}Bza3o6$hhl>s{O;h?baM`H9!hX7cH?|R5&9l=_2X=Z!7SX#o4|{p zs58z_5X68d3aTPtH4<3?_IO7Th;FGxtd4_58EEjeOX>Sf@?7D2a_8a=@}*yAiz)-D zEG-QlD zBM=XaSfPT}j&3dQDyeCd->fSre-!tr0K5(Riz)aCg76d)6B856!(<*PtB1h4UI6RKsEUC1 zy#pE(-X93c&bk(P70F?#nVaRC&*saRX_-bLV1+>pGxhQP^>?`8x9ecxm&hOA{2RhL z`=!y>ZhklY{X(9%@}p1b>jAK?0l$mwC0K_sv!5UcFOMBoMF3%e5!SGwrB?aiNy{rB z@yKvQ^9SG|=u#JQRfA7fZQCO=^0vwM7ZqwdSXiH*h7RZ{IBTtT@jY!1jDBZ)Z+!Rd z`u*`cnBPM!{9u<00^gnSyg@J%_vl;3)o%oUb8~Y7K@db-RSkd_-~qb|>l5G}>_2hl z3B)TS5e*q=EZNuAsY!)9>RMDNEhwlk7AY*#d>Q*#mP0W}=VZeug5GJ>kFqTLug=Po zDbE+Fu>W((-_^QD(w?^6#dp{5?}AI;t@!>)unGI$ffKE#HKS2<2pDcKe)tF=) z_77vNuv!ee5FT(ZY!Hq_R4BpqY%Ef>OlzA0QdC(lFBDg4g5e#DH=vzto{WZoLqGZc zUV}k5_Kx>D(@(*`*!~5crb?<(B z{{&ctzbg$KkNt4m5!Ue&1TpB61?|a$mw`XTV^@f^&zUDM*ch?e6~jHPRx7MmRSfEV zZ8}7zpkkjad#74vt}d4ck*R*`bL+L0@#wTn2uNE&05zlC@*)^Uv8LS==+1VzBb6CW z;jRkKWoel@8-MaMYvrCLMKU#OvnJr4?(S|ICy?+-N|xd~5!s403$| zFb2>0;qI16Wvta81j5q<#MbuTiwA!AwbkI2; z{b4(7FKn0XhjLJ!P%g?B%1L=C_XGlbX$1ZP4&Zm3(=X#4n8vsQzYs$Bd;?q*XJQR5 z#@B3CGt$E7E&@4zYeJ|n^!0_H+k@S`ND$7LJyarsy=G`;?;It|(ci*%G5oGO7}m#n zu`dL4+g{1<^9ek{S0L@cw{Qu|b1N=%Ai->?F%8qf2Fs#*WM{$QxebRLc5gwRy^jFfdYj zOY>R6x~_x}LfnALfL)AnIX3u3I#24A0WRPQVsheM9doCDo^xq0P5Vz`osZ&A&?*%Z zLI`n#p(0==xKP{R!Q1c&@(rYIqyv5-A68{RUL)ua;}B*!XWB@2a!x%?TF0`znm4IC zeIbOH;u8{a(YDG!nv3=v(z{sRfcwd3FpooO9Vq#LKY|56A2Q?*;w!iqYkeMzgr3(I zLWrA?GiT1&I|gh3;Vi5mUym!e;J?oDQPO@?>p*GU<>%3k`w7xrq|e|&@)ZR96qea* ztA!9kOe1=FdaObax(cv{{5_-_x%huhdLDDA!GzN<;OD$Azln4s-p#SEAx-mHlDr<- z6GDhmp`)Y2DgKO}z~FJKPys7k@q7^R={<8qAS^Z}K) z=Sd&%<5z(xjI$gQLI@%LmHz(znB6H7P^?0bCSOGmSxCAPxA55~q`%-L@_)4ypvDti z|7QFs*bm}G{Mq;a7F_AarOJ+zAFr}2SFXf_5JHIS0)b`*CVXO-a?)yCKz;>o!adkx z&CjG+4_J8|!eO--T=R~87>A-jZ~G6s$hY8b+=R<<0ap3`PucN6_2CO4g!q3U2+bfE zC2$s|u@Yx*6!g8$irtLcVvG4Wf?zR4y;#Pc*{60KHz7EE6)yumUm)m2OSVz7JE3w@F-zdmy zaGnrC2;nmyMzbGU-*(a;Q%vD(^5s|&RTi+C@7Cgc01mtUs4cp-!kVv^o= z*wxi#-452}U=@crX*{YxM6x`S&u7{w`LWvP_V+R0PxyM`wq9F5+X>qfLI@#*5JCtc zgb+dqA%qY@2qAq%;9 literal 0 HcmV?d00001 diff --git a/phoenicis-javafx/src/main/deploy/package/macosx/phoenicis-blue.svg b/phoenicis-javafx/src/main/deploy/package/macosx/phoenicis-blue.svg new file mode 100644 index 00000000000..f8a25f01a74 --- /dev/null +++ b/phoenicis-javafx/src/main/deploy/package/macosx/phoenicis-blue.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/phoenicis-library/pom.xml b/phoenicis-library/pom.xml index ae055259fb8..1e0032b2556 100644 --- a/phoenicis-library/pom.xml +++ b/phoenicis-library/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-library diff --git a/phoenicis-multithreading/pom.xml b/phoenicis-multithreading/pom.xml index 9e338c71f90..9259d7d2578 100644 --- a/phoenicis-multithreading/pom.xml +++ b/phoenicis-multithreading/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-multithreading diff --git a/phoenicis-repository/pom.xml b/phoenicis-repository/pom.xml index d3d24297ae6..f6ad23645a0 100644 --- a/phoenicis-repository/pom.xml +++ b/phoenicis-repository/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-repository diff --git a/phoenicis-scripts/pom.xml b/phoenicis-scripts/pom.xml index 2c71ce66dd3..c7172c85b5f 100644 --- a/phoenicis-scripts/pom.xml +++ b/phoenicis-scripts/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-scripts diff --git a/phoenicis-settings/pom.xml b/phoenicis-settings/pom.xml index a84e7e942ae..226b6ac2d9c 100644 --- a/phoenicis-settings/pom.xml +++ b/phoenicis-settings/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-settings diff --git a/phoenicis-tests/pom.xml b/phoenicis-tests/pom.xml index a5a6fc70ee1..020e2d7d907 100644 --- a/phoenicis-tests/pom.xml +++ b/phoenicis-tests/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-tests diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 0c73c513432..0a2ed2d41e1 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-tools diff --git a/phoenicis-win32/pom.xml b/phoenicis-win32/pom.xml index 4d8d653852d..ad128ef8ef2 100644 --- a/phoenicis-win32/pom.xml +++ b/phoenicis-win32/pom.xml @@ -21,7 +21,7 @@ org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT phoenicis-win32 diff --git a/pom.xml b/pom.xml index fe502a257b5..5537e622af4 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.phoenicis phoenicis - ${phoenicis.version} + 5.0-SNAPSHOT pom phoenicis-javafx @@ -43,8 +43,9 @@ scm:git:https://github.com/PhoenicisOrg/phoenicis.git - 5.0-SNAPSHOT - ${project.basedir}/settings + ${project.version} + ${project.basedir} + ${project.root}/settings UTF-8 1.46 1.7.25 @@ -227,7 +228,7 @@ - i18n/keys.pot + ${project.root}/i18n/keys.pot "POT-Creation-Date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}\+\d{4}\\n" From 6eb000a6fb99b4b4311f2f43951ae41daf3bc798 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 25 Nov 2018 20:47:36 +0100 Subject: [PATCH 05/39] Fixed maven build --- phoenicis-configuration/pom.xml | 2 +- phoenicis-containers/pom.xml | 2 +- phoenicis-dist/pom.xml | 2 +- phoenicis-engines/pom.xml | 2 +- phoenicis-entities/pom.xml | 2 +- phoenicis-library/pom.xml | 2 +- phoenicis-multithreading/pom.xml | 2 +- phoenicis-repository/pom.xml | 2 +- phoenicis-scripts/pom.xml | 2 +- phoenicis-settings/pom.xml | 2 +- phoenicis-tests/pom.xml | 2 +- phoenicis-tools/pom.xml | 2 +- phoenicis-win32/pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/phoenicis-configuration/pom.xml b/phoenicis-configuration/pom.xml index dcb35c3f0d1..c814eaf2bca 100644 --- a/phoenicis-configuration/pom.xml +++ b/phoenicis-configuration/pom.xml @@ -25,7 +25,7 @@ phoenicis-configuration - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-containers/pom.xml b/phoenicis-containers/pom.xml index 3460d94c9fa..5703bd1c9db 100644 --- a/phoenicis-containers/pom.xml +++ b/phoenicis-containers/pom.xml @@ -25,7 +25,7 @@ phoenicis-containers - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-dist/pom.xml b/phoenicis-dist/pom.xml index 9f3f4abd4b7..0b403f626ca 100644 --- a/phoenicis-dist/pom.xml +++ b/phoenicis-dist/pom.xml @@ -25,7 +25,7 @@ phoenicis-dist - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-engines/pom.xml b/phoenicis-engines/pom.xml index 9717f01e99c..8b8c9cf40f8 100644 --- a/phoenicis-engines/pom.xml +++ b/phoenicis-engines/pom.xml @@ -25,7 +25,7 @@ phoenicis-engines - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-entities/pom.xml b/phoenicis-entities/pom.xml index 99a3d4497d7..af438930f79 100644 --- a/phoenicis-entities/pom.xml +++ b/phoenicis-entities/pom.xml @@ -25,6 +25,6 @@ phoenicis-entities - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-library/pom.xml b/phoenicis-library/pom.xml index 1e0032b2556..3c227cad13b 100644 --- a/phoenicis-library/pom.xml +++ b/phoenicis-library/pom.xml @@ -25,7 +25,7 @@ phoenicis-library - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-multithreading/pom.xml b/phoenicis-multithreading/pom.xml index 9259d7d2578..da5e3c9a3f9 100644 --- a/phoenicis-multithreading/pom.xml +++ b/phoenicis-multithreading/pom.xml @@ -25,7 +25,7 @@ phoenicis-multithreading - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-repository/pom.xml b/phoenicis-repository/pom.xml index f6ad23645a0..b261c2669bf 100644 --- a/phoenicis-repository/pom.xml +++ b/phoenicis-repository/pom.xml @@ -25,7 +25,7 @@ phoenicis-repository - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-scripts/pom.xml b/phoenicis-scripts/pom.xml index c7172c85b5f..f31dd1b6251 100644 --- a/phoenicis-scripts/pom.xml +++ b/phoenicis-scripts/pom.xml @@ -25,7 +25,7 @@ phoenicis-scripts - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-settings/pom.xml b/phoenicis-settings/pom.xml index 226b6ac2d9c..197e5044a5f 100644 --- a/phoenicis-settings/pom.xml +++ b/phoenicis-settings/pom.xml @@ -25,7 +25,7 @@ phoenicis-settings - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-tests/pom.xml b/phoenicis-tests/pom.xml index 020e2d7d907..e7c63366e55 100644 --- a/phoenicis-tests/pom.xml +++ b/phoenicis-tests/pom.xml @@ -25,7 +25,7 @@ phoenicis-tests - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 0a2ed2d41e1..565a17cc34b 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -25,7 +25,7 @@ phoenicis-tools - ${project.basedir}/../settings + ${project.basedir}/../ diff --git a/phoenicis-win32/pom.xml b/phoenicis-win32/pom.xml index ad128ef8ef2..93fd76b16df 100644 --- a/phoenicis-win32/pom.xml +++ b/phoenicis-win32/pom.xml @@ -25,7 +25,7 @@ phoenicis-win32 - ${project.basedir}/../settings + ${project.basedir}/../ From c4de66c5061fbc8c068a76d000e34a4200fe84f1 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 25 Nov 2018 20:48:57 +0100 Subject: [PATCH 06/39] Fixed maven build --- phoenicis-cli/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoenicis-cli/pom.xml b/phoenicis-cli/pom.xml index 6ab75a05bc3..3c8f7ff52e8 100644 --- a/phoenicis-cli/pom.xml +++ b/phoenicis-cli/pom.xml @@ -25,7 +25,7 @@ phoenicis-cli - ${project.basedir}/../settings + ${project.basedir}/../ From ddee09007c0fab70df0b715074426c18c8d43c73 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Mon, 26 Nov 2018 20:14:46 +0100 Subject: [PATCH 07/39] Removed ressource --- phoenicis-blue.svg | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 phoenicis-blue.svg diff --git a/phoenicis-blue.svg b/phoenicis-blue.svg deleted file mode 100644 index f8a25f01a74..00000000000 --- a/phoenicis-blue.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - From 0e43247a0ad31b342a4970276a3123f0cc0e5cff Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 2 Dec 2018 11:06:40 +0100 Subject: [PATCH 08/39] Travis: Build OSX packages --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8c6c7049a65..e74521c7a26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,6 +82,14 @@ jobs: after_script: - cd phoenicis-dist/target/ - apt install -y *.deb + - stage: Packages + name: dmg + os: osx + osx_image: xcode9.3 + after_script: + - mvn clean install + - cd phoenicis-dist/target/ + - mvn jfx:native # Check GitHub Pages - stage: Check GitHub Pages From e579cabcd7e78736dc1a31a6fbfcdfd7620ad97e Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 2 Dec 2018 11:08:04 +0100 Subject: [PATCH 09/39] Fix whitespace --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e74521c7a26..ebcd1ed53ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -87,7 +87,7 @@ jobs: os: osx osx_image: xcode9.3 after_script: - - mvn clean install + - mvn clean install - cd phoenicis-dist/target/ - mvn jfx:native From 87116317fa997859d26e961bf2ded7c8b8246441 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 2 Dec 2018 12:18:18 +0100 Subject: [PATCH 10/39] Fixed merge issue --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 870cdc8665b..f48153a1e4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ jobs: name: OSX XCode 9.2 os: osx osx_image: xcode9.2 - + # Build + test packages - stage: Packages name: Flatpak @@ -79,9 +79,6 @@ jobs: dist: trusty jdk: openjdk11 sudo: required - after_script: - - cd phoenicis-dist/target/ - - apt install -y *.deb services: - docker before_install: From c9c78f331c6a259af532fd97911045146df41fe1 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Wed, 2 Jan 2019 23:33:52 +0100 Subject: [PATCH 11/39] Implemented a .lnk parser --- .../phoenicis/tools/ToolsConfiguration.java | 6 + .../tools/files/FilesManipulator.java | 4 +- .../phoenicis/tools/lnk/BytesUtilities.java | 26 +++ .../java/org/phoenicis/tools/lnk/LnkData.java | 34 ++++ .../java/org/phoenicis/tools/lnk/LnkFile.java | 34 ++++ .../phoenicis/tools/lnk/LnkFileAttribute.java | 22 +++ .../org/phoenicis/tools/lnk/LnkParser.java | 152 ++++++++++++++++++ .../phoenicis/tools/lnk/LnkParserTest.java | 47 ++++++ .../org/phoenicis/tools/lnk/teenagent.lnk | Bin 0 -> 1297 bytes .../org/phoenicis/tools/lnk/xiii.lnk | Bin 0 -> 1036 bytes 10 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java create mode 100644 phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java create mode 100644 phoenicis-tools/src/test/resources/org/phoenicis/tools/lnk/teenagent.lnk create mode 100644 phoenicis-tools/src/test/resources/org/phoenicis/tools/lnk/xiii.lnk diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java index 06529885def..3007d85ca30 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java @@ -26,6 +26,7 @@ import org.phoenicis.tools.config.CompatibleConfigFileFormatFactory; import org.phoenicis.tools.files.*; import org.phoenicis.tools.http.Downloader; +import org.phoenicis.tools.lnk.LnkParser; import org.phoenicis.tools.system.ArchitectureFetcher; import org.phoenicis.tools.system.OperatingSystemFetcher; import org.phoenicis.tools.system.SystemConfiguration; @@ -149,4 +150,9 @@ Opener macOsOpener() { public Opener opener() { return new AutomaticOpener(linuxOpener(), macOsOpener(), operatingSystemFetcher()); } + + @Bean + public LnkParser lnkParser() { + return new LnkParser(); + } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FilesManipulator.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FilesManipulator.java index 9d2c86f9012..e73c1dad5ce 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FilesManipulator.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FilesManipulator.java @@ -26,7 +26,7 @@ import static java.lang.String.format; @Safe -class FilesManipulator { +public class FilesManipulator { @Value("${application.user.root}") private String userRoot; @@ -35,7 +35,7 @@ private boolean isInSubDirectory(File directory, File fileIside) { && (fileIside.equals(directory) || isInSubDirectory(directory, fileIside.getParentFile())); } - void assertInDirectory(File file) { + protected void assertInDirectory(File file) { if (!isInSubDirectory(new File(userRoot), file)) { throw new IllegalArgumentException( format("The file (%s) must be in the Phoenicis root directory (%s)", file, userRoot)); diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java new file mode 100644 index 00000000000..e7a2725cd98 --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java @@ -0,0 +1,26 @@ +package org.phoenicis.tools.lnk; + +public class BytesUtilities { + /* + * Convert two little endian bytes into a short note + */ + static int bytes2short(byte[] bytes, int offset) { + return ((bytes[offset + 1] & 0xff) << 8) | (bytes[offset] & 0xff); + } + + /** + * Fetches a string inside a byte array delimited by an offset and the null characte r + * + * @param bytes The byte array + * @param offset The start offset + * @return The given string + */ + static String getNullDelimitedString(byte[] bytes, int offset) { + int len = 0; + + while (bytes[offset + len] != 0) { + len++; + } + return new String(bytes, offset, len); + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java new file mode 100644 index 00000000000..c322a841841 --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java @@ -0,0 +1,34 @@ +package org.phoenicis.tools.lnk; + +public class LnkData { + private static final int DATA_FLAG_OFFSET = 20; + + private final static byte MASK_SHELL = (byte) 0x01; + private final static byte MASK_RELATIVE_PATH = (byte) 0x8; + private final static byte MASK_HAS_WORKING_DIR = (byte) 0x10; + private final static byte MASK_HAS_ARGUMENTS = (byte) 0x20; + private final static byte MASK_HAS_EXP_ICON = (byte) 0x4000; + + private final byte[] rawBytes; + + public LnkData(byte[] rawLnkContent) { + this.rawBytes = new byte[] { + rawLnkContent[DATA_FLAG_OFFSET], + rawLnkContent[DATA_FLAG_OFFSET + 1], + rawLnkContent[DATA_FLAG_OFFSET + 2], + rawLnkContent[DATA_FLAG_OFFSET + 3] + }; + } + + public boolean hasWorkingDir() { + return (rawBytes[0] & MASK_HAS_WORKING_DIR) > 1; + } + + public boolean hasArguments() { + return (rawBytes[0] & MASK_HAS_ARGUMENTS) > 1; + } + + public boolean hasShell() { + return (rawBytes[0] & MASK_SHELL) > 0; + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java new file mode 100644 index 00000000000..6de0eb933ad --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java @@ -0,0 +1,34 @@ +package org.phoenicis.tools.lnk; + +public class LnkFile { + private final boolean isDirectory; + private final boolean isLocal; + private final String realFilename; + private final boolean hasArguments; + + public LnkFile(boolean isDirectory, + boolean isLocal, + String realFilename, + boolean hasArguments) { + this.isDirectory = isDirectory; + this.isLocal = isLocal; + this.realFilename = realFilename; + this.hasArguments = hasArguments; + } + + public boolean isDirectory() { + return isDirectory; + } + + public boolean isLocal() { + return isLocal; + } + + public String getRealFilename() { + return realFilename; + } + + public boolean isHasArguments() { + return hasArguments; + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java new file mode 100644 index 00000000000..c0d8e1d6531 --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java @@ -0,0 +1,22 @@ +package org.phoenicis.tools.lnk; + +public class LnkFileAttribute { + private static final int FILE_ATTRIBUTES_OFFSET = 24; + + private final static byte FILE_ATTRIBUTE_DIRECTORY = (byte) 0x10; + + private final byte[] rawBytes; + + public LnkFileAttribute(byte[] rawLnkContent) { + this.rawBytes = new byte[] { + rawLnkContent[FILE_ATTRIBUTES_OFFSET], + rawLnkContent[FILE_ATTRIBUTES_OFFSET + 1], + rawLnkContent[FILE_ATTRIBUTES_OFFSET + 2], + rawLnkContent[FILE_ATTRIBUTES_OFFSET + 3] + }; + } + + public boolean hasDirMask() { + return (rawBytes[0] & FILE_ATTRIBUTE_DIRECTORY) > 1; + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java new file mode 100644 index 00000000000..4ab89c78bcd --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java @@ -0,0 +1,152 @@ +package org.phoenicis.tools.lnk; + +import org.apache.commons.io.IOUtils; +import org.phoenicis.configuration.security.Safe; +import org.phoenicis.tools.files.FilesManipulator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +/** + * This file parses .lnk files + * https://msdn.microsoft.com/en-us/library/dd871305.aspx + */ +@Safe +public class LnkParser extends FilesManipulator { + private final static int LNK_HEADER_SIZE = 0x4C; + + private final static int FILE_LOCATION_INFO_FLAG_OFFSET_OFFSET = 0x08; + private final static int BASENAME_OFFSET_OFFSET = 0x10; + private final static int NETWORK_VOLUME_TABLE_OFFSET_OFFSET = 0x14; + private final static int FINALNAME_OFFSET_OFFSET = 0x18; + private final static int SHARE_NAME_OFFSET_OFFSET = 0x08; + + /** + * Parses a .lnk shortuct + * + * @param file The path to the file + * @return a {@link LnkFile} + * @throws IOException if any error occurs + */ + public LnkFile parse(File file) throws IOException { + this.assertInDirectory(file); + try (final InputStream inputStream = new FileInputStream(file)) { + return parse(inputStream); + } + } + + /** + * Parses a .lnk shortuct + * + * @param rawLnkShortcutByteArray The byte array content of the sortcut + * @return a {@link LnkFile} + */ + public LnkFile parse(byte[] rawLnkShortcutByteArray) { + final byte dataFlags = rawLnkShortcutByteArray[20]; + + final LnkFileAttribute fileAttributes = fetchFilesAttributes(rawLnkShortcutByteArray); + final LnkData lnkData = fetchLnkData(rawLnkShortcutByteArray); + + final boolean isDirectory = fileAttributes.hasDirMask(); + final boolean hasArguments = lnkData.hasArguments(); + + final int fileStart = fetchFileStart(rawLnkShortcutByteArray, lnkData); + final byte[] rawLnkContentWithoutHeader = + Arrays.copyOfRange(rawLnkShortcutByteArray, fileStart, rawLnkShortcutByteArray.length - 1); + + final boolean isLocal = this.isLnkLocal(rawLnkContentWithoutHeader); + final String fileName = parseLnkContent(rawLnkContentWithoutHeader, isLocal); + + return new LnkFile(isDirectory, isLocal, fileName, hasArguments); + } + + private LnkData fetchLnkData(byte[] rawLnkShortcutByteArray) { + return new LnkData(rawLnkShortcutByteArray); + } + + /** + * Parses a .lnk shortuct + * + * @param shortcutInputStream The input stream from the sortcut + * @return a {@link LnkFile} + * @throws IOException if any error occurs + */ + LnkFile parse(InputStream shortcutInputStream) throws IOException { + return parse(IOUtils.toByteArray(shortcutInputStream)); + } + + /** + * Tests if the shortcuts points to a local file + * + * @param rawLnkContent The content + * @return true or false + */ + private boolean isLnkLocal(byte[] rawLnkContent) { + final int fileLocationInfoFlag = rawLnkContent[FILE_LOCATION_INFO_FLAG_OFFSET_OFFSET]; + return (fileLocationInfoFlag & 2) == 0; + } + + /** + * Parse the acutal content of the shortcut + * + * @param rawLnkShortcutByteArrayWithoutHeader Raw lnk file content + * @param isLocal Search for local or network filename + */ + private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, boolean isLocal) { + final int finalNameOffset = rawLnkShortcutByteArrayWithoutHeader[FINALNAME_OFFSET_OFFSET]; + + final String finalName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, finalNameOffset); + + if (isLocal) { + final int basenameOffset = rawLnkShortcutByteArrayWithoutHeader[BASENAME_OFFSET_OFFSET]; + final String basename = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, basenameOffset); + return basename + finalName; + } else { + final int networkVolumeTableOffset = rawLnkShortcutByteArrayWithoutHeader[NETWORK_VOLUME_TABLE_OFFSET_OFFSET]; + final int shareNameOffset = rawLnkShortcutByteArrayWithoutHeader[networkVolumeTableOffset + SHARE_NAME_OFFSET_OFFSET] + networkVolumeTableOffset; + final String shareName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, shareNameOffset); + return shareName + "\\" + finalName; + } + } + + /** + * Fetches the start of the .lnk file once the header is removed + * + * @param rawLnkContent The content of the .lnk + * @param dataFlags The dataflags that will be used to determine if there is a shell section + * @return The offset where the sortcuts file starts + */ + private int fetchFileStart(byte[] rawLnkContent, LnkData dataFlags) { + return LNK_HEADER_SIZE + fetchShellLength(rawLnkContent, dataFlags); + } + + /** + * Fetches shell section length + * + * @param rawLnkContent raw .lnk content + * @param dataFlags {@link LnkData to fetch shell} + * @return the size of the section + */ + private int fetchShellLength(byte[] rawLnkContent, LnkData dataFlags) { + int shellLength = 0; + if (dataFlags.hasShell()) { + final int lengthMarkerSize = 2; + shellLength = BytesUtilities.bytes2short(rawLnkContent, LNK_HEADER_SIZE) + lengthMarkerSize; + } + + return shellLength; + } + + /** + * Fetches the file attributes + * + * @param rawLnkContent raw .lnk content + * @return a {@link LnkFileAttribute} property + */ + private LnkFileAttribute fetchFilesAttributes(byte[] rawLnkContent) { + return new LnkFileAttribute(rawLnkContent); + } +} \ No newline at end of file diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java new file mode 100644 index 00000000000..ccaa3181124 --- /dev/null +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java @@ -0,0 +1,47 @@ +package org.phoenicis.tools.lnk; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.Assert.*; + +public class LnkParserTest { + private final InputStream game1inputStream = getClass().getResourceAsStream("xiii.lnk"); + private final InputStream game2inputStream = getClass().getResourceAsStream("teenagent.lnk"); + private LnkFile game1; + private LnkFile game2; + + @Before + public void setUp() throws IOException { + this.game1 = new LnkParser().parse(game1inputStream); + this.game2 = new LnkParser().parse(game2inputStream); + } + + @Test + public void testLnkParser_testPath() { + assertEquals("C:\\GOG Games\\XIII\\system\\xiii.exe", game1.getRealFilename()); + } + + @Test + public void testLnkParser_testPath_dosbox() { + assertEquals("c:\\gog games\\teenagent\\DOSBOX\\dosbox.exe", game2.getRealFilename()); + } + + @Test + public void testLnkParser_testIsDirectory() { + assertFalse(game1.isDirectory()); + } + + @Test + public void testLnkParser_testHasArguments_noArguments() { + assertTrue(game1.isHasArguments()); + } + + @Test + public void testLnkParser_testHasArguments_arguments() { + assertTrue(game2.isHasArguments()); + } +} \ No newline at end of file diff --git a/phoenicis-tools/src/test/resources/org/phoenicis/tools/lnk/teenagent.lnk b/phoenicis-tools/src/test/resources/org/phoenicis/tools/lnk/teenagent.lnk new file mode 100644 index 0000000000000000000000000000000000000000..961ad80e36f91542730bb815002c6210a3736fdd GIT binary patch literal 1297 zcmb7EO=}ZT6g{=#LR^dzN+_a9Pzz#$wzQT|p_I^tK{mH$Wjm&*}3iCa90sTo247Ujpx2(rZE+y58k`;KF)dPo_CWhfQiT@j=-@RvHEim z|2s~MowzX>!EJp15bNB%(#ssZZ@fK96hANEkz!uTZT2*5G)=FG#1JWK@%)#4nQov7 z6COt(f;3T^*MM&B_eTwly5ZT}xHH7bYJh8?4ud#bA*ZlJI(CH@-w)A7yPJF6k?9(?;8L+emdfz< zC{~4myWQN&cN!|L;~TakDdT~F*kmWrULk`6p9*ZoAp2>>a-z=mIvRG#Q1{H5-5O=- zbpn>%A-c{yOw>5zK}@S`mYGdF|8-wO(KH>yG95q6M%Lr`c9DlsBvNuQV5#VHEvmeP z@?+*uL)Ei)%u;2<%bCMKLsh-N4zVS=5qin5Zceb4YsD;M+Z^>$(BIVCTlB#~jNQOC zIiv=nO8@a?(ch|kl`>oA*$c={fUZ)1leOg1`4n}u*yRO)=j=DxDMH00 zqqJx2*V!j!!lubkN$a6cdYG?1(5;V(xJ8rG&}rDXaXQOs+zK6kr>7%Hyp&#zrY9&l zMXu2#&X+?fL?np0IKD)Kq{uD2qP}r9hiV3jn*5RzgF8f>*l9B>`O^^GMe-i_6O>hw Z0v@6g{cnLR>sCmJ)WV2c@xlZhoGb6!iEKAeLX{O)$Wk0- zpToK^@V1|Sd+MNAtuz8t5y^NWAP%`v-p7voFOs2x1}iXJ$LbT71*}7P3&#$cWT=Ju zz_g|;ax-RWj?gvg&^X|ZA0mP0;)G?n=s4KQX0t$2(sC&8qBF*CA!~o)ySO*Zyv@w} zp*hk!#ff{$+h*3msW9wY&ySdKhdL(iS#H6)1vlviWj%Foi0)bCE(&m2jZ5uia>%RC z*@|&Ps$1;-UGZX9d7mu$YQ@qiKazsU5EHedhAv<2lnhaKZKp(h<%n);W|VqYu>=fa zXupi!9iwH8N$Zvd9`ja)oGb61#kT0G{L)0XIHg+Y0pLC-O}6V(t}+UocQ`4<4oBkJ zvOn>}m)G!z#ro6C39Pa6eY$ep9Uo)OJ~d2r_#~F>^Z$iBWOp?AUL`6yWK46F9vqH8 Da1^uD literal 0 HcmV?d00001 From 6406b23876d6b5fe42c93b35c3c4efa4e368776d Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Wed, 2 Jan 2019 23:36:17 +0100 Subject: [PATCH 12/39] Implemented a .lnk parser --- .../tools/http/PhoenicisUrlConnection.java | 2 +- .../phoenicis/tools/lnk/BytesUtilities.java | 2 +- .../java/org/phoenicis/tools/lnk/LnkData.java | 10 ++++++++++ .../java/org/phoenicis/tools/lnk/LnkFile.java | 9 ++++++--- .../phoenicis/tools/lnk/LnkFileAttribute.java | 4 ++++ .../org/phoenicis/tools/lnk/LnkParser.java | 20 +++++++++++-------- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/http/PhoenicisUrlConnection.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/http/PhoenicisUrlConnection.java index 3c8aec63bad..a9b44a2e475 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/http/PhoenicisUrlConnection.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/http/PhoenicisUrlConnection.java @@ -19,7 +19,7 @@ class PhoenicisUrlConnection { * Creates the instance. Stores internally the {@link URL} and the {@link java.net.URLConnection} objects * * @param urlConnection The URL connection - * @param url The URL + * @param url The URL * @see #fromURL(URL) to build */ private PhoenicisUrlConnection(HttpURLConnection urlConnection, URL url) { diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java index e7a2725cd98..ad4e80b2acf 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java @@ -11,7 +11,7 @@ static int bytes2short(byte[] bytes, int offset) { /** * Fetches a string inside a byte array delimited by an offset and the null characte r * - * @param bytes The byte array + * @param bytes The byte array * @param offset The start offset * @return The given string */ diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java index c322a841841..fb810954d67 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java @@ -20,10 +20,20 @@ public LnkData(byte[] rawLnkContent) { }; } + /** + * Determines if the .lnk has a working directory + * + * @return true if the .lnk has a working directory + */ public boolean hasWorkingDir() { return (rawBytes[0] & MASK_HAS_WORKING_DIR) > 1; } + /** + * Determines if the .lnk has arguments + * + * @return true if the .lnk has arguments + */ public boolean hasArguments() { return (rawBytes[0] & MASK_HAS_ARGUMENTS) > 1; } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java index 6de0eb933ad..8f98473711c 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java @@ -1,5 +1,8 @@ package org.phoenicis.tools.lnk; +/** + * Parsed data structure + */ public class LnkFile { private final boolean isDirectory; private final boolean isLocal; @@ -7,9 +10,9 @@ public class LnkFile { private final boolean hasArguments; public LnkFile(boolean isDirectory, - boolean isLocal, - String realFilename, - boolean hasArguments) { + boolean isLocal, + String realFilename, + boolean hasArguments) { this.isDirectory = isDirectory; this.isLocal = isLocal; this.realFilename = realFilename; diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java index c0d8e1d6531..436c04d2f8a 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java @@ -16,6 +16,10 @@ public LnkFileAttribute(byte[] rawLnkContent) { }; } + /** + * Determines is the .lnk is a directory + * @return true if it is a directory + */ public boolean hasDirMask() { return (rawBytes[0] & FILE_ATTRIBUTE_DIRECTORY) > 1; } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java index 4ab89c78bcd..596b871714e 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java @@ -54,8 +54,8 @@ public LnkFile parse(byte[] rawLnkShortcutByteArray) { final boolean hasArguments = lnkData.hasArguments(); final int fileStart = fetchFileStart(rawLnkShortcutByteArray, lnkData); - final byte[] rawLnkContentWithoutHeader = - Arrays.copyOfRange(rawLnkShortcutByteArray, fileStart, rawLnkShortcutByteArray.length - 1); + final byte[] rawLnkContentWithoutHeader = Arrays.copyOfRange(rawLnkShortcutByteArray, fileStart, + rawLnkShortcutByteArray.length - 1); final boolean isLocal = this.isLnkLocal(rawLnkContentWithoutHeader); final String fileName = parseLnkContent(rawLnkContentWithoutHeader, isLocal); @@ -93,21 +93,25 @@ private boolean isLnkLocal(byte[] rawLnkContent) { * Parse the acutal content of the shortcut * * @param rawLnkShortcutByteArrayWithoutHeader Raw lnk file content - * @param isLocal Search for local or network filename + * @param isLocal Search for local or network filename */ private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, boolean isLocal) { final int finalNameOffset = rawLnkShortcutByteArrayWithoutHeader[FINALNAME_OFFSET_OFFSET]; - final String finalName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, finalNameOffset); + final String finalName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, + finalNameOffset); if (isLocal) { final int basenameOffset = rawLnkShortcutByteArrayWithoutHeader[BASENAME_OFFSET_OFFSET]; - final String basename = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, basenameOffset); + final String basename = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, + basenameOffset); return basename + finalName; } else { final int networkVolumeTableOffset = rawLnkShortcutByteArrayWithoutHeader[NETWORK_VOLUME_TABLE_OFFSET_OFFSET]; - final int shareNameOffset = rawLnkShortcutByteArrayWithoutHeader[networkVolumeTableOffset + SHARE_NAME_OFFSET_OFFSET] + networkVolumeTableOffset; - final String shareName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, shareNameOffset); + final int shareNameOffset = rawLnkShortcutByteArrayWithoutHeader[networkVolumeTableOffset + + SHARE_NAME_OFFSET_OFFSET] + networkVolumeTableOffset; + final String shareName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, + shareNameOffset); return shareName + "\\" + finalName; } } @@ -116,7 +120,7 @@ private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, bool * Fetches the start of the .lnk file once the header is removed * * @param rawLnkContent The content of the .lnk - * @param dataFlags The dataflags that will be used to determine if there is a shell section + * @param dataFlags The dataflags that will be used to determine if there is a shell section * @return The offset where the sortcuts file starts */ private int fetchFileStart(byte[] rawLnkContent, LnkData dataFlags) { From 5f8bae533dc1951bc2ffb57791f76632306dc9f4 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Thu, 3 Jan 2019 20:35:19 +0100 Subject: [PATCH 13/39] Support the extraction of arguments --- .../phoenicis/tools/lnk/AbstractLnkFlags.java | 21 +++ .../phoenicis/tools/lnk/BytesUtilities.java | 23 ++- .../java/org/phoenicis/tools/lnk/LnkData.java | 44 ------ .../org/phoenicis/tools/lnk/LnkDataFlags.java | 85 +++++++++++ .../java/org/phoenicis/tools/lnk/LnkFile.java | 33 +++- .../phoenicis/tools/lnk/LnkFileAttribute.java | 18 ++- .../org/phoenicis/tools/lnk/LnkParser.java | 143 +++++++++++++++--- .../phoenicis/tools/lnk/LnkStringData.java | 63 ++++++++ .../phoenicis/tools/lnk/LnkParserTest.java | 17 +++ 9 files changed, 369 insertions(+), 78 deletions(-) create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java new file mode 100644 index 00000000000..8397eb34a4d --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java @@ -0,0 +1,21 @@ +package org.phoenicis.tools.lnk; + +/** + * This class represent a byte flag + */ +class AbstractLnkFlags { + protected final byte[] rawBytes; + + protected AbstractLnkFlags(byte[] rawBytes) { + this.rawBytes = rawBytes; + } + + /** + * Tests the flags with a mask (stored in a byte) + * @param flags The flags + * @return True if the flags matches the mask + */ + protected boolean testMask(byte flags) { + return (rawBytes[0] & flags) > 0; + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java index ad4e80b2acf..9c2eac94e38 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java @@ -1,15 +1,26 @@ package org.phoenicis.tools.lnk; -public class BytesUtilities { - /* - * Convert two little endian bytes into a short note +class BytesUtilities { + /** + * Convert little endian bytes into an integer + * @param bytes The byte + * @param offset The offset to starts with + * @param numberOfBytes The number of bytes + * + * @return An integer */ - static int bytes2short(byte[] bytes, int offset) { - return ((bytes[offset + 1] & 0xff) << 8) | (bytes[offset] & 0xff); + static int bytes2int(byte[] bytes, int offset, int numberOfBytes) { + int result = 0; + + for (int i = 0; i < numberOfBytes; i++) { + result |= (bytes[offset + i] & 0xff) << i * 8; + } + + return result; } /** - * Fetches a string inside a byte array delimited by an offset and the null characte r + * Fetches a string inside a byte array delimited by an offset and the null character * * @param bytes The byte array * @param offset The start offset diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java deleted file mode 100644 index fb810954d67..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkData.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.phoenicis.tools.lnk; - -public class LnkData { - private static final int DATA_FLAG_OFFSET = 20; - - private final static byte MASK_SHELL = (byte) 0x01; - private final static byte MASK_RELATIVE_PATH = (byte) 0x8; - private final static byte MASK_HAS_WORKING_DIR = (byte) 0x10; - private final static byte MASK_HAS_ARGUMENTS = (byte) 0x20; - private final static byte MASK_HAS_EXP_ICON = (byte) 0x4000; - - private final byte[] rawBytes; - - public LnkData(byte[] rawLnkContent) { - this.rawBytes = new byte[] { - rawLnkContent[DATA_FLAG_OFFSET], - rawLnkContent[DATA_FLAG_OFFSET + 1], - rawLnkContent[DATA_FLAG_OFFSET + 2], - rawLnkContent[DATA_FLAG_OFFSET + 3] - }; - } - - /** - * Determines if the .lnk has a working directory - * - * @return true if the .lnk has a working directory - */ - public boolean hasWorkingDir() { - return (rawBytes[0] & MASK_HAS_WORKING_DIR) > 1; - } - - /** - * Determines if the .lnk has arguments - * - * @return true if the .lnk has arguments - */ - public boolean hasArguments() { - return (rawBytes[0] & MASK_HAS_ARGUMENTS) > 1; - } - - public boolean hasShell() { - return (rawBytes[0] & MASK_SHELL) > 0; - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java new file mode 100644 index 00000000000..b6df42a110d --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java @@ -0,0 +1,85 @@ +package org.phoenicis.tools.lnk; + +/** + * Lnk format data flat + * + */ +class LnkDataFlags extends AbstractLnkFlags { + private static final int DATA_FLAG_OFFSET = 20; + + private final static byte MASK_HAS_LINK_TARGET_ID_LIST = (byte) 0x01; + private final static byte MASK_HAS_NAME = (byte) 0x04; + private final static byte MASK_RELATIVE_PATH = (byte) 0x8; + private final static byte MASK_HAS_WORKING_DIR = (byte) 0x10; + private final static byte MASK_HAS_ARGUMENTS = (byte) 0x20; + private final static byte MASK_ICON_LOCATION = (byte) 0x40; + + LnkDataFlags(byte[] rawLnkContent) { + super(new byte[] { + rawLnkContent[DATA_FLAG_OFFSET], + rawLnkContent[DATA_FLAG_OFFSET + 1], + rawLnkContent[DATA_FLAG_OFFSET + 2], + rawLnkContent[DATA_FLAG_OFFSET + 3] + }); + } + + /** + * Determines if the .lnk has a working directory + * + * @return true if the .lnk has a working directory + */ + boolean hasWorkingDir() { + return testMask(MASK_HAS_WORKING_DIR); + } + + /** + * Determines if the .lnk has arguments + * + * @return true if the .lnk has arguments + */ + boolean hasArguments() { + return testMask(MASK_HAS_ARGUMENTS); + } + + boolean hasLinkTargetIdList() { + return testMask(MASK_HAS_LINK_TARGET_ID_LIST); + } + + boolean hasName() { + return testMask(MASK_HAS_NAME); + } + + boolean hasRelativePath() { + return testMask(MASK_RELATIVE_PATH); + } + + boolean hasIconLocation() { + return testMask(MASK_ICON_LOCATION); + } + + int fetchNumberOfStringData() { + int number = 0; + + if (hasName()) { + number++; + } + + if (hasRelativePath()) { + number++; + } + + if (hasWorkingDir()) { + number++; + } + + if (hasArguments()) { + number++; + } + + if (hasIconLocation()) { + number++; + } + + return number; + } +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java index 8f98473711c..462a060a324 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java @@ -1,37 +1,66 @@ package org.phoenicis.tools.lnk; /** - * Parsed data structure + * Parsed data immutable structure. + * Contains only data */ public class LnkFile { private final boolean isDirectory; private final boolean isLocal; private final String realFilename; private final boolean hasArguments; + private final LnkStringData stringData; public LnkFile(boolean isDirectory, boolean isLocal, String realFilename, - boolean hasArguments) { + boolean hasArguments, + LnkStringData stringData) { this.isDirectory = isDirectory; this.isLocal = isLocal; this.realFilename = realFilename; this.hasArguments = hasArguments; + this.stringData = stringData; } + /** + * Tests whether the shortcut points to a directory + * @return true or false + */ public boolean isDirectory() { return isDirectory; } + /** + * Tests whether the shortcut points to a local + * @return true or false + */ public boolean isLocal() { return isLocal; } + /** + * Fetches the real name from the sortcut + * @return the real file name + */ public String getRealFilename() { return realFilename; } + /** + * Tests whether the shortcuts has arguments + * @return true or false + */ public boolean isHasArguments() { return hasArguments; } + + /** + * Fetches shortcut string data + * @see LnkStringData + * @return The string data + */ + public LnkStringData getStringData() { + return stringData; + } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java index 436c04d2f8a..459794cdd1e 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java @@ -1,26 +1,28 @@ package org.phoenicis.tools.lnk; -public class LnkFileAttribute { +/** + * Lnk file attribute flag + */ +class LnkFileAttribute extends AbstractLnkFlags { private static final int FILE_ATTRIBUTES_OFFSET = 24; private final static byte FILE_ATTRIBUTE_DIRECTORY = (byte) 0x10; - private final byte[] rawBytes; - - public LnkFileAttribute(byte[] rawLnkContent) { - this.rawBytes = new byte[] { + LnkFileAttribute(byte[] rawLnkContent) { + super(new byte[] { rawLnkContent[FILE_ATTRIBUTES_OFFSET], rawLnkContent[FILE_ATTRIBUTES_OFFSET + 1], rawLnkContent[FILE_ATTRIBUTES_OFFSET + 2], rawLnkContent[FILE_ATTRIBUTES_OFFSET + 3] - }; + }); } /** * Determines is the .lnk is a directory + * * @return true if it is a directory */ - public boolean hasDirMask() { - return (rawBytes[0] & FILE_ATTRIBUTE_DIRECTORY) > 1; + boolean hasDirMask() { + return testMask(FILE_ATTRIBUTE_DIRECTORY); } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java index 596b871714e..6690266c2ec 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java @@ -8,7 +8,11 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Optional; /** * This file parses .lnk files @@ -45,26 +49,129 @@ public LnkFile parse(File file) throws IOException { * @return a {@link LnkFile} */ public LnkFile parse(byte[] rawLnkShortcutByteArray) { - final byte dataFlags = rawLnkShortcutByteArray[20]; - final LnkFileAttribute fileAttributes = fetchFilesAttributes(rawLnkShortcutByteArray); - final LnkData lnkData = fetchLnkData(rawLnkShortcutByteArray); + final LnkDataFlags lnkDataFlag = fetchLnkData(rawLnkShortcutByteArray); final boolean isDirectory = fileAttributes.hasDirMask(); - final boolean hasArguments = lnkData.hasArguments(); + final boolean hasArguments = lnkDataFlag.hasArguments(); - final int fileStart = fetchFileStart(rawLnkShortcutByteArray, lnkData); + final int fileStart = fetchFileStart(rawLnkShortcutByteArray, lnkDataFlag); final byte[] rawLnkContentWithoutHeader = Arrays.copyOfRange(rawLnkShortcutByteArray, fileStart, rawLnkShortcutByteArray.length - 1); final boolean isLocal = this.isLnkLocal(rawLnkContentWithoutHeader); final String fileName = parseLnkContent(rawLnkContentWithoutHeader, isLocal); - return new LnkFile(isDirectory, isLocal, fileName, hasArguments); + final LnkStringData lnkStringData = fetchStringData( + rawLnkContentWithoutHeader, + lnkDataFlag); + + return new LnkFile(isDirectory, isLocal, fileName, hasArguments, lnkStringData); } - private LnkData fetchLnkData(byte[] rawLnkShortcutByteArray) { - return new LnkData(rawLnkShortcutByteArray); + /** + * Fetches string data inside an array + * + * @param rawLnkContentWithoutHeader The byte array content of the sortcut + * @param lnkDataFlags The lig data flags + * @return a {@link LnkDataFlags} + */ + private LnkStringData fetchStringData(byte[] rawLnkContentWithoutHeader, + LnkDataFlags lnkDataFlags) { + final int numberOfStrings = lnkDataFlags.fetchNumberOfStringData(); + final List stringDatas = fetchStringData(rawLnkContentWithoutHeader, numberOfStrings); + + int nameIndex = 0; + int relativePathIndex = 1; + int workingDirIndex = 2; + int commandLineArgumentsIndex = 3; + int iconLocationIndex = 4; + + final Optional name; + final Optional relativePath; + final Optional workingDir; + final Optional arguments; + final Optional iconLocation; + + if (lnkDataFlags.hasName()) { + name = Optional.of(stringDatas.get(nameIndex)); + } else { + name = Optional.empty(); + relativePathIndex--; + workingDirIndex--; + commandLineArgumentsIndex--; + iconLocationIndex--; + } + + if (lnkDataFlags.hasRelativePath()) { + relativePath = Optional.of(stringDatas.get(relativePathIndex)); + } else { + relativePath = Optional.empty(); + workingDirIndex--; + commandLineArgumentsIndex--; + iconLocationIndex--; + } + + if (lnkDataFlags.hasWorkingDir()) { + workingDir = Optional.of(stringDatas.get(workingDirIndex)); + } else { + workingDir = Optional.empty(); + commandLineArgumentsIndex--; + iconLocationIndex--; + } + + if (lnkDataFlags.hasArguments()) { + arguments = Optional.of(stringDatas.get(commandLineArgumentsIndex)); + } else { + arguments = Optional.empty(); + iconLocationIndex--; + } + + if (lnkDataFlags.hasArguments()) { + iconLocation = Optional.of(stringDatas.get(iconLocationIndex)); + } else { + iconLocation = Optional.empty(); + } + + return new LnkStringData(name, relativePath, workingDir, arguments, iconLocation); + } + + /** + * Fetches string data inside an array + * + * @param rawLnkContentWithoutHeader Raw lnk file content + * @param numberOfStringToRead The number of string datas to read + * @return a list of string containing string datas + */ + private List fetchStringData(byte[] rawLnkContentWithoutHeader, + int numberOfStringToRead) { + final List stringDatas = new ArrayList<>(); + + final int linkInfoSize = BytesUtilities.bytes2int(rawLnkContentWithoutHeader, 0, 3); + + int index = linkInfoSize; + for (int i = 0; i < numberOfStringToRead; i++) { + final int stringSize = BytesUtilities.bytes2int(rawLnkContentWithoutHeader, index, 2); + + final String decodedString = new String(Arrays.copyOfRange( + rawLnkContentWithoutHeader, index + 2, index + (stringSize) * 2), Charset.forName("UTF-16LE")); + stringDatas.add(decodedString); + + index = index + (stringSize) * 2 + 2; + } + + return stringDatas; + } + + /** + * Fetches LnkDataFlags + * + * @param rawLnkShortcutByteArray Raw lnk file content + * @return The Data Flags + * @see LnkDataFlags + */ + private LnkDataFlags fetchLnkData(byte[] rawLnkShortcutByteArray) { + return new LnkDataFlags(rawLnkShortcutByteArray); } /** @@ -90,7 +197,7 @@ private boolean isLnkLocal(byte[] rawLnkContent) { } /** - * Parse the acutal content of the shortcut + * Parse the actual content of the shortcut * * @param rawLnkShortcutByteArrayWithoutHeader Raw lnk file content * @param isLocal Search for local or network filename @@ -123,25 +230,25 @@ private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, bool * @param dataFlags The dataflags that will be used to determine if there is a shell section * @return The offset where the sortcuts file starts */ - private int fetchFileStart(byte[] rawLnkContent, LnkData dataFlags) { - return LNK_HEADER_SIZE + fetchShellLength(rawLnkContent, dataFlags); + private int fetchFileStart(byte[] rawLnkContent, LnkDataFlags dataFlags) { + return LNK_HEADER_SIZE + fetchLinkTargetIdListLength(rawLnkContent, dataFlags); } /** * Fetches shell section length * * @param rawLnkContent raw .lnk content - * @param dataFlags {@link LnkData to fetch shell} + * @param dataFlags {@link LnkDataFlags to fetch shell} * @return the size of the section */ - private int fetchShellLength(byte[] rawLnkContent, LnkData dataFlags) { - int shellLength = 0; - if (dataFlags.hasShell()) { + private int fetchLinkTargetIdListLength(byte[] rawLnkContent, LnkDataFlags dataFlags) { + int linkTargetIdListLength = 0; + if (dataFlags.hasLinkTargetIdList()) { final int lengthMarkerSize = 2; - shellLength = BytesUtilities.bytes2short(rawLnkContent, LNK_HEADER_SIZE) + lengthMarkerSize; + linkTargetIdListLength = BytesUtilities.bytes2int(rawLnkContent, LNK_HEADER_SIZE, 2) + lengthMarkerSize; } - return shellLength; + return linkTargetIdListLength; } /** @@ -153,4 +260,4 @@ private int fetchShellLength(byte[] rawLnkContent, LnkData dataFlags) { private LnkFileAttribute fetchFilesAttributes(byte[] rawLnkContent) { return new LnkFileAttribute(rawLnkContent); } -} \ No newline at end of file +} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java new file mode 100644 index 00000000000..69b2613b04e --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java @@ -0,0 +1,63 @@ +package org.phoenicis.tools.lnk; + +import java.util.Optional; + +/** + * Parsed immutable Lng String Data structure + */ +class LnkStringData { + private final Optional nameString; + private final Optional relativePath; + private final Optional workingDirectory; + private final Optional commandLineArguments; + private final Optional iconLocation; + + LnkStringData(Optional nameString, Optional relativePath, Optional workingDirectory, + Optional commandLineArguments, Optional iconLocation) { + this.nameString = nameString; + this.relativePath = relativePath; + this.workingDirectory = workingDirectory; + this.commandLineArguments = commandLineArguments; + this.iconLocation = iconLocation; + } + + /** + * Fetches NAME_STRING + * @return + */ + Optional getNameString() { + return nameString; + } + + /** + * Fetches RELATIVE_PATH + * @return + */ + Optional getRelativePath() { + return relativePath; + } + + /** + * Fetches WORKING_DIR + * @return + */ + Optional getWorkingDirectory() { + return workingDirectory; + } + + /** + * Fetches COMMAND_LINE_ARGUMENTS + * @return + */ + Optional getCommandLineArguments() { + return commandLineArguments; + } + + /** + * Fetches ICON_LOCATION + * @return + */ + Optional getIconLocation() { + return iconLocation; + } +} diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java index ccaa3181124..48a4b2fed7b 100644 --- a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java @@ -44,4 +44,21 @@ public void testLnkParser_testHasArguments_noArguments() { public void testLnkParser_testHasArguments_arguments() { assertTrue(game2.isHasArguments()); } + + @Test + public void testLnkParser_fetchArguments_example2() { + assertEquals( + "-conf \"..\\dosboxTeenagent.conf\" -conf \"..\\dosboxTeenagent_single.conf\" -noconsole -c \"exit\"", + game2.getStringData().getCommandLineArguments().get()); + } + + @Test + public void testLnkParser_fetchIconLocation_example2() { + assertEquals("c:\\gog games\\teenagent\\goggame-1207658753.ico", game2.getStringData().getIconLocation().get()); + } + + @Test + public void testLnkParser_fetchIconLocation_example1() { + assertEquals("C:\\GOG Games\\XIII\\gfw_high.ico", game1.getStringData().getIconLocation().get()); + } } \ No newline at end of file From cf2b1434f55059313e704c0b948064f8cdd89116 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Thu, 3 Jan 2019 23:15:06 +0100 Subject: [PATCH 14/39] EOL --- .../src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java index 48a4b2fed7b..7567fb3ef2b 100644 --- a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java @@ -61,4 +61,4 @@ public void testLnkParser_fetchIconLocation_example2() { public void testLnkParser_fetchIconLocation_example1() { assertEquals("C:\\GOG Games\\XIII\\gfw_high.ico", game1.getStringData().getIconLocation().get()); } -} \ No newline at end of file +} From ebe89cc4f0ba60c7c4b854a73660b90f05feccd6 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Fri, 4 Jan 2019 21:04:31 +0100 Subject: [PATCH 15/39] Javadoc improvements --- ...Flags.java => AbstractLnkFlagsParser.java} | 7 +++-- ....java => LnkFileAttributeFlagsParser.java} | 8 +++-- ...DataFlags.java => LnkLinkFlagsParser.java} | 8 ++--- .../org/phoenicis/tools/lnk/LnkParser.java | 30 ++++++++++--------- 4 files changed, 29 insertions(+), 24 deletions(-) rename phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/{AbstractLnkFlags.java => AbstractLnkFlagsParser.java} (68%) rename phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/{LnkFileAttribute.java => LnkFileAttributeFlagsParser.java} (58%) rename phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/{LnkDataFlags.java => LnkLinkFlagsParser.java} (87%) diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java similarity index 68% rename from phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java rename to phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java index 8397eb34a4d..5ab47aed3a4 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlags.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java @@ -1,12 +1,13 @@ package org.phoenicis.tools.lnk; /** - * This class represent a byte flag + * This class represent a byte flags. + * It helps to apply a mask and read them */ -class AbstractLnkFlags { +class AbstractLnkFlagsParser { protected final byte[] rawBytes; - protected AbstractLnkFlags(byte[] rawBytes) { + protected AbstractLnkFlagsParser(byte[] rawBytes) { this.rawBytes = rawBytes; } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java similarity index 58% rename from phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java rename to phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java index 459794cdd1e..55180583d47 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttribute.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java @@ -1,14 +1,16 @@ package org.phoenicis.tools.lnk; /** - * Lnk file attribute flag + * The FileAttributesFlags structure defines bits that specify the file attributes of the link target, if the target is + * a file system item. File attributes can be used if the link target is not available, or if accessing the target would + * be inefficient. It is possible for the target items attributes to be out of sync with this value. */ -class LnkFileAttribute extends AbstractLnkFlags { +class LnkFileAttributeFlagsParser extends AbstractLnkFlagsParser { private static final int FILE_ATTRIBUTES_OFFSET = 24; private final static byte FILE_ATTRIBUTE_DIRECTORY = (byte) 0x10; - LnkFileAttribute(byte[] rawLnkContent) { + LnkFileAttributeFlagsParser(byte[] rawLnkContent) { super(new byte[] { rawLnkContent[FILE_ATTRIBUTES_OFFSET], rawLnkContent[FILE_ATTRIBUTES_OFFSET + 1], diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java similarity index 87% rename from phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java rename to phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java index b6df42a110d..be109bf8ffb 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkDataFlags.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java @@ -1,10 +1,10 @@ package org.phoenicis.tools.lnk; /** - * Lnk format data flat - * + * The LinkFlags structure defines bits that specify which shell link structures are present in the file format after + * the ShellLinkHeader structure (section 2.1). */ -class LnkDataFlags extends AbstractLnkFlags { +class LnkLinkFlagsParser extends AbstractLnkFlagsParser { private static final int DATA_FLAG_OFFSET = 20; private final static byte MASK_HAS_LINK_TARGET_ID_LIST = (byte) 0x01; @@ -14,7 +14,7 @@ class LnkDataFlags extends AbstractLnkFlags { private final static byte MASK_HAS_ARGUMENTS = (byte) 0x20; private final static byte MASK_ICON_LOCATION = (byte) 0x40; - LnkDataFlags(byte[] rawLnkContent) { + LnkLinkFlagsParser(byte[] rawLnkContent) { super(new byte[] { rawLnkContent[DATA_FLAG_OFFSET], rawLnkContent[DATA_FLAG_OFFSET + 1], diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java index 6690266c2ec..ed0158c1da6 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java @@ -16,6 +16,8 @@ /** * This file parses .lnk files + * LNK is a file extension for a shortcut file used by Microsoft Windows to point to an executable file. + * * https://msdn.microsoft.com/en-us/library/dd871305.aspx */ @Safe @@ -49,8 +51,8 @@ public LnkFile parse(File file) throws IOException { * @return a {@link LnkFile} */ public LnkFile parse(byte[] rawLnkShortcutByteArray) { - final LnkFileAttribute fileAttributes = fetchFilesAttributes(rawLnkShortcutByteArray); - final LnkDataFlags lnkDataFlag = fetchLnkData(rawLnkShortcutByteArray); + final LnkFileAttributeFlagsParser fileAttributes = fetchFilesAttributes(rawLnkShortcutByteArray); + final LnkLinkFlagsParser lnkDataFlag = fetchLnkData(rawLnkShortcutByteArray); final boolean isDirectory = fileAttributes.hasDirMask(); final boolean hasArguments = lnkDataFlag.hasArguments(); @@ -74,10 +76,10 @@ public LnkFile parse(byte[] rawLnkShortcutByteArray) { * * @param rawLnkContentWithoutHeader The byte array content of the sortcut * @param lnkDataFlags The lig data flags - * @return a {@link LnkDataFlags} + * @return a {@link LnkLinkFlagsParser} */ private LnkStringData fetchStringData(byte[] rawLnkContentWithoutHeader, - LnkDataFlags lnkDataFlags) { + LnkLinkFlagsParser lnkDataFlags) { final int numberOfStrings = lnkDataFlags.fetchNumberOfStringData(); final List stringDatas = fetchStringData(rawLnkContentWithoutHeader, numberOfStrings); @@ -164,14 +166,14 @@ private List fetchStringData(byte[] rawLnkContentWithoutHeader, } /** - * Fetches LnkDataFlags + * Fetches LnkLinkFlagsParser * * @param rawLnkShortcutByteArray Raw lnk file content * @return The Data Flags - * @see LnkDataFlags + * @see LnkLinkFlagsParser */ - private LnkDataFlags fetchLnkData(byte[] rawLnkShortcutByteArray) { - return new LnkDataFlags(rawLnkShortcutByteArray); + private LnkLinkFlagsParser fetchLnkData(byte[] rawLnkShortcutByteArray) { + return new LnkLinkFlagsParser(rawLnkShortcutByteArray); } /** @@ -230,7 +232,7 @@ private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, bool * @param dataFlags The dataflags that will be used to determine if there is a shell section * @return The offset where the sortcuts file starts */ - private int fetchFileStart(byte[] rawLnkContent, LnkDataFlags dataFlags) { + private int fetchFileStart(byte[] rawLnkContent, LnkLinkFlagsParser dataFlags) { return LNK_HEADER_SIZE + fetchLinkTargetIdListLength(rawLnkContent, dataFlags); } @@ -238,10 +240,10 @@ private int fetchFileStart(byte[] rawLnkContent, LnkDataFlags dataFlags) { * Fetches shell section length * * @param rawLnkContent raw .lnk content - * @param dataFlags {@link LnkDataFlags to fetch shell} + * @param dataFlags {@link LnkLinkFlagsParser to fetch shell} * @return the size of the section */ - private int fetchLinkTargetIdListLength(byte[] rawLnkContent, LnkDataFlags dataFlags) { + private int fetchLinkTargetIdListLength(byte[] rawLnkContent, LnkLinkFlagsParser dataFlags) { int linkTargetIdListLength = 0; if (dataFlags.hasLinkTargetIdList()) { final int lengthMarkerSize = 2; @@ -255,9 +257,9 @@ private int fetchLinkTargetIdListLength(byte[] rawLnkContent, LnkDataFlags dataF * Fetches the file attributes * * @param rawLnkContent raw .lnk content - * @return a {@link LnkFileAttribute} property + * @return a {@link LnkFileAttributeFlagsParser} property */ - private LnkFileAttribute fetchFilesAttributes(byte[] rawLnkContent) { - return new LnkFileAttribute(rawLnkContent); + private LnkFileAttributeFlagsParser fetchFilesAttributes(byte[] rawLnkContent) { + return new LnkFileAttributeFlagsParser(rawLnkContent); } } From 42366611f52e21cd34a1ff94a47e9ac866abe696 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Fri, 4 Jan 2019 21:25:09 +0100 Subject: [PATCH 16/39] Lnk doc --- .../src/main/java/org/phoenicis/tools/lnk/LnkFile.java | 2 +- .../main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java index 462a060a324..cde7bf1b1da 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java @@ -2,7 +2,7 @@ /** * Parsed data immutable structure. - * Contains only data + * Contains the lnk file data */ public class LnkFile { private final boolean isDirectory; diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java index be109bf8ffb..3c3986ac622 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java @@ -3,6 +3,8 @@ /** * The LinkFlags structure defines bits that specify which shell link structures are present in the file format after * the ShellLinkHeader structure (section 2.1). + * + * https://msdn.microsoft.com/en-us/library/dd871305.aspx */ class LnkLinkFlagsParser extends AbstractLnkFlagsParser { private static final int DATA_FLAG_OFFSET = 20; From f65e2a2861cdaf46338b4c8fbbe3a49996dc992c Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Fri, 4 Jan 2019 21:36:36 +0100 Subject: [PATCH 17/39] Abstract LnkFlagsParser --- .../java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java index 5ab47aed3a4..b1db1f94800 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java @@ -2,7 +2,7 @@ /** * This class represent a byte flags. - * It helps to apply a mask and read them + * It helps to apply a mask on the byte flags and allows reading them */ class AbstractLnkFlagsParser { protected final byte[] rawBytes; From f12f38ad477b7d91a76fc417188a2c819594cbf1 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 13 Jan 2019 13:15:09 +0100 Subject: [PATCH 18/39] Fixed issue #791 --- .../src/main/java/org/phoenicis/tools/http/Downloader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/http/Downloader.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/http/Downloader.java index b3a5ca5237f..9cfd6f2dd4b 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/http/Downloader.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/http/Downloader.java @@ -256,6 +256,7 @@ private void get(URL url, connection.setHeaders(headers); } + connection.connect(); saveConnectionToStream(url, connection, outputStream, onChange); } catch (IOException e) { throw new DownloadException(String.format(EXCEPTION_ITEM_DOWNLOAD_FAILED, url), e); From 2259a64fdf8dc40a5b86bdf2932808df9096cf4b Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 13 Jan 2019 15:13:58 +0100 Subject: [PATCH 19/39] .tar.xz uncompression support --- phoenicis-tools/pom.xml | 12 +- .../phoenicis/tools/files/FileAnalyser.java | 2 +- phoenicis-tools/src/main/resources/magic.dtd | 17 + phoenicis-tools/src/main/resources/magic.xml | 3562 +++++++++++++++++ .../src/main/resources/magic_1_0.dtd | 18 + .../src/main/resources/template.xml | 97 + .../tools/archive/ExtractorTest.java | 5 + .../org/phoenicis/tools/archive/test5.tar.xz | Bin 0 -> 252 bytes 8 files changed, 3709 insertions(+), 4 deletions(-) create mode 100644 phoenicis-tools/src/main/resources/magic.dtd create mode 100644 phoenicis-tools/src/main/resources/magic.xml create mode 100644 phoenicis-tools/src/main/resources/magic_1_0.dtd create mode 100644 phoenicis-tools/src/main/resources/template.xml create mode 100644 phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test5.tar.xz diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 832f58c909e..34a24363808 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -16,7 +16,8 @@ ~ with this program; if not, write to the Free Software Foundation, Inc., ~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --> - + 4.0.0 org.phoenicis @@ -59,9 +60,9 @@ 2.6 - jmimemagic + net.sf.jmimemagic jmimemagic - 0.1.2 + 0.1.5 log4j @@ -74,6 +75,11 @@ commons-compress 1.18 + + org.tukaani + xz + 1.8 + commons-lang commons-lang diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java index 887a732edac..5b44f1e3a66 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java @@ -65,7 +65,7 @@ private MagicMatch getMatch(File inputFile) throws MagicMatchNotFoundException { try { byte[] data = Files.readAllBytes(path); - return Magic.getMagicMatch(data); + return Magic.getMagicMatch(data, true); } catch (MagicException | MagicParseException | IOException e) { throw new IllegalStateException("Unable to detect mimetype of the file", e); } diff --git a/phoenicis-tools/src/main/resources/magic.dtd b/phoenicis-tools/src/main/resources/magic.dtd new file mode 100644 index 00000000000..00945b1042b --- /dev/null +++ b/phoenicis-tools/src/main/resources/magic.dtd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/phoenicis-tools/src/main/resources/magic.xml b/phoenicis-tools/src/main/resources/magic.xml new file mode 100644 index 00000000000..babe1be14d4 --- /dev/null +++ b/phoenicis-tools/src/main/resources/magic.xml @@ -0,0 +1,3562 @@ + + + + + 0.2 + dcastro + magic file + + + + audio/mp3 + + MP3 + 0xfffa + + + + + b, 32 kBits + + 0x10 + + + + + b, 40 kBits + + 0x20 + + + + + b, 48 kBits + + 0x30 + + + + + b, 56 kBits + + 0x40 + + + + + b, 64 kBits + + 0x50 + + + + + b, 80 kBits + + 0x60 + + + + + b, 96 kBits + + 0x70 + + + + + b, 112 kBits + + 0x80 + + + + + b, 128 kBits + + 0x90 + + + + + b, 160 kBits + + 0xA0 + + + + + b, 192 kBits + + 0xB0 + + + + + b, 224 kBits + + 0xC0 + + + + + b, 256 kBits + + 0xD0 + + + + + b, 320 kBits + + 0xE0 + + + + + b, 44.1 kHz + + 0x00 + + + + + b, 48 kHz + + 0x04 + + + + + b, 32 kHz + + 0x08 + + + + + b, Stereo + + 0x00 + + + + + b, JStereo + + 0x40 + + + + + b, Dual-Ch + + 0x80 + + + + + b, Mono + + 0xC0 + + + + + image/gif + + GIF image data + GIF8 + + + b, version 8%s, + + b, version 8%s, + 7a + + + b, version 8%s, + + b, version 8%s, + 9a + + + + + video/mpeg + mpg + MPEG video stream data + 0x1b3 + + + video/mpeg + mpg + MPEG system stream data + 0x1ba + + + ??? + ??? + MP + 0xfff0 + + + ??? + ??? + \b + 0x8 + + + ??? + ??? + \b3 + 0x2 + + + ??? + ??? + 32 kBits + 0x10 + + + ??? + ??? + 40 kBits + 0x20 + + + ??? + ??? + 48 kBits + 0x30 + + + ??? + ??? + 56 kBits + 0x40 + + + ??? + ??? + 64 kBits + 0x50 + + + ??? + ??? + 80 kBits + 0x60 + + + ??? + ??? + 96 kBits + 0x70 + + + ??? + ??? + 112 kBits + 0x80 + + + ??? + ??? + 128 kBits + 0x90 + + + ??? + ??? + 160 kBits + 0xa0 + + + ??? + ??? + 192 kBits + 0xb0 + + + ??? + ??? + 224 kBits + 0xc0 + + + ??? + ??? + 256 kBits + 0xd0 + + + ??? + ??? + 320 kBits + 0xe0 + + + + + ??? + ??? + \b2 + 0x4 + + + ??? + ??? + 32 kBits + 0x10 + + + ??? + ??? + 48 kBits + 0x20 + + + ??? + ??? + 56 kBits + 0x30 + + + ??? + ??? + 64 kBits + 0x40 + + + ??? + ??? + 80 kBits + 0x50 + + + ??? + ??? + 96 kBits + 0x60 + + + ??? + ??? + 112 kBits + 0x70 + + + ??? + ??? + 128 kBits + 0x80 + + + ??? + ??? + 160 kBits + 0x90 + + + ??? + ??? + 192 kBits + 0xa0 + + + ??? + ??? + 224 kBits + 0xb0 + + + ??? + ??? + 256 kBits + 0xc0 + + + ??? + ??? + 320 kBits + 0xd0 + + + ??? + ??? + 384 kBits + 0xe0 + + + + + ??? + ??? + 44.1 kHz + 0x0 + + + ??? + ??? + 48 kHz + 0x4 + + + ??? + ??? + 32 kHz + 0x8 + + + + + ??? + ??? + \b + 0x0 + + + ??? + ??? + \b3 + 0x2 + + + ??? + ??? + \b2 + 0x4 + + + ??? + ??? + 8 kBits + 0x10 + + + ??? + ??? + 16 kBits + 0x20 + + + ??? + ??? + 24 kBits + 0x30 + + + ??? + ??? + 32 kBits + 0x40 + + + ??? + ??? + 40 kBits + 0x50 + + + ??? + ??? + 48 kBits + 0x60 + + + ??? + ??? + 56 kBits + 0x70 + + + ??? + ??? + 64 kBits + 0x80 + + + ??? + ??? + 80 kBits + 0x90 + + + ??? + ??? + 96 kBits + 0xa0 + + + ??? + ??? + 112 kBits + 0xb0 + + + ??? + ??? + 128 kBits + 0xc0 + + + ??? + ??? + 144 kBits + 0xd0 + + + ??? + ??? + 160 kBits + 0xe0 + + + ??? + ??? + 22.05 kHz + 0x0 + + + ??? + ??? + 24 kHz + 0x4 + + + ??? + ??? + 16 kHz + 0x8 + + + + + ??? + ??? + Stereo + 0x0 + + + ??? + ??? + JStereo + 0x40 + + + ??? + ??? + Dual-Ch + 0x80 + + + ??? + ??? + Mono + 0xc0 + + + + + ??? + ??? + FLI file + 0xaf11 + + + ??? + ??? + - %d frames, + + + + ??? + ??? + width=%d pixels, + + + + ??? + ??? + height=%d pixels, + + + + ??? + ??? + depth=%d, + + + + ??? + ??? + ticks/frame=%d + + + + + + ??? + ??? + FLC file + 0xaf12 + + + ??? + ??? + - %d frames + + + + ??? + ??? + width=%d pixels, + + + + ??? + ??? + height=%d pixels, + + + + ??? + ??? + depth=%d, + + + + ??? + ??? + ticks/frame=%d + + + + + + ??? + ??? + Silicon Graphics movie file + MOVI + + + video/quicktime + mov + Apple QuickTime movie file (moov) + moov + + + video/quicktime + mov + Apple QuickTime movie file (mdat) + mdat + + + ??? + ??? + Applixware + *BEGIN + + + ??? + ??? + Words Document + WORDS + + + ??? + ??? + Graphic + GRAPHICS + + + ??? + ??? + Bitmap + RASTER + + + ??? + ??? + Spreadsheet + SPREADSHEETS + + + ??? + ??? + Macro + MACRO + + + ??? + ??? + Builder Object + BUILDER + + + + + application/x-tar + tar + POSIX tar archive + ustar + + + application/x-tar + tar + tar archive + ustar \000GNU + + + application/zip + zip + Zip archive data + PK\003\004 + + + application/zip + zip + at least v0.9 to extract + 0x9 + + + application/zip + zip + at least v1.0 to extract + 0xa + + + application/zip + zip + at least v1.1 to extract + 0xb + + + application/zip + zip + at least v2.0 to extract + 0x14 + + + + + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx + Microsoft Office Open XML Document + PK\003\004 + + + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docm + Microsoft Office Open XML Document + PK\003\004 + + + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsm + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptm + Microsoft Office Open XML Workbook + PK\003\004 + + + ??? + ??? + Standard MIDI data + MThd + + + ??? + ??? + (format %d) + 0x0 + + + ??? + ??? + using %d tracks + 0x1 + + + + + ??? + ??? + Creative Music (CMF) data + CTMF + + + ??? + ??? + SoundBlaster instrument data + SBI + + + ??? + ??? + Creative Labs voice data + Creative Voice File + + + ??? + ??? + + 0x1a + + + ??? + ??? + - version %d + 0x0 + + + ??? + ??? + \b.%d + 0x0 + + + + + ??? + ??? + Extended MOD sound data, + EMOD + + + ??? + ??? + version %d + + + + ??? + ??? + \b.%d, + + + + ??? + ??? + %d instruments + + + + ??? + ??? + (module) + 0x0 + + + ??? + ??? + (song) + 0x1 + + + + + ??? + ??? + realaudio sound file + 0x2e7261fd + + + ??? + ??? + file + .RMF\000\000\000realmedia + + + ??? + ??? + data + \037\235compress'd + + + ??? + ??? + block compressed + 0x0 + + + ??? + ??? + %d bits + + + + + + application/x-gzip + gz + gzip compressed data + \037\213 + + + application/x-gzip + gz + reserved method, + 0x8 + + + application/x-gzip + gz + deflated, + 0x8 + + + application/x-gzip + gz + ASCII, + 0x1 + + + application/x-gzip + gz + continuation, + 0x2 + + + application/x-gzip + gz + extra field, + 0x4 + + + application/x-gzip + gz + original filename, + 0x8 + + + application/x-gzip + gz + comment, + 0x10 + + + application/x-gzip + gz + encrypted, + 0x20 + + + application/x-gzip + gz + last modified: %s, + + + + application/x-gzip + gz + max compression, + 0x2 + + + application/x-gzip + gz + max speed, + 0x4 + + + application/x-gzip + gz + os: MS-DOS + 0x0 + + + application/x-gzip + gz + os: Amiga + 0x1 + + + application/x-gzip + gz + os: VMS + 0x2 + + + application/x-gzip + gz + os: Unix + 0x3 + + + application/x-gzip + gz + os: Atari + 0x5 + + + application/x-gzip + gz + os: OS/2 + 0x6 + + + application/x-gzip + gz + os: MacOS + 0x7 + + + application/x-gzip + gz + os: Tops/20 + 0xa + + + application/x-gzip + gz + os: Win/32 + 0xb + + + + + application/x-bzip2 + bz2 + bzip2 compressed data + BZh + + + application/x-bzip2 + bz2 + block size = %c00k + 0x2f + + + + + application/x-xz + xz + xz compressed data + 7zXZ + + + application/x-shockwave-flash + swf + Macromedia Flash data, + FWS + + + application/x-shockwave-flash + swf + version %d + + + + + + ??? + ??? + PostScript Type 1 font text + %!PS-AdobeFont-1. + + + ??? + ??? + (%s) + \000 + + + + + ??? + ??? + PostScript Type 1 font program data + %!PS-AdobeFont-1.0 + + + application/postscript + ps + PostScript Level 2 program data + %!PS-Adobe-2.0 + + + application/vnd.framemaker + ??? + FrameMaker document + <MakerFile + + + application/vnd.framemaker + ??? + (5.5 + 5.5 + + + application/vnd.framemaker + ??? + (5.0 + 5.0 + + + application/vnd.framemaker + ??? + (4.0 + 4.0 + + + application/vnd.framemaker + ??? + (3.0 + 3.0 + + + application/vnd.framemaker + ??? + (2.0 + 2.0 + + + application/vnd.framemaker + ??? + (1.0 + 1.0 + + + application/vnd.framemaker + ??? + %c) + + + + + + ??? + ??? + FrameMaker MIF (ASCII) file + <MIFFile + + + ??? + ??? + (4.0) + 4.0 + + + ??? + ??? + (3.0) + 3.0 + + + ??? + ??? + (2.0) + 2.0 + + + ??? + ??? + (1.x) + 1.0 + + + + + ??? + ??? + FrameMaker Dictionary text + <MakerDictionary + + + ??? + ??? + (3.0) + 3.0 + + + ??? + ??? + (2.0) + 2.0 + + + ??? + ??? + (1.x) + 1.0 + + + + + ??? + ??? + FrameMaker Font file + <MakerScreenFont + + + ??? + ??? + (%s) + 1.01 + + + + + ??? + ??? + FrameMaker MML file + <MML + + + ??? + ??? + FrameMaker Book file + <BookFile + + + ??? + ??? + (3.0 + 3.0 + + + ??? + ??? + (2.0 + 2.0 + + + ??? + ??? + (1.0 + 1.0 + + + ??? + ??? + %c) + + + + + + ??? + ??? + Intermediate Print File FrameMaker IPL file + <Maker + + + ??? + ??? + GIMP gradient data + GIMP Gradient + + + ??? + ??? + GIMP XCF image data, + gimp xcf file + + + ??? + ??? + %ld x + + + + ??? + ??? + %ld, + + + + ??? + ??? + RGB Color + 0x0 + + + ??? + ??? + Greyscale + 0x1 + + + ??? + ??? + Indexed Color + 0x2 + + + + + ??? + ??? + GIMP pattern data, + GPAT + + + ??? + ??? + %s + + + + + + ??? + ??? + GIMP brush data + GIMP + + + ??? + ??? + PBM image text + P1 + + + ??? + ??? + PGM image text + P2 + + + ??? + ??? + PPM image text + P3 + + + image/x-portable-bitmap + pbm + PBM "rawbits" image data + P4 + + + ??? + ??? + PGM "rawbits" image data + P5 + + + image/x-portable-graymap + ??? + PPM "rawbits" image data + P6 + + + ??? + ??? + NIFF image data + IIN1 + + + image/tiff + tif + TIFF image data, big-endian + MM\000\052 + + + image/tiff + tif + TIFF image data, little-endian + II\052\000 + + + image/tiff + tiff + TIFF image data, big-endian + MM\000\052 + + + image/tiff + tiff + TIFF image data, little-endian + II\052\000 + + + image/png + png + PNG image data, + \211NG + + + image/png + png + CORRUPTED, + 0xd0a1a0a + + + image/png + png + + 0xd0a1a0a + + + image/png + png + %ld x + + + + image/png + png + %ld, + + + + image/png + png + %d-bit + + + + image/png + png + grayscale, + 0x0 + + + image/png + png + \b/color RGB, + 0x2 + + + image/png + png + colormap, + 0x3 + + + image/png + png + gray+alpha, + 0x4 + + + image/png + png + \b/color RGBA, + 0x6 + + + image/png + png + non-interlaced + 0x0 + + + image/png + png + interlaced + 0x1 + + + + + + + image/png + png + PNG image data, CORRUPTED + PNG + + + image/gif + gif + GIF image data + GIF8 + + + image/gif + gif + version 8%s, + 7a + + + image/gif + gif + version 8%s, + 9a + + + image/gif + gif + %hd x + 0x0 + + + image/gif + gif + %hd, + 0x0 + + + + + ??? + ??? + window manager raster image data + \361\000@\273CMU + + + ??? + ??? + %d x + 0x0 + + + ??? + ??? + %d, + 0x0 + + + ??? + ??? + %d-bit + 0x0 + + + + + application/x-miff + miff + MIFF image data + id=ImageMagick + + + image/g3fax + fax + group 3 fax data + PC Research, Inc + + + image/g3fax + fax + normal resolution (204x98 DPI) + 0x0 + + + image/g3fax + fax + fine resolution (204x196 DPI) + 0x1 + + + + + image/jpeg + jpg + JPEG image data + 0xffd8 + + + image/jpeg + jpg + JFIF standard + JFIF + + + + + image/jpeg + jpg + JPEG image data, HSI proprietary + hsi1 + + + ??? + ??? + PC icon data + IC + + + ??? + ??? + PC pointer image data + PI + + + ??? + ??? + PC color icon data + CI + + + ??? + ??? + PC color pointer image data + CP + + + ??? + ??? + X pixmap image text + /* XPM */ + + + ??? + ??? + iff image data + Imagefile version- + + + ??? + ??? + %s + \000 + + + + + ??? + ??? + Kodak Photo CD image pack file + PCD_IPI + + + ??? + ??? + Kodak Photo CD overview pack file + PCD_OPA + + + ??? + ??? + %s + Visio (TM) Drawing + + + application/java + class + Compiled Java Class Data + 0xcafebabe + + + ??? + ??? + Java Class Version 1.2 + 0x0000002e + + + ??? + ??? + Java Class Version 1.3 + 0x0000002f + + + ??? + ??? + Java Class Version 1.4 + 0x00000030 + + + ??? + ??? + Java Class Version 1.5 + 0x00000031 + + + + + ??? + ??? + Java serialization data + 0xaced + + + ??? + ??? + version %d + 0x4 + + + + + application/mac-binhex40 + ??? + BinHex binary text + must be converted with BinHex + + + application/mac-binhex40 + ??? + version %.3s + + + + + + ??? + ??? + StuffIt Archive (data) + SIT! + + + ??? + ??? + : %s + + + + + + ??? + ??? + StuffIt Archive (rsrc + data) + SIT! + + + ??? + ??? + : %s + + + + + + ??? + ??? + StuffIt Deluxe (data) + SITD + + + ??? + ??? + : %s + + + + + + ??? + ??? + StuffIt Deluxe (rsrc + data) + SITD + + + ??? + ??? + : %s + + + + + + ??? + ??? + StuffIt Deluxe Segment (data) + Seg + + + ??? + ??? + : %s + + + + + + ??? + ??? + StuffIt Deluxe Segment (rsrc + data) + Seg + + + ??? + ??? + : %s + + + + + + application/pdf + pdf + Macintosh PDF File (data) + PDF + + + application/pdf + pdf + : %s + + + + + + application/pdf + pdf + Macintosh PDF File(rsrc + data) + PDF + + + application/pdf + pdf + : %s + + + + + + ??? + ??? + MIME entity text + MIME-Version: + + + ??? + ??? + + Content-Type: + + + ??? + ??? + %s + \000 + + + + + ??? + ??? + + Content-Type + + + ??? + ??? + %s + \000 + + + + + ??? + ??? + MS-DOS batch file text + @echo off + + + ??? + ??? + Windows PE + PE\000\000MS + + + ??? + ??? + 32-bit + 0x0 + + + ??? + ??? + unknown processor + 0x0 + + + ??? + ??? + Intel 80386 + 0x14c + + + ??? + ??? + MIPS R4000 + 0x166 + + + ??? + ??? + Alpha + 0x184 + + + ??? + ??? + Motorola 68000 + 0x268 + + + ??? + ??? + PowerPC + 0x1f0 + + + ??? + ??? + PA-RISC + 0x290 + + + ??? + ??? + + 0x1b + + + ??? + ??? + unknown subsystem + 0x0 + + + ??? + ??? + native + 0x1 + + + ??? + ??? + GUI + 0x2 + + + ??? + ??? + console + 0x3 + + + ??? + ??? + POSIX + 0x7 + + + + + ??? + ??? + executable + 0x0 + + + ??? + ??? + not relocatable + 0x0 + + + ??? + ??? + system file + 0x0 + + + + + ??? + ??? + DLL + 0x0 + + + ??? + ??? + not relocatable + 0x0 + + + ??? + ??? + system file + 0x0 + + + + + + + ??? + ??? + MS Windows COFF Intel 80386 object file + 0x14c + + + ??? + ??? + MS-DOS executable (EXE) + MZ + + + ??? + ??? + OS/2 or MS Windows + @ + + + ??? + ??? + %s + LH/2 Self-Extract + + + ??? + ??? + %s + PKSFX2 + + + ??? + ??? + %s + Windows self-extracting ZIP + + + + + ??? + ??? + ARJ SFX + RJSX\377\377b, + + + ??? + ??? + diet compressed + diet\371\234b, + + + ??? + ??? + PKSFX + Copyright 1989-1990 PKWARE Inc. + + + ??? + ??? + %.6s compressed + PKLITE Copr. + + + ??? + ??? + %.15s + LHa's SFX + + + ??? + ??? + %.15s + LHA's SFX + + + ??? + ??? + LHa SFX archive v2.13S + -lh5- + + + ??? + ??? + RAR self-extracting archive + Rar! + + + ??? + ??? + PKZIP SFX archive v1.1 + PK\003\004b, + + + ??? + ??? + PKZIP SFX archive v1.93a + PK\003\004b, + + + ??? + ??? + PKZIP2 SFX archive v1.09 + PK\003\004b, + + + ??? + ??? + PKZIP SFX archive v2.04g + PK\003\004b, + + + ??? + ??? + PKZIP2 SFX archive v1.02 + PK\003\004b, + + + ??? + ??? + Info-ZIP SFX archive v5.12 + PK\003\004b, + + + ??? + ??? + Info-ZIP SFX archive v5.12 w/decryption + PK\003\004b, + + + ??? + ??? + Info-ZIP SFX archive v5.12 + PK\003\004b, + + + ??? + ??? + Info-ZIP SFX archive v5.12 w/decryption + PK\003\004b, + + + ??? + ??? + Info-ZIP NT SFX archive v5.12 w/decryption + PK\003\004b, + + + ??? + ??? + CODEC archive v3.21 + y\377\200\377v\377b, + + + ??? + ??? + 1 file + 0x1 + + + ??? + ??? + %u files + 0x1 + + + + + + + ??? + ??? + MS-DOS executable (built-in) + LZ + + + ??? + ??? + Windows NT Registry file + regf + + + application/msword + doc + %s + Microsoft Word 6.0 Document + + + application/msword + doc + Spanish Microsoft Word 6 document data + Documento Microsoft Word 6 + + + application/msword + doc + Microsoft Word document data + MSWordDoc + + + application/msword + doc + Microsoft Word Document + 0x31be0000 + + + application/msword + doc + Microsoft Word 6.0 Document + PO^Q` + + + application/msword + doc + Microsoft Office Document + \376\067\000\043 + + + application/msword + doc + Microsoft Office Document + \320\317\021\340\241\261 + + + application/msword + doc + Microsoft Office Document + \333\245-\000\000\000 + + + application/msexcel + ??? + %s + Microsoft Excel 5.0 Worksheet + + + ??? + ??? + Microsoft Excel 5.0 Worksheet + Biff5 + + + text/xml + ??? + XML 1.0 Document + <?xml version="1.0"?> + + + application/msexcel + ??? + Microsoft Excel Spreadsheet + <Workbook + xmlns="urn:schemas-microsoft-com:office:spreadsheet" + + + + + + ??? + ??? + Lotus 1-2-3 + 0x1a00 + + + ??? + ??? + wk3 document data + 0x100400 + + + ??? + ??? + wk4 document data + 0x2100400 + + + ??? + ??? + fm3 or fmb document data + 0x7800100 + + + ??? + ??? + fm3 or fmb document data + 0x7800000 + + + + + ??? + ??? + Lotus 1-2-3 + 0x200 + + + ??? + ??? + wk1 document data + 0x6040600 + + + ??? + ??? + fmt document data + 0x6800200 + + + + + ??? + ??? + WordPerfect document + WPC + + + ??? + ??? + MS Windows Help Data + ?_\003\000 + + + + application/pdf + pdf + PDF document + %PDF- + + + application/pdf + pdf + version %c + + + + application/pdf + pdf + \b.%c + + + + + + ??? + ??? + DOS EPS Binary File + 0xc5d0d3c6 + + + ??? + ??? + Postscript starts at byte %d + + + + ??? + ??? + length %d + + + + ??? + ??? + Metafile starts at byte %d + + + + ??? + ??? + length %d + + + + + + ??? + ??? + TIFF starts at byte %d + + + + ??? + ??? + length %d + + + + + + + + + + + + ??? + ??? + PPD file + *PPD-Adobe: + + + ??? + ??? + ve + + + + + + ??? + ??? + RIFF (little-endian) data + RIFF + + + ??? + ??? + palette + PAL + + + ??? + ??? + version %d + + + + ??? + ??? + %d entries + + + + + + ??? + ??? + device-independent bitmap + RDIB + + + ??? + ??? + + BM + + + ??? + ??? + OS/2 1.x format + 0xc + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + ??? + ??? + OS/2 2.x format + 0x40 + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + ??? + ??? + Windows 3.x format + 0x28 + + + ??? + ??? + %d x + + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + + + + + ??? + ??? + MIDI + RMID + + + ??? + ??? + multimedia movie + RMMP + + + application/x-wav + wav + WAVE audio + WAVE + + + ??? + ??? + Microsoft PCM + 0x1 + + + ??? + ??? + %d bit + 0x0 + + + + + ??? + ??? + mono + 0x1 + + + ??? + ??? + stereo + 0x2 + + + ??? + ??? + %d channels + 0x2 + + + ??? + ??? + %d Hz + 0x0 + + + + + ??? + ??? + AVI + AVI + + + ??? + ??? + animated cursor + ACON + + + + + ??? + ??? + RIFF (big-endian) data + RIFX + + + ??? + ??? + palette + PAL + + + ??? + ??? + version %d + + + + ??? + ??? + %d entries + + + + + + ??? + ??? + device-independent bitmap + RDIB + + + ??? + ??? + + BM + + + ??? + ??? + OS/2 1.x format + 0xc + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + ??? + ??? + OS/2 2.x format + 0x40 + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + ??? + ??? + Windows 3.x format + 0x28 + + + ??? + ??? + %d x + + + + ??? + ??? + %d x + + + + ??? + ??? + %d + + + + + + + + + + ??? + ??? + MIDI + RMID + + + ??? + ??? + multimedia movie + RMMP + + + ??? + ??? + WAVE audio + WAVE + + + ??? + ??? + Microsoft PCM + 0x1 + + + ??? + ??? + %d bit + 0x0 + + + + + ??? + ??? + mono + 0x1 + + + ??? + ??? + stereo + 0x2 + + + ??? + ??? + %d channels + 0x2 + + + ??? + ??? + %d Hz + 0x0 + + + + + ??? + ??? + AVI + AVI + + + ??? + ??? + animated cursor + ACON + + + ??? + ??? + Notation Interchange File Format + NIFF + + + + + ??? + ??? + + 0xedab + + + ??? + ??? + RPM + 0xeedb + + + ??? + ??? + v%d + + + + ??? + ??? + bin + 0x0 + + + ??? + ??? + src + 0x1 + + + ??? + ??? + i386 + 0x1 + + + ??? + ??? + Alpha + 0x2 + + + ??? + ??? + Sparc + 0x3 + + + ??? + ??? + MIPS + 0x4 + + + ??? + ??? + PowerPC + 0x5 + + + ??? + ??? + 68000 + 0x6 + + + ??? + ??? + SGI + 0x7 + + + ??? + ??? + %s + + + + + + + + text/rtf + rtf + Rich Text Format data, + {\rtf + + + text/rtf + rtf + version %c, + + + + text/rtf + rtf + ANSI + \ansi + + + text/rtf + rtf + Apple Macintosh + \mac + + + text/rtf + rtf + IBM PC, code page 437 + \pc + + + text/rtf + rtf + IBM PS/2, code page 850 + \pca + + + + + text/html + html + HTML document text + <!DOCTYPE HTML + + + text/html + html + HTML document text + <!doctype html + + + text/html + html + HTML document text + <HEAD + + + text/html + html + HTML document text + <head + + + text/html + html + HTML document text + <TITLE + + + text/html + html + HTML document text + <title + + + text/html + html + HTML document text + <html + + + text/html + html + HTML document text + <HTML + + + text/sgml + sgml + exported SGML document text + <!DOCTYPE + + + text/sgml + sgml + exported SGML document text + <!doctype + + + text/sgml + sgml + exported SGML subdocument text + <!SUBDOC + + + text/sgml + sgml + exported SGML subdocument text + <!subdoc + + + text/sgml + sgml + exported SGML document text + <!-- + + + ??? + ??? + (Corel/WP) + WPC + + + ??? + ??? + WordPerfect macro + + + + ??? + ??? + WordPerfect help file + + + + ??? + ??? + WordPerfect keyboard file + + + + ??? + ??? + WordPerfect document + + + + ??? + ??? + WordPerfect dictionary + + + + ??? + ??? + WordPerfect thesaurus + + + + ??? + ??? + WordPerfect block + + + + ??? + ??? + WordPerfect rectangular block + + + + ??? + ??? + WordPerfect column block + + + + ??? + ??? + WordPerfect printer data + + + + ??? + ??? + WordPerfect printer data + + + + ??? + ??? + WordPerfect driver resource data + + + + ??? + ??? + WordPerfect hyphenation code + + + + ??? + ??? + WordPerfect hyphenation data + + + + ??? + ??? + WordPerfect macro resource data + + + + ??? + ??? + WordPerfect hyphenation lex + + + + ??? + ??? + WordPerfect wordlist + + + + ??? + ??? + WordPerfect equation resource data + + + + ??? + ??? + WordPerfect spell rules + + + + ??? + ??? + WordPerfect dictionary rules + + + + ??? + ??? + WordPerfect spell rules (Microlytics) + + + + ??? + ??? + WordPerfect settings file + + + + ??? + ??? + WordPerfect 4.2 document + + + + ??? + ??? + WordPerfect dialog file + + + + ??? + ??? + WordPerfect button bar + + + + ??? + ??? + Shell macro + + + + ??? + ??? + Shell definition + + + + ??? + ??? + Notebook macro + + + + ??? + ??? + Notebook help file + + + + ??? + ??? + Notebook keyboard file + + + + ??? + ??? + Notebook definition + + + + ??? + ??? + Calculator help file + + + + ??? + ??? + Calendar help file + + + + ??? + ??? + Calendar data file + + + + ??? + ??? + Editor macro + + + + ??? + ??? + Editor help file + + + + ??? + ??? + Editor keyboard file + + + + ??? + ??? + Editor macro resource file + + + + ??? + ??? + Macro editor macro + + + + ??? + ??? + Macro editor help file + + + + ??? + ??? + Macro editor keyboard file + + + + ??? + ??? + PlanPerfect macro + + + + ??? + ??? + PlanPerfect help file + + + + ??? + ??? + PlanPerfect keyboard file + + + + ??? + ??? + PlanPerfect worksheet + + + + ??? + ??? + PlanPerfect printer definition + + + + ??? + ??? + PlanPerfect graphic definition + + + + ??? + ??? + PlanPerfect data + + + + ??? + ??? + PlanPerfect temporary printer + + + + ??? + ??? + PlanPerfect macro resource data + + + + ??? + ??? + Mail + 0xb + + + ??? + ??? + help file + + + + ??? + ??? + distribution list + + + + ??? + ??? + out box + + + + ??? + ??? + in box + + + + ??? + ??? + users archived mailbox + + + + ??? + ??? + archived message database + + + + ??? + ??? + archived attachments + + + + ??? + ??? + Printer temporary file + + + + ??? + ??? + Scheduler help file + + + + ??? + ??? + Scheduler in file + + + + ??? + ??? + Scheduler out file + + + + ??? + ??? + GroupWise settings file + + + + ??? + ??? + GroupWise directory services + + + + ??? + ??? + GroupWise settings file + + + + ??? + ??? + Terminal resource data + + + + ??? + ??? + Terminal resource data + + + + ??? + ??? + Terminal resource data + + + + ??? + ??? + GUI loadable text + + + + ??? + ??? + graphics resource data + + + + ??? + ??? + printer settings file + + + + ??? + ??? + port definition file + + + + ??? + ??? + print queue parameters + + + + ??? + ??? + compressed file + + + + ??? + ??? + Network service msg file + + + + ??? + ??? + Network service msg file + + + + ??? + ??? + Async gateway login msg + + + + ??? + ??? + GroupWise message file + + + + ??? + ??? + GroupWise admin domain database + + + + ??? + ??? + GroupWise admin host database + + + + ??? + ??? + GroupWise admin remote host database + + + + ??? + ??? + GroupWise admin ADS deferment data file + + + + ??? + ??? + IntelliTAG (SGML) compiled DTD + + + + ??? + ??? + WordPerfect graphic image (1.0) + + + + ??? + ??? + WordPerfect graphic image (2.0) + + + + + + text/x-java + java + Java source file + /^\s*package/ + + + text/x-perl + pl + Perl source file + /^#!\/usr\/bin\/perl/ + + + text/x-c + c + C source file + /^#include/m + + + application/x-sh + sh + sh script + /^#!\/bin\/sh/ + + + application/x-bash + sh + bash script + /^#!\/bin\/bash/ + + + application/x-csh + sh + csh script + /^#!\/bin\/csh/ + + + application/x-ksh + sh + ksh script + /^#!\/bin\/ksh/ + + + text/html + html + HTML Document + /^\s*<!DOCTYPE HTML PUBLIC/ + + + text/html + html + HTML Document + /^\s*<html>/ + + + + text/plain + + net.sf.jmimemagic.detectors.TextFileDetector + + net.sf.jmimemagic.detectors.TextFileDetector + + + + application/vnd.oasis.opendocument.text + odt + Open Document Text + PK\003\004 + + + diff --git a/phoenicis-tools/src/main/resources/magic_1_0.dtd b/phoenicis-tools/src/main/resources/magic_1_0.dtd new file mode 100644 index 00000000000..92a88d9e890 --- /dev/null +++ b/phoenicis-tools/src/main/resources/magic_1_0.dtd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/phoenicis-tools/src/main/resources/template.xml b/phoenicis-tools/src/main/resources/template.xml new file mode 100644 index 00000000000..880a4699b16 --- /dev/null +++ b/phoenicis-tools/src/main/resources/template.xml @@ -0,0 +1,97 @@ + + + + + + +file + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java index 4096b5cb5c6..d0755595ac0 100644 --- a/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java @@ -56,6 +56,11 @@ public void testUncompressTarBz2File() throws IOException, URISyntaxException { testUncompress("test3.tar.bz2"); } + @Test + public void testUncompressTarXzFile() throws IOException, URISyntaxException { + testUncompress("test5.tar.xz"); + } + @Test public void testUncompressZipFile() throws IOException, URISyntaxException { testUncompress("test4.zip"); diff --git a/phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test5.tar.xz b/phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test5.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..2f436b26864f8c21b1ac0616cd17d25cb1104b79 GIT binary patch literal 252 zcmVvr~N~-6Df+L6?$~AQ? z+4!|BbZo%4j@G+KpH$$1J$(0~`lXLTefLfheXLG;WSzMhS*U*wx)M&16bkcJ1h5YS zizBInSHV6{{}5lWHcg0Kzo!A+)%nf?0JsVVq&|*tObErmKU#!ulZ^G2sG+kvCFfi>B=bH{46rG`qmbG%Yt%eSF`qIrr1%jvGmem znP+UE30f#d!NT7dx~NtF0001jgt^TBZ6=8T0oDP4Pyhhbnzy^L#Ao{g000001X)^~ C`*Cpq literal 0 HcmV?d00001 From a31a39f3181db64b31e088014c4cff4deed45399 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Tue, 15 Jan 2019 21:59:30 +0100 Subject: [PATCH 20/39] .exe uncompression support --- .../phoenicis/tools/archive/Extractor.java | 1 + .../java/org/phoenicis/tools/archive/Zip.java | 7 +- .../tools/stream/CursorFinderInputStream.java | 60 ++++++++++++++++++ phoenicis-tools/src/main/resources/magic.xml | 4 +- .../tools/archive/ExtractorTest.java | 5 ++ .../stream/CursorFinderInputStreamTest.java | 45 +++++++++++++ .../org/phoenicis/tools/archive/test6.exe | Bin 0 -> 60022 bytes 7 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java create mode 100644 phoenicis-tools/src/test/java/org/phoenicis/tools/stream/CursorFinderInputStreamTest.java create mode 100755 phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test6.exe diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/archive/Extractor.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/archive/Extractor.java index 63606fb2cf0..76014308460 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/archive/Extractor.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/archive/Extractor.java @@ -64,6 +64,7 @@ public List uncompress(File inputFile, File outputDir, Consumer uncompressZipFile(File inputFile, File outputDir, Consumer stateCallback) { @@ -62,8 +64,9 @@ List uncompressZipFile(File inputFile, File outputDir, Consumer uncompress(final InputStream inputStream, CountingInputStream countingInputStream, final File outputDir, long finalSize, Consumer stateCallback) { final List uncompressedFiles = new LinkedList<>(); - try (ArchiveInputStream debInputStream = new ArchiveStreamFactory().createArchiveInputStream("zip", - inputStream)) { + try (InputStream cursorInputStream = new CursorFinderInputStream(inputStream, ZIP_MAGICK_BYTE); + ArchiveInputStream debInputStream = new ArchiveStreamFactory().createArchiveInputStream("zip", + cursorInputStream)) { ZipArchiveEntry entry; while ((entry = (ZipArchiveEntry) debInputStream.getNextEntry()) != null) { final File outputFile = new File(outputDir, entry.getName()); diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java new file mode 100644 index 00000000000..aa4181d4527 --- /dev/null +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java @@ -0,0 +1,60 @@ +package org.phoenicis.tools.stream; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This input stream will takes an input stream, a cursor (a byte-array) and find it + * before creating a sub input stream that skip all data before the cursor. + * Example: + *

* Let's suppose we have an input stream that is sending 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06... @@ -15,7 +15,10 @@ public class CursorFinderInputStream extends InputStream { private final byte[] cursor; private final InputStream inputStream; private int cursorPosition = 0; - private int readPosition = 0; // Once the cursor is found, we need to send it through the input stream. + /** + * Once the cursor is found, we need to send it through the input stream. + */ + private int readPosition = 0; /** * @param inputStream The input stream source From 7b9f2c5cfe38c442267474e126693c0ebc1b63c3 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 15:07:43 +0100 Subject: [PATCH 22/39] - ThreadPoolExecutorService: Debugger --- phoenicis-multithreading/pom.xml | 5 ++ .../ControlledThreadPoolExecutorService.java | 4 + .../MultithreadingConfiguration.java | 15 ++++ .../ControlledThreadPoolExecutorDebugger.java | 65 +++++++++++++++ ...lledThreadPoolExecutorDebuggerWatcher.java | 80 +++++++++++++++++++ 5 files changed, 169 insertions(+) create mode 100644 phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java create mode 100644 phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java diff --git a/phoenicis-multithreading/pom.xml b/phoenicis-multithreading/pom.xml index da5e3c9a3f9..62fb8d17e30 100644 --- a/phoenicis-multithreading/pom.xml +++ b/phoenicis-multithreading/pom.xml @@ -61,5 +61,10 @@ org.mockito mockito-core + + org.phoenicis + phoenicis-configuration + ${project.version} + diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java index 0577c916ce9..d341e35bf18 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java @@ -109,4 +109,8 @@ int getQueueNumberOfItems() { public String getName() { return name; } + + public long getProcessed() { + return processed.get(); + } } diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/MultithreadingConfiguration.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/MultithreadingConfiguration.java index 2483bbe6536..5f1ad649958 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/MultithreadingConfiguration.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/MultithreadingConfiguration.java @@ -18,9 +18,13 @@ package org.phoenicis.multithreading; +import org.phoenicis.multithreading.debug.ControlledThreadPoolExecutorDebugger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @Configuration public class MultithreadingConfiguration { @Bean @@ -43,4 +47,15 @@ public ControlledThreadPoolExecutorServiceCloser controllerThreadPoolExecutorSer return new ControlledThreadPoolExecutorServiceCloser(appsExecutorService(), containersExecutorService(), scriptExecutorService()); } + + @Bean + public ExecutorService debugExecutorService() { + return Executors.newSingleThreadExecutor(); + } + + @Bean + public ControlledThreadPoolExecutorDebugger controlledThreadPoolExecutorDebugger() { + return new ControlledThreadPoolExecutorDebugger(debugExecutorService(), scriptExecutorService(), + appsExecutorService(), containersExecutorService()); + } } diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java new file mode 100644 index 00000000000..49819b59ec9 --- /dev/null +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java @@ -0,0 +1,65 @@ +package org.phoenicis.multithreading.debug; + +import org.apache.commons.lang.mutable.MutableBoolean; +import org.phoenicis.configuration.security.Safe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; + +/** + * This class allows to run a debugger that will show indications about + * It will print out the following information + */ +@Safe +public class ControlledThreadPoolExecutorDebugger implements AutoCloseable { + private static final Logger LOGGER = LoggerFactory.getLogger(ControlledThreadPoolExecutorDebugger.class); + private static final int REFRESH_TIME = 5000; + + private final ExecutorService executorService; + private final ExecutorService[] poolsToObserve; + + private MutableBoolean running = new MutableBoolean(true); + private ControlledThreadPoolExecutorDebuggerWatcher watcher; + + /** + * + * @param executorService The {@link ExecutorService that will watch the pools to be observed} + * @param poolsToObserve The {@link ExecutorService} of the pools to be observed + * @see ExecutorService + */ + public ControlledThreadPoolExecutorDebugger(ExecutorService executorService, + ExecutorService... poolsToObserve) { + this.poolsToObserve = poolsToObserve; + this.executorService = executorService; + } + + /** + * Starts the debugger + */ + public void start() { + if (watcher == null) { + watcher = new ControlledThreadPoolExecutorDebuggerWatcher(running, poolsToObserve, LOGGER, REFRESH_TIME); + executorService.submit(watcher); + } + } + + /** + * Stops the debugger + */ + public void stop() { + if (watcher != null) { + watcher.stop(); + watcher = null; + } + } + + @PreDestroy + @Override + public void close() { + stop(); + executorService.shutdownNow(); + running.setValue(false); + } +} \ No newline at end of file diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java new file mode 100644 index 00000000000..8d07f2ce9de --- /dev/null +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java @@ -0,0 +1,80 @@ +package org.phoenicis.multithreading.debug; + +import org.apache.commons.lang.mutable.MutableBoolean; +import org.phoenicis.multithreading.ControlledThreadPoolExecutorService; +import org.slf4j.Logger; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +/** + * Watcher for {@link ControlledThreadPoolExecutorDebugger} + * + * @see ControlledThreadPoolExecutorDebugger + */ +class ControlledThreadPoolExecutorDebuggerWatcher implements Runnable { + private final long startTime = System.currentTimeMillis(); + private final MutableBoolean running; + private final ExecutorService[] poolsToObserve; + private final Logger logger; + private final int sleepTime; + private final Map lastNumberOfTasks = new HashMap<>(); + private final Map lastTime = new HashMap<>(); + + ControlledThreadPoolExecutorDebuggerWatcher(MutableBoolean running, + ExecutorService[] poolsToObserve, + Logger logger, + int sleepTime) { + this.running = running; + this.poolsToObserve = poolsToObserve; + this.logger = logger; + this.sleepTime = sleepTime; + + for (ExecutorService poolToObserve : poolsToObserve) { + lastNumberOfTasks.put(poolToObserve, 0L); + lastTime.put(poolToObserve, System.currentTimeMillis()); + } + } + + /** + * Stops the watcher + */ + public void stop() { + running.setValue(false); + } + + @Override + public void run() { + while (running.isTrue()) { + for (ExecutorService executorServiceToWatch : poolsToObserve) { + final ControlledThreadPoolExecutorService pool = (ControlledThreadPoolExecutorService) executorServiceToWatch; + final long deltaStartTime = (System.currentTimeMillis() - startTime) / 1000; + final long deltaLastTime = (System.currentTimeMillis() - lastTime.get(executorServiceToWatch)) / 1000; + + final long numberOfTasks = pool.getProcessed(); + final long numberOfItems = pool.getQueue().size(); + final long queueSize = numberOfItems + pool.getQueue().remainingCapacity(); + + final long deltaNumberOfTasks = numberOfTasks - lastNumberOfTasks.get(executorServiceToWatch); + + logger.info(String.format("[ %10s ] Done: %20s, Running: %20s, Queue: %10s, Avg: %6s t/s, Speed: %6s t/s", + pool.getName(), + numberOfTasks, + pool.getActiveCount(), + String.format("%s / %s", numberOfItems, queueSize), + deltaStartTime == 0 ? 0 : numberOfTasks / deltaStartTime, + deltaLastTime == 0 ? 0 : deltaNumberOfTasks / deltaLastTime)); + + lastTime.put(executorServiceToWatch, System.currentTimeMillis()); + lastNumberOfTasks.put(executorServiceToWatch, numberOfTasks); + } + + try { + Thread.sleep(sleepTime); + } catch (InterruptedException ignored) { + break; + } + } + } +} \ No newline at end of file From 330327e8d42a62f7c79e4bd353d604d1f53adde4 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 15:09:56 +0100 Subject: [PATCH 23/39] - ThreadPoolExecutorService: Debugger --- ...ntrolledThreadPoolExecutorDebuggerWatcher.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java index 8d07f2ce9de..5fb4d1c1fe9 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java @@ -58,13 +58,14 @@ public void run() { final long deltaNumberOfTasks = numberOfTasks - lastNumberOfTasks.get(executorServiceToWatch); - logger.info(String.format("[ %10s ] Done: %20s, Running: %20s, Queue: %10s, Avg: %6s t/s, Speed: %6s t/s", - pool.getName(), - numberOfTasks, - pool.getActiveCount(), - String.format("%s / %s", numberOfItems, queueSize), - deltaStartTime == 0 ? 0 : numberOfTasks / deltaStartTime, - deltaLastTime == 0 ? 0 : deltaNumberOfTasks / deltaLastTime)); + logger.info( + String.format("[ %10s ] Done: %20s, Running: %20s, Queue: %10s, Avg: %6s t/s, Speed: %6s t/s", + pool.getName(), + numberOfTasks, + pool.getActiveCount(), + String.format("%s / %s", numberOfItems, queueSize), + deltaStartTime == 0 ? 0 : numberOfTasks / deltaStartTime, + deltaLastTime == 0 ? 0 : deltaNumberOfTasks / deltaLastTime)); lastTime.put(executorServiceToWatch, System.currentTimeMillis()); lastNumberOfTasks.put(executorServiceToWatch, numberOfTasks); From 1f1e654fe6282ad201642269c7ff10f11575ca85 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 16:45:36 +0100 Subject: [PATCH 24/39] - ThreadPoolExecutorService: Debugger --- .../debug/ControlledThreadPoolExecutorDebugger.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java index 49819b59ec9..326a30bf86e 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java @@ -9,8 +9,7 @@ import java.util.concurrent.ExecutorService; /** - * This class allows to run a debugger that will show indications about - * It will print out the following information + * This class allows to run a debugger that will show indications about the thread pools */ @Safe public class ControlledThreadPoolExecutorDebugger implements AutoCloseable { From bd13444db3afb42cf816ab09989a3001a48e99ec Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 16:45:59 +0100 Subject: [PATCH 25/39] EOF --- .../debug/ControlledThreadPoolExecutorDebugger.java | 2 +- .../debug/ControlledThreadPoolExecutorDebuggerWatcher.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java index 326a30bf86e..4d88b0e62a5 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebugger.java @@ -61,4 +61,4 @@ public void close() { executorService.shutdownNow(); running.setValue(false); } -} \ No newline at end of file +} diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java index 5fb4d1c1fe9..da3654bf2f2 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java @@ -78,4 +78,4 @@ public void run() { } } } -} \ No newline at end of file +} From 6a5a1060161ebe11ca8970df551962134450a0f5 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 16:47:51 +0100 Subject: [PATCH 26/39] EOF --- .../ControlledThreadPoolExecutorDebuggerWatcher.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java index da3654bf2f2..db8343f87e1 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/debug/ControlledThreadPoolExecutorDebuggerWatcher.java @@ -22,6 +22,14 @@ class ControlledThreadPoolExecutorDebuggerWatcher implements Runnable { private final Map lastNumberOfTasks = new HashMap<>(); private final Map lastTime = new HashMap<>(); + /** + * Constructor + * + * @param running A mutable boolean that tells if the watcher is running + * @param poolsToObserve The list of pools to watch + * @param logger The logger to send the result to + * @param sleepTime The amount time between when the observation is made + */ ControlledThreadPoolExecutorDebuggerWatcher(MutableBoolean running, ExecutorService[] poolsToObserve, Logger logger, From 789a890c7d1c5df8defc7dafafad184a197c740f Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 16:53:14 +0100 Subject: [PATCH 27/39] Jmimemagic: removed open document --- phoenicis-tools/src/main/resources/magic.xml | 36 -------------------- 1 file changed, 36 deletions(-) diff --git a/phoenicis-tools/src/main/resources/magic.xml b/phoenicis-tools/src/main/resources/magic.xml index acf3c0f5911..0b4f5035439 100644 --- a/phoenicis-tools/src/main/resources/magic.xml +++ b/phoenicis-tools/src/main/resources/magic.xml @@ -740,42 +740,6 @@ - - application/vnd.openxmlformats-officedocument.wordprocessingml.document - docx - Microsoft Office Open XML Document - PK\003\004 - - - application/vnd.openxmlformats-officedocument.wordprocessingml.document - docm - Microsoft Office Open XML Document - PK\003\004 - - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - xlsx - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - xlsm - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.presentationml.presentation - pptx - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.presentationml.presentation - pptm - Microsoft Office Open XML Workbook - PK\003\004 - ??? ??? From 6013ab3f4db926250b411333821a9e45b1ad4405 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 16:53:56 +0100 Subject: [PATCH 28/39] Jmimemagic: removed open document --- phoenicis-tools/src/main/resources/magic.xml | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/phoenicis-tools/src/main/resources/magic.xml b/phoenicis-tools/src/main/resources/magic.xml index 0b4f5035439..acf3c0f5911 100644 --- a/phoenicis-tools/src/main/resources/magic.xml +++ b/phoenicis-tools/src/main/resources/magic.xml @@ -740,6 +740,42 @@ + + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx + Microsoft Office Open XML Document + PK\003\004 + + + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docm + Microsoft Office Open XML Document + PK\003\004 + + + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsm + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx + Microsoft Office Open XML Workbook + PK\003\004 + + + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptm + Microsoft Office Open XML Workbook + PK\003\004 + ??? ??? From fbda424846da7713e4f454e33b3ee44a2df49e7a Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sat, 26 Jan 2019 17:22:08 +0100 Subject: [PATCH 29/39] Clean dependency + fix extractor --- phoenicis-javafx/pom.xml | 6 - phoenicis-tools/pom.xml | 15 +- .../phoenicis/tools/ToolsConfiguration.java | 2 +- .../tools/lnk/AbstractLnkFlagsParser.java | 22 - .../phoenicis/tools/lnk/BytesUtilities.java | 37 - .../java/org/phoenicis/tools/lnk/LnkFile.java | 66 - .../lnk/LnkFileAttributeFlagsParser.java | 30 - .../tools/lnk/LnkLinkFlagsParser.java | 87 - .../org/phoenicis/tools/lnk/LnkParser.java | 227 +- .../phoenicis/tools/lnk/LnkStringData.java | 63 - phoenicis-tools/src/main/resources/magic.dtd | 17 - phoenicis-tools/src/main/resources/magic.xml | 3562 ----------------- .../src/main/resources/magic_1_0.dtd | 18 - .../src/main/resources/template.xml | 97 - .../phoenicis/tools/lnk/LnkParserTest.java | 60 +- pom.xml | 6 + 16 files changed, 30 insertions(+), 4285 deletions(-) delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java delete mode 100644 phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java delete mode 100644 phoenicis-tools/src/main/resources/magic.dtd delete mode 100644 phoenicis-tools/src/main/resources/magic.xml delete mode 100644 phoenicis-tools/src/main/resources/magic_1_0.dtd delete mode 100644 phoenicis-tools/src/main/resources/template.xml diff --git a/phoenicis-javafx/pom.xml b/phoenicis-javafx/pom.xml index 6a0df1e7b1d..22989e156fe 100644 --- a/phoenicis-javafx/pom.xml +++ b/phoenicis-javafx/pom.xml @@ -130,12 +130,6 @@ 1.0.0 - - - jitpack.io - https://jitpack.io - - diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 2fea2b832e6..633ece565a3 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -59,15 +59,14 @@ 2.6 - net.sf.jmimemagic + com.github.PhoenicisOrg jmimemagic - 0.1.5 - - - log4j - log4j - - + 0.2.3 + + + com.github.PhoenicisOrg + lnk-parser + 1.0.0 org.apache.commons diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java index 3007d85ca30..330804ca549 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/ToolsConfiguration.java @@ -153,6 +153,6 @@ public Opener opener() { @Bean public LnkParser lnkParser() { - return new LnkParser(); + return new LnkParser(new org.phoenicis.lnk.LnkParser()); } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java deleted file mode 100644 index b1db1f94800..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/AbstractLnkFlagsParser.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.phoenicis.tools.lnk; - -/** - * This class represent a byte flags. - * It helps to apply a mask on the byte flags and allows reading them - */ -class AbstractLnkFlagsParser { - protected final byte[] rawBytes; - - protected AbstractLnkFlagsParser(byte[] rawBytes) { - this.rawBytes = rawBytes; - } - - /** - * Tests the flags with a mask (stored in a byte) - * @param flags The flags - * @return True if the flags matches the mask - */ - protected boolean testMask(byte flags) { - return (rawBytes[0] & flags) > 0; - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java deleted file mode 100644 index 9c2eac94e38..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/BytesUtilities.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.phoenicis.tools.lnk; - -class BytesUtilities { - /** - * Convert little endian bytes into an integer - * @param bytes The byte - * @param offset The offset to starts with - * @param numberOfBytes The number of bytes - * - * @return An integer - */ - static int bytes2int(byte[] bytes, int offset, int numberOfBytes) { - int result = 0; - - for (int i = 0; i < numberOfBytes; i++) { - result |= (bytes[offset + i] & 0xff) << i * 8; - } - - return result; - } - - /** - * Fetches a string inside a byte array delimited by an offset and the null character - * - * @param bytes The byte array - * @param offset The start offset - * @return The given string - */ - static String getNullDelimitedString(byte[] bytes, int offset) { - int len = 0; - - while (bytes[offset + len] != 0) { - len++; - } - return new String(bytes, offset, len); - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java deleted file mode 100644 index cde7bf1b1da..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFile.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.phoenicis.tools.lnk; - -/** - * Parsed data immutable structure. - * Contains the lnk file data - */ -public class LnkFile { - private final boolean isDirectory; - private final boolean isLocal; - private final String realFilename; - private final boolean hasArguments; - private final LnkStringData stringData; - - public LnkFile(boolean isDirectory, - boolean isLocal, - String realFilename, - boolean hasArguments, - LnkStringData stringData) { - this.isDirectory = isDirectory; - this.isLocal = isLocal; - this.realFilename = realFilename; - this.hasArguments = hasArguments; - this.stringData = stringData; - } - - /** - * Tests whether the shortcut points to a directory - * @return true or false - */ - public boolean isDirectory() { - return isDirectory; - } - - /** - * Tests whether the shortcut points to a local - * @return true or false - */ - public boolean isLocal() { - return isLocal; - } - - /** - * Fetches the real name from the sortcut - * @return the real file name - */ - public String getRealFilename() { - return realFilename; - } - - /** - * Tests whether the shortcuts has arguments - * @return true or false - */ - public boolean isHasArguments() { - return hasArguments; - } - - /** - * Fetches shortcut string data - * @see LnkStringData - * @return The string data - */ - public LnkStringData getStringData() { - return stringData; - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java deleted file mode 100644 index 55180583d47..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkFileAttributeFlagsParser.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.phoenicis.tools.lnk; - -/** - * The FileAttributesFlags structure defines bits that specify the file attributes of the link target, if the target is - * a file system item. File attributes can be used if the link target is not available, or if accessing the target would - * be inefficient. It is possible for the target items attributes to be out of sync with this value. - */ -class LnkFileAttributeFlagsParser extends AbstractLnkFlagsParser { - private static final int FILE_ATTRIBUTES_OFFSET = 24; - - private final static byte FILE_ATTRIBUTE_DIRECTORY = (byte) 0x10; - - LnkFileAttributeFlagsParser(byte[] rawLnkContent) { - super(new byte[] { - rawLnkContent[FILE_ATTRIBUTES_OFFSET], - rawLnkContent[FILE_ATTRIBUTES_OFFSET + 1], - rawLnkContent[FILE_ATTRIBUTES_OFFSET + 2], - rawLnkContent[FILE_ATTRIBUTES_OFFSET + 3] - }); - } - - /** - * Determines is the .lnk is a directory - * - * @return true if it is a directory - */ - boolean hasDirMask() { - return testMask(FILE_ATTRIBUTE_DIRECTORY); - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java deleted file mode 100644 index 3c3986ac622..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkLinkFlagsParser.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.phoenicis.tools.lnk; - -/** - * The LinkFlags structure defines bits that specify which shell link structures are present in the file format after - * the ShellLinkHeader structure (section 2.1). - * - * https://msdn.microsoft.com/en-us/library/dd871305.aspx - */ -class LnkLinkFlagsParser extends AbstractLnkFlagsParser { - private static final int DATA_FLAG_OFFSET = 20; - - private final static byte MASK_HAS_LINK_TARGET_ID_LIST = (byte) 0x01; - private final static byte MASK_HAS_NAME = (byte) 0x04; - private final static byte MASK_RELATIVE_PATH = (byte) 0x8; - private final static byte MASK_HAS_WORKING_DIR = (byte) 0x10; - private final static byte MASK_HAS_ARGUMENTS = (byte) 0x20; - private final static byte MASK_ICON_LOCATION = (byte) 0x40; - - LnkLinkFlagsParser(byte[] rawLnkContent) { - super(new byte[] { - rawLnkContent[DATA_FLAG_OFFSET], - rawLnkContent[DATA_FLAG_OFFSET + 1], - rawLnkContent[DATA_FLAG_OFFSET + 2], - rawLnkContent[DATA_FLAG_OFFSET + 3] - }); - } - - /** - * Determines if the .lnk has a working directory - * - * @return true if the .lnk has a working directory - */ - boolean hasWorkingDir() { - return testMask(MASK_HAS_WORKING_DIR); - } - - /** - * Determines if the .lnk has arguments - * - * @return true if the .lnk has arguments - */ - boolean hasArguments() { - return testMask(MASK_HAS_ARGUMENTS); - } - - boolean hasLinkTargetIdList() { - return testMask(MASK_HAS_LINK_TARGET_ID_LIST); - } - - boolean hasName() { - return testMask(MASK_HAS_NAME); - } - - boolean hasRelativePath() { - return testMask(MASK_RELATIVE_PATH); - } - - boolean hasIconLocation() { - return testMask(MASK_ICON_LOCATION); - } - - int fetchNumberOfStringData() { - int number = 0; - - if (hasName()) { - number++; - } - - if (hasRelativePath()) { - number++; - } - - if (hasWorkingDir()) { - number++; - } - - if (hasArguments()) { - number++; - } - - if (hasIconLocation()) { - number++; - } - - return number; - } -} diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java index ed0158c1da6..2189a4e3d99 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkParser.java @@ -2,6 +2,7 @@ import org.apache.commons.io.IOUtils; import org.phoenicis.configuration.security.Safe; +import org.phoenicis.lnk.LnkFile; import org.phoenicis.tools.files.FilesManipulator; import java.io.File; @@ -22,13 +23,11 @@ */ @Safe public class LnkParser extends FilesManipulator { - private final static int LNK_HEADER_SIZE = 0x4C; + private final org.phoenicis.lnk.LnkParser delegated; - private final static int FILE_LOCATION_INFO_FLAG_OFFSET_OFFSET = 0x08; - private final static int BASENAME_OFFSET_OFFSET = 0x10; - private final static int NETWORK_VOLUME_TABLE_OFFSET_OFFSET = 0x14; - private final static int FINALNAME_OFFSET_OFFSET = 0x18; - private final static int SHARE_NAME_OFFSET_OFFSET = 0x08; + public LnkParser(org.phoenicis.lnk.LnkParser delegated) { + this.delegated = delegated; + } /** * Parses a .lnk shortuct @@ -38,10 +37,7 @@ public class LnkParser extends FilesManipulator { * @throws IOException if any error occurs */ public LnkFile parse(File file) throws IOException { - this.assertInDirectory(file); - try (final InputStream inputStream = new FileInputStream(file)) { - return parse(inputStream); - } + return delegated.parse(file); } /** @@ -51,215 +47,6 @@ public LnkFile parse(File file) throws IOException { * @return a {@link LnkFile} */ public LnkFile parse(byte[] rawLnkShortcutByteArray) { - final LnkFileAttributeFlagsParser fileAttributes = fetchFilesAttributes(rawLnkShortcutByteArray); - final LnkLinkFlagsParser lnkDataFlag = fetchLnkData(rawLnkShortcutByteArray); - - final boolean isDirectory = fileAttributes.hasDirMask(); - final boolean hasArguments = lnkDataFlag.hasArguments(); - - final int fileStart = fetchFileStart(rawLnkShortcutByteArray, lnkDataFlag); - final byte[] rawLnkContentWithoutHeader = Arrays.copyOfRange(rawLnkShortcutByteArray, fileStart, - rawLnkShortcutByteArray.length - 1); - - final boolean isLocal = this.isLnkLocal(rawLnkContentWithoutHeader); - final String fileName = parseLnkContent(rawLnkContentWithoutHeader, isLocal); - - final LnkStringData lnkStringData = fetchStringData( - rawLnkContentWithoutHeader, - lnkDataFlag); - - return new LnkFile(isDirectory, isLocal, fileName, hasArguments, lnkStringData); - } - - /** - * Fetches string data inside an array - * - * @param rawLnkContentWithoutHeader The byte array content of the sortcut - * @param lnkDataFlags The lig data flags - * @return a {@link LnkLinkFlagsParser} - */ - private LnkStringData fetchStringData(byte[] rawLnkContentWithoutHeader, - LnkLinkFlagsParser lnkDataFlags) { - final int numberOfStrings = lnkDataFlags.fetchNumberOfStringData(); - final List stringDatas = fetchStringData(rawLnkContentWithoutHeader, numberOfStrings); - - int nameIndex = 0; - int relativePathIndex = 1; - int workingDirIndex = 2; - int commandLineArgumentsIndex = 3; - int iconLocationIndex = 4; - - final Optional name; - final Optional relativePath; - final Optional workingDir; - final Optional arguments; - final Optional iconLocation; - - if (lnkDataFlags.hasName()) { - name = Optional.of(stringDatas.get(nameIndex)); - } else { - name = Optional.empty(); - relativePathIndex--; - workingDirIndex--; - commandLineArgumentsIndex--; - iconLocationIndex--; - } - - if (lnkDataFlags.hasRelativePath()) { - relativePath = Optional.of(stringDatas.get(relativePathIndex)); - } else { - relativePath = Optional.empty(); - workingDirIndex--; - commandLineArgumentsIndex--; - iconLocationIndex--; - } - - if (lnkDataFlags.hasWorkingDir()) { - workingDir = Optional.of(stringDatas.get(workingDirIndex)); - } else { - workingDir = Optional.empty(); - commandLineArgumentsIndex--; - iconLocationIndex--; - } - - if (lnkDataFlags.hasArguments()) { - arguments = Optional.of(stringDatas.get(commandLineArgumentsIndex)); - } else { - arguments = Optional.empty(); - iconLocationIndex--; - } - - if (lnkDataFlags.hasArguments()) { - iconLocation = Optional.of(stringDatas.get(iconLocationIndex)); - } else { - iconLocation = Optional.empty(); - } - - return new LnkStringData(name, relativePath, workingDir, arguments, iconLocation); - } - - /** - * Fetches string data inside an array - * - * @param rawLnkContentWithoutHeader Raw lnk file content - * @param numberOfStringToRead The number of string datas to read - * @return a list of string containing string datas - */ - private List fetchStringData(byte[] rawLnkContentWithoutHeader, - int numberOfStringToRead) { - final List stringDatas = new ArrayList<>(); - - final int linkInfoSize = BytesUtilities.bytes2int(rawLnkContentWithoutHeader, 0, 3); - - int index = linkInfoSize; - for (int i = 0; i < numberOfStringToRead; i++) { - final int stringSize = BytesUtilities.bytes2int(rawLnkContentWithoutHeader, index, 2); - - final String decodedString = new String(Arrays.copyOfRange( - rawLnkContentWithoutHeader, index + 2, index + (stringSize) * 2), Charset.forName("UTF-16LE")); - stringDatas.add(decodedString); - - index = index + (stringSize) * 2 + 2; - } - - return stringDatas; - } - - /** - * Fetches LnkLinkFlagsParser - * - * @param rawLnkShortcutByteArray Raw lnk file content - * @return The Data Flags - * @see LnkLinkFlagsParser - */ - private LnkLinkFlagsParser fetchLnkData(byte[] rawLnkShortcutByteArray) { - return new LnkLinkFlagsParser(rawLnkShortcutByteArray); - } - - /** - * Parses a .lnk shortuct - * - * @param shortcutInputStream The input stream from the sortcut - * @return a {@link LnkFile} - * @throws IOException if any error occurs - */ - LnkFile parse(InputStream shortcutInputStream) throws IOException { - return parse(IOUtils.toByteArray(shortcutInputStream)); - } - - /** - * Tests if the shortcuts points to a local file - * - * @param rawLnkContent The content - * @return true or false - */ - private boolean isLnkLocal(byte[] rawLnkContent) { - final int fileLocationInfoFlag = rawLnkContent[FILE_LOCATION_INFO_FLAG_OFFSET_OFFSET]; - return (fileLocationInfoFlag & 2) == 0; - } - - /** - * Parse the actual content of the shortcut - * - * @param rawLnkShortcutByteArrayWithoutHeader Raw lnk file content - * @param isLocal Search for local or network filename - */ - private String parseLnkContent(byte[] rawLnkShortcutByteArrayWithoutHeader, boolean isLocal) { - final int finalNameOffset = rawLnkShortcutByteArrayWithoutHeader[FINALNAME_OFFSET_OFFSET]; - - final String finalName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, - finalNameOffset); - - if (isLocal) { - final int basenameOffset = rawLnkShortcutByteArrayWithoutHeader[BASENAME_OFFSET_OFFSET]; - final String basename = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, - basenameOffset); - return basename + finalName; - } else { - final int networkVolumeTableOffset = rawLnkShortcutByteArrayWithoutHeader[NETWORK_VOLUME_TABLE_OFFSET_OFFSET]; - final int shareNameOffset = rawLnkShortcutByteArrayWithoutHeader[networkVolumeTableOffset - + SHARE_NAME_OFFSET_OFFSET] + networkVolumeTableOffset; - final String shareName = BytesUtilities.getNullDelimitedString(rawLnkShortcutByteArrayWithoutHeader, - shareNameOffset); - return shareName + "\\" + finalName; - } - } - - /** - * Fetches the start of the .lnk file once the header is removed - * - * @param rawLnkContent The content of the .lnk - * @param dataFlags The dataflags that will be used to determine if there is a shell section - * @return The offset where the sortcuts file starts - */ - private int fetchFileStart(byte[] rawLnkContent, LnkLinkFlagsParser dataFlags) { - return LNK_HEADER_SIZE + fetchLinkTargetIdListLength(rawLnkContent, dataFlags); - } - - /** - * Fetches shell section length - * - * @param rawLnkContent raw .lnk content - * @param dataFlags {@link LnkLinkFlagsParser to fetch shell} - * @return the size of the section - */ - private int fetchLinkTargetIdListLength(byte[] rawLnkContent, LnkLinkFlagsParser dataFlags) { - int linkTargetIdListLength = 0; - if (dataFlags.hasLinkTargetIdList()) { - final int lengthMarkerSize = 2; - linkTargetIdListLength = BytesUtilities.bytes2int(rawLnkContent, LNK_HEADER_SIZE, 2) + lengthMarkerSize; - } - - return linkTargetIdListLength; - } - - /** - * Fetches the file attributes - * - * @param rawLnkContent raw .lnk content - * @return a {@link LnkFileAttributeFlagsParser} property - */ - private LnkFileAttributeFlagsParser fetchFilesAttributes(byte[] rawLnkContent) { - return new LnkFileAttributeFlagsParser(rawLnkContent); + return delegated.parse(rawLnkShortcutByteArray); } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java deleted file mode 100644 index 69b2613b04e..00000000000 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/lnk/LnkStringData.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.phoenicis.tools.lnk; - -import java.util.Optional; - -/** - * Parsed immutable Lng String Data structure - */ -class LnkStringData { - private final Optional nameString; - private final Optional relativePath; - private final Optional workingDirectory; - private final Optional commandLineArguments; - private final Optional iconLocation; - - LnkStringData(Optional nameString, Optional relativePath, Optional workingDirectory, - Optional commandLineArguments, Optional iconLocation) { - this.nameString = nameString; - this.relativePath = relativePath; - this.workingDirectory = workingDirectory; - this.commandLineArguments = commandLineArguments; - this.iconLocation = iconLocation; - } - - /** - * Fetches NAME_STRING - * @return - */ - Optional getNameString() { - return nameString; - } - - /** - * Fetches RELATIVE_PATH - * @return - */ - Optional getRelativePath() { - return relativePath; - } - - /** - * Fetches WORKING_DIR - * @return - */ - Optional getWorkingDirectory() { - return workingDirectory; - } - - /** - * Fetches COMMAND_LINE_ARGUMENTS - * @return - */ - Optional getCommandLineArguments() { - return commandLineArguments; - } - - /** - * Fetches ICON_LOCATION - * @return - */ - Optional getIconLocation() { - return iconLocation; - } -} diff --git a/phoenicis-tools/src/main/resources/magic.dtd b/phoenicis-tools/src/main/resources/magic.dtd deleted file mode 100644 index 00945b1042b..00000000000 --- a/phoenicis-tools/src/main/resources/magic.dtd +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/phoenicis-tools/src/main/resources/magic.xml b/phoenicis-tools/src/main/resources/magic.xml deleted file mode 100644 index acf3c0f5911..00000000000 --- a/phoenicis-tools/src/main/resources/magic.xml +++ /dev/null @@ -1,3562 +0,0 @@ - - - - - 0.2 - dcastro - magic file - - - - audio/mp3 - - MP3 - 0xfffa - - - - - b, 32 kBits - - 0x10 - - - - - b, 40 kBits - - 0x20 - - - - - b, 48 kBits - - 0x30 - - - - - b, 56 kBits - - 0x40 - - - - - b, 64 kBits - - 0x50 - - - - - b, 80 kBits - - 0x60 - - - - - b, 96 kBits - - 0x70 - - - - - b, 112 kBits - - 0x80 - - - - - b, 128 kBits - - 0x90 - - - - - b, 160 kBits - - 0xA0 - - - - - b, 192 kBits - - 0xB0 - - - - - b, 224 kBits - - 0xC0 - - - - - b, 256 kBits - - 0xD0 - - - - - b, 320 kBits - - 0xE0 - - - - - b, 44.1 kHz - - 0x00 - - - - - b, 48 kHz - - 0x04 - - - - - b, 32 kHz - - 0x08 - - - - - b, Stereo - - 0x00 - - - - - b, JStereo - - 0x40 - - - - - b, Dual-Ch - - 0x80 - - - - - b, Mono - - 0xC0 - - - - - image/gif - - GIF image data - GIF8 - - - b, version 8%s, - - b, version 8%s, - 7a - - - b, version 8%s, - - b, version 8%s, - 9a - - - - - video/mpeg - mpg - MPEG video stream data - 0x1b3 - - - video/mpeg - mpg - MPEG system stream data - 0x1ba - - - ??? - ??? - MP - 0xfff0 - - - ??? - ??? - \b - 0x8 - - - ??? - ??? - \b3 - 0x2 - - - ??? - ??? - 32 kBits - 0x10 - - - ??? - ??? - 40 kBits - 0x20 - - - ??? - ??? - 48 kBits - 0x30 - - - ??? - ??? - 56 kBits - 0x40 - - - ??? - ??? - 64 kBits - 0x50 - - - ??? - ??? - 80 kBits - 0x60 - - - ??? - ??? - 96 kBits - 0x70 - - - ??? - ??? - 112 kBits - 0x80 - - - ??? - ??? - 128 kBits - 0x90 - - - ??? - ??? - 160 kBits - 0xa0 - - - ??? - ??? - 192 kBits - 0xb0 - - - ??? - ??? - 224 kBits - 0xc0 - - - ??? - ??? - 256 kBits - 0xd0 - - - ??? - ??? - 320 kBits - 0xe0 - - - - - ??? - ??? - \b2 - 0x4 - - - ??? - ??? - 32 kBits - 0x10 - - - ??? - ??? - 48 kBits - 0x20 - - - ??? - ??? - 56 kBits - 0x30 - - - ??? - ??? - 64 kBits - 0x40 - - - ??? - ??? - 80 kBits - 0x50 - - - ??? - ??? - 96 kBits - 0x60 - - - ??? - ??? - 112 kBits - 0x70 - - - ??? - ??? - 128 kBits - 0x80 - - - ??? - ??? - 160 kBits - 0x90 - - - ??? - ??? - 192 kBits - 0xa0 - - - ??? - ??? - 224 kBits - 0xb0 - - - ??? - ??? - 256 kBits - 0xc0 - - - ??? - ??? - 320 kBits - 0xd0 - - - ??? - ??? - 384 kBits - 0xe0 - - - - - ??? - ??? - 44.1 kHz - 0x0 - - - ??? - ??? - 48 kHz - 0x4 - - - ??? - ??? - 32 kHz - 0x8 - - - - - ??? - ??? - \b - 0x0 - - - ??? - ??? - \b3 - 0x2 - - - ??? - ??? - \b2 - 0x4 - - - ??? - ??? - 8 kBits - 0x10 - - - ??? - ??? - 16 kBits - 0x20 - - - ??? - ??? - 24 kBits - 0x30 - - - ??? - ??? - 32 kBits - 0x40 - - - ??? - ??? - 40 kBits - 0x50 - - - ??? - ??? - 48 kBits - 0x60 - - - ??? - ??? - 56 kBits - 0x70 - - - ??? - ??? - 64 kBits - 0x80 - - - ??? - ??? - 80 kBits - 0x90 - - - ??? - ??? - 96 kBits - 0xa0 - - - ??? - ??? - 112 kBits - 0xb0 - - - ??? - ??? - 128 kBits - 0xc0 - - - ??? - ??? - 144 kBits - 0xd0 - - - ??? - ??? - 160 kBits - 0xe0 - - - ??? - ??? - 22.05 kHz - 0x0 - - - ??? - ??? - 24 kHz - 0x4 - - - ??? - ??? - 16 kHz - 0x8 - - - - - ??? - ??? - Stereo - 0x0 - - - ??? - ??? - JStereo - 0x40 - - - ??? - ??? - Dual-Ch - 0x80 - - - ??? - ??? - Mono - 0xc0 - - - - - ??? - ??? - FLI file - 0xaf11 - - - ??? - ??? - - %d frames, - - - - ??? - ??? - width=%d pixels, - - - - ??? - ??? - height=%d pixels, - - - - ??? - ??? - depth=%d, - - - - ??? - ??? - ticks/frame=%d - - - - - - ??? - ??? - FLC file - 0xaf12 - - - ??? - ??? - - %d frames - - - - ??? - ??? - width=%d pixels, - - - - ??? - ??? - height=%d pixels, - - - - ??? - ??? - depth=%d, - - - - ??? - ??? - ticks/frame=%d - - - - - - ??? - ??? - Silicon Graphics movie file - MOVI - - - video/quicktime - mov - Apple QuickTime movie file (moov) - moov - - - video/quicktime - mov - Apple QuickTime movie file (mdat) - mdat - - - ??? - ??? - Applixware - *BEGIN - - - ??? - ??? - Words Document - WORDS - - - ??? - ??? - Graphic - GRAPHICS - - - ??? - ??? - Bitmap - RASTER - - - ??? - ??? - Spreadsheet - SPREADSHEETS - - - ??? - ??? - Macro - MACRO - - - ??? - ??? - Builder Object - BUILDER - - - - - application/x-tar - tar - POSIX tar archive - ustar - - - application/x-tar - tar - tar archive - ustar \000GNU - - - application/zip - zip - Zip archive data - PK\003\004 - - - application/zip - zip - at least v0.9 to extract - 0x9 - - - application/zip - zip - at least v1.0 to extract - 0xa - - - application/zip - zip - at least v1.1 to extract - 0xb - - - application/zip - zip - at least v2.0 to extract - 0x14 - - - - - application/vnd.openxmlformats-officedocument.wordprocessingml.document - docx - Microsoft Office Open XML Document - PK\003\004 - - - application/vnd.openxmlformats-officedocument.wordprocessingml.document - docm - Microsoft Office Open XML Document - PK\003\004 - - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - xlsx - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - xlsm - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.presentationml.presentation - pptx - Microsoft Office Open XML Workbook - PK\003\004 - - - application/vnd.openxmlformats-officedocument.presentationml.presentation - pptm - Microsoft Office Open XML Workbook - PK\003\004 - - - ??? - ??? - Standard MIDI data - MThd - - - ??? - ??? - (format %d) - 0x0 - - - ??? - ??? - using %d tracks - 0x1 - - - - - ??? - ??? - Creative Music (CMF) data - CTMF - - - ??? - ??? - SoundBlaster instrument data - SBI - - - ??? - ??? - Creative Labs voice data - Creative Voice File - - - ??? - ??? - - 0x1a - - - ??? - ??? - - version %d - 0x0 - - - ??? - ??? - \b.%d - 0x0 - - - - - ??? - ??? - Extended MOD sound data, - EMOD - - - ??? - ??? - version %d - - - - ??? - ??? - \b.%d, - - - - ??? - ??? - %d instruments - - - - ??? - ??? - (module) - 0x0 - - - ??? - ??? - (song) - 0x1 - - - - - ??? - ??? - realaudio sound file - 0x2e7261fd - - - ??? - ??? - file - .RMF\000\000\000realmedia - - - ??? - ??? - data - \037\235compress'd - - - ??? - ??? - block compressed - 0x0 - - - ??? - ??? - %d bits - - - - - - application/x-gzip - gz - gzip compressed data - \037\213 - - - application/x-gzip - gz - reserved method, - 0x8 - - - application/x-gzip - gz - deflated, - 0x8 - - - application/x-gzip - gz - ASCII, - 0x1 - - - application/x-gzip - gz - continuation, - 0x2 - - - application/x-gzip - gz - extra field, - 0x4 - - - application/x-gzip - gz - original filename, - 0x8 - - - application/x-gzip - gz - comment, - 0x10 - - - application/x-gzip - gz - encrypted, - 0x20 - - - application/x-gzip - gz - last modified: %s, - - - - application/x-gzip - gz - max compression, - 0x2 - - - application/x-gzip - gz - max speed, - 0x4 - - - application/x-gzip - gz - os: MS-DOS - 0x0 - - - application/x-gzip - gz - os: Amiga - 0x1 - - - application/x-gzip - gz - os: VMS - 0x2 - - - application/x-gzip - gz - os: Unix - 0x3 - - - application/x-gzip - gz - os: Atari - 0x5 - - - application/x-gzip - gz - os: OS/2 - 0x6 - - - application/x-gzip - gz - os: MacOS - 0x7 - - - application/x-gzip - gz - os: Tops/20 - 0xa - - - application/x-gzip - gz - os: Win/32 - 0xb - - - - - application/x-bzip2 - bz2 - bzip2 compressed data - BZh - - - application/x-bzip2 - bz2 - block size = %c00k - 0x2f - - - - - application/x-xz - xz - xz compressed data - 7zXZ - - - application/x-shockwave-flash - swf - Macromedia Flash data, - FWS - - - application/x-shockwave-flash - swf - version %d - - - - - - ??? - ??? - PostScript Type 1 font text - %!PS-AdobeFont-1. - - - ??? - ??? - (%s) - \000 - - - - - ??? - ??? - PostScript Type 1 font program data - %!PS-AdobeFont-1.0 - - - application/postscript - ps - PostScript Level 2 program data - %!PS-Adobe-2.0 - - - application/vnd.framemaker - ??? - FrameMaker document - <MakerFile - - - application/vnd.framemaker - ??? - (5.5 - 5.5 - - - application/vnd.framemaker - ??? - (5.0 - 5.0 - - - application/vnd.framemaker - ??? - (4.0 - 4.0 - - - application/vnd.framemaker - ??? - (3.0 - 3.0 - - - application/vnd.framemaker - ??? - (2.0 - 2.0 - - - application/vnd.framemaker - ??? - (1.0 - 1.0 - - - application/vnd.framemaker - ??? - %c) - - - - - - ??? - ??? - FrameMaker MIF (ASCII) file - <MIFFile - - - ??? - ??? - (4.0) - 4.0 - - - ??? - ??? - (3.0) - 3.0 - - - ??? - ??? - (2.0) - 2.0 - - - ??? - ??? - (1.x) - 1.0 - - - - - ??? - ??? - FrameMaker Dictionary text - <MakerDictionary - - - ??? - ??? - (3.0) - 3.0 - - - ??? - ??? - (2.0) - 2.0 - - - ??? - ??? - (1.x) - 1.0 - - - - - ??? - ??? - FrameMaker Font file - <MakerScreenFont - - - ??? - ??? - (%s) - 1.01 - - - - - ??? - ??? - FrameMaker MML file - <MML - - - ??? - ??? - FrameMaker Book file - <BookFile - - - ??? - ??? - (3.0 - 3.0 - - - ??? - ??? - (2.0 - 2.0 - - - ??? - ??? - (1.0 - 1.0 - - - ??? - ??? - %c) - - - - - - ??? - ??? - Intermediate Print File FrameMaker IPL file - <Maker - - - ??? - ??? - GIMP gradient data - GIMP Gradient - - - ??? - ??? - GIMP XCF image data, - gimp xcf file - - - ??? - ??? - %ld x - - - - ??? - ??? - %ld, - - - - ??? - ??? - RGB Color - 0x0 - - - ??? - ??? - Greyscale - 0x1 - - - ??? - ??? - Indexed Color - 0x2 - - - - - ??? - ??? - GIMP pattern data, - GPAT - - - ??? - ??? - %s - - - - - - ??? - ??? - GIMP brush data - GIMP - - - ??? - ??? - PBM image text - P1 - - - ??? - ??? - PGM image text - P2 - - - ??? - ??? - PPM image text - P3 - - - image/x-portable-bitmap - pbm - PBM "rawbits" image data - P4 - - - ??? - ??? - PGM "rawbits" image data - P5 - - - image/x-portable-graymap - ??? - PPM "rawbits" image data - P6 - - - ??? - ??? - NIFF image data - IIN1 - - - image/tiff - tif - TIFF image data, big-endian - MM\000\052 - - - image/tiff - tif - TIFF image data, little-endian - II\052\000 - - - image/tiff - tiff - TIFF image data, big-endian - MM\000\052 - - - image/tiff - tiff - TIFF image data, little-endian - II\052\000 - - - image/png - png - PNG image data, - \211NG - - - image/png - png - CORRUPTED, - 0xd0a1a0a - - - image/png - png - - 0xd0a1a0a - - - image/png - png - %ld x - - - - image/png - png - %ld, - - - - image/png - png - %d-bit - - - - image/png - png - grayscale, - 0x0 - - - image/png - png - \b/color RGB, - 0x2 - - - image/png - png - colormap, - 0x3 - - - image/png - png - gray+alpha, - 0x4 - - - image/png - png - \b/color RGBA, - 0x6 - - - image/png - png - non-interlaced - 0x0 - - - image/png - png - interlaced - 0x1 - - - - - - - image/png - png - PNG image data, CORRUPTED - PNG - - - image/gif - gif - GIF image data - GIF8 - - - image/gif - gif - version 8%s, - 7a - - - image/gif - gif - version 8%s, - 9a - - - image/gif - gif - %hd x - 0x0 - - - image/gif - gif - %hd, - 0x0 - - - - - ??? - ??? - window manager raster image data - \361\000@\273CMU - - - ??? - ??? - %d x - 0x0 - - - ??? - ??? - %d, - 0x0 - - - ??? - ??? - %d-bit - 0x0 - - - - - application/x-miff - miff - MIFF image data - id=ImageMagick - - - image/g3fax - fax - group 3 fax data - PC Research, Inc - - - image/g3fax - fax - normal resolution (204x98 DPI) - 0x0 - - - image/g3fax - fax - fine resolution (204x196 DPI) - 0x1 - - - - - image/jpeg - jpg - JPEG image data - 0xffd8 - - - image/jpeg - jpg - JFIF standard - JFIF - - - - - image/jpeg - jpg - JPEG image data, HSI proprietary - hsi1 - - - ??? - ??? - PC icon data - IC - - - ??? - ??? - PC pointer image data - PI - - - ??? - ??? - PC color icon data - CI - - - ??? - ??? - PC color pointer image data - CP - - - ??? - ??? - X pixmap image text - /* XPM */ - - - ??? - ??? - iff image data - Imagefile version- - - - ??? - ??? - %s - \000 - - - - - ??? - ??? - Kodak Photo CD image pack file - PCD_IPI - - - ??? - ??? - Kodak Photo CD overview pack file - PCD_OPA - - - ??? - ??? - %s - Visio (TM) Drawing - - - application/java - class - Compiled Java Class Data - 0xcafebabe - - - ??? - ??? - Java Class Version 1.2 - 0x0000002e - - - ??? - ??? - Java Class Version 1.3 - 0x0000002f - - - ??? - ??? - Java Class Version 1.4 - 0x00000030 - - - ??? - ??? - Java Class Version 1.5 - 0x00000031 - - - - - ??? - ??? - Java serialization data - 0xaced - - - ??? - ??? - version %d - 0x4 - - - - - application/mac-binhex40 - ??? - BinHex binary text - must be converted with BinHex - - - application/mac-binhex40 - ??? - version %.3s - - - - - - ??? - ??? - StuffIt Archive (data) - SIT! - - - ??? - ??? - : %s - - - - - - ??? - ??? - StuffIt Archive (rsrc + data) - SIT! - - - ??? - ??? - : %s - - - - - - ??? - ??? - StuffIt Deluxe (data) - SITD - - - ??? - ??? - : %s - - - - - - ??? - ??? - StuffIt Deluxe (rsrc + data) - SITD - - - ??? - ??? - : %s - - - - - - ??? - ??? - StuffIt Deluxe Segment (data) - Seg - - - ??? - ??? - : %s - - - - - - ??? - ??? - StuffIt Deluxe Segment (rsrc + data) - Seg - - - ??? - ??? - : %s - - - - - - application/pdf - pdf - Macintosh PDF File (data) - PDF - - - application/pdf - pdf - : %s - - - - - - application/pdf - pdf - Macintosh PDF File(rsrc + data) - PDF - - - application/pdf - pdf - : %s - - - - - - ??? - ??? - MIME entity text - MIME-Version: - - - ??? - ??? - - Content-Type: - - - ??? - ??? - %s - \000 - - - - - ??? - ??? - - Content-Type - - - ??? - ??? - %s - \000 - - - - - ??? - ??? - MS-DOS batch file text - @echo off - - - ??? - ??? - Windows PE - PE\000\000MS - - - ??? - ??? - 32-bit - 0x0 - - - ??? - ??? - unknown processor - 0x0 - - - ??? - ??? - Intel 80386 - 0x14c - - - ??? - ??? - MIPS R4000 - 0x166 - - - ??? - ??? - Alpha - 0x184 - - - ??? - ??? - Motorola 68000 - 0x268 - - - ??? - ??? - PowerPC - 0x1f0 - - - ??? - ??? - PA-RISC - 0x290 - - - ??? - ??? - - 0x1b - - - ??? - ??? - unknown subsystem - 0x0 - - - ??? - ??? - native - 0x1 - - - ??? - ??? - GUI - 0x2 - - - ??? - ??? - console - 0x3 - - - ??? - ??? - POSIX - 0x7 - - - - - ??? - ??? - executable - 0x0 - - - ??? - ??? - not relocatable - 0x0 - - - ??? - ??? - system file - 0x0 - - - - - ??? - ??? - DLL - 0x0 - - - ??? - ??? - not relocatable - 0x0 - - - ??? - ??? - system file - 0x0 - - - - - - - ??? - ??? - MS Windows COFF Intel 80386 object file - 0x14c - - - application/x-dosexec - exe - MS-DOS executable (EXE) - MZ - - - ??? - ??? - OS/2 or MS Windows - @ - - - ??? - ??? - %s - LH/2 Self-Extract - - - ??? - ??? - %s - PKSFX2 - - - ??? - ??? - %s - Windows self-extracting ZIP - - - - - ??? - ??? - ARJ SFX - RJSX\377\377b, - - - ??? - ??? - diet compressed - diet\371\234b, - - - ??? - ??? - PKSFX - Copyright 1989-1990 PKWARE Inc. - - - ??? - ??? - %.6s compressed - PKLITE Copr. - - - ??? - ??? - %.15s - LHa's SFX - - - ??? - ??? - %.15s - LHA's SFX - - - ??? - ??? - LHa SFX archive v2.13S - -lh5- - - - ??? - ??? - RAR self-extracting archive - Rar! - - - ??? - ??? - PKZIP SFX archive v1.1 - PK\003\004b, - - - ??? - ??? - PKZIP SFX archive v1.93a - PK\003\004b, - - - ??? - ??? - PKZIP2 SFX archive v1.09 - PK\003\004b, - - - ??? - ??? - PKZIP SFX archive v2.04g - PK\003\004b, - - - ??? - ??? - PKZIP2 SFX archive v1.02 - PK\003\004b, - - - ??? - ??? - Info-ZIP SFX archive v5.12 - PK\003\004b, - - - ??? - ??? - Info-ZIP SFX archive v5.12 w/decryption - PK\003\004b, - - - ??? - ??? - Info-ZIP SFX archive v5.12 - PK\003\004b, - - - ??? - ??? - Info-ZIP SFX archive v5.12 w/decryption - PK\003\004b, - - - ??? - ??? - Info-ZIP NT SFX archive v5.12 w/decryption - PK\003\004b, - - - ??? - ??? - CODEC archive v3.21 - y\377\200\377v\377b, - - - ??? - ??? - 1 file - 0x1 - - - ??? - ??? - %u files - 0x1 - - - - - - - ??? - ??? - MS-DOS executable (built-in) - LZ - - - ??? - ??? - Windows NT Registry file - regf - - - application/msword - doc - %s - Microsoft Word 6.0 Document - - - application/msword - doc - Spanish Microsoft Word 6 document data - Documento Microsoft Word 6 - - - application/msword - doc - Microsoft Word document data - MSWordDoc - - - application/msword - doc - Microsoft Word Document - 0x31be0000 - - - application/msword - doc - Microsoft Word 6.0 Document - PO^Q` - - - application/msword - doc - Microsoft Office Document - \376\067\000\043 - - - application/msword - doc - Microsoft Office Document - \320\317\021\340\241\261 - - - application/msword - doc - Microsoft Office Document - \333\245-\000\000\000 - - - application/msexcel - ??? - %s - Microsoft Excel 5.0 Worksheet - - - ??? - ??? - Microsoft Excel 5.0 Worksheet - Biff5 - - - text/xml - ??? - XML 1.0 Document - <?xml version="1.0"?> - - - application/msexcel - ??? - Microsoft Excel Spreadsheet - <Workbook - xmlns="urn:schemas-microsoft-com:office:spreadsheet" - - - - - - ??? - ??? - Lotus 1-2-3 - 0x1a00 - - - ??? - ??? - wk3 document data - 0x100400 - - - ??? - ??? - wk4 document data - 0x2100400 - - - ??? - ??? - fm3 or fmb document data - 0x7800100 - - - ??? - ??? - fm3 or fmb document data - 0x7800000 - - - - - ??? - ??? - Lotus 1-2-3 - 0x200 - - - ??? - ??? - wk1 document data - 0x6040600 - - - ??? - ??? - fmt document data - 0x6800200 - - - - - ??? - ??? - WordPerfect document - WPC - - - ??? - ??? - MS Windows Help Data - ?_\003\000 - - - - application/pdf - pdf - PDF document - %PDF- - - - application/pdf - pdf - version %c - - - - application/pdf - pdf - \b.%c - - - - - - ??? - ??? - DOS EPS Binary File - 0xc5d0d3c6 - - - ??? - ??? - Postscript starts at byte %d - - - - ??? - ??? - length %d - - - - ??? - ??? - Metafile starts at byte %d - - - - ??? - ??? - length %d - - - - - - ??? - ??? - TIFF starts at byte %d - - - - ??? - ??? - length %d - - - - - - - - - - - - ??? - ??? - PPD file - *PPD-Adobe: - - - ??? - ??? - ve - - - - - - ??? - ??? - RIFF (little-endian) data - RIFF - - - ??? - ??? - palette - PAL - - - ??? - ??? - version %d - - - - ??? - ??? - %d entries - - - - - - ??? - ??? - device-independent bitmap - RDIB - - - ??? - ??? - - BM - - - ??? - ??? - OS/2 1.x format - 0xc - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - ??? - ??? - OS/2 2.x format - 0x40 - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - ??? - ??? - Windows 3.x format - 0x28 - - - ??? - ??? - %d x - - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - - - - - ??? - ??? - MIDI - RMID - - - ??? - ??? - multimedia movie - RMMP - - - application/x-wav - wav - WAVE audio - WAVE - - - ??? - ??? - Microsoft PCM - 0x1 - - - ??? - ??? - %d bit - 0x0 - - - - - ??? - ??? - mono - 0x1 - - - ??? - ??? - stereo - 0x2 - - - ??? - ??? - %d channels - 0x2 - - - ??? - ??? - %d Hz - 0x0 - - - - - ??? - ??? - AVI - AVI - - - ??? - ??? - animated cursor - ACON - - - - - ??? - ??? - RIFF (big-endian) data - RIFX - - - ??? - ??? - palette - PAL - - - ??? - ??? - version %d - - - - ??? - ??? - %d entries - - - - - - ??? - ??? - device-independent bitmap - RDIB - - - ??? - ??? - - BM - - - ??? - ??? - OS/2 1.x format - 0xc - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - ??? - ??? - OS/2 2.x format - 0x40 - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - ??? - ??? - Windows 3.x format - 0x28 - - - ??? - ??? - %d x - - - - ??? - ??? - %d x - - - - ??? - ??? - %d - - - - - - - - - - ??? - ??? - MIDI - RMID - - - ??? - ??? - multimedia movie - RMMP - - - ??? - ??? - WAVE audio - WAVE - - - ??? - ??? - Microsoft PCM - 0x1 - - - ??? - ??? - %d bit - 0x0 - - - - - ??? - ??? - mono - 0x1 - - - ??? - ??? - stereo - 0x2 - - - ??? - ??? - %d channels - 0x2 - - - ??? - ??? - %d Hz - 0x0 - - - - - ??? - ??? - AVI - AVI - - - ??? - ??? - animated cursor - ACON - - - ??? - ??? - Notation Interchange File Format - NIFF - - - - - ??? - ??? - - 0xedab - - - ??? - ??? - RPM - 0xeedb - - - ??? - ??? - v%d - - - - ??? - ??? - bin - 0x0 - - - ??? - ??? - src - 0x1 - - - ??? - ??? - i386 - 0x1 - - - ??? - ??? - Alpha - 0x2 - - - ??? - ??? - Sparc - 0x3 - - - ??? - ??? - MIPS - 0x4 - - - ??? - ??? - PowerPC - 0x5 - - - ??? - ??? - 68000 - 0x6 - - - ??? - ??? - SGI - 0x7 - - - ??? - ??? - %s - - - - - - - - text/rtf - rtf - Rich Text Format data, - {\rtf - - - text/rtf - rtf - version %c, - - - - text/rtf - rtf - ANSI - \ansi - - - text/rtf - rtf - Apple Macintosh - \mac - - - text/rtf - rtf - IBM PC, code page 437 - \pc - - - text/rtf - rtf - IBM PS/2, code page 850 - \pca - - - - - text/html - html - HTML document text - <!DOCTYPE HTML - - - text/html - html - HTML document text - <!doctype html - - - text/html - html - HTML document text - <HEAD - - - text/html - html - HTML document text - <head - - - text/html - html - HTML document text - <TITLE - - - text/html - html - HTML document text - <title - - - text/html - html - HTML document text - <html - - - text/html - html - HTML document text - <HTML - - - text/sgml - sgml - exported SGML document text - <!DOCTYPE - - - text/sgml - sgml - exported SGML document text - <!doctype - - - text/sgml - sgml - exported SGML subdocument text - <!SUBDOC - - - text/sgml - sgml - exported SGML subdocument text - <!subdoc - - - text/sgml - sgml - exported SGML document text - <!-- - - - ??? - ??? - (Corel/WP) - WPC - - - ??? - ??? - WordPerfect macro - - - - ??? - ??? - WordPerfect help file - - - - ??? - ??? - WordPerfect keyboard file - - - - ??? - ??? - WordPerfect document - - - - ??? - ??? - WordPerfect dictionary - - - - ??? - ??? - WordPerfect thesaurus - - - - ??? - ??? - WordPerfect block - - - - ??? - ??? - WordPerfect rectangular block - - - - ??? - ??? - WordPerfect column block - - - - ??? - ??? - WordPerfect printer data - - - - ??? - ??? - WordPerfect printer data - - - - ??? - ??? - WordPerfect driver resource data - - - - ??? - ??? - WordPerfect hyphenation code - - - - ??? - ??? - WordPerfect hyphenation data - - - - ??? - ??? - WordPerfect macro resource data - - - - ??? - ??? - WordPerfect hyphenation lex - - - - ??? - ??? - WordPerfect wordlist - - - - ??? - ??? - WordPerfect equation resource data - - - - ??? - ??? - WordPerfect spell rules - - - - ??? - ??? - WordPerfect dictionary rules - - - - ??? - ??? - WordPerfect spell rules (Microlytics) - - - - ??? - ??? - WordPerfect settings file - - - - ??? - ??? - WordPerfect 4.2 document - - - - ??? - ??? - WordPerfect dialog file - - - - ??? - ??? - WordPerfect button bar - - - - ??? - ??? - Shell macro - - - - ??? - ??? - Shell definition - - - - ??? - ??? - Notebook macro - - - - ??? - ??? - Notebook help file - - - - ??? - ??? - Notebook keyboard file - - - - ??? - ??? - Notebook definition - - - - ??? - ??? - Calculator help file - - - - ??? - ??? - Calendar help file - - - - ??? - ??? - Calendar data file - - - - ??? - ??? - Editor macro - - - - ??? - ??? - Editor help file - - - - ??? - ??? - Editor keyboard file - - - - ??? - ??? - Editor macro resource file - - - - ??? - ??? - Macro editor macro - - - - ??? - ??? - Macro editor help file - - - - ??? - ??? - Macro editor keyboard file - - - - ??? - ??? - PlanPerfect macro - - - - ??? - ??? - PlanPerfect help file - - - - ??? - ??? - PlanPerfect keyboard file - - - - ??? - ??? - PlanPerfect worksheet - - - - ??? - ??? - PlanPerfect printer definition - - - - ??? - ??? - PlanPerfect graphic definition - - - - ??? - ??? - PlanPerfect data - - - - ??? - ??? - PlanPerfect temporary printer - - - - ??? - ??? - PlanPerfect macro resource data - - - - ??? - ??? - Mail - 0xb - - - ??? - ??? - help file - - - - ??? - ??? - distribution list - - - - ??? - ??? - out box - - - - ??? - ??? - in box - - - - ??? - ??? - users archived mailbox - - - - ??? - ??? - archived message database - - - - ??? - ??? - archived attachments - - - - ??? - ??? - Printer temporary file - - - - ??? - ??? - Scheduler help file - - - - ??? - ??? - Scheduler in file - - - - ??? - ??? - Scheduler out file - - - - ??? - ??? - GroupWise settings file - - - - ??? - ??? - GroupWise directory services - - - - ??? - ??? - GroupWise settings file - - - - ??? - ??? - Terminal resource data - - - - ??? - ??? - Terminal resource data - - - - ??? - ??? - Terminal resource data - - - - ??? - ??? - GUI loadable text - - - - ??? - ??? - graphics resource data - - - - ??? - ??? - printer settings file - - - - ??? - ??? - port definition file - - - - ??? - ??? - print queue parameters - - - - ??? - ??? - compressed file - - - - ??? - ??? - Network service msg file - - - - ??? - ??? - Network service msg file - - - - ??? - ??? - Async gateway login msg - - - - ??? - ??? - GroupWise message file - - - - ??? - ??? - GroupWise admin domain database - - - - ??? - ??? - GroupWise admin host database - - - - ??? - ??? - GroupWise admin remote host database - - - - ??? - ??? - GroupWise admin ADS deferment data file - - - - ??? - ??? - IntelliTAG (SGML) compiled DTD - - - - ??? - ??? - WordPerfect graphic image (1.0) - - - - ??? - ??? - WordPerfect graphic image (2.0) - - - - - - text/x-java - java - Java source file - /^\s*package/ - - - text/x-perl - pl - Perl source file - /^#!\/usr\/bin\/perl/ - - - text/x-c - c - C source file - /^#include/m - - - application/x-sh - sh - sh script - /^#!\/bin\/sh/ - - - application/x-bash - sh - bash script - /^#!\/bin\/bash/ - - - application/x-csh - sh - csh script - /^#!\/bin\/csh/ - - - application/x-ksh - sh - ksh script - /^#!\/bin\/ksh/ - - - text/html - html - HTML Document - /^\s*<!DOCTYPE HTML PUBLIC/ - - - text/html - html - HTML Document - /^\s*<html>/ - - - - text/plain - - net.sf.jmimemagic.detectors.TextFileDetector - - net.sf.jmimemagic.detectors.TextFileDetector - - - - application/vnd.oasis.opendocument.text - odt - Open Document Text - PK\003\004 - - - diff --git a/phoenicis-tools/src/main/resources/magic_1_0.dtd b/phoenicis-tools/src/main/resources/magic_1_0.dtd deleted file mode 100644 index 92a88d9e890..00000000000 --- a/phoenicis-tools/src/main/resources/magic_1_0.dtd +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/phoenicis-tools/src/main/resources/template.xml b/phoenicis-tools/src/main/resources/template.xml deleted file mode 100644 index 880a4699b16..00000000000 --- a/phoenicis-tools/src/main/resources/template.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - -file - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java index 7567fb3ef2b..648159bb34c 100644 --- a/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/lnk/LnkParserTest.java @@ -1,64 +1,22 @@ package org.phoenicis.tools.lnk; -import org.junit.Before; import org.junit.Test; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class LnkParserTest { - private final InputStream game1inputStream = getClass().getResourceAsStream("xiii.lnk"); - private final InputStream game2inputStream = getClass().getResourceAsStream("teenagent.lnk"); - private LnkFile game1; - private LnkFile game2; - - @Before - public void setUp() throws IOException { - this.game1 = new LnkParser().parse(game1inputStream); - this.game2 = new LnkParser().parse(game2inputStream); - } - - @Test - public void testLnkParser_testPath() { - assertEquals("C:\\GOG Games\\XIII\\system\\xiii.exe", game1.getRealFilename()); - } - - @Test - public void testLnkParser_testPath_dosbox() { - assertEquals("c:\\gog games\\teenagent\\DOSBOX\\dosbox.exe", game2.getRealFilename()); - } - - @Test - public void testLnkParser_testIsDirectory() { - assertFalse(game1.isDirectory()); - } + private final org.phoenicis.lnk.LnkParser delegated = mock(org.phoenicis.lnk.LnkParser.class); + private final LnkParser lnkParser = new LnkParser(delegated); @Test - public void testLnkParser_testHasArguments_noArguments() { - assertTrue(game1.isHasArguments()); + public void testLnkParser_testPath() throws IOException { + File fileData = mock(File.class); + lnkParser.parse(fileData); + verify(delegated).parse(fileData); } - @Test - public void testLnkParser_testHasArguments_arguments() { - assertTrue(game2.isHasArguments()); - } - - @Test - public void testLnkParser_fetchArguments_example2() { - assertEquals( - "-conf \"..\\dosboxTeenagent.conf\" -conf \"..\\dosboxTeenagent_single.conf\" -noconsole -c \"exit\"", - game2.getStringData().getCommandLineArguments().get()); - } - - @Test - public void testLnkParser_fetchIconLocation_example2() { - assertEquals("c:\\gog games\\teenagent\\goggame-1207658753.ico", game2.getStringData().getIconLocation().get()); - } - - @Test - public void testLnkParser_fetchIconLocation_example1() { - assertEquals("C:\\GOG Games\\XIII\\gfw_high.ico", game1.getStringData().getIconLocation().get()); - } } diff --git a/pom.xml b/pom.xml index 2b276d737ca..e162fb35c68 100644 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,12 @@ + + + jitpack.io + https://jitpack.io + + From a7d66473350e7e0d572b0aa8c860d35f2443d453 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 9 Jun 2019 16:00:52 +0200 Subject: [PATCH 30/39] Fix bouncycastle conflict --- phoenicis-repository/pom.xml | 14 ++++++++++++++ phoenicis-tools/pom.xml | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/phoenicis-repository/pom.xml b/phoenicis-repository/pom.xml index 9bd17d0f011..1d1584df9fb 100644 --- a/phoenicis-repository/pom.xml +++ b/phoenicis-repository/pom.xml @@ -88,6 +88,20 @@ org.eclipse.jgit org.eclipse.jgit 5.3.1.201904271842-r + + + bcprov-jdk15on + org.bouncycastle + + + bcpg-jdk15on + org.bouncycastle + + + bcpkix-jdk15on + org.bouncycastle + + diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 3fd03949128..e1a4953e6d2 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -115,6 +115,10 @@ bcprov-jdk15on org.bouncycastle + + bcpkix-jdk15on + org.bouncycastle + From 77d0a6fbeb55492a7faab741bcd44a3f6db4e6a7 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Mon, 10 Jun 2019 12:04:27 +0200 Subject: [PATCH 31/39] Free git ressource when pull is used --- .../java/org/phoenicis/repository/types/GitRepository.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phoenicis-repository/src/main/java/org/phoenicis/repository/types/GitRepository.java b/phoenicis-repository/src/main/java/org/phoenicis/repository/types/GitRepository.java index c9b072122c6..79a71c50560 100644 --- a/phoenicis-repository/src/main/java/org/phoenicis/repository/types/GitRepository.java +++ b/phoenicis-repository/src/main/java/org/phoenicis/repository/types/GitRepository.java @@ -151,6 +151,11 @@ private void cloneOrUpdate() throws RepositoryException { gitRepository.pull().call(); } catch (Exception e) { LOGGER.warn("Could not update {0}. Local checkout will be used.", e); + } finally { + // close repository to free resources + if (gitRepository != null) { + gitRepository.close(); + } } } } From b69f6eedcb10990a62aaddd9980eb5381ab1b929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20P=C3=A2ris?= Date: Mon, 10 Jun 2019 12:39:24 +0200 Subject: [PATCH 32/39] Gitpod: add freetype --- .gitpod.dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index e72d536291c..d3624b956fb 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -1,5 +1,9 @@ FROM gitpod/workspace-full-vnc:latest +RUN dpkg --add-architecture i386 +RUN apt-get update +RUN apt-get -y install libfreetype6 libfreetype6:i386 + USER gitpod # activate java 11. It's already installed in the base image. From f73ced4fbc475f7a8b6ab64f38e9ee55e239e747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20P=C3=A2ris?= Date: Mon, 10 Jun 2019 10:55:29 +0000 Subject: [PATCH 33/39] Gitpod: add freetype --- .gitpod.dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index e72d536291c..3c5f3d39e83 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -1,5 +1,8 @@ FROM gitpod/workspace-full-vnc:latest +RUN dpkg --add-architecture i386 +RUN apt-get update && apt-get -y install libxext6 libxext6:i386 libfreetype6 libfreetype6:i386 + USER gitpod # activate java 11. It's already installed in the base image. From 10de88503934e5b6880c920032cb24ea44880f90 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Mon, 10 Jun 2019 23:53:19 +0200 Subject: [PATCH 34/39] Thread pool factory: remove unused threads --- .../multithreading/ControlledThreadPoolExecutorService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java index d341e35bf18..29309f63530 100644 --- a/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java +++ b/phoenicis-multithreading/src/main/java/org/phoenicis/multithreading/ControlledThreadPoolExecutorService.java @@ -48,6 +48,7 @@ public ControlledThreadPoolExecutorService(String name, int numberOfThread, int @Override public void execute(Runnable runnable) { try { + this.setCorePoolSize(numberOfThreads); remainingTasks.incrementAndGet(); semaphore.acquire(); } catch (InterruptedException e) { @@ -65,6 +66,7 @@ public void afterExecute(Runnable runnable, Throwable throwable) { semaphore.release(); processed.addAndGet(1); if (remainingTasks.decrementAndGet() == 0) { + this.setCorePoolSize(1); if (shouldShutdown) { shutdown(); } From 01b0a4e4932357dd9d91790818eb29db520925ec Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 23 Jun 2019 13:03:50 +0200 Subject: [PATCH 35/39] GraalVM sandbox implementation --- .../localisation/Localisation.java | 2 ++ .../scripts/ScriptsConfiguration.java | 22 +++++++++---- .../engine/PhoenicisScriptEngineFactory.java | 8 +++-- .../scripts/engine/ScriptEngineType.java | 9 ++--- .../implementation/PhoenicisSandbox.java | 33 +++++++++++++++++++ .../implementation/PolyglotScriptEngine.java | 7 +++- .../scripts/ui/InstallationType.java | 3 ++ 7 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java diff --git a/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java b/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java index b19b9d66b90..44f2b31bf8a 100644 --- a/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java +++ b/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java @@ -18,6 +18,7 @@ package org.phoenicis.configuration.localisation; +import org.phoenicis.configuration.security.Safe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; @@ -30,6 +31,7 @@ import java.util.*; import java.util.stream.Collectors; +@Safe public final class Localisation { private static final Logger LOGGER = LoggerFactory.getLogger(Localisation.class); private static final LocalisationHelper localisationHelper = new LocalisationHelper(); diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ScriptsConfiguration.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ScriptsConfiguration.java index 12c3b80b32d..e52c930858d 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ScriptsConfiguration.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ScriptsConfiguration.java @@ -21,6 +21,7 @@ import org.phoenicis.multithreading.MultithreadingConfiguration; import org.phoenicis.repository.RepositoryConfiguration; import org.phoenicis.scripts.engine.PhoenicisScriptEngineFactory; +import org.phoenicis.scripts.engine.implementation.PhoenicisSandbox; import org.phoenicis.scripts.interpreter.PhoenicisScriptInterpreter; import org.phoenicis.scripts.engine.ScriptEngineType; import org.phoenicis.scripts.engine.injectors.*; @@ -51,18 +52,27 @@ public class ScriptsConfiguration { @Autowired private MultithreadingConfiguration multithreadingConfiguration; + @Bean + public PhoenicisSandbox phoenicisSandbox() { + return new PhoenicisSandbox(); + } + @Bean public PhoenicisScriptEngineFactory graalScriptEngineFactory() { - return new PhoenicisScriptEngineFactory(ScriptEngineType.GRAAL, Arrays.asList(new ScriptUtilitiesInjector(), - new BeanInjector(applicationContext), new SetupWizardInjector(wizardConfiguration.setupWizardFactory()), - new IncludeInjector(scriptFetcher()), new LocalisationInjector())); + return new PhoenicisScriptEngineFactory(phoenicisSandbox(), ScriptEngineType.GRAAL, + Arrays.asList(new ScriptUtilitiesInjector(), + new BeanInjector(applicationContext), + new SetupWizardInjector(wizardConfiguration.setupWizardFactory()), + new IncludeInjector(scriptFetcher()), new LocalisationInjector())); } @Bean public PhoenicisScriptEngineFactory nashornScriptEngineFactory() { - return new PhoenicisScriptEngineFactory(ScriptEngineType.NASHORN, Arrays.asList(new ScriptUtilitiesInjector(), - new BeanInjector(applicationContext), new SetupWizardInjector(wizardConfiguration.setupWizardFactory()), - new IncludeInjector(scriptFetcher()), new LocalisationInjector())); + return new PhoenicisScriptEngineFactory(phoenicisSandbox(), ScriptEngineType.NASHORN, + Arrays.asList(new ScriptUtilitiesInjector(), + new BeanInjector(applicationContext), + new SetupWizardInjector(wizardConfiguration.setupWizardFactory()), + new IncludeInjector(scriptFetcher()), new LocalisationInjector())); } @Bean diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/PhoenicisScriptEngineFactory.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/PhoenicisScriptEngineFactory.java index 79c8b9952d4..d1e550fc16b 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/PhoenicisScriptEngineFactory.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/PhoenicisScriptEngineFactory.java @@ -18,6 +18,7 @@ package org.phoenicis.scripts.engine; +import org.phoenicis.scripts.engine.implementation.PhoenicisSandbox; import org.phoenicis.scripts.engine.injectors.EngineInjector; import org.phoenicis.scripts.engine.implementation.PhoenicisScriptEngine; @@ -25,15 +26,18 @@ public class PhoenicisScriptEngineFactory { private final ScriptEngineType type; + private final PhoenicisSandbox phoenicisSandbox; private final List engineInjectors; - public PhoenicisScriptEngineFactory(ScriptEngineType type, List engineInjectors) { + public PhoenicisScriptEngineFactory(PhoenicisSandbox phoenicisSandbox, ScriptEngineType type, + List engineInjectors) { this.type = type; this.engineInjectors = engineInjectors; + this.phoenicisSandbox = phoenicisSandbox; } public PhoenicisScriptEngine createEngine() { - final PhoenicisScriptEngine phoenicisScriptEngine = type.createScriptEngine(); + final PhoenicisScriptEngine phoenicisScriptEngine = type.createScriptEngine(this.phoenicisSandbox); engineInjectors.forEach(engineInjector -> engineInjector.injectInto(phoenicisScriptEngine)); diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/ScriptEngineType.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/ScriptEngineType.java index 9918026c777..5162b6a323a 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/ScriptEngineType.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/ScriptEngineType.java @@ -1,6 +1,7 @@ package org.phoenicis.scripts.engine; import org.phoenicis.scripts.engine.implementation.JSAScriptEngine; +import org.phoenicis.scripts.engine.implementation.PhoenicisSandbox; import org.phoenicis.scripts.engine.implementation.PhoenicisScriptEngine; import org.phoenicis.scripts.engine.implementation.PolyglotScriptEngine; @@ -12,15 +13,15 @@ public enum ScriptEngineType { NASHORN("nashorn") { @Override - public PhoenicisScriptEngine createScriptEngine() { + public PhoenicisScriptEngine createScriptEngine(PhoenicisSandbox sandbox) { return new JSAScriptEngine("nashorn"); } }, GRAAL("graal.js") { @Override - public PhoenicisScriptEngine createScriptEngine() { - return new PolyglotScriptEngine("js", Map.of("js.nashorn-compat", "true")); + public PhoenicisScriptEngine createScriptEngine(PhoenicisSandbox sandbox) { + return new PolyglotScriptEngine(sandbox, "js", Map.of("js.nashorn-compat", "true")); } }; @@ -43,7 +44,7 @@ public PhoenicisScriptEngine createScriptEngine() { * * @return The new instance of the {@link ScriptEngineType} */ - public abstract PhoenicisScriptEngine createScriptEngine(); + public abstract PhoenicisScriptEngine createScriptEngine(PhoenicisSandbox phoenicisSandbox); @Override public String toString() { diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java new file mode 100644 index 00000000000..87a7fccea47 --- /dev/null +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java @@ -0,0 +1,33 @@ +package org.phoenicis.scripts.engine.implementation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PhoenicisSandbox { + private static final Logger LOGGER = LoggerFactory.getLogger(PhoenicisSandbox.class); + + public boolean isSafe(String identifier) { + LOGGER.debug("Loading {} in javascript context", identifier); + if (identifier.startsWith("org.phoenicis")) { + return true; + } + + if (identifier.startsWith("java.lang")) { + // FIXME: This should be more fine-tuned later + // Contains process builder + return true; + } + + if (identifier.startsWith("java.util")) { + // FIXME: This should be more fine-tuned later + return true; + } + + // Needed by GraalVM + if (identifier.startsWith("java.net.URLClassLoader")) { + return true; + } + + return false; + } +} diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java index 69406983fca..3f8f0482d00 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java @@ -15,6 +15,8 @@ * A {@link PhoenicisScriptEngine} wrapping around a polyglot {@link Context} object defined by Graal */ public class PolyglotScriptEngine implements PhoenicisScriptEngine { + private final PhoenicisSandbox phoenicisSandbox; + /** * A list of error handlers */ @@ -33,16 +35,19 @@ public class PolyglotScriptEngine implements PhoenicisScriptEngine { /** * Constructor * + * @param phoenicisSandbox a Phoenicis Sandbox bean * @param language The language name * @param options A map of options for the Polyglot context */ - public PolyglotScriptEngine(String language, Map options) { + public PolyglotScriptEngine(PhoenicisSandbox phoenicisSandbox, String language, Map options) { super(); + this.phoenicisSandbox = phoenicisSandbox; this.errorHandlers = new ArrayList<>(); this.language = language; this.context = Context.newBuilder(language) .allowExperimentalOptions(true) + .allowHostClassLookup(phoenicisSandbox::isSafe) .options(options).allowHostAccess(true).build(); } diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ui/InstallationType.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ui/InstallationType.java index 9459a116c33..874d41094dc 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ui/InstallationType.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/ui/InstallationType.java @@ -1,8 +1,11 @@ package org.phoenicis.scripts.ui; +import org.phoenicis.configuration.security.Safe; + /** * type of the installation */ +@Safe public enum InstallationType { APPS("Apps"), ENGINES("Engines"), VERBS("Verbs"); From b955432f47a89e2147fa89f3816c5e288a0cf8ba Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 23 Jun 2019 13:04:28 +0200 Subject: [PATCH 36/39] GraalVM sandbox implementation --- .../org/phoenicis/configuration/localisation/Localisation.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java b/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java index 44f2b31bf8a..b19b9d66b90 100644 --- a/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java +++ b/phoenicis-configuration/src/main/java/org/phoenicis/configuration/localisation/Localisation.java @@ -18,7 +18,6 @@ package org.phoenicis.configuration.localisation; -import org.phoenicis.configuration.security.Safe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; @@ -31,7 +30,6 @@ import java.util.*; import java.util.stream.Collectors; -@Safe public final class Localisation { private static final Logger LOGGER = LoggerFactory.getLogger(Localisation.class); private static final LocalisationHelper localisationHelper = new LocalisationHelper(); From c2a4431886a6bbf5a0cfe28c62cb08f99dd074eb Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 23 Jun 2019 13:52:44 +0200 Subject: [PATCH 37/39] GraalVM sandbox implementation --- .../scripts/engine/implementation/PhoenicisSandbox.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java index 87a7fccea47..645a7916699 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java @@ -3,6 +3,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Filters classes that are allowed to be used inside scripts + */ public class PhoenicisSandbox { private static final Logger LOGGER = LoggerFactory.getLogger(PhoenicisSandbox.class); From c8d8850ae0dd337f558633a58e285123b1ec5457 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 23 Jun 2019 14:02:36 +0200 Subject: [PATCH 38/39] GraalVM sandbox implementation --- .../scripts/engine/implementation/PolyglotScriptEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java index 3f8f0482d00..b6f05f4c500 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java @@ -41,8 +41,8 @@ public class PolyglotScriptEngine implements PhoenicisScriptEngine { */ public PolyglotScriptEngine(PhoenicisSandbox phoenicisSandbox, String language, Map options) { super(); - this.phoenicisSandbox = phoenicisSandbox; + this.phoenicisSandbox = phoenicisSandbox; this.errorHandlers = new ArrayList<>(); this.language = language; this.context = Context.newBuilder(language) From 96f0977264aaed2c8e2e415e0fc3893055af5869 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Sun, 23 Jun 2019 14:18:11 +0200 Subject: [PATCH 39/39] GraalVM sandbox implementation --- .../scripts/engine/implementation/PhoenicisSandbox.java | 5 +---- .../scripts/engine/implementation/PolyglotScriptEngine.java | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java index 645a7916699..ae1dc5f1575 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PhoenicisSandbox.java @@ -27,10 +27,7 @@ public boolean isSafe(String identifier) { } // Needed by GraalVM - if (identifier.startsWith("java.net.URLClassLoader")) { - return true; - } + return identifier.startsWith("java.net.URLClassLoader"); - return false; } } diff --git a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java index b6f05f4c500..3d144bfd220 100644 --- a/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java +++ b/phoenicis-scripts/src/main/java/org/phoenicis/scripts/engine/implementation/PolyglotScriptEngine.java @@ -15,8 +15,6 @@ * A {@link PhoenicisScriptEngine} wrapping around a polyglot {@link Context} object defined by Graal */ public class PolyglotScriptEngine implements PhoenicisScriptEngine { - private final PhoenicisSandbox phoenicisSandbox; - /** * A list of error handlers */ @@ -42,7 +40,6 @@ public class PolyglotScriptEngine implements PhoenicisScriptEngine { public PolyglotScriptEngine(PhoenicisSandbox phoenicisSandbox, String language, Map options) { super(); - this.phoenicisSandbox = phoenicisSandbox; this.errorHandlers = new ArrayList<>(); this.language = language; this.context = Context.newBuilder(language)

+ * Let's suppose we have an input stream that is sending 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06... + * Then, if the cursor is [0x02, 0x03], the new input stream will send 0x02, 0x03, 0x04, 0x05, 0x06... + */ +public class CursorFinderInputStream extends InputStream { + private final byte[] cursor; + private final InputStream inputStream; + private int cursorPosition = 0; + private int readPosition = 0; // Once the cursor is found, we need to send it through the input stream. + + /** + * @param inputStream The input stream source + * @param cursor The cursor to find + */ + public CursorFinderInputStream(InputStream inputStream, byte[] cursor) { + this.inputStream = inputStream; + this.cursor = cursor; + } + + @Override + public int read() throws IOException { + while (true) { + if (cursorHasBeenFound()) { + if (!cursorHasBeenRead()) { + return cursor[readPosition++]; + } + return inputStream.read(); + } + + int delegatedInputStreamValue = inputStream.read(); + + if (delegatedInputStreamValue == -1) { + return -1; + } + + if (cursor[cursorPosition] == delegatedInputStreamValue) { + cursorPosition++; + } else { + cursorPosition = 0; + } + } + } + + private boolean cursorHasBeenRead() { + return readPosition >= cursor.length; + } + + private boolean cursorHasBeenFound() { + return cursorPosition >= cursor.length; + } +} diff --git a/phoenicis-tools/src/main/resources/magic.xml b/phoenicis-tools/src/main/resources/magic.xml index babe1be14d4..acf3c0f5911 100644 --- a/phoenicis-tools/src/main/resources/magic.xml +++ b/phoenicis-tools/src/main/resources/magic.xml @@ -1995,8 +1995,8 @@ 0x14c - ??? - ??? + application/x-dosexec + exe MS-DOS executable (EXE) MZ diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java index d0755595ac0..071a4101e59 100644 --- a/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/archive/ExtractorTest.java @@ -66,6 +66,11 @@ public void testUncompressZipFile() throws IOException, URISyntaxException { testUncompress("test4.zip"); } + @Test + public void testUncompressZipExeFile() throws IOException, URISyntaxException { + testUncompress("test6.exe"); + } + @Test public void testUncompressWithSymbolicLinks() throws IOException, URISyntaxException { File inputFile = new File(inputUrl.toURI().getPath(), "tarLink.tar.gz"); diff --git a/phoenicis-tools/src/test/java/org/phoenicis/tools/stream/CursorFinderInputStreamTest.java b/phoenicis-tools/src/test/java/org/phoenicis/tools/stream/CursorFinderInputStreamTest.java new file mode 100644 index 00000000000..8c4c45b7f7d --- /dev/null +++ b/phoenicis-tools/src/test/java/org/phoenicis/tools/stream/CursorFinderInputStreamTest.java @@ -0,0 +1,45 @@ +package org.phoenicis.tools.stream; + +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class CursorFinderInputStreamTest { + private CursorFinderInputStream cursorFinderInputStream; + + @Test + public void testNormalCase() throws IOException { + this.cursorFinderInputStream = new CursorFinderInputStream(new ByteArrayInputStream("ABCDEFG".getBytes()), + "CD".getBytes()); + + assertEquals("CDEFG", IOUtils.toString(cursorFinderInputStream, "UTF-8")); + } + + @Test + public void testEmptyCursor() throws IOException { + this.cursorFinderInputStream = new CursorFinderInputStream(new ByteArrayInputStream("ABCDEFG".getBytes()), + new byte[0]); + + assertEquals("ABCDEFG", IOUtils.toString(cursorFinderInputStream, "UTF-8")); + } + + @Test + public void testDirectStart() throws IOException { + this.cursorFinderInputStream = new CursorFinderInputStream(new ByteArrayInputStream("ABCDEFG".getBytes()), + "ABCDEFG".getBytes()); + + assertEquals("ABCDEFG", IOUtils.toString(cursorFinderInputStream, "UTF-8")); + } + + @Test + public void testCursorNotFound() throws IOException { + this.cursorFinderInputStream = new CursorFinderInputStream(new ByteArrayInputStream("ABCDEFG".getBytes()), + "IJ".getBytes()); + + assertEquals("", IOUtils.toString(cursorFinderInputStream, "UTF-8")); + } +} \ No newline at end of file diff --git a/phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test6.exe b/phoenicis-tools/src/test/resources/org/phoenicis/tools/archive/test6.exe new file mode 100755 index 0000000000000000000000000000000000000000..322f4185c8a951d841699bee6f4ffd60d9f9773d GIT binary patch literal 60022 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 Vsd9PGi)1~CKY4|~B`_-We*qify~O|k literal 0 HcmV?d00001 From 8b6985b66919f9824ed8399637e63e333f20dba7 Mon Sep 17 00:00:00 2001 From: Quentin PARIS Date: Tue, 15 Jan 2019 22:27:19 +0100 Subject: [PATCH 21/39] - Warkaround mimetype detection - Fix typos --- phoenicis-tools/pom.xml | 3 +-- .../phoenicis/tools/files/FileAnalyser.java | 21 +++++++++++++++---- .../tools/stream/CursorFinderInputStream.java | 9 +++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/phoenicis-tools/pom.xml b/phoenicis-tools/pom.xml index 34a24363808..2fea2b832e6 100644 --- a/phoenicis-tools/pom.xml +++ b/phoenicis-tools/pom.xml @@ -16,8 +16,7 @@ ~ with this program; if not, write to the Free Software Foundation, Inc., ~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --> - + 4.0.0 org.phoenicis diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java index 5b44f1e3a66..a31a16447b5 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/files/FileAnalyser.java @@ -37,9 +37,8 @@ public class FileAnalyser { /** * Identify which line delimiter is used in a file - * - * @param fileContent - * string to analyse + * + * @param fileContent string to analyse * @return the line separator as a string. Null if the file has no line * separator */ @@ -81,11 +80,25 @@ public String getDescription(File inputFile) { public String getMimetype(File inputFile) { try { - return getMatch(inputFile).getMimeType(); + final MagicMatch match = getMatch(inputFile); + final String mimeType = match.getMimeType(); + if ("???".equals(mimeType)) { + return guessMimeTypeFromDescription(match); + } + + return mimeType; } catch (MagicMatchNotFoundException e) { LOGGER.debug("Failed to get Mime Type", e); final MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); return mimeTypesMap.getContentType(inputFile); } } + + private String guessMimeTypeFromDescription(MagicMatch match) { + if ("MS-DOS executable (EXE)".equals(match.getDescription())) { + return "application/x-dosexec"; + } + + return "???"; + } } diff --git a/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java b/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java index aa4181d4527..23e0a106c7a 100644 --- a/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java +++ b/phoenicis-tools/src/main/java/org/phoenicis/tools/stream/CursorFinderInputStream.java @@ -4,8 +4,8 @@ import java.io.InputStream; /** - * This input stream will takes an input stream, a cursor (a byte-array) and find it - * before creating a sub input stream that skip all data before the cursor. + * This input stream will take an input stream, a cursor (a byte-array) and find it + * before creating a sub input stream that skips all data before the cursor. * Example: *