You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2014/12/25 02:56:30 UTC
svn commit: r1647867 [3/4] - in /poi: site/src/documentation/content/xdocs/
trunk/src/java/org/apache/poi/ trunk/src/java/org/apache/poi/poifs/crypt/
trunk/src/java/org/apache/poi/poifs/crypt/binaryrc4/
trunk/src/java/org/apache/poi/poifs/crypt/cryptoa...
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/EncryptedSlideShow.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/EncryptedSlideShow.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/EncryptedSlideShow.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/EncryptedSlideShow.java Thu Dec 25 01:56:29 2014
@@ -17,117 +17,478 @@
package org.apache.poi.hslf;
-import java.io.FileNotFoundException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
-import org.apache.poi.hslf.record.CurrentUserAtom;
+import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.hslf.record.DocumentEncryptionAtom;
import org.apache.poi.hslf.record.PersistPtrHolder;
+import org.apache.poi.hslf.record.PositionDependentRecord;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.UserEditAtom;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndian;
/**
- * This class provides helper functions for determining if a
- * PowerPoint document is Encrypted.
- * In future, it may also provide Encryption and Decryption
- * functions, but first we'd need to figure out how
- * PowerPoint encryption is really done!
- *
- * @author Nick Burch
+ * This class provides helper functions for encrypted PowerPoint documents.
*/
+@Internal
+public class EncryptedSlideShow {
+ DocumentEncryptionAtom dea;
+ CryptoAPIEncryptor enc = null;
+ CryptoAPIDecryptor dec = null;
+ Cipher cipher = null;
+ CipherOutputStream cyos = null;
+
+ private static final BitField fieldRecInst = new BitField(0xFFF0);
+
+ protected EncryptedSlideShow(DocumentEncryptionAtom dea) {
+ this.dea = dea;
+ }
+
+ protected EncryptedSlideShow(byte[] docstream, NavigableMap<Integer,Record> recordMap) {
+ // check for DocumentEncryptionAtom, which would be at the last offset
+ // need to ignore already set UserEdit and PersistAtoms
+ UserEditAtom userEditAtomWithEncryption = null;
+ for (Map.Entry<Integer, Record> me : recordMap.descendingMap().entrySet()) {
+ Record r = me.getValue();
+ if (!(r instanceof UserEditAtom)) continue;
+ UserEditAtom uea = (UserEditAtom)r;
+ if (uea.getEncryptSessionPersistIdRef() != -1) {
+ userEditAtomWithEncryption = uea;
+ break;
+ }
+ }
+
+ if (userEditAtomWithEncryption == null) {
+ dea = null;
+ return;
+ }
+
+ Record r = recordMap.get(userEditAtomWithEncryption.getPersistPointersOffset());
+ assert(r instanceof PersistPtrHolder);
+ PersistPtrHolder ptr = (PersistPtrHolder)r;
+
+ Integer encOffset = ptr.getSlideLocationsLookup().get(userEditAtomWithEncryption.getEncryptSessionPersistIdRef());
+ assert(encOffset != null);
+
+ r = recordMap.get(encOffset);
+ if (r == null) {
+ r = Record.buildRecordAtOffset(docstream, encOffset);
+ recordMap.put(encOffset, r);
+ }
+ assert(r instanceof DocumentEncryptionAtom);
+ this.dea = (DocumentEncryptionAtom)r;
+
+ CryptoAPIDecryptor dec = (CryptoAPIDecryptor)dea.getEncryptionInfo().getDecryptor();
+ String pass = Biff8EncryptionKey.getCurrentUserPassword();
+ if(!dec.verifyPassword(pass != null ? pass : Decryptor.DEFAULT_PASSWORD)) {
+ throw new EncryptedPowerPointFileException("PowerPoint file is encrypted. The correct password needs to be set via Biff8EncryptionKey.setCurrentUserPassword()");
+ }
+ }
+
+ public DocumentEncryptionAtom getDocumentEncryptionAtom() {
+ return dea;
+ }
+
+ protected void setPersistId(int persistId) {
+ if (enc != null && dec != null) {
+ throw new EncryptedPowerPointFileException("Use instance either for en- or decryption");
+ }
+
+ try {
+ if (enc != null) cipher = enc.initCipherForBlock(cipher, persistId);
+ if (dec != null) cipher = dec.initCipherForBlock(cipher, persistId);
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedPowerPointFileException(e);
+ }
+ }
+
+ protected void decryptInit() {
+ if (dec != null) return;
+ EncryptionInfo ei = dea.getEncryptionInfo();
+ dec = (CryptoAPIDecryptor)ei.getDecryptor();
+ }
+
+ protected void encryptInit() {
+ if (enc != null) return;
+ EncryptionInfo ei = dea.getEncryptionInfo();
+ enc = (CryptoAPIEncryptor)ei.getEncryptor();
+ }
+
+
+
+ protected OutputStream encryptRecord(OutputStream plainStream, int persistId, Record record) {
+ boolean isPlain = (dea == null
+ || record instanceof UserEditAtom
+ || record instanceof PersistPtrHolder
+ || record instanceof DocumentEncryptionAtom
+ );
+ if (isPlain) return plainStream;
+
+ encryptInit();
+ setPersistId(persistId);
+
+ if (cyos == null) {
+ cyos = new CipherOutputStream(plainStream, cipher);
+ }
+ return cyos;
+ }
+
+ protected void decryptRecord(byte[] docstream, int persistId, int offset) {
+ if (dea == null) return;
+
+ decryptInit();
+ setPersistId(persistId);
+
+ try {
+ // decrypt header and read length to be decrypted
+ cipher.update(docstream, offset, 8, docstream, offset);
+ // decrypt the rest of the record
+ int rlen = (int)LittleEndian.getUInt(docstream, offset+4);
+ cipher.update(docstream, offset+8, rlen, docstream, offset+8);
+ } catch (GeneralSecurityException e) {
+ throw new CorruptPowerPointFileException(e);
+ }
+ }
+
+ protected void decryptPicture(byte[] pictstream, int offset) {
+ if (dea == null) return;
+
+ decryptInit();
+ setPersistId(0);
+
+ try {
+ // decrypt header and read length to be decrypted
+ cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ int recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
+ int recType = LittleEndian.getUShort(pictstream, offset+2);
+ int rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
+ offset += 8;
+ int endOffset = offset + rlen;
+
+ if (recType == 0xF007) {
+ // TOOD: get a real example file ... to actual test the FBSE entry
+ // not sure where the foDelay block is
+
+ // File BLIP Store Entry (FBSE)
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btWin32
+ offset++;
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btMacOS
+ offset++;
+ cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid
+ offset += 16;
+ cipher.doFinal(pictstream, offset, 2, pictstream, offset); // tag
+ offset += 2;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // size
+ offset += 4;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // cRef
+ offset += 4;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // foDelay
+ offset += 4;
+ cipher.doFinal(pictstream, offset+0, 1, pictstream, offset+0); // unused1
+ cipher.doFinal(pictstream, offset+1, 1, pictstream, offset+1); // cbName
+ cipher.doFinal(pictstream, offset+2, 1, pictstream, offset+2); // unused2
+ cipher.doFinal(pictstream, offset+3, 1, pictstream, offset+3); // unused3
+ int cbName = LittleEndian.getUShort(pictstream, offset+1);
+ offset += 4;
+ if (cbName > 0) {
+ cipher.doFinal(pictstream, offset, cbName, pictstream, offset); // nameData
+ offset += cbName;
+ }
+ if (offset == endOffset) {
+ return; // no embedded blip
+ }
+ // fall through, read embedded blip now
+
+ // update header data
+ cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
+ recType = LittleEndian.getUShort(pictstream, offset+2);
+ rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
+ offset += 8;
+ }
+
+ int rgbUidCnt = (recInst == 0x217 || recInst == 0x3D5 || recInst == 0x46B || recInst == 0x543 ||
+ recInst == 0x6E1 || recInst == 0x6E3 || recInst == 0x6E5 || recInst == 0x7A9) ? 2 : 1;
+
+ for (int i=0; i<rgbUidCnt; i++) {
+ cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid 1/2
+ offset += 16;
+ }
+
+ if (recType == 0xF01A || recType == 0XF01B || recType == 0XF01C) {
+ cipher.doFinal(pictstream, offset, 34, pictstream, offset); // metafileHeader
+ offset += 34;
+ } else {
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // tag
+ offset += 1;
+ }
+
+ int blipLen = endOffset - offset;
+ cipher.doFinal(pictstream, offset, blipLen, pictstream, offset);
+ } catch (GeneralSecurityException e) {
+ throw new CorruptPowerPointFileException(e);
+ }
+ }
+
+ protected void encryptPicture(byte[] pictstream, int offset) {
+ if (dea == null) return;
+
+ encryptInit();
+ setPersistId(0);
+
+ try {
+ int recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
+ int recType = LittleEndian.getUShort(pictstream, offset+2);
+ int rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
+ cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ offset += 8;
+ int endOffset = offset + rlen;
+
+ if (recType == 0xF007) {
+ // TOOD: get a real example file ... to actual test the FBSE entry
+ // not sure where the foDelay block is
+
+ // File BLIP Store Entry (FBSE)
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btWin32
+ offset++;
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btMacOS
+ offset++;
+ cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid
+ offset += 16;
+ cipher.doFinal(pictstream, offset, 2, pictstream, offset); // tag
+ offset += 2;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // size
+ offset += 4;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // cRef
+ offset += 4;
+ cipher.doFinal(pictstream, offset, 4, pictstream, offset); // foDelay
+ offset += 4;
+ int cbName = LittleEndian.getUShort(pictstream, offset+1);
+ cipher.doFinal(pictstream, offset+0, 1, pictstream, offset+0); // unused1
+ cipher.doFinal(pictstream, offset+1, 1, pictstream, offset+1); // cbName
+ cipher.doFinal(pictstream, offset+2, 1, pictstream, offset+2); // unused2
+ cipher.doFinal(pictstream, offset+3, 1, pictstream, offset+3); // unused3
+ offset += 4;
+ if (cbName > 0) {
+ cipher.doFinal(pictstream, offset, cbName, pictstream, offset); // nameData
+ offset += cbName;
+ }
+ if (offset == endOffset) {
+ return; // no embedded blip
+ }
+ // fall through, read embedded blip now
+
+ // update header data
+ recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
+ recType = LittleEndian.getUShort(pictstream, offset+2);
+ rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
+ cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ offset += 8;
+ }
+
+ int rgbUidCnt = (recInst == 0x217 || recInst == 0x3D5 || recInst == 0x46B || recInst == 0x543 ||
+ recInst == 0x6E1 || recInst == 0x6E3 || recInst == 0x6E5 || recInst == 0x7A9) ? 2 : 1;
+
+ for (int i=0; i<rgbUidCnt; i++) {
+ cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid 1/2
+ offset += 16;
+ }
+
+ if (recType == 0xF01A || recType == 0XF01B || recType == 0XF01C) {
+ cipher.doFinal(pictstream, offset, 34, pictstream, offset); // metafileHeader
+ offset += 34;
+ } else {
+ cipher.doFinal(pictstream, offset, 1, pictstream, offset); // tag
+ offset += 1;
+ }
+
+ int blipLen = endOffset - offset;
+ cipher.doFinal(pictstream, offset, blipLen, pictstream, offset);
+ } catch (GeneralSecurityException e) {
+ throw new CorruptPowerPointFileException(e);
+ }
+ }
+
+ protected Record[] updateEncryptionRecord(Record records[]) {
+ String password = Biff8EncryptionKey.getCurrentUserPassword();
+ if (password == null) {
+ if (dea == null) {
+ // no password given, no encryption record exits -> done
+ return records;
+ } else {
+ // need to remove password data
+ dea = null;
+ return removeEncryptionRecord(records);
+ }
+ } else {
+ // create password record
+ if (dea == null) {
+ dea = new DocumentEncryptionAtom();
+ }
+ EncryptionInfo ei = dea.getEncryptionInfo();
+ byte salt[] = ei.getVerifier().getSalt();
+ Encryptor enc = ei.getEncryptor();
+ if (salt == null) {
+ enc.confirmPassword(password);
+ } else {
+ byte verifier[] = ei.getDecryptor().getVerifier();
+ enc.confirmPassword(password, null, null, verifier, salt, null);
+ }
+
+ // move EncryptionRecord to last slide position
+ records = normalizeRecords(records);
+ return addEncryptionRecord(records, dea);
+ }
+ }
+
+ /**
+ * remove duplicated UserEditAtoms and merge PersistPtrHolder.
+ * Before this method is called, make sure that the offsets are correct,
+ * i.e. call {@link HSLFSlideShow#updateAndWriteDependantRecords(OutputStream, Map)}
+ */
+ protected static Record[] normalizeRecords(Record records[]) {
+ // http://msdn.microsoft.com/en-us/library/office/gg615594(v=office.14).aspx
+ // repeated slideIds can be overwritten, i.e. ignored
+
+ UserEditAtom uea = null;
+ PersistPtrHolder pph = null;
+ TreeMap<Integer,Integer> slideLocations = new TreeMap<Integer,Integer>();
+ TreeMap<Integer,Record> recordMap = new TreeMap<Integer,Record>();
+ List<Integer> obsoleteOffsets = new ArrayList<Integer>();
+ int duplicatedCount = 0;
+ for (Record r : records) {
+ assert(r instanceof PositionDependentRecord);
+ PositionDependentRecord pdr = (PositionDependentRecord)r;
+ if (pdr instanceof UserEditAtom) {
+ uea = (UserEditAtom)pdr;
+ continue;
+ }
+
+ if (pdr instanceof PersistPtrHolder) {
+ if (pph != null) {
+ duplicatedCount++;
+ }
+ pph = (PersistPtrHolder)pdr;
+ for (Map.Entry<Integer,Integer> me : pph.getSlideLocationsLookup().entrySet()) {
+ Integer oldOffset = slideLocations.put(me.getKey(), me.getValue());
+ if (oldOffset != null) obsoleteOffsets.add(oldOffset);
+ }
+ continue;
+ }
+
+ recordMap.put(pdr.getLastOnDiskOffset(), r);
+ }
+ recordMap.put(pph.getLastOnDiskOffset(), pph);
+ recordMap.put(uea.getLastOnDiskOffset(), uea);
+
+ assert(uea != null && pph != null && uea.getPersistPointersOffset() == pph.getLastOnDiskOffset());
+
+ if (duplicatedCount == 0 && obsoleteOffsets.isEmpty()) {
+ return records;
+ }
+
+ uea.setLastUserEditAtomOffset(0);
+ pph.clear();
+ for (Map.Entry<Integer,Integer> me : slideLocations.entrySet()) {
+ pph.addSlideLookup(me.getKey(), me.getValue());
+ }
+
+ for (Integer oldOffset : obsoleteOffsets) {
+ recordMap.remove(oldOffset);
+ }
+
+ return recordMap.values().toArray(new Record[recordMap.size()]);
+ }
+
+
+ protected static Record[] removeEncryptionRecord(Record records[]) {
+ int deaSlideId = -1;
+ int deaOffset = -1;
+ PersistPtrHolder ptr = null;
+ UserEditAtom uea = null;
+ List<Record> recordList = new ArrayList<Record>();
+ for (Record r : records) {
+ if (r instanceof DocumentEncryptionAtom) {
+ deaOffset = ((DocumentEncryptionAtom)r).getLastOnDiskOffset();
+ continue;
+ } else if (r instanceof UserEditAtom) {
+ uea = (UserEditAtom)r;
+ deaSlideId = uea.getEncryptSessionPersistIdRef();
+ uea.setEncryptSessionPersistIdRef(-1);
+ } else if (r instanceof PersistPtrHolder) {
+ ptr = (PersistPtrHolder)r;
+ }
+ recordList.add(r);
+ }
+
+ assert(ptr != null);
+ if (deaSlideId == -1 && deaOffset == -1) return records;
+
+ TreeMap<Integer,Integer> tm = new TreeMap<Integer,Integer>(ptr.getSlideLocationsLookup());
+ ptr.clear();
+ int maxSlideId = -1;
+ for (Map.Entry<Integer,Integer> me : tm.entrySet()) {
+ if (me.getKey() == deaSlideId || me.getValue() == deaOffset) continue;
+ ptr.addSlideLookup(me.getKey(), me.getValue());
+ maxSlideId = Math.max(me.getKey(), maxSlideId);
+ }
+
+ uea.setMaxPersistWritten(maxSlideId);
+
+ records = recordList.toArray(new Record[recordList.size()]);
+
+ return records;
+ }
+
+
+ protected static Record[] addEncryptionRecord(Record records[], DocumentEncryptionAtom dea) {
+ assert(dea != null);
+ int ueaIdx = -1, ptrIdx = -1, deaIdx = -1, idx = -1;
+ for (Record r : records) {
+ idx++;
+ if (r instanceof UserEditAtom) ueaIdx = idx;
+ else if (r instanceof PersistPtrHolder) ptrIdx = idx;
+ else if (r instanceof DocumentEncryptionAtom) deaIdx = idx;
+ }
+ assert(ueaIdx != -1 && ptrIdx != -1 && ptrIdx < ueaIdx);
+ if (deaIdx != -1) {
+ DocumentEncryptionAtom deaOld = (DocumentEncryptionAtom)records[deaIdx];
+ dea.setLastOnDiskOffset(deaOld.getLastOnDiskOffset());
+ records[deaIdx] = dea;
+ return records;
+ } else {
+ PersistPtrHolder ptr = (PersistPtrHolder)records[ptrIdx];
+ UserEditAtom uea = ((UserEditAtom)records[ueaIdx]);
+ dea.setLastOnDiskOffset(ptr.getLastOnDiskOffset()-1);
+ int nextSlideId = uea.getMaxPersistWritten()+1;
+ ptr.addSlideLookup(nextSlideId, ptr.getLastOnDiskOffset()-1);
+ uea.setEncryptSessionPersistIdRef(nextSlideId);
+ uea.setMaxPersistWritten(nextSlideId);
+
+ Record newRecords[] = new Record[records.length+1];
+ if (ptrIdx > 0) System.arraycopy(records, 0, newRecords, 0, ptrIdx);
+ if (ptrIdx < records.length-1) System.arraycopy(records, ptrIdx, newRecords, ptrIdx+1, records.length-ptrIdx);
+ newRecords[ptrIdx] = dea;
+ return newRecords;
+ }
+ }
-public final class EncryptedSlideShow
-{
- /**
- * Check to see if a HSLFSlideShow represents an encrypted
- * PowerPoint document, or not
- * @param hss The HSLFSlideShow to check
- * @return true if encrypted, otherwise false
- */
- public static boolean checkIfEncrypted(HSLFSlideShow hss) {
- // Easy way to check - contains a stream
- // "EncryptedSummary"
- try {
- hss.getPOIFSDirectory().getEntry("EncryptedSummary");
- return true;
- } catch(FileNotFoundException fnfe) {
- // Doesn't have encrypted properties
- }
-
- // If they encrypted the document but not the properties,
- // it's harder.
- // We need to see what the last record pointed to by the
- // first PersistPrtHolder is - if it's a
- // DocumentEncryptionAtom, then the file's Encrypted
- DocumentEncryptionAtom dea = fetchDocumentEncryptionAtom(hss);
- if(dea != null) {
- return true;
- }
- return false;
- }
-
- /**
- * Return the DocumentEncryptionAtom for a HSLFSlideShow, or
- * null if there isn't one.
- * @return a DocumentEncryptionAtom, or null if there isn't one
- */
- public static DocumentEncryptionAtom fetchDocumentEncryptionAtom(HSLFSlideShow hss) {
- // Will be the last Record pointed to by the
- // first PersistPrtHolder, if there is one
-
- CurrentUserAtom cua = hss.getCurrentUserAtom();
- if(cua.getCurrentEditOffset() != 0) {
- // Check it's not past the end of the file
- if(cua.getCurrentEditOffset() > hss.getUnderlyingBytes().length) {
- throw new CorruptPowerPointFileException("The CurrentUserAtom claims that the offset of last edit details are past the end of the file");
- }
-
- // Grab the details of the UserEditAtom there
- // If the record's messed up, we could AIOOB
- Record r = null;
- try {
- r = Record.buildRecordAtOffset(
- hss.getUnderlyingBytes(),
- (int)cua.getCurrentEditOffset()
- );
- } catch (ArrayIndexOutOfBoundsException e) {
- return null;
- }
- if(r == null) { return null; }
- if(! (r instanceof UserEditAtom)) { return null; }
- UserEditAtom uea = (UserEditAtom)r;
-
- // Now get the PersistPtrHolder
- Record r2 = Record.buildRecordAtOffset(
- hss.getUnderlyingBytes(),
- uea.getPersistPointersOffset()
- );
- if(! (r2 instanceof PersistPtrHolder)) { return null; }
- PersistPtrHolder pph = (PersistPtrHolder)r2;
-
- // Now get the last record
- int[] slideIds = pph.getKnownSlideIDs();
- int maxSlideId = -1;
- for(int i=0; i<slideIds.length; i++) {
- if(slideIds[i] > maxSlideId) { maxSlideId = slideIds[i]; }
- }
- if(maxSlideId == -1) { return null; }
-
- int offset = (
- (Integer)pph.getSlideLocationsLookup().get(
- Integer.valueOf(maxSlideId)
- ) ).intValue();
- Record r3 = Record.buildRecordAtOffset(
- hss.getUnderlyingBytes(),
- offset
- );
-
- // If we have a DocumentEncryptionAtom, it'll be this one
- if(r3 instanceof DocumentEncryptionAtom) {
- return (DocumentEncryptionAtom)r3;
- }
- }
-
- return null;
- }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java Thu Dec 25 01:56:29 2014
@@ -23,6 +23,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
@@ -32,10 +33,11 @@ import java.util.NavigableMap;
import java.util.TreeMap;
import org.apache.poi.POIDocument;
+import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
-import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.record.CurrentUserAtom;
+import org.apache.poi.hslf.record.DocumentEncryptionAtom;
import org.apache.poi.hslf.record.ExOleObjStg;
import org.apache.poi.hslf.record.PersistPtrHolder;
import org.apache.poi.hslf.record.PersistRecord;
@@ -45,6 +47,7 @@ import org.apache.poi.hslf.record.Record
import org.apache.poi.hslf.record.UserEditAtom;
import org.apache.poi.hslf.usermodel.ObjectData;
import org.apache.poi.hslf.usermodel.PictureData;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
@@ -182,13 +185,6 @@ public final class HSLFSlideShow extends
// PowerPoint stream
readPowerPointStream();
- // Check to see if we have an encrypted document,
- // bailing out if we do
- boolean encrypted = EncryptedSlideShow.checkIfEncrypted(this);
- if(encrypted) {
- throw new EncryptedPowerPointFileException("Encrypted PowerPoint files are not supported");
- }
-
// Now, build records based on the PowerPoint stream
buildRecords();
@@ -278,6 +274,7 @@ public final class HSLFSlideShow extends
NavigableMap<Integer,Record> records = new TreeMap<Integer,Record>(); // offset -> record
Map<Integer,Integer> persistIds = new HashMap<Integer,Integer>(); // offset -> persistId
initRecordOffsets(docstream, usrOffset, records, persistIds);
+ EncryptedSlideShow decryptData = new EncryptedSlideShow(docstream, records);
for (Map.Entry<Integer,Record> entry : records.entrySet()) {
Integer offset = entry.getKey();
@@ -286,6 +283,7 @@ public final class HSLFSlideShow extends
if (record == null) {
// all plain records have been already added,
// only new records need to be decrypted (tbd #35897)
+ decryptData.decryptRecord(docstream, persistId, offset);
record = Record.buildRecordAtOffset(docstream, offset);
entry.setValue(record);
}
@@ -335,6 +333,16 @@ public final class HSLFSlideShow extends
}
}
+ public DocumentEncryptionAtom getDocumentEncryptionAtom() {
+ for (Record r : _records) {
+ if (r instanceof DocumentEncryptionAtom) {
+ return (DocumentEncryptionAtom)r;
+ }
+ }
+ return null;
+ }
+
+
/**
* Find the "Current User" stream, and load it
*/
@@ -353,6 +361,7 @@ public final class HSLFSlideShow extends
private void readOtherStreams() {
// Currently, there aren't any
}
+
/**
* Find and read in pictures contained in this presentation.
* This is lazily called as and when we want to touch pictures.
@@ -363,6 +372,8 @@ public final class HSLFSlideShow extends
// if the presentation doesn't contain pictures - will use a null set instead
if (!directory.hasEntry("Pictures")) return;
+
+ EncryptedSlideShow decryptData = new EncryptedSlideShow(getDocumentEncryptionAtom());
DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
byte[] pictstream = new byte[entry.getSize()];
@@ -375,6 +386,8 @@ public final class HSLFSlideShow extends
// An empty picture record (length 0) will take up 8 bytes
while (pos <= (pictstream.length-8)) {
int offset = pos;
+
+ decryptData.decryptPicture(pictstream, offset);
// Image signature
int signature = LittleEndian.getUShort(pictstream, pos);
@@ -422,7 +435,21 @@ public final class HSLFSlideShow extends
pos += imgsize;
}
}
-
+
+ /**
+ * remove duplicated UserEditAtoms and merge PersistPtrHolder, i.e.
+ * remove document edit history
+ */
+ public void normalizeRecords() {
+ try {
+ updateAndWriteDependantRecords(null, null);
+ } catch (IOException e) {
+ throw new CorruptPowerPointFileException(e);
+ }
+ _records = EncryptedSlideShow.normalizeRecords(_records);
+ }
+
+
/**
* This is a helper functions, which is needed for adding new position dependent records
* or finally write the slideshow to a file.
@@ -444,55 +471,67 @@ public final class HSLFSlideShow extends
// records are going to end up, in the new scheme
// (Annoyingly, some powerpoint files have PersistPtrHolders
// that reference slides after the PersistPtrHolder)
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ UserEditAtom usr = null;
+ PersistPtrHolder ptr = null;
+ CountingOS cos = new CountingOS();
for (Record record : _records) {
- if(record instanceof PositionDependentRecord) {
- PositionDependentRecord pdr = (PositionDependentRecord)record;
- int oldPos = pdr.getLastOnDiskOffset();
- int newPos = baos.size();
- pdr.setLastOnDiskOffset(newPos);
- if (oldPos != UNSET_OFFSET) {
- // new records don't need a mapping, as they aren't in a relation yet
- oldToNewPositions.put(Integer.valueOf(oldPos),Integer.valueOf(newPos));
- }
+ // all top level records are position dependent
+ assert(record instanceof PositionDependentRecord);
+ PositionDependentRecord pdr = (PositionDependentRecord)record;
+ int oldPos = pdr.getLastOnDiskOffset();
+ int newPos = cos.size();
+ pdr.setLastOnDiskOffset(newPos);
+ if (oldPos != UNSET_OFFSET) {
+ // new records don't need a mapping, as they aren't in a relation yet
+ oldToNewPositions.put(oldPos,newPos);
+ }
+
+ // Grab interesting records as they come past
+ // this will only save the very last record of each type
+ RecordTypes.Type saveme = null;
+ int recordType = (int)record.getRecordType();
+ if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) {
+ saveme = RecordTypes.PersistPtrIncrementalBlock;
+ ptr = (PersistPtrHolder)pdr;
+ } else if (recordType == RecordTypes.UserEditAtom.typeID) {
+ saveme = RecordTypes.UserEditAtom;
+ usr = (UserEditAtom)pdr;
+ }
+ if (interestingRecords != null && saveme != null) {
+ interestingRecords.put(saveme,pdr);
}
// Dummy write out, so the position winds on properly
- record.writeOut(baos);
+ record.writeOut(cos);
}
- baos = null;
- // For now, we're only handling PositionDependentRecord's that
- // happen at the top level.
- // In future, we'll need the handle them everywhere, but that's
- // a bit trickier
- UserEditAtom usr = null;
- for (Record record : _records) {
- if (record instanceof PositionDependentRecord) {
- // We've already figured out their new location, and
- // told them that
- // Tell them of the positions of the other records though
- PositionDependentRecord pdr = (PositionDependentRecord)record;
- pdr.updateOtherRecordReferences(oldToNewPositions);
-
- // Grab interesting records as they come past
- // this will only save the very last record of each type
- RecordTypes.Type saveme = null;
- int recordType = (int)record.getRecordType();
- if (recordType == RecordTypes.PersistPtrIncrementalBlock.typeID) {
- saveme = RecordTypes.PersistPtrIncrementalBlock;
- } else if (recordType == RecordTypes.UserEditAtom.typeID) {
- saveme = RecordTypes.UserEditAtom;
- usr = (UserEditAtom)pdr;
- }
- if (interestingRecords != null && saveme != null) {
- interestingRecords.put(saveme,pdr);
- }
- }
+ assert(usr != null && ptr != null);
+
+ Map<Integer,Integer> persistIds = new HashMap<Integer,Integer>();
+ for (Map.Entry<Integer,Integer> entry : ptr.getSlideLocationsLookup().entrySet()) {
+ persistIds.put(oldToNewPositions.get(entry.getValue()), entry.getKey());
+ }
+
+ EncryptedSlideShow encData = new EncryptedSlideShow(getDocumentEncryptionAtom());
+
+ for (Record record : _records) {
+ assert(record instanceof PositionDependentRecord);
+ // We've already figured out their new location, and
+ // told them that
+ // Tell them of the positions of the other records though
+ PositionDependentRecord pdr = (PositionDependentRecord)record;
+ Integer persistId = persistIds.get(pdr.getLastOnDiskOffset());
+ if (persistId == null) persistId = 0;
+
+ // For now, we're only handling PositionDependentRecord's that
+ // happen at the top level.
+ // In future, we'll need the handle them everywhere, but that's
+ // a bit trickier
+ pdr.updateOtherRecordReferences(oldToNewPositions);
// Whatever happens, write out that record tree
if (os != null) {
- record.writeOut(os);
+ record.writeOut(encData.encryptRecord(os, persistId, record));
}
}
@@ -504,7 +543,7 @@ public final class HSLFSlideShow extends
}
currentUser.setCurrentEditOffset(usr.getLastOnDiskOffset());
}
-
+
/**
* Writes out the slideshow file the is represented by an instance
* of this class.
@@ -529,6 +568,16 @@ public final class HSLFSlideShow extends
* the passed in OutputStream
*/
public void write(OutputStream out, boolean preserveNodes) throws IOException {
+ // read properties and pictures, with old encryption settings where appropriate
+ if(_pictures == null) {
+ readPictures();
+ }
+ getDocumentSummaryInformation();
+
+ // set new encryption settings
+ EncryptedSlideShow encryptedSS = new EncryptedSlideShow(getDocumentEncryptionAtom());
+ _records = encryptedSS.updateEncryptionRecord(_records);
+
// Get a new Filesystem to write into
POIFSFileSystem outFS = new POIFSFileSystem();
@@ -537,8 +586,8 @@ public final class HSLFSlideShow extends
// Write out the Property Streams
writeProperties(outFS, writtenEntries);
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ BufAccessBAOS baos = new BufAccessBAOS();
// For position dependent records, hold where they were and now are
// As we go along, update, and hand over, to any Position Dependent
@@ -546,27 +595,28 @@ public final class HSLFSlideShow extends
updateAndWriteDependantRecords(baos, null);
// Update our cached copy of the bytes that make up the PPT stream
- _docstream = baos.toByteArray();
+ _docstream = new byte[baos.size()];
+ System.arraycopy(baos.getBuf(), 0, _docstream, 0, baos.size());
// Write the PPT stream into the POIFS layer
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ByteArrayInputStream bais = new ByteArrayInputStream(_docstream);
outFS.createDocument(bais,"PowerPoint Document");
writtenEntries.add("PowerPoint Document");
+
+ currentUser.setEncrypted(encryptedSS.getDocumentEncryptionAtom() != null);
currentUser.writeToFS(outFS);
writtenEntries.add("Current User");
- // Write any pictures, into another stream
- if(_pictures == null) {
- readPictures();
- }
if (_pictures.size() > 0) {
- ByteArrayOutputStream pict = new ByteArrayOutputStream();
+ BufAccessBAOS pict = new BufAccessBAOS();
for (PictureData p : _pictures) {
+ int offset = pict.size();
p.write(pict);
+ encryptedSS.encryptPicture(pict.getBuf(), offset);
}
outFS.createDocument(
- new ByteArrayInputStream(pict.toByteArray()), "Pictures"
+ new ByteArrayInputStream(pict.getBuf(), 0, pict.size()), "Pictures"
);
writtenEntries.add("Pictures");
}
@@ -580,8 +630,44 @@ public final class HSLFSlideShow extends
outFS.writeFilesystem(out);
}
+ /**
+ * For a given named property entry, either return it or null if
+ * if it wasn't found
+ *
+ * @param setName The property to read
+ * @return The value of the given property or null if it wasn't found.
+ */
+ protected PropertySet getPropertySet(String setName) {
+ DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
+ return (dea == null)
+ ? super.getPropertySet(setName)
+ : super.getPropertySet(setName, dea.getEncryptionInfo());
+ }
- /* ******************* adding methods follow ********************* */
+ /**
+ * Writes out the standard Documment Information Properties (HPSF)
+ * @param outFS the POIFSFileSystem to write the properties into
+ * @param writtenEntries a list of POIFS entries to add the property names too
+ *
+ * @throws IOException if an error when writing to the
+ * {@link POIFSFileSystem} occurs
+ */
+ protected void writeProperties(POIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
+ super.writeProperties(outFS, writtenEntries);
+ DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
+ if (dea != null) {
+ CryptoAPIEncryptor enc = (CryptoAPIEncryptor)dea.getEncryptionInfo().getEncryptor();
+ try {
+ enc.getDataStream(outFS.getRoot()); // ignore OutputStream
+ } catch (IOException e) {
+ throw e;
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ /* ******************* adding methods follow ********************* */
/**
* Adds a new root level record, at the end, but before the last
@@ -688,4 +774,30 @@ public final class HSLFSlideShow extends
}
return _objects;
}
+
+
+ private static class BufAccessBAOS extends ByteArrayOutputStream {
+ public byte[] getBuf() {
+ return buf;
+ }
+ }
+
+ private static class CountingOS extends OutputStream {
+ int count = 0;
+ public void write(int b) throws IOException {
+ count++;
+ }
+
+ public void write(byte[] b) throws IOException {
+ count += b.length;
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ count += len;
+ }
+
+ public int size() {
+ return count;
+ }
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/SlideIdListing.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/SlideIdListing.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/SlideIdListing.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/SlideIdListing.java Thu Dec 25 01:56:29 2014
@@ -17,15 +17,23 @@
package org.apache.poi.hslf.dev;
-import org.apache.poi.hslf.*;
-import org.apache.poi.hslf.record.*;
-import org.apache.poi.hslf.usermodel.SlideShow;
+import java.io.ByteArrayOutputStream;
+import java.util.Map;
+import org.apache.poi.hslf.HSLFSlideShow;
+import org.apache.poi.hslf.record.Document;
+import org.apache.poi.hslf.record.Notes;
+import org.apache.poi.hslf.record.NotesAtom;
+import org.apache.poi.hslf.record.PersistPtrHolder;
+import org.apache.poi.hslf.record.PositionDependentRecord;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.Slide;
+import org.apache.poi.hslf.record.SlideAtom;
+import org.apache.poi.hslf.record.SlideListWithText;
+import org.apache.poi.hslf.record.SlidePersistAtom;
+import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.util.LittleEndian;
-import java.io.*;
-import java.util.Hashtable;
-
/**
* Gets all the different things that have Slide IDs (of sorts)
* in them, and displays them, so you can try to guess what they
@@ -122,10 +130,10 @@ public final class SlideIdListing {
// Check the sheet offsets
int[] sheetIDs = pph.getKnownSlideIDs();
- Hashtable sheetOffsets = pph.getSlideLocationsLookup();
+ Map<Integer,Integer> sheetOffsets = pph.getSlideLocationsLookup();
for(int j=0; j<sheetIDs.length; j++) {
- Integer id = Integer.valueOf(sheetIDs[j]);
- Integer offset = (Integer)sheetOffsets.get(id);
+ Integer id = sheetIDs[j];
+ Integer offset = sheetOffsets.get(id);
System.out.println(" Knows about sheet " + id);
System.out.println(" That sheet lives at " + offset);
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/dev/UserEditAndPersistListing.java Thu Dec 25 01:56:29 2014
@@ -18,10 +18,14 @@
package org.apache.poi.hslf.dev;
import java.io.ByteArrayOutputStream;
-import java.util.Hashtable;
+import java.util.Map;
import org.apache.poi.hslf.HSLFSlideShow;
-import org.apache.poi.hslf.record.*;
+import org.apache.poi.hslf.record.CurrentUserAtom;
+import org.apache.poi.hslf.record.PersistPtrHolder;
+import org.apache.poi.hslf.record.PositionDependentRecord;
+import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.UserEditAtom;
import org.apache.poi.util.LittleEndian;
/**
@@ -61,10 +65,10 @@ public final class UserEditAndPersistLis
// Check the sheet offsets
int[] sheetIDs = pph.getKnownSlideIDs();
- Hashtable sheetOffsets = pph.getSlideLocationsLookup();
+ Map<Integer,Integer> sheetOffsets = pph.getSlideLocationsLookup();
for(int j=0; j<sheetIDs.length; j++) {
- Integer id = Integer.valueOf(sheetIDs[j]);
- Integer offset = (Integer)sheetOffsets.get(id);
+ Integer id = sheetIDs[j];
+ Integer offset = sheetOffsets.get(id);
System.out.println(" Knows about sheet " + id);
System.out.println(" That sheet lives at " + offset);
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/CorruptPowerPointFileException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/CorruptPowerPointFileException.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/CorruptPowerPointFileException.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/CorruptPowerPointFileException.java Thu Dec 25 01:56:29 2014
@@ -29,4 +29,12 @@ public final class CorruptPowerPointFile
public CorruptPowerPointFileException(String s) {
super(s);
}
+
+ public CorruptPowerPointFileException(String s, Throwable t) {
+ super(s,t);
+ }
+
+ public CorruptPowerPointFileException(Throwable t) {
+ super(t);
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/EncryptedPowerPointFileException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/EncryptedPowerPointFileException.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/EncryptedPowerPointFileException.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/exceptions/EncryptedPowerPointFileException.java Thu Dec 25 01:56:29 2014
@@ -28,4 +28,12 @@ public final class EncryptedPowerPointFi
public EncryptedPowerPointFileException(String s) {
super(s);
}
+
+ public EncryptedPowerPointFileException(String s, Throwable t) {
+ super(s, t);
+ }
+
+ public EncryptedPowerPointFileException(Throwable t) {
+ super(t);
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java Thu Dec 25 01:56:29 2014
@@ -20,15 +20,21 @@
package org.apache.poi.hslf.record;
-import java.io.*;
-import org.apache.poi.poifs.filesystem.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
+import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.StringUtil;
-import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
-import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
-import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
/**
@@ -47,7 +53,7 @@ public class CurrentUserAtom
public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
/** The PowerPoint magic number for a non-encrypted file */
public static final byte[] headerToken = new byte[] { 95, -64, -111, -29 };
- /** The PowerPoint magic number for an encrytpted file */
+ /** The PowerPoint magic number for an encrypted file */
public static final byte[] encHeaderToken = new byte[] { -33, -60, -47, -13 };
/** The Powerpoint 97 version, major and minor numbers */
public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
@@ -66,6 +72,9 @@ public class CurrentUserAtom
/** Only correct after reading in or writing out */
private byte[] _contents;
+
+ /** Flag for encryption state of the whole file */
+ private boolean isEncrypted;
/* ********************* getter/setter follows *********************** */
@@ -84,6 +93,9 @@ public class CurrentUserAtom
public String getLastEditUsername() { return lastEditUser; }
public void setLastEditUsername(String u) { lastEditUser = u; }
+ public boolean isEncrypted() { return isEncrypted; }
+ public void setEncrypted(boolean isEncrypted) { this.isEncrypted = isEncrypted; }
+
/* ********************* real code follows *************************** */
@@ -100,6 +112,7 @@ public class CurrentUserAtom
releaseVersion = 8;
currentEditOffset = 0;
lastEditUser = "Apache POI";
+ isEncrypted = false;
}
/**
@@ -157,14 +170,10 @@ public class CurrentUserAtom
*/
private void init() {
// First up is the size, in 4 bytes, which is fixed
- // Then is the header - check for encrypted
- if(_contents[12] == encHeaderToken[0] &&
- _contents[13] == encHeaderToken[1] &&
- _contents[14] == encHeaderToken[2] &&
- _contents[15] == encHeaderToken[3]) {
- throw new EncryptedPowerPointFileException("The CurrentUserAtom specifies that the document is encrypted");
- }
+ // Then is the header
+ isEncrypted = (LittleEndian.getInt(encHeaderToken) == LittleEndian.getInt(_contents,12));
+
// Grab the edit offset
currentEditOffset = LittleEndian.getUInt(_contents,16);
@@ -229,7 +238,7 @@ public class CurrentUserAtom
LittleEndian.putInt(_contents,8,20);
// Now the ppt un-encrypted header token (4 bytes)
- System.arraycopy(headerToken,0,_contents,12,4);
+ System.arraycopy((isEncrypted ? encHeaderToken : headerToken),0,_contents,12,4);
// Now the current edit offset
LittleEndian.putInt(_contents,16,(int)currentEditOffset);
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java Thu Dec 25 01:56:29 2014
@@ -17,10 +17,20 @@
package org.apache.poi.hslf.record;
-import org.apache.poi.util.StringUtil;
-
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Hashtable;
+
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionVerifier;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianInputStream;
/**
* A Document Encryption Atom (type 12052). Holds information
@@ -28,56 +38,64 @@ import java.io.OutputStream;
*
* @author Nick Burch
*/
-public final class DocumentEncryptionAtom extends RecordAtom {
+public final class DocumentEncryptionAtom extends PositionDependentRecordAtom {
+ private static long _type = 12052l;
private byte[] _header;
- private static long _type = 12052l;
-
- private byte[] data;
- private String encryptionProviderName;
+ private EncryptionInfo ei;
/**
* For the Document Encryption Atom
*/
- protected DocumentEncryptionAtom(byte[] source, int start, int len) {
+ protected DocumentEncryptionAtom(byte[] source, int start, int len) throws IOException {
// Get the header
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
- // Grab everything else, for now
- data = new byte[len-8];
- System.arraycopy(source, start+8, data, 0, len-8);
-
- // Grab the provider, from byte 8+44 onwards
- // It's a null terminated Little Endian String
- int endPos = -1;
- int pos = start + 8+44;
- while(pos < (start+len) && endPos < 0) {
- if(source[pos] == 0 && source[pos+1] == 0) {
- // Hit the end
- endPos = pos;
- }
- pos += 2;
- }
- pos = start + 8+44;
- int stringLen = (endPos-pos) / 2;
- encryptionProviderName = StringUtil.getFromUnicodeLE(source, pos, stringLen);
+ ByteArrayInputStream bis = new ByteArrayInputStream(source, start+8, len-8);
+ LittleEndianInputStream leis = new LittleEndianInputStream(bis);
+ ei = new EncryptionInfo(leis, true);
}
+ public DocumentEncryptionAtom() {
+ _header = new byte[8];
+ LittleEndian.putShort(_header, 0, (short)0x000F);
+ LittleEndian.putShort(_header, 2, (short)_type);
+ // record length not yet known ...
+
+ ei = new EncryptionInfo(EncryptionMode.cryptoAPI);
+ }
+
+ /**
+ * Initializes the encryption settings
+ *
+ * @param keyBits see {@link CipherAlgorithm#rc4} for allowed values, use -1 for default size
+ */
+ public void initializeEncryptionInfo(int keyBits) {
+ ei = new EncryptionInfo(EncryptionMode.cryptoAPI, CipherAlgorithm.rc4, HashAlgorithm.sha1, keyBits, -1, null);
+ }
+
/**
* Return the length of the encryption key, in bits
*/
public int getKeyLength() {
- return data[28];
+ return ei.getHeader().getKeySize();
}
/**
* Return the name of the encryption provider used
*/
public String getEncryptionProviderName() {
- return encryptionProviderName;
+ return ei.getHeader().getCspName();
}
-
+ /**
+ * @return the {@link EncryptionInfo} object for details about encryption settings
+ */
+ public EncryptionInfo getEncryptionInfo() {
+ return ei;
+ }
+
+
/**
* We are of type 12052
*/
@@ -88,10 +106,24 @@ public final class DocumentEncryptionAto
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
- // Header
- out.write(_header);
// Data
- out.write(data);
+ byte data[] = new byte[1024];
+ LittleEndianByteArrayOutputStream bos = new LittleEndianByteArrayOutputStream(data, 0);
+ bos.writeShort(ei.getVersionMajor());
+ bos.writeShort(ei.getVersionMinor());
+ bos.writeInt(ei.getEncryptionFlags());
+
+ ((CryptoAPIEncryptionHeader)ei.getHeader()).write(bos);
+ ((CryptoAPIEncryptionVerifier)ei.getVerifier()).write(bos);
+
+ // Header
+ LittleEndian.putInt(_header, 4, bos.getWriteIndex());
+ out.write(_header);
+ out.write(data, 0, bos.getWriteIndex());
}
+
+ public void updateOtherRecordReferences(Hashtable<Integer,Integer> oldToNewReferencesLookup) {
+
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java Thu Dec 25 01:56:29 2014
@@ -17,13 +17,17 @@
package org.apache.poi.hslf.record;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.POILogger;
-
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogger;
/**
* General holder for PersistPtrFullBlock and PersistPtrIncrementalBlock
@@ -49,12 +53,14 @@ public final class PersistPtrHolder exte
* that knows about a given slide to find the right location
*/
private Hashtable<Integer,Integer> _slideLocations;
- /**
- * Holds the lookup from slide id to where their offset is
- * held inside _ptrData. Used when writing out, and updating
- * the positions of the slides
- */
- private Hashtable<Integer,Integer> _slideOffsetDataLocation;
+
+ private static final BitField persistIdFld = new BitField(0X000FFFFF);
+ private static final BitField cntPersistFld = new BitField(0XFFF00000);
+
+ /**
+ * Return the value we were given at creation, be it 6001 or 6002
+ */
+ public long getRecordType() { return _type; }
/**
* Get the list of slides that this PersistPtrHolder knows about.
@@ -63,10 +69,9 @@ public final class PersistPtrHolder exte
*/
public int[] getKnownSlideIDs() {
int[] ids = new int[_slideLocations.size()];
- Enumeration<Integer> e = _slideLocations.keys();
- for(int i=0; i<ids.length; i++) {
- Integer id = e.nextElement();
- ids[i] = id.intValue();
+ int i = 0;
+ for (Integer slideId : _slideLocations.keySet()) {
+ ids[i++] = slideId;
}
return ids;
}
@@ -78,46 +83,16 @@ public final class PersistPtrHolder exte
public Hashtable<Integer,Integer> getSlideLocationsLookup() {
return _slideLocations;
}
+
/**
* Get the lookup from slide numbers to their offsets inside
* _ptrData, used when adding or moving slides.
+ *
+ * @deprecated since POI 3.11, not supported anymore
*/
+ @Deprecated
public Hashtable<Integer,Integer> getSlideOffsetDataLocationsLookup() {
- return _slideOffsetDataLocation;
- }
-
- /**
- * Adds a new slide, notes or similar, to be looked up by this.
- * For now, won't look for the most optimal on disk representation.
- */
- public void addSlideLookup(int slideID, int posOnDisk) {
- // PtrData grows by 8 bytes:
- // 4 bytes for the new info block
- // 4 bytes for the slide offset
- byte[] newPtrData = new byte[_ptrData.length + 8];
- System.arraycopy(_ptrData,0,newPtrData,0,_ptrData.length);
-
- // Add to the slide location lookup hash
- _slideLocations.put(Integer.valueOf(slideID), Integer.valueOf(posOnDisk));
- // Add to the ptrData offset lookup hash
- _slideOffsetDataLocation.put(Integer.valueOf(slideID),
- Integer.valueOf(_ptrData.length + 4));
-
- // Build the info block
- // First 20 bits = offset number = slide ID
- // Remaining 12 bits = offset count = 1
- int infoBlock = slideID;
- infoBlock += (1 << 20);
-
- // Write out the data for this
- LittleEndian.putInt(newPtrData,newPtrData.length-8,infoBlock);
- LittleEndian.putInt(newPtrData,newPtrData.length-4,posOnDisk);
-
- // Save the new ptr data
- _ptrData = newPtrData;
-
- // Update the atom header
- LittleEndian.putInt(_header,4,newPtrData.length);
+ throw new UnsupportedOperationException("PersistPtrHolder.getSlideOffsetDataLocationsLookup() is not supported since 3.12-Beta1");
}
/**
@@ -141,30 +116,27 @@ public final class PersistPtrHolder exte
// count * 32 bit offsets
// Repeat as many times as you have data
_slideLocations = new Hashtable<Integer,Integer>();
- _slideOffsetDataLocation = new Hashtable<Integer,Integer>();
_ptrData = new byte[len-8];
System.arraycopy(source,start+8,_ptrData,0,_ptrData.length);
int pos = 0;
while(pos < _ptrData.length) {
- // Grab the info field
- long info = LittleEndian.getUInt(_ptrData,pos);
+ // Grab the info field
+ int info = LittleEndian.getInt(_ptrData,pos);
// First 20 bits = offset number
// Remaining 12 bits = offset count
- int offset_count = (int)(info >> 20);
- int offset_no = (int)(info - (offset_count << 20));
-//System.out.println("Info is " + info + ", count is " + offset_count + ", number is " + offset_no);
-
+ int offset_no = persistIdFld.getValue(info);
+ int offset_count = cntPersistFld.getValue(info);
+
// Wind on by the 4 byte info header
pos += 4;
// Grab the offsets for each of the sheets
for(int i=0; i<offset_count; i++) {
int sheet_no = offset_no + i;
- long sheet_offset = LittleEndian.getUInt(_ptrData,pos);
- _slideLocations.put(Integer.valueOf(sheet_no), Integer.valueOf((int)sheet_offset));
- _slideOffsetDataLocation.put(Integer.valueOf(sheet_no), Integer.valueOf(pos));
+ int sheet_offset = (int)LittleEndian.getUInt(_ptrData,pos);
+ _slideLocations.put(sheet_no, sheet_offset);
// Wind on by 4 bytes per sheet found
pos += 4;
@@ -172,48 +144,108 @@ public final class PersistPtrHolder exte
}
}
- /**
- * Return the value we were given at creation, be it 6001 or 6002
- */
- public long getRecordType() { return _type; }
+ /**
+ * remove all slide references
+ *
+ * Convenience method provided, for easier reviewing of invocations
+ */
+ public void clear() {
+ _slideLocations.clear();
+ }
+
+ /**
+ * Adds a new slide, notes or similar, to be looked up by this.
+ */
+ public void addSlideLookup(int slideID, int posOnDisk) {
+ if (_slideLocations.containsKey(slideID)) {
+ throw new CorruptPowerPointFileException("A record with persistId "+slideID+" already exists.");
+ }
+
+ _slideLocations.put(slideID, posOnDisk);
+ }
/**
* At write-out time, update the references to the sheets to their
* new positions
*/
public void updateOtherRecordReferences(Hashtable<Integer,Integer> oldToNewReferencesLookup) {
- int[] slideIDs = getKnownSlideIDs();
-
// Loop over all the slides we know about
// Find where they used to live, and where they now live
- // Then, update the right bit of _ptrData with their new location
- for(int i=0; i<slideIDs.length; i++) {
- Integer id = Integer.valueOf(slideIDs[i]);
- Integer oldPos = (Integer)_slideLocations.get(id);
- Integer newPos = (Integer)oldToNewReferencesLookup.get(oldPos);
-
- if(newPos == null) {
- logger.log(POILogger.WARN, "Couldn't find the new location of the \"slide\" with id " + id + " that used to be at " + oldPos);
- logger.log(POILogger.WARN, "Not updating the position of it, you probably won't be able to find it any more (if you ever could!)");
- newPos = oldPos;
- }
+ for (Map.Entry<Integer,Integer> me : _slideLocations.entrySet()) {
+ Integer oldPos = me.getValue();
+ Integer newPos = oldToNewReferencesLookup.get(oldPos);
+
+ if (newPos == null) {
+ Integer id = me.getKey();
+ logger.log(POILogger.WARN, "Couldn't find the new location of the \"slide\" with id " + id + " that used to be at " + oldPos);
+ logger.log(POILogger.WARN, "Not updating the position of it, you probably won't be able to find it any more (if you ever could!)");
+ } else {
+ me.setValue(newPos);
+ }
+ }
+ }
+
+ private void normalizePersistDirectory() {
+ TreeMap<Integer,Integer> orderedSlideLocations = new TreeMap<Integer,Integer>(_slideLocations);
+
+ @SuppressWarnings("resource")
+ BufAccessBAOS bos = new BufAccessBAOS();
+ byte intbuf[] = new byte[4];
+ int lastPersistEntry = -1;
+ int lastSlideId = -1;
+ for (Map.Entry<Integer,Integer> me : orderedSlideLocations.entrySet()) {
+ int nextSlideId = me.getKey();
+ int offset = me.getValue();
+ try {
+ // Building the info block
+ // First 20 bits = offset number = slide ID (persistIdFld, i.e. first slide ID of a continuous group)
+ // Remaining 12 bits = offset count = 1 (cntPersistFld, i.e. continuous entries in a group)
+
+ if (lastSlideId+1 == nextSlideId) {
+ // use existing PersistDirectoryEntry, need to increase entry count
+ assert(lastPersistEntry != -1);
+ int infoBlock = LittleEndian.getInt(bos.getBuf(), lastPersistEntry);
+ int entryCnt = cntPersistFld.getValue(infoBlock);
+ infoBlock = cntPersistFld.setValue(infoBlock, entryCnt+1);
+ LittleEndian.putInt(bos.getBuf(), lastPersistEntry, infoBlock);
+ } else {
+ // start new PersistDirectoryEntry
+ lastPersistEntry = bos.size();
+ int infoBlock = persistIdFld.setValue(0, nextSlideId);
+ infoBlock = cntPersistFld.setValue(infoBlock, 1);
+ LittleEndian.putInt(intbuf, 0, infoBlock);
+ bos.write(intbuf);
+ }
+ // Add to the ptrData offset lookup hash
+ LittleEndian.putInt(intbuf, 0, offset);
+ bos.write(intbuf);
+ lastSlideId = nextSlideId;
+ } catch (IOException e) {
+ // ByteArrayOutputStream is very unlikely throwing a IO exception (maybe because of OOM ...)
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Save the new ptr data
+ _ptrData = bos.toByteArray();
- // Write out the new location
- Integer dataOffset = (Integer)_slideOffsetDataLocation.get(id);
- LittleEndian.putInt(_ptrData,dataOffset.intValue(),newPos.intValue());
-
- // Update our hashtable
- _slideLocations.remove(id);
- _slideLocations.put(id,newPos);
- }
+ // Update the atom header
+ LittleEndian.putInt(_header,4,bos.size());
}
-
+
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
+ normalizePersistDirectory();
out.write(_header);
out.write(_ptrData);
}
+
+ private static class BufAccessBAOS extends ByteArrayOutputStream {
+ public byte[] getBuf() {
+ return buf;
+ }
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Record.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Record.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Record.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/Record.java Thu Dec 25 01:56:29 2014
@@ -74,7 +74,7 @@ public abstract class Record
*/
public static void writeLittleEndian(int i,OutputStream o) throws IOException {
byte[] bi = new byte[4];
- LittleEndian.putInt(bi,i);
+ LittleEndian.putInt(bi,0,i);
o.write(bi);
}
/**
@@ -82,7 +82,7 @@ public abstract class Record
*/
public static void writeLittleEndian(short s,OutputStream o) throws IOException {
byte[] bs = new byte[2];
- LittleEndian.putShort(bs,s);
+ LittleEndian.putShort(bs,0,s);
o.write(bs);
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java Thu Dec 25 01:56:29 2014
@@ -18,6 +18,8 @@
package org.apache.poi.hslf.record;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;
@@ -42,7 +44,7 @@ public final class UserEditAtom extends
private byte[] _header;
private static long _type = 4085l;
- private byte[] reserved;
+ private short unused;
private int lastViewedSlideID;
private int pptVersion;
@@ -51,6 +53,7 @@ public final class UserEditAtom extends
private int docPersistRef;
private int maxPersistWritten;
private short lastViewType;
+ private int encryptSessionPersistIdRef = -1;
// Somewhat user facing getters
public int getLastViewedSlideID() { return lastViewedSlideID; }
@@ -61,12 +64,17 @@ public final class UserEditAtom extends
public int getPersistPointersOffset() { return persistPointersOffset; }
public int getDocPersistRef() { return docPersistRef; }
public int getMaxPersistWritten() { return maxPersistWritten; }
+ public int getEncryptSessionPersistIdRef() { return encryptSessionPersistIdRef; }
// More scary internal setters
public void setLastUserEditAtomOffset(int offset) { lastUserEditAtomOffset = offset; }
public void setPersistPointersOffset(int offset) { persistPointersOffset = offset; }
public void setLastViewType(short type) { lastViewType=type; }
- public void setMaxPersistWritten(int max) { maxPersistWritten=max; }
+ public void setMaxPersistWritten(int max) { maxPersistWritten=max; }
+ public void setEncryptSessionPersistIdRef(int id) {
+ encryptSessionPersistIdRef=id;
+ LittleEndian.putInt(_header,4,(id == -1 ? 28 : 32));
+ }
/* *************** record code follows ********************** */
@@ -77,39 +85,56 @@ public final class UserEditAtom extends
// Sanity Checking
if(len < 34) { len = 34; }
+ int offset = start;
// Get the header
_header = new byte[8];
- System.arraycopy(source,start,_header,0,8);
+ System.arraycopy(source,offset,_header,0,8);
+ offset += 8;
// Get the last viewed slide ID
- lastViewedSlideID = LittleEndian.getInt(source,start+0+8);
+ lastViewedSlideID = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Get the PPT version
- pptVersion = LittleEndian.getInt(source,start+4+8);
+ pptVersion = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Get the offset to the previous incremental save's UserEditAtom
// This will be the byte offset on disk where the previous one
// starts, or 0 if this is the first one
- lastUserEditAtomOffset = LittleEndian.getInt(source,start+8+8);
+ lastUserEditAtomOffset = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Get the offset to the persist pointers
// This will be the byte offset on disk where the preceding
// PersistPtrFullBlock or PersistPtrIncrementalBlock starts
- persistPointersOffset = LittleEndian.getInt(source,start+12+8);
+ persistPointersOffset = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Get the persist reference for the document persist object
// Normally seems to be 1
- docPersistRef = LittleEndian.getInt(source,start+16+8);
+ docPersistRef = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Maximum number of persist objects written
- maxPersistWritten = LittleEndian.getInt(source,start+20+8);
+ maxPersistWritten = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
// Last view type
- lastViewType = LittleEndian.getShort(source,start+24+8);
+ lastViewType = LittleEndian.getShort(source,offset);
+ offset += LittleEndianConsts.SHORT_SIZE;
+
+ // unused
+ unused = LittleEndian.getShort(source,offset);
+ offset += LittleEndianConsts.SHORT_SIZE;
// There might be a few more bytes, which are a reserved field
- reserved = new byte[len-26-8];
- System.arraycopy(source,start+26+8,reserved,0,reserved.length);
+ if (offset-start<len) {
+ encryptSessionPersistIdRef = LittleEndian.getInt(source,offset);
+ offset += LittleEndianConsts.INT_SIZE;
+ }
+
+ assert(offset-start == len);
}
/**
@@ -155,8 +180,10 @@ public final class UserEditAtom extends
writeLittleEndian(docPersistRef,out);
writeLittleEndian(maxPersistWritten,out);
writeLittleEndian(lastViewType,out);
-
- // Reserved fields
- out.write(reserved);
+ writeLittleEndian(unused,out);
+ if (encryptSessionPersistIdRef != -1) {
+ // optional field
+ writeLittleEndian(encryptSessionPersistIdRef,out);
+ }
}
}
Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/AllHSLFRecordTests.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/AllHSLFRecordTests.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/AllHSLFRecordTests.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/AllHSLFRecordTests.java Thu Dec 25 01:56:29 2014
@@ -17,56 +17,54 @@
package org.apache.poi.hslf.record;
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
/**
* Collects all tests from the package <tt>org.apache.poi.hslf.record</tt>.
*
* @author Josh Micich
*/
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ TestAnimationInfoAtom.class,
+ TestCString.class,
+ TestColorSchemeAtom.class,
+ TestComment2000.class,
+ TestComment2000Atom.class,
+ TestCurrentUserAtom.class,
+ TestDocument.class,
+ TestDocumentAtom.class,
+ TestDocumentEncryptionAtom.class,
+ TestExControl.class,
+ TestExHyperlink.class,
+ TestExHyperlinkAtom.class,
+ TestExMediaAtom.class,
+ TestExObjList.class,
+ TestExObjListAtom.class,
+ TestExOleObjAtom.class,
+ TestExOleObjStg.class,
+ TestExVideoContainer.class,
+ TestFontCollection.class,
+ TestHeadersFootersAtom.class,
+ TestHeadersFootersContainer.class,
+ TestInteractiveInfo.class,
+ TestInteractiveInfoAtom.class,
+ TestNotesAtom.class,
+ TestRecordContainer.class,
+ TestRecordTypes.class,
+ TestSlideAtom.class,
+ TestSlidePersistAtom.class,
+ TestSound.class,
+ TestStyleTextPropAtom.class,
+ TestTextBytesAtom.class,
+ TestTextCharsAtom.class,
+ TestTextHeaderAtom.class,
+ TestTextRulerAtom.class,
+ TestTextSpecInfoAtom.class,
+ TestTxInteractiveInfoAtom.class,
+ TestTxMasterStyleAtom.class,
+ TestUserEditAtom.class
+})
public class AllHSLFRecordTests {
-
- public static Test suite() {
- TestSuite result = new TestSuite(AllHSLFRecordTests.class.getName());
- result.addTestSuite(TestAnimationInfoAtom.class);
- result.addTestSuite(TestCString.class);
- result.addTestSuite(TestColorSchemeAtom.class);
- result.addTestSuite(TestComment2000.class);
- result.addTestSuite(TestComment2000Atom.class);
- result.addTestSuite(TestCurrentUserAtom.class);
- result.addTestSuite(TestDocument.class);
- result.addTestSuite(TestDocumentAtom.class);
- result.addTestSuite(TestDocumentEncryptionAtom.class);
- result.addTestSuite(TestExControl.class);
- result.addTestSuite(TestExHyperlink.class);
- result.addTestSuite(TestExHyperlinkAtom.class);
- result.addTestSuite(TestExMediaAtom.class);
- result.addTestSuite(TestExObjList.class);
- result.addTestSuite(TestExObjListAtom.class);
- result.addTestSuite(TestExOleObjAtom.class);
- result.addTestSuite(TestExOleObjStg.class);
- result.addTestSuite(TestExVideoContainer.class);
- result.addTestSuite(TestFontCollection.class);
- result.addTestSuite(TestHeadersFootersAtom.class);
- result.addTestSuite(TestHeadersFootersContainer.class);
- result.addTestSuite(TestInteractiveInfo.class);
- result.addTestSuite(TestInteractiveInfoAtom.class);
- result.addTestSuite(TestNotesAtom.class);
- result.addTestSuite(TestRecordContainer.class);
- result.addTestSuite(TestRecordTypes.class);
- result.addTestSuite(TestSlideAtom.class);
- result.addTestSuite(TestSlidePersistAtom.class);
- result.addTestSuite(TestSound.class);
- result.addTestSuite(TestStyleTextPropAtom.class);
- result.addTestSuite(TestTextBytesAtom.class);
- result.addTestSuite(TestTextCharsAtom.class);
- result.addTestSuite(TestTextHeaderAtom.class);
- result.addTestSuite(TestTextRulerAtom.class);
- result.addTestSuite(TestTextSpecInfoAtom.class);
- result.addTestSuite(TestTxInteractiveInfoAtom.class);
- result.addTestSuite(TestTxMasterStyleAtom.class);
- result.addTestSuite(TestUserEditAtom.class);
- return result;
- }
}
Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java?rev=1647867&r1=1647866&r2=1647867&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestCurrentUserAtom.java Thu Dec 25 01:56:29 2014
@@ -17,36 +17,33 @@
package org.apache.poi.hslf.record;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.POIDataSamples;
+import org.junit.Test;
/**
* Tests that CurrentUserAtom works properly.
*
* @author Nick Burch (nick at torchbox dot com)
*/
-public final class TestCurrentUserAtom extends TestCase {
+public final class TestCurrentUserAtom {
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
/** Not encrypted */
- private String normalFile;
+ private static final String normalFile = "basic_test_ppt_file.ppt";
/** Encrypted */
- private String encFile;
-
- protected void setUp() throws Exception {
- super.setUp();
-
- normalFile = "basic_test_ppt_file.ppt";
- encFile = "Password_Protected-hello.ppt";
- }
+ private static final String encFile = "Password_Protected-hello.ppt";
- public void testReadNormal() throws Exception {
+ @Test
+ public void readNormal() throws Exception {
POIFSFileSystem fs = new POIFSFileSystem(
_slTests.openResourceAsStream(normalFile)
);
@@ -66,20 +63,20 @@ public final class TestCurrentUserAtom e
assertEquals(0x2942, cu2.getCurrentEditOffset());
}
- public void testReadEnc() throws Exception {
+ @Test(expected = EncryptedPowerPointFileException.class)
+ public void readEnc() throws Exception {
POIFSFileSystem fs = new POIFSFileSystem(
_slTests.openResourceAsStream(encFile)
);
- try {
- new CurrentUserAtom(fs);
- fail();
- } catch(EncryptedPowerPointFileException e) {
- // Good
- }
+ new CurrentUserAtom(fs);
+ assertTrue(true); // not yet failed
+
+ new HSLFSlideShow(fs);
}
- public void testWriteNormal() throws Exception {
+ @Test
+ public void writeNormal() throws Exception {
// Get raw contents from a known file
POIFSFileSystem fs = new POIFSFileSystem(
_slTests.openResourceAsStream(normalFile)
Added: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java?rev=1647867&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java (added)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java Thu Dec 25 01:56:29 2014
@@ -0,0 +1,182 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hslf.record;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.MessageDigest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hslf.HSLFSlideShow;
+import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.usermodel.PictureData;
+import org.apache.poi.hslf.usermodel.SlideShow;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests that DocumentEncryption works properly.
+ */
+public class TestDocumentEncryption {
+ POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
+
+ @Before
+ public void resetPassword() {
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+ }
+
+ @Test
+ public void cryptoAPIDecryptionOther() throws Exception {
+ Biff8EncryptionKey.setCurrentUserPassword("hello");
+ String encPpts[] = {
+ "Password_Protected-56-hello.ppt",
+ "Password_Protected-hello.ppt",
+ "Password_Protected-np-hello.ppt",
+ };
+
+ for (String pptFile : encPpts) {
+ try {
+ NPOIFSFileSystem fs = new NPOIFSFileSystem(slTests.getFile(pptFile), true);
+ HSLFSlideShow hss = new HSLFSlideShow(fs);
+ new SlideShow(hss);
+ fs.close();
+ } catch (EncryptedPowerPointFileException e) {
+ fail(pptFile+" can't be decrypted");
+ }
+ }
+ }
+
+ @Test
+ public void cryptoAPIChangeKeySize() throws Exception {
+ String pptFile = "cryptoapi-proc2356.ppt";
+ Biff8EncryptionKey.setCurrentUserPassword("crypto");
+ NPOIFSFileSystem fs = new NPOIFSFileSystem(slTests.getFile(pptFile), true);
+ HSLFSlideShow hss = new HSLFSlideShow(fs);
+ // need to cache data (i.e. read all data) before changing the key size
+ PictureData picsExpected[] = hss.getPictures();
+ hss.getDocumentSummaryInformation();
+ EncryptionInfo ei = hss.getDocumentEncryptionAtom().getEncryptionInfo();
+ ((CryptoAPIEncryptionHeader)ei.getHeader()).setKeySize(0x78);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ hss.write(bos);
+ fs.close();
+
+ fs = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
+ hss = new HSLFSlideShow(fs);
+ PictureData picsActual[] = hss.getPictures();
+ fs.close();
+
+ assertEquals(picsExpected.length, picsActual.length);
+ for (int i=0; i<picsExpected.length; i++) {
+ assertArrayEquals(picsExpected[i].getRawData(), picsActual[i].getRawData());
+ }
+ }
+
+ @Test
+ public void cryptoAPIEncryption() throws Exception {
+ /* documents with multiple edits need to be normalized for encryption */
+ String pptFile = "57272_corrupted_usereditatom.ppt";
+ NPOIFSFileSystem fs = new NPOIFSFileSystem(slTests.getFile(pptFile), true);
+ HSLFSlideShow hss = new HSLFSlideShow(fs);
+ hss.normalizeRecords();
+
+ // normalized ppt
+ ByteArrayOutputStream expected = new ByteArrayOutputStream();
+ hss.write(expected);
+
+ // encrypted
+ Biff8EncryptionKey.setCurrentUserPassword("hello");
+ ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
+ hss.write(encrypted);
+ fs.close();
+
+ // decrypted
+ ByteArrayInputStream bis = new ByteArrayInputStream(encrypted.toByteArray());
+ fs = new NPOIFSFileSystem(bis);
+ hss = new HSLFSlideShow(fs);
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+ ByteArrayOutputStream actual = new ByteArrayOutputStream();
+ hss.write(actual);
+ fs.close();
+
+ assertArrayEquals(expected.toByteArray(), actual.toByteArray());
+ }
+
+ @Test
+ public void cryptoAPIDecryption() throws Exception {
+ // taken from a msdn blog:
+ // http://blogs.msdn.com/b/openspecification/archive/2009/05/08/dominic-salemno.aspx
+ Biff8EncryptionKey.setCurrentUserPassword("crypto");
+ NPOIFSFileSystem fs = new NPOIFSFileSystem(slTests.getFile("cryptoapi-proc2356.ppt"));
+ HSLFSlideShow hss = new HSLFSlideShow(fs);
+ SlideShow ss = new SlideShow(hss);
+
+ Slide slide = ss.getSlides()[0];
+ assertEquals("Dominic Salemno", slide.getTextRuns()[0].getText());
+
+ String picCmp[][] = {
+ {"0","nKsDTKqxTCR8LFkVVWlP9GSTvZ0="},
+ {"95163","SuNOR+9V1UVYZIoeD65l3VTaLoc="},
+ {"100864","Ql3IGrr4bNq07ZTp5iPg7b+pva8="},
+ {"714114","8pdst9NjBGSfWezSZE8+aVhIRe0="},
+ {"723752","go6xqW7lvkCtlOO5tYLiMfb4oxw="},
+ {"770128","gZUM8YqRNL5kGNfyyYvEEernvCc="},
+ {"957958","CNU2iiqUFAnk3TDXsXV1ihH9eRM="},
+ };
+
+ MessageDigest md = CryptoFunctions.getMessageDigest(HashAlgorithm.sha1);
+ PictureData pd[] = hss.getPictures();
+ int i = 0;
+ for (PictureData p : pd) {
+ byte hash[] = md.digest(p.getData());
+ assertEquals(Integer.parseInt(picCmp[i][0]), p.getOffset());
+ assertEquals(picCmp[i][1], Base64.encodeBase64String(hash));
+ i++;
+ }
+
+ DocumentEncryptionAtom dea = hss.getDocumentEncryptionAtom();
+
+ POIFSFileSystem fs2 = new POIFSFileSystem(dea.getEncryptionInfo().getDecryptor().getDataStream(fs));
+ PropertySet ps = PropertySetFactory.create(fs2.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME);
+ assertTrue(ps.isSummaryInformation());
+ assertEquals("RC4 CryptoAPI Encryption", ps.getProperties()[1].getValue());
+ ps = PropertySetFactory.create(fs2.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ assertTrue(ps.isDocumentSummaryInformation());
+ assertEquals("On-screen Show (4:3)", ps.getProperties()[1].getValue());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org