46
46
/**
47
47
* Serialized format reader.
48
48
*/
49
- class ODPresentation implements ReaderInterface
49
+ class ODPresentation extends AbstractReader implements ReaderInterface
50
50
{
51
51
/**
52
52
* Output Object.
@@ -62,6 +62,11 @@ class ODPresentation implements ReaderInterface
62
62
*/
63
63
protected $ oZip ;
64
64
65
+ /**
66
+ * @var string
67
+ */
68
+ protected $ filename ;
69
+
65
70
/**
66
71
* @var array<string, array{alignment: null|Alignment, background: null, shadow: null|Shadow, fill: null|Fill, spacingAfter: null|int, spacingBefore: null|int, lineSpacingMode: null, lineSpacing: null, font: null, listStyle: null}>
67
72
*/
@@ -77,6 +82,11 @@ class ODPresentation implements ReaderInterface
77
82
*/
78
83
protected $ oXMLReader ;
79
84
85
+ /**
86
+ * @var XMLReader
87
+ */
88
+ protected $ oXMLMetaInfManifest ;
89
+
80
90
/**
81
91
* @var int
82
92
*/
@@ -142,22 +152,21 @@ public function load(string $pFilename, int $flags = 0): PhpPresentation
142
152
*/
143
153
protected function loadFile ($ pFilename )
144
154
{
155
+ $ this ->filename = $ pFilename ;
156
+
145
157
$ this ->oPhpPresentation = new PhpPresentation ();
146
158
$ this ->oPhpPresentation ->removeSlideByIndex ();
147
159
148
160
$ this ->oZip = new ZipArchive ();
149
- $ this ->oZip ->open ($ pFilename );
161
+ $ this ->oZip ->open ($ this -> filename );
150
162
151
- $ this ->oXMLReader = new XMLReader ();
152
- if (false !== $ this ->oXMLReader ->getDomFromZip ($ pFilename , 'meta.xml ' )) {
163
+ if ($ this ->loadFileFromODP ('meta.xml ' ) !== false ) {
153
164
$ this ->loadDocumentProperties ();
154
165
}
155
- $ this ->oXMLReader = new XMLReader ();
156
- if (false !== $ this ->oXMLReader ->getDomFromZip ($ pFilename , 'styles.xml ' )) {
166
+ if ($ this ->loadFileFromODP ('styles.xml ' ) !== false ) {
157
167
$ this ->loadStylesFile ();
158
168
}
159
- $ this ->oXMLReader = new XMLReader ();
160
- if (false !== $ this ->oXMLReader ->getDomFromZip ($ pFilename , 'content.xml ' )) {
169
+ if ($ this ->loadFileFromODP ('content.xml ' ) !== false ) {
161
170
$ this ->loadSlides ();
162
171
$ this ->loadPresentationProperties ();
163
172
}
@@ -762,6 +771,107 @@ protected function loadStylesFile(): void
762
771
}
763
772
}
764
773
774
+ /**
775
+ * @param string $filename
776
+ *
777
+ * @return bool
778
+ */
779
+ protected function loadFileFromODP ($ filename )
780
+ {
781
+ $ bEncrypted = false ;
782
+
783
+ if (!$ this ->oXMLMetaInfManifest ) {
784
+ $ this ->oXMLMetaInfManifest = new XMLReader ();
785
+ if ($ this ->oXMLMetaInfManifest ->getDomFromZip ($ this ->filename , 'META-INF/manifest.xml ' ) === false ) {
786
+ return false ;
787
+ }
788
+ }
789
+ // Search file in META-INF/manifest.xml
790
+ $ oElement = $ this ->oXMLMetaInfManifest ->getElement ('/manifest:manifest/manifest:file-entry[@manifest:full-path= \'' . $ filename . '\'] ' );
791
+ if (!$ oElement ) {
792
+ return false ;
793
+ }
794
+ // Has it some manifest:encryption-data ?
795
+ $ oElementEncryption = $ this ->oXMLMetaInfManifest ->getElement ('manifest:encryption-data ' , $ oElement );
796
+ if ($ oElementEncryption ) {
797
+ $ bEncrypted = true ;
798
+ }
799
+
800
+ $ fileContent = $ this ->oZip ->getFromName ($ filename );
801
+ if (!$ fileContent ) {
802
+ return false ;
803
+ }
804
+
805
+ // No Encrypted file
806
+ if (!$ bEncrypted ) {
807
+ $ this ->oXMLReader = new XMLReader ();
808
+ $ this ->oXMLReader ->getDomFromString ($ fileContent );
809
+
810
+ return true ;
811
+ }
812
+ var_dump ($ filename );
813
+
814
+ //return false;
815
+ /*
816
+ <manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml" manifest:size="2090">
817
+ <manifest:encryption-data
818
+ manifest:checksum-type="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k"
819
+ manifest:checksum="BfB+taOY0kcVO/9WNi4DfqioRp3LMwVoNbqfAQ37yac=">
820
+ <manifest:algorithm
821
+ manifest:algorithm-name="http://www.w3.org/2001/04/xmlenc#aes256-cbc"
822
+ manifest:initialisation-vector="I7rMXmvuynJFxJtm+EQ5qA=="/>
823
+ <manifest:key-derivation
824
+ manifest:key-derivation-name="PBKDF2"
825
+ manifest:key-size="32"
826
+ manifest:iteration-count="1024"
827
+ manifest:salt="Mows9XX/YiNKNJ0qll3jgA=="/>
828
+ <manifest:start-key-generation
829
+ manifest:start-key-generation-name="http://www.w3.org/2000/09/xmldsig#sha256"
830
+ manifest:key-size="32"/>
831
+ </manifest:encryption-data>
832
+ </manifest:file-entry>
833
+ */
834
+ // Encrypted file
835
+ $ checksum = $ oElementEncryption ->getAttribute ('manifest:checksum ' );
836
+
837
+ $ oEltKeyDerivation = $ this ->oXMLMetaInfManifest ->getElement ('manifest:key-derivation ' , $ oElementEncryption );
838
+ $ salt = $ oEltKeyDerivation ->getAttribute ('manifest:salt ' );
839
+ //$salt = base64_decode($salt);
840
+ echo 'manifest:salt : ' . $ salt . PHP_EOL ;
841
+ $ iterationCount = (int ) $ oEltKeyDerivation ->getAttribute ('manifest:iteration-count ' );
842
+ echo 'manifest:iteration-count : ' . $ iterationCount . PHP_EOL ;
843
+ $ keySize = (int ) $ oEltKeyDerivation ->getAttribute ('manifest:key-size ' ) ?? 16 ;
844
+ echo 'manifest:key-size : ' . $ keySize . PHP_EOL ;
845
+
846
+ $ oEltAlgorithm = $ this ->oXMLMetaInfManifest ->getElement ('manifest:algorithm ' , $ oElementEncryption );
847
+ $ iv = $ oEltAlgorithm ->getAttribute ('manifest:initialisation-vector ' );
848
+ $ iv = base64_decode ($ iv );
849
+ echo 'manifest:initialisation-vector : ' . $ iv . PHP_EOL ;
850
+
851
+ // manifest:start-key-generation-name == sha256 sinon sha1
852
+ $ pwdHash = hash ('sha256 ' , $ this ->getPassword ());
853
+ echo 'sha256( ' . $ this ->getPassword () . '): ' . $ pwdHash . PHP_EOL ;
854
+ //$pwdHash = substr($pwdHash, 0 , 32);
855
+ //var_dump($pwdHash);
856
+
857
+ // ifmanifest:key-derivation-name="PBKDF2" THEN PBKDF2WithHmacSHA1 SINON ?
858
+ $ key = hash_pbkdf2 ('sha1 ' , $ pwdHash , $ salt , $ iterationCount , $ keySize , true );
859
+ echo 'hash_pbkdf2 (sha1, hash, salt, iterationCount, $iterationCount) : ' . $ key . PHP_EOL ;
860
+
861
+ $ data = openssl_decrypt ($ fileContent , 'AES-256-CBC ' , $ key , 0 , $ iv );
862
+ if (!$ data ) {
863
+ while ($ msg = openssl_error_string ()) {
864
+ var_dump ($ msg );
865
+ }
866
+ die ();
867
+ }
868
+ var_dump ($ data );
869
+ $ data = gzinflate ($ data );
870
+ var_dump ($ data );
871
+
872
+ return false ;
873
+ }
874
+
765
875
private function getExpressionUnit (string $ expr ): string
766
876
{
767
877
if (substr ($ expr , -1 ) == '% ' ) {
0 commit comments