import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
import org.apache.poi.POIDocument;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
// Embedded objects stored in storage records in the document stream, lazily populated.
private ObjectData[] _objects;
-
+
/**
* Returns the underlying POIFSFileSystem for the document
* that is open.
// Look for any other streams
readOtherStreams();
}
+
+
+
/**
* Constructs a new, empty, Powerpoint document.
*/
_records = read(_docstream, (int)currentUser.getCurrentEditOffset());
}
- private Record[] read(byte[] docstream, int usrOffset){
- ArrayList<Integer> lst = new ArrayList<Integer>();
- HashMap<Integer,Integer> offset2id = new HashMap<Integer,Integer>();
+ private Record[] read(byte[] docstream, int usrOffset){
+ //sort found records by offset.
+ //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
+ 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);
+
+ for (Map.Entry<Integer,Record> entry : records.entrySet()) {
+ Integer offset = entry.getKey();
+ Record record = entry.getValue();
+ Integer persistId = persistIds.get(offset);
+ if (record == null) {
+ // all plain records have been already added,
+ // only new records need to be decrypted (tbd #35897)
+ record = Record.buildRecordAtOffset(docstream, offset);
+ entry.setValue(record);
+ }
+
+ if (record instanceof PersistRecord) {
+ ((PersistRecord)record).setPersistId(persistId);
+ }
+ }
+
+ return records.values().toArray(new Record[records.size()]);
+ }
+
+ private void initRecordOffsets(byte[] docstream, int usrOffset, NavigableMap<Integer,Record> recordMap, Map<Integer,Integer> offset2id) {
while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
- lst.add(usrOffset);
+ recordMap.put(usrOffset, usr);
+
int psrOffset = usr.getPersistPointersOffset();
-
PersistPtrHolder ptr = (PersistPtrHolder)Record.buildRecordAtOffset(docstream, psrOffset);
- lst.add(psrOffset);
- Hashtable<Integer,Integer> entries = ptr.getSlideLocationsLookup();
- for(Integer id : entries.keySet()) {
- Integer offset = entries.get(id);
- lst.add(offset);
+ recordMap.put(psrOffset, ptr);
+
+ for(Map.Entry<Integer,Integer> entry : ptr.getSlideLocationsLookup().entrySet()) {
+ Integer offset = entry.getValue();
+ Integer id = entry.getKey();
+ recordMap.put(offset, null); // reserve a slot for the record
offset2id.put(offset, id);
}
-
+
usrOffset = usr.getLastUserEditAtomOffset();
- }
- //sort found records by offset.
- //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
- Integer a[] = lst.toArray(new Integer[lst.size()]);
- Arrays.sort(a);
- Record[] rec = new Record[lst.size()];
- for (int i = 0; i < a.length; i++) {
- Integer offset = a[i];
- rec[i] = Record.buildRecordAtOffset(docstream, offset.intValue());
- if(rec[i] instanceof PersistRecord) {
- PersistRecord psr = (PersistRecord)rec[i];
- Integer id = offset2id.get(offset);
- psr.setPersistId(id.intValue());
- }
- }
- return rec;
+ // check for corrupted user edit atom and try to repair it
+ // if the next user edit atom offset is already known, we would go into an endless loop
+ if (usrOffset > 0 && recordMap.containsKey(usrOffset)) {
+ // a user edit atom is usually located 36 byte before the smallest known record offset
+ usrOffset = recordMap.firstKey()-36;
+ // check that we really are located on a user edit atom
+ int ver_inst = LittleEndian.getUShort(docstream, usrOffset);
+ int type = LittleEndian.getUShort(docstream, usrOffset+2);
+ int len = LittleEndian.getInt(docstream, usrOffset+4);
+ if (ver_inst == 0 && type == 4085 && (len == 0x1C || len == 0x20)) {
+ logger.log(POILogger.WARN, "Repairing invalid user edit atom");
+ usr.setLastUserEditAtomOffset(usrOffset);
+ } else {
+ throw new CorruptPowerPointFileException("Powerpoint document contains invalid user edit atom");
+ }
+ }
+ }
}
/**
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.
*/
+ @SuppressWarnings("unused")
private void readPictures() throws IOException {
_pictures = new ArrayList<PictureData>();
- byte[] pictstream;
-
- try {
- DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
- pictstream = new byte[entry.getSize()];
- DocumentInputStream is = directory.createDocumentInputStream("Pictures");
- is.read(pictstream);
- } catch (FileNotFoundException e){
- // Silently catch exceptions if the presentation doesn't
- // contain pictures - will use a null set instead
- return;
- }
+ // if the presentation doesn't contain pictures - will use a null set instead
+ if (!directory.hasEntry("Pictures")) return;
+
+ DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
+ byte[] pictstream = new byte[entry.getSize()];
+ DocumentInputStream is = directory.createDocumentInputStream(entry);
+ is.read(pictstream);
+ is.close();
+
int pos = 0;
// An empty picture record (length 0) will take up 8 bytes
while (pos <= (pictstream.length-8)) {
int offset = pos;
-
+
// Image signature
- @SuppressWarnings("unused")
int signature = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE;
// Image type + 0xF018