getResponseLevels() {
31 | return this.responseLevels;
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return this.guid + "@" + this.deliveryTime + " " + this.responseLevels.size() + " ResponseLevels";
37 | }
38 |
39 | public class ResponseLevel {
40 | short deltaCode;
41 | long timeDelta;
42 | short random;
43 |
44 | public ResponseLevel(final short deltaCode, final long timeDelta, final short random) {
45 | this.deltaCode = deltaCode;
46 | this.timeDelta = timeDelta;
47 | this.random = random;
48 | }
49 |
50 | public short getDeltaCode() {
51 | return this.deltaCode;
52 | }
53 |
54 | public long getTimeDelta() {
55 | return this.timeDelta;
56 | }
57 |
58 | public short getRandom() {
59 | return this.random;
60 | }
61 |
62 | public Date withOffset(final Date anchorDate) {
63 | return new Date(anchorDate.getTime() + this.timeDelta);
64 | }
65 | }
66 |
67 | protected PSTConversationIndex(final byte[] rawConversationIndex) {
68 | if (rawConversationIndex != null && rawConversationIndex.length >= MINIMUM_HEADER_SIZE) {
69 | this.decodeHeader(rawConversationIndex);
70 | if (rawConversationIndex.length >= MINIMUM_HEADER_SIZE + RESPONSE_LEVEL_SIZE) {
71 | this.decodeResponseLevel(rawConversationIndex);
72 | }
73 | }
74 | }
75 |
76 | private void decodeHeader(final byte[] rawConversationIndex) {
77 | // According to the Spec the first byte is not included, but I believe
78 | // the spec is incorrect!
79 | // int reservedheaderMarker = (int)
80 | // PSTObject.convertBigEndianBytesToLong(rawConversationIndex, 0, 1);
81 |
82 | final long deliveryTimeHigh = PSTObject.convertBigEndianBytesToLong(rawConversationIndex, 0, 4);
83 | final long deliveryTimeLow = PSTObject.convertBigEndianBytesToLong(rawConversationIndex, 4, 6) << 16;
84 | this.deliveryTime = PSTObject.filetimeToDate((int) deliveryTimeHigh, (int) deliveryTimeLow);
85 |
86 | final long guidHigh = PSTObject.convertBigEndianBytesToLong(rawConversationIndex, 6, 14);
87 | final long guidLow = PSTObject.convertBigEndianBytesToLong(rawConversationIndex, 14, 22);
88 |
89 | this.guid = new UUID(guidHigh, guidLow);
90 | }
91 |
92 | private void decodeResponseLevel(final byte[] rawConversationIndex) {
93 | final int responseLevelCount = (rawConversationIndex.length - MINIMUM_HEADER_SIZE) / RESPONSE_LEVEL_SIZE;
94 | this.responseLevels = new ArrayList<>(responseLevelCount);
95 |
96 | for (int responseLevelIndex = 0, position = 22; responseLevelIndex < responseLevelCount; responseLevelIndex++, position += RESPONSE_LEVEL_SIZE) {
97 |
98 | final long responseLevelValue = PSTObject.convertBigEndianBytesToLong(rawConversationIndex, position,
99 | position + 5);
100 | final short deltaCode = (short) (responseLevelValue >> 39);
101 | final short random = (short) (responseLevelValue & 0xFF);
102 |
103 | // shift by 1 byte (remove the random) and mask off the deltaCode
104 | long deltaTime = (responseLevelValue >> 8) & 0x7FFFFFFF;
105 |
106 | if (deltaCode == 0) {
107 | deltaTime <<= 18;
108 | } else {
109 | deltaTime <<= 23;
110 | }
111 |
112 | deltaTime /= HUNDRED_NS_TO_MS;
113 |
114 | this.responseLevels.add(responseLevelIndex, new ResponseLevel(deltaCode, deltaTime, random));
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTDescriptorItem.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | */
33 | package com.pff;
34 |
35 | import java.io.IOException;
36 |
37 | /**
38 | * The descriptor items contain information that describes a PST object.
39 | * This is like extended table entries, usually when the data cannot fit in a
40 | * traditional table item.
41 | *
42 | * This is an entry of type SLENTRY or SIENTRY
43 | * see [MS-PST]: Outlook Personal Folders (.pst) File Format
44 | *
45 | * @author Richard Johnson
46 | */
47 | class PSTDescriptorItem {
48 | PSTDescriptorItem(final byte[] data, final int offset, final PSTFile pstFile, int entryType) {
49 | this.pstFile = pstFile;
50 |
51 | if (pstFile.getPSTFileType() == PSTFile.PST_TYPE_ANSI) {
52 | this.descriptorIdentifier = (int) PSTObject.convertLittleEndianBytesToLong(data, offset, offset + 4);
53 | this.offsetIndexIdentifier = ((int) PSTObject.convertLittleEndianBytesToLong(data, offset + 4, offset + 8))
54 | & 0xfffffffe;
55 | if (entryType == PSTFile.SLBLOCK_ENTRY)
56 | this.subNodeOffsetIndexIdentifier = (int) PSTObject.convertLittleEndianBytesToLong(data, offset + 8,
57 | offset + 12) & 0xfffffffe;
58 | else
59 | this.subNodeOffsetIndexIdentifier = 0;
60 | } else {
61 | this.descriptorIdentifier = (int) PSTObject.convertLittleEndianBytesToLong(data, offset, offset + 4);
62 | this.offsetIndexIdentifier = ((int) PSTObject.convertLittleEndianBytesToLong(data, offset + 8, offset + 16))
63 | & 0xfffffffe;
64 | if (entryType == PSTFile.SLBLOCK_ENTRY)
65 | this.subNodeOffsetIndexIdentifier = (int) PSTObject.convertLittleEndianBytesToLong(data, offset + 16,
66 | offset + 24) & 0xfffffffe;
67 | else
68 | this.subNodeOffsetIndexIdentifier = 0;
69 | }
70 | }
71 |
72 | public byte[] getData() throws IOException, PSTException {
73 | if (this.dataBlockData != null) {
74 | return this.dataBlockData;
75 | }
76 |
77 | final PSTNodeInputStream in = this.pstFile.readLeaf(this.offsetIndexIdentifier);
78 | final byte[] out = new byte[(int) in.length()];
79 | in.readCompletely(out);
80 | this.dataBlockData = out;
81 | return this.dataBlockData;
82 | }
83 |
84 | public int[] getBlockOffsets() throws IOException, PSTException {
85 | if (this.dataBlockOffsets != null) {
86 |
87 | return this.dataBlockOffsets;
88 | }
89 | final Long[] offsets = this.pstFile.readLeaf(this.offsetIndexIdentifier).getBlockOffsets();
90 | final int[] offsetsOut = new int[offsets.length];
91 | for (int x = 0; x < offsets.length; x++) {
92 | offsetsOut[x] = offsets[x].intValue();
93 | }
94 | return offsetsOut;
95 | }
96 |
97 | public int getDataSize() throws IOException, PSTException {
98 | return this.pstFile.getLeafSize(this.offsetIndexIdentifier);
99 | }
100 |
101 | // Public data
102 | int descriptorIdentifier;
103 | int offsetIndexIdentifier;
104 | int subNodeOffsetIndexIdentifier;
105 |
106 | // These are private to ensure that getData()/getBlockOffets() are used
107 | // private PSTFile.PSTFileBlock dataBlock = null;
108 | byte[] dataBlockData = null;
109 | int[] dataBlockOffsets = null;
110 | private final PSTFile pstFile;
111 |
112 | @Override
113 | public String toString() {
114 | return "PSTDescriptorItem\n" + " descriptorIdentifier: " + this.descriptorIdentifier + "\n"
115 | + " offsetIndexIdentifier: " + this.offsetIndexIdentifier + "\n" + " subNodeOffsetIndexIdentifier: "
116 | + this.subNodeOffsetIndexIdentifier + "\n";
117 |
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTDistList.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.IOException;
37 | import java.util.Arrays;
38 | import java.util.HashMap;
39 |
40 | /**
41 | * PST DistList for extracting Addresses from Distribution lists.
42 | *
43 | * @author Richard Johnson
44 | */
45 | public class PSTDistList extends PSTMessage {
46 |
47 | /**
48 | * constructor.
49 | *
50 | * @param theFile
51 | * pst file
52 | * @param descriptorIndexNode
53 | * index of the list
54 | * @throws PSTException
55 | * on parsing error
56 | * @throws IOException
57 | * on data access error
58 | */
59 | PSTDistList(final PSTFile theFile, final DescriptorIndexNode descriptorIndexNode) throws PSTException, IOException {
60 | super(theFile, descriptorIndexNode);
61 | }
62 |
63 | /**
64 | * Internal constructor for performance.
65 | *
66 | * @param theFile
67 | * pst file
68 | * @param folderIndexNode
69 | * index of the list
70 | * @param table
71 | * the PSTTableBC this object is represented by
72 | * @param localDescriptorItems
73 | * additional external items that represent
74 | * this object.
75 | */
76 | PSTDistList(final PSTFile theFile, final DescriptorIndexNode folderIndexNode, final PSTTableBC table,
77 | final HashMap localDescriptorItems) {
78 | super(theFile, folderIndexNode, table, localDescriptorItems);
79 | }
80 |
81 | /**
82 | * Find the next two null bytes in an array given start.
83 | *
84 | * @param data
85 | * the array to search
86 | * @param start
87 | * the starting index
88 | * @return position of the next null char
89 | */
90 | private int findNextNullChar(final byte[] data, int start) {
91 | for (; start < data.length; start += 2) {
92 | if (data[start] == 0 && data[start + 1] == 0) {
93 | break;
94 | }
95 | }
96 | return start;
97 | }
98 |
99 | /**
100 | * identifier for one-off entries.
101 | */
102 | private final byte[] oneOffEntryIdUid = { (byte) 0x81, (byte) 0x2b, (byte) 0x1f, (byte) 0xa4, (byte) 0xbe,
103 | (byte) 0xa3, (byte) 0x10, (byte) 0x19, (byte) 0x9d, (byte) 0x6e, (byte) 0x00, (byte) 0xdd, (byte) 0x01,
104 | (byte) 0x0f, (byte) 0x54, (byte) 0x02 };
105 |
106 | /**
107 | * identifier for wrapped entries.
108 | */
109 | private final byte[] wrappedEntryIdUid = { (byte) 0xc0, (byte) 0x91, (byte) 0xad, (byte) 0xd3, (byte) 0x51,
110 | (byte) 0x9d, (byte) 0xcf, (byte) 0x11, (byte) 0xa4, (byte) 0xa9, (byte) 0x00, (byte) 0xaa, (byte) 0x00,
111 | (byte) 0x47, (byte) 0xfa, (byte) 0xa4 };
112 |
113 | /**
114 | * Inner class to represent distribution list one-off entries.
115 | */
116 | public class OneOffEntry {
117 | /** display name. */
118 | private String displayName = "";
119 |
120 | /**
121 | * @return display name
122 | */
123 | public String getDisplayName() {
124 | return this.displayName;
125 | }
126 |
127 | /** address type (smtp). */
128 | private String addressType = "";
129 |
130 | /**
131 | * @return address type
132 | */
133 | public String getAddressType() {
134 | return this.addressType;
135 | }
136 |
137 | /** email address. */
138 | private String emailAddress = "";
139 |
140 | /**
141 | * @return email address.
142 | */
143 | public String getEmailAddress() {
144 | return this.emailAddress;
145 | }
146 |
147 | /** ending position of this object in the data array. */
148 | private int pos = 0;
149 |
150 | /**
151 | * @return formatted record
152 | */
153 | @Override
154 | public String toString() {
155 | return String.format("Display Name: %s\n" + "Address Type: %s\n" + "Email Address: %s\n", this.displayName,
156 | this.addressType, this.emailAddress);
157 | }
158 | }
159 |
160 | /**
161 | * Parse a one-off entry from this Distribution List.
162 | *
163 | * @param data
164 | * the item data
165 | * @param pos
166 | * the current position in the data.
167 | * @throws IOException
168 | * on string reading fail
169 | * @return the one-off entry
170 | */
171 | private OneOffEntry parseOneOffEntry(final byte[] data, int pos) throws IOException {
172 | final int version = (int) PSTObject.convertLittleEndianBytesToLong(data, pos, pos + 2);
173 | pos += 2;
174 |
175 | // http://msdn.microsoft.com/en-us/library/ee202811(v=exchg.80).aspx
176 | final int additionalFlags = (int) PSTObject.convertLittleEndianBytesToLong(data, pos, pos + 2);
177 | pos += 2;
178 |
179 | final int pad = additionalFlags & 0x8000;
180 | final int mae = additionalFlags & 0x0C00;
181 | final int format = additionalFlags & 0x1E00;
182 | final int m = additionalFlags & 0x0100;
183 | final int u = additionalFlags & 0x0080;
184 | final int r = additionalFlags & 0x0060;
185 | final int l = additionalFlags & 0x0010;
186 | final int pad2 = additionalFlags & 0x000F;
187 |
188 | int stringEnd = this.findNextNullChar(data, pos);
189 | final byte[] displayNameBytes = new byte[stringEnd - pos];
190 | System.arraycopy(data, pos, displayNameBytes, 0, displayNameBytes.length);
191 | final String displayName = new String(displayNameBytes, "UTF-16LE");
192 | pos = stringEnd + 2;
193 |
194 | stringEnd = this.findNextNullChar(data, pos);
195 | final byte[] addressTypeBytes = new byte[stringEnd - pos];
196 | System.arraycopy(data, pos, addressTypeBytes, 0, addressTypeBytes.length);
197 | final String addressType = new String(addressTypeBytes, "UTF-16LE");
198 | pos = stringEnd + 2;
199 |
200 | stringEnd = this.findNextNullChar(data, pos);
201 | final byte[] emailAddressBytes = new byte[stringEnd - pos];
202 | System.arraycopy(data, pos, emailAddressBytes, 0, emailAddressBytes.length);
203 | final String emailAddress = new String(emailAddressBytes, "UTF-16LE");
204 | pos = stringEnd + 2;
205 |
206 | final OneOffEntry out = new OneOffEntry();
207 | out.displayName = displayName;
208 | out.addressType = addressType;
209 | out.emailAddress = emailAddress;
210 | out.pos = pos;
211 | return out;
212 | }
213 |
214 | /**
215 | * Get an array of the members in this distribution list.
216 | *
217 | * @throws PSTException
218 | * on corrupted data
219 | * @throws IOException
220 | * on bad string reading
221 | * @return array of entries that can either be PSTDistList.OneOffEntry
222 | * or a PSTObject, generally PSTContact.
223 | */
224 | public Object[] getDistributionListMembers() throws PSTException, IOException {
225 | final PSTTableBCItem item = this.items.get(this.pstFile.getNameToIdMapItem(0x8055, PSTFile.PSETID_Address));
226 | Object[] out = {};
227 | if (item != null) {
228 | int pos = 0;
229 | final int count = (int) PSTObject.convertLittleEndianBytesToLong(item.data, pos, pos + 4);
230 | out = new Object[count];
231 | pos += 4;
232 | pos = (int) PSTObject.convertLittleEndianBytesToLong(item.data, pos, pos + 4);
233 |
234 | for (int x = 0; x < count; x++) {
235 | // http://msdn.microsoft.com/en-us/library/ee218661(v=exchg.80).aspx
236 | // http://msdn.microsoft.com/en-us/library/ee200559(v=exchg.80).aspx
237 | final int flags = (int) PSTObject.convertLittleEndianBytesToLong(item.data, pos, pos + 4);
238 | pos += 4;
239 |
240 | final byte[] guid = new byte[16];
241 | System.arraycopy(item.data, pos, guid, 0, guid.length);
242 | pos += 16;
243 |
244 | if (Arrays.equals(guid, this.wrappedEntryIdUid)) {
245 | /* c3 */
246 | final int entryType = item.data[pos] & 0x0F;
247 | final int entryAddressType = item.data[pos] & 0x70 >> 4;
248 | final boolean isOneOffEntryId = (item.data[pos] & 0x80) > 0;
249 | pos++;
250 | final int wrappedflags = (int) PSTObject.convertLittleEndianBytesToLong(item.data, pos, pos + 4);
251 | pos += 4;
252 |
253 | final byte[] guid2 = new byte[16];
254 | System.arraycopy(item.data, pos, guid, 0, guid.length);
255 | pos += 16;
256 |
257 | final int descriptorIndex = (int) PSTObject.convertLittleEndianBytesToLong(item.data, pos, pos + 3);
258 | pos += 3;
259 |
260 | final byte empty = item.data[pos];
261 | pos++;
262 |
263 | out[x] = PSTObject.detectAndLoadPSTObject(this.pstFile, descriptorIndex);
264 |
265 | } else if (Arrays.equals(guid, this.oneOffEntryIdUid)) {
266 | final OneOffEntry entry = this.parseOneOffEntry(item.data, pos);
267 | pos = entry.pos;
268 | out[x] = entry;
269 | }
270 | }
271 | }
272 | return out;
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | /**
37 | * Simple exception for PST File related errors
38 | *
39 | * @author Richard Johnson
40 | */
41 | public class PSTException extends Exception {
42 | /**
43 | * eclipse generated serial UID
44 | */
45 | private static final long serialVersionUID = 4284698344354718143L;
46 |
47 | PSTException(final String error) {
48 | super(error);
49 | }
50 |
51 | PSTException(final String error, final Exception orig) {
52 | super(error, orig);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTFileContent.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.IOException;
4 |
5 | public abstract class PSTFileContent {
6 | public abstract void seek(long index) throws IOException;
7 |
8 | public abstract long getFilePointer() throws IOException;
9 |
10 | public abstract int read() throws IOException;
11 |
12 | public abstract int read(byte[] target) throws IOException;
13 |
14 | public final void readCompletely(final byte[] target) throws IOException {
15 | int read = this.read(target);
16 | // bail in common case
17 | if (read <= 0 || read == target.length) {
18 | return;
19 | }
20 |
21 | byte[] buffer = new byte[8192];
22 | int offset = read;
23 | while (offset < target.length) {
24 | read = this.read(buffer);
25 | if (read <= 0) {
26 | break;
27 | }
28 | System.arraycopy(buffer, 0, target, offset, read);
29 | offset += read;
30 | }
31 | }
32 |
33 | public abstract byte readByte() throws IOException;
34 |
35 | public abstract void close() throws IOException;
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTGlobalObjectId.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 | import java.util.Arrays;
6 | import java.util.Date;
7 |
8 | /**
9 | * Class to represent a decoded PidLidGlobalObjectId or
10 | * PidLidCleanGlobalObjectId (for Meeting Exceptions)
11 | * See [MS-OXOCAL]
12 | * "https://interoperability.blob.core.windows.net/files/MS-OXOCAL/%5bMS-OXOCAL%5d.pdf"
13 | * sections 2.2.1.27(PidLidGlobalObjectId) & 2.2.1.28(PidLidCleanGlobalObjectId)
14 | *
15 | * @author Nick Buller
16 | * NOTE: Following MS Doc for variable names so are exactly the same as
17 | * in MS-OXOCAL (not following Java conventions)
18 | */
19 | public class PSTGlobalObjectId {
20 | final protected static byte[] ReferenceByteArrayID = { 0x04, 0x00, 0x00, 0x00, (byte) 0x82, 0x00, (byte) 0xe0, 0x00,
21 | 0x74, (byte) 0xc5, (byte) 0xb7, 0x10, 0x1a, (byte) 0x82, (byte) 0xe0, 0x08 };
22 | final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
23 |
24 | byte[] ByteArrayID = new byte[16];
25 | byte YH;
26 | byte YL;
27 | byte M;
28 | byte D;
29 | int CreationTimeH;
30 | int CreationTimeL;
31 | Date CreationTime;
32 | long X = 0x0L;
33 | int Size;
34 | byte[] Data;
35 |
36 | public PSTGlobalObjectId(final byte[] pidData) {
37 | if (pidData.length < 32) {
38 | throw new AssertionError("pidDate is too short");
39 | }
40 |
41 | System.arraycopy(pidData, 0, this.ByteArrayID, 0, ReferenceByteArrayID.length);
42 |
43 | if (!Arrays.equals(this.ByteArrayID, ReferenceByteArrayID)) {
44 | throw new AssertionError("ByteArrayID is incorrect");
45 | }
46 |
47 | final ByteBuffer buffer = ByteBuffer
48 | .wrap(pidData, ReferenceByteArrayID.length, pidData.length - ReferenceByteArrayID.length)
49 | .order(ByteOrder.LITTLE_ENDIAN);
50 |
51 | this.YH = buffer.get();
52 | this.YL = buffer.get();
53 | this.M = buffer.get();
54 | this.D = buffer.get();
55 | this.CreationTimeL = buffer.getInt();
56 | this.CreationTimeH = buffer.getInt();
57 | this.CreationTime = PSTObject.filetimeToDate(this.CreationTimeH, this.CreationTimeL);
58 | this.X = buffer.getLong();
59 | this.Size = buffer.getInt();
60 |
61 | if (buffer.remaining() != this.Size) {
62 | throw new AssertionError("Incorrect remaining date in buffer to extract data");
63 | }
64 |
65 | this.Data = new byte[buffer.remaining()];
66 | buffer.get(this.Data, 0, buffer.remaining());
67 | }
68 |
69 | public static String bytesToHex(final byte[] bytes) {
70 | final char[] hexChars = new char[bytes.length * 2];
71 | for (int j = 0; j < bytes.length; j++) {
72 | final int v = bytes[j] & 0xFF;
73 | hexChars[j * 2] = hexArray[v >>> 4];
74 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
75 | }
76 | return new String(hexChars);
77 | }
78 |
79 | protected int getYearHigh() {
80 | return this.YH;
81 | }
82 |
83 | protected int getYearLow() {
84 | return (this.YL & 0xFF);
85 | }
86 |
87 | public int getYear() {
88 | return (this.YH << 8) | (this.YL & 0xFF);
89 | }
90 |
91 | public int getMonth() {
92 | return this.M;
93 | }
94 |
95 | public int getDay() {
96 | return this.D;
97 | }
98 |
99 | public Date getCreationTime() {
100 | return this.CreationTime;
101 | }
102 |
103 | protected int getCreationTimeLow() {
104 | return this.CreationTimeL;
105 | }
106 |
107 | protected int getCreationTimeHigh() {
108 | return this.CreationTimeH;
109 | }
110 |
111 | public int getDataSize() {
112 | return this.Size;
113 | }
114 |
115 | public byte[] getData() {
116 | return this.Data;
117 | }
118 |
119 | @Override
120 | public String toString() {
121 | return "Byte Array ID[" + bytesToHex(this.ByteArrayID) + "] " + "Year [" + this.getYear() + "] " + "Month["
122 | + this.M + "] " + "Day[" + this.D + "] CreationTime[" + this.CreationTime + "] " + "X[" + this.X + "] "
123 | + "Size[" + this.Size + "] " + "Data[" + bytesToHex(this.Data) + "]";
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTMessageStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.IOException;
37 | import java.util.UUID;
38 |
39 | /**
40 | * Object that represents the message store.
41 | * Not much use other than to get the "name" of the PST file.
42 | *
43 | * @author Richard Johnson
44 | */
45 | public class PSTMessageStore extends PSTObject {
46 |
47 | PSTMessageStore(final PSTFile theFile, final DescriptorIndexNode descriptorIndexNode)
48 | throws PSTException, IOException {
49 | super(theFile, descriptorIndexNode);
50 | }
51 |
52 | /**
53 | * Get the tag record key, unique to this pst
54 | *
55 | * @return the tag record key as uuid
56 | */
57 | public UUID getTagRecordKeyAsUUID() {
58 | // attempt to find in the table.
59 | final int guidEntryType = 0x0ff9;
60 | if (this.items.containsKey(guidEntryType)) {
61 | final PSTTableBCItem item = this.items.get(guidEntryType);
62 | final int offset = 0;
63 | final byte[] bytes = item.data;
64 | final long mostSigBits = (PSTObject.convertLittleEndianBytesToLong(bytes, offset, offset + 4) << 32)
65 | | (PSTObject.convertLittleEndianBytesToLong(bytes, offset + 4, offset + 6) << 16)
66 | | PSTObject.convertLittleEndianBytesToLong(bytes, offset + 6, offset + 8);
67 | final long leastSigBits = PSTObject.convertBigEndianBytesToLong(bytes, offset + 8, offset + 16);
68 | return new UUID(mostSigBits, leastSigBits);
69 | }
70 | return null;
71 | }
72 |
73 | /**
74 | * get the message store display name
75 | */
76 | @Override
77 | public String getDisplayName() {
78 | // attempt to find in the table.
79 | final int displayNameEntryType = 0x3001;
80 | if (this.items.containsKey(displayNameEntryType)) {
81 | return this.getStringItem(displayNameEntryType);
82 | // PSTTableBCItem item =
83 | // (PSTTableBCItem)this.items.get(displayNameEntryType);
84 | // return new String(item.getStringValue());
85 | }
86 | return "";
87 | }
88 |
89 | public String getDetails() {
90 | return this.items.toString();
91 | }
92 |
93 | /**
94 | * Is this pst file is password protected.
95 | *
96 | * @throws PSTException
97 | * on corrupted pst
98 | * @throws IOException
99 | * on bad read
100 | * @return - true if protected,false otherwise
101 | * pstfile has the password stored against identifier 0x67FF.
102 | * if there is no password the value stored is 0x00000000.
103 | */
104 | public boolean isPasswordProtected() throws PSTException, IOException {
105 | return (this.getLongItem(0x67FF) != 0);
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTRAFileContent.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.IOException;
6 | import java.io.RandomAccessFile;
7 |
8 | public class PSTRAFileContent extends PSTFileContent {
9 |
10 | protected RandomAccessFile file;
11 |
12 | public PSTRAFileContent(final File file) throws FileNotFoundException {
13 | this.file = new RandomAccessFile(file, "r");
14 | }
15 |
16 | public RandomAccessFile getFile() {
17 | return this.file;
18 | }
19 |
20 | public void setFile(final RandomAccessFile file) {
21 | this.file = file;
22 | }
23 |
24 | @Override
25 | public void seek(final long index) throws IOException {
26 | this.file.seek(index);
27 | }
28 |
29 | @Override
30 | public long getFilePointer() throws IOException {
31 | return this.file.getFilePointer();
32 | }
33 |
34 | @Override
35 | public int read() throws IOException {
36 | return this.file.read();
37 | }
38 |
39 | @Override
40 | public int read(final byte[] target) throws IOException {
41 | return this.file.read(target);
42 | }
43 |
44 | @Override
45 | public byte readByte() throws IOException {
46 | return this.file.readByte();
47 | }
48 |
49 | @Override
50 | public void close() throws IOException {
51 | this.file.close();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTRecipient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | // import java.util.Date;
37 | import java.util.HashMap;
38 |
39 | /**
40 | * Class containing recipient information
41 | *
42 | * @author Orin Eman
43 | *
44 | *
45 | */
46 | public class PSTRecipient {
47 | private final HashMap details;
48 |
49 | public static final int MAPI_TO = 1;
50 | public static final int MAPI_CC = 2;
51 | public static final int MAPI_BCC = 3;
52 |
53 | private PSTMessage message;
54 |
55 | PSTRecipient(PSTMessage message,final HashMap recipientDetails) {
56 | this.message=message;
57 | this.details = recipientDetails;
58 | }
59 |
60 | public String getDisplayName() {
61 | return this.getString(0x3001);
62 | }
63 |
64 | public int getRecipientType() {
65 | return this.getInt(0x0c15);
66 | }
67 |
68 | public String getEmailAddressType() {
69 | return this.getString(0x3002);
70 | }
71 |
72 | public String getEmailAddress() {
73 | return this.getString(0x3003);
74 | }
75 |
76 | public int getRecipientFlags() {
77 | return this.getInt(0x5ffd);
78 | }
79 |
80 | public int getRecipientOrder() {
81 | return this.getInt(0x5fdf);
82 | }
83 |
84 | public String getSmtpAddress() {
85 | // If the recipient address type is SMTP,
86 | // we can simply return the recipient address.
87 | final String addressType = this.getEmailAddressType();
88 | if (addressType != null && addressType.equalsIgnoreCase("smtp")) {
89 | final String addr = this.getEmailAddress();
90 | if (addr != null && addr.length() != 0) {
91 | return addr;
92 | }
93 | }
94 | // Otherwise, we have to hope the SMTP address is
95 | // present as the PidTagPrimarySmtpAddress property.
96 | return this.getString(0x39FE);
97 | }
98 |
99 | private String getString(final int id) {
100 | if (this.details.containsKey(id)) {
101 | final PSTTable7CItem item = this.details.get(id);
102 | return item.getStringValue(message.getStringCodepage());
103 | }
104 |
105 | return "";
106 | }
107 |
108 | /*
109 | * private boolean getBoolean(int id) {
110 | * if ( details.containsKey(id) ) {
111 | * PSTTable7CItem item = details.get(id);
112 | * if ( item.entryValueType == 0x000B )
113 | * {
114 | * return (item.entryValueReference & 0xFF) == 0 ? false : true;
115 | * }
116 | * }
117 | *
118 | * return false;
119 | * }
120 | */
121 | private int getInt(final int id) {
122 | if (this.details.containsKey(id)) {
123 | final PSTTable7CItem item = this.details.get(id);
124 | if (item.entryValueType == 0x0003) {
125 | return item.entryValueReference;
126 | }
127 |
128 | if (item.entryValueType == 0x0002) {
129 | final short s = (short) item.entryValueReference;
130 | return s;
131 | }
132 | }
133 |
134 | return 0;
135 | }
136 |
137 | /*
138 | * private Date getDate(int id) {
139 | * long lDate = 0;
140 | *
141 | * if ( details.containsKey(id) ) {
142 | * PSTTable7CItem item = details.get(id);
143 | * if ( item.entryValueType == 0x0040 ) {
144 | * int high = (int)PSTObject.convertLittleEndianBytesToLong(item.data, 4,
145 | * 8);
146 | * int low = (int)PSTObject.convertLittleEndianBytesToLong(item.data, 0, 4);
147 | *
148 | * return PSTObject.filetimeToDate(high, low);
149 | * }
150 | * }
151 | * return new Date(lDate);
152 | * }
153 | */
154 | }
155 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTRss.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.IOException;
37 | import java.util.HashMap;
38 |
39 | /**
40 | * Object that represents a RSS item
41 | *
42 | * @author Richard Johnson
43 | */
44 | public class PSTRss extends PSTMessage {
45 |
46 | /**
47 | * Instantiates a new Pst rss.
48 | *
49 | * @param theFile the the file
50 | * @param descriptorIndexNode the descriptor index node
51 | * @throws PSTException the pst exception
52 | * @throws IOException the io exception
53 | */
54 | public PSTRss(final PSTFile theFile, final DescriptorIndexNode descriptorIndexNode)
55 | throws PSTException, IOException {
56 | super(theFile, descriptorIndexNode);
57 | }
58 |
59 | /**
60 | * Instantiates a new Pst rss.
61 | *
62 | * @param theFile the the file
63 | * @param folderIndexNode the folder index node
64 | * @param table the table
65 | * @param localDescriptorItems the local descriptor items
66 | */
67 | public PSTRss(final PSTFile theFile, final DescriptorIndexNode folderIndexNode, final PSTTableBC table,
68 | final HashMap localDescriptorItems) {
69 | super(theFile, folderIndexNode, table, localDescriptorItems);
70 | }
71 |
72 | /**
73 | * Channel
74 | *
75 | * @return the post rss channel link
76 | */
77 | public String getPostRssChannelLink() {
78 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008900, PSTFile.PSETID_PostRss));
79 | }
80 |
81 | /**
82 | * Item link
83 | *
84 | * @return the post rss item link
85 | */
86 | public String getPostRssItemLink() {
87 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008901, PSTFile.PSETID_PostRss));
88 | }
89 |
90 | /**
91 | * Item hash Integer 32-bit signed
92 | *
93 | * @return the post rss item hash
94 | */
95 | public int getPostRssItemHash() {
96 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008902, PSTFile.PSETID_PostRss));
97 | }
98 |
99 | /**
100 | * Item GUID
101 | *
102 | * @return the post rss item guid
103 | */
104 | public String getPostRssItemGuid() {
105 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008903, PSTFile.PSETID_PostRss));
106 | }
107 |
108 | /**
109 | * Channel GUID
110 | *
111 | * @return the post rss channel
112 | */
113 | public String getPostRssChannel() {
114 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008904, PSTFile.PSETID_PostRss));
115 | }
116 |
117 | /**
118 | * Item XML
119 | *
120 | * @return the post rss item xml
121 | */
122 | public String getPostRssItemXml() {
123 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008905, PSTFile.PSETID_PostRss));
124 | }
125 |
126 | /**
127 | * Subscription
128 | *
129 | * @return the post rss subscription
130 | */
131 | public String getPostRssSubscription() {
132 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008906, PSTFile.PSETID_PostRss));
133 | }
134 |
135 | @Override
136 | public String toString() {
137 | return "Channel ASCII or Unicode string values: " + this.getPostRssChannelLink() + "\n"
138 | + "Item link ASCII or Unicode string values: " + this.getPostRssItemLink() + "\n"
139 | + "Item hash Integer 32-bit signed: " + this.getPostRssItemHash() + "\n"
140 | + "Item GUID ASCII or Unicode string values: " + this.getPostRssItemGuid() + "\n"
141 | + "Channel GUID ASCII or Unicode string values: " + this.getPostRssChannel() + "\n"
142 | + "Item XML ASCII or Unicode string values: " + this.getPostRssItemXml() + "\n"
143 | + "Subscription ASCII or Unicode string values: " + this.getPostRssSubscription();
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTable.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.IOException;
37 | import java.util.HashMap;
38 |
39 | /**
40 | * The PST Table is the workhorse of the whole system.
41 | * It allows for an item to be read and broken down into the individual
42 | * properties that it consists of.
43 | * For most PST Objects, it appears that only 7c and bc table types are used.
44 | *
45 | * @author Richard Johnson
46 | */
47 | class PSTTable {
48 |
49 | protected String tableType;
50 | protected byte tableTypeByte;
51 | protected int hidUserRoot;
52 |
53 | protected Long[] arrayBlocks = null;
54 |
55 | // info from the b5 header
56 | protected int sizeOfItemKey;
57 | protected int sizeOfItemValue;
58 | protected int hidRoot;
59 | protected int numberOfKeys = 0;
60 | protected int numberOfIndexLevels = 0;
61 |
62 | private final PSTNodeInputStream in;
63 |
64 | // private int[][] rgbiAlloc = null;
65 | // private byte[] data = null;
66 | private HashMap subNodeDescriptorItems = null;
67 |
68 | protected String description = "";
69 |
70 | protected PSTTable(final PSTNodeInputStream in, final HashMap subNodeDescriptorItems)
71 | throws PSTException, IOException {
72 | this.subNodeDescriptorItems = subNodeDescriptorItems;
73 | this.in = in;
74 |
75 | this.arrayBlocks = in.getBlockOffsets();
76 |
77 | // the next two bytes should be the table type (bSig)
78 | // 0xEC is HN (Heap-on-Node)
79 | in.seek(0);
80 | final byte[] headdata = new byte[4];
81 | in.readCompletely(headdata);
82 | if (headdata[2] != 0xffffffec) {
83 | // System.out.println(in.isEncrypted());
84 | PSTObject.decode(headdata);
85 | PSTObject.printHexFormatted(headdata, true);
86 | throw new PSTException("Unable to parse table, bad table type...");
87 | }
88 |
89 | this.tableTypeByte = headdata[3];
90 | switch (this.tableTypeByte) { // bClientSig
91 | case 0x7c: // Table Context (TC/HN)
92 | this.tableType = "7c";
93 | break;
94 | // case 0x9c:
95 | // tableType = "9c";
96 | // valid = true;
97 | // break;
98 | // case 0xa5:
99 | // tableType = "a5";
100 | // valid = true;
101 | // break;
102 | // case 0xac:
103 | // tableType = "ac";
104 | // valid = true;
105 | // break;
106 | // case 0xFFFFFFb5: // BTree-on-Heap (BTH)
107 | // tableType = "b5";
108 | // valid = true;
109 | // break;
110 | case 0xffffffbc:
111 | this.tableType = "bc"; // Property Context (PC/BTH)
112 | break;
113 | default:
114 | throw new PSTException(
115 | "Unable to parse table, bad table type. Unknown identifier: 0x" + Long.toHexString(headdata[3]));
116 | }
117 |
118 | this.hidUserRoot = (int) in.seekAndReadLong(4, 4); // hidUserRoot
119 | /*
120 | * System.out.printf("Table %s: hidUserRoot 0x%08X\n", tableType,
121 | * hidUserRoot);
122 | * /
123 | **/
124 |
125 | // all tables should have a BTHHEADER at hnid == 0x20
126 | final NodeInfo headerNodeInfo = this.getNodeInfo(0x20);
127 | headerNodeInfo.in.seek(headerNodeInfo.startOffset);
128 | int headerByte = headerNodeInfo.in.read() & 0xFF;
129 | if (headerByte != 0xb5) {
130 | headerNodeInfo.in.seek(headerNodeInfo.startOffset);
131 | headerByte = headerNodeInfo.in.read() & 0xFF;
132 | headerNodeInfo.in.seek(headerNodeInfo.startOffset);
133 | final byte[] tmp = new byte[1024];
134 | headerNodeInfo.in.readCompletely(tmp);
135 | PSTObject.printHexFormatted(tmp, true);
136 | // System.out.println(PSTObject.compEnc[headerByte]);
137 | throw new PSTException("Unable to parse table, can't find BTHHEADER header information: " + headerByte);
138 | }
139 |
140 | this.sizeOfItemKey = headerNodeInfo.in.read() & 0xFF; // Size of key in
141 | // key table
142 | this.sizeOfItemValue = headerNodeInfo.in.read() & 0xFF; // Size of value
143 | // in key table
144 |
145 | this.numberOfIndexLevels = headerNodeInfo.in.read() & 0xFF;
146 | if (this.numberOfIndexLevels != 0) {
147 | // System.out.println(this.tableType);
148 | // System.out.printf("Table with %d index levels\n",
149 | // numberOfIndexLevels);
150 | }
151 | // hidRoot = (int)PSTObject.convertLittleEndianBytesToLong(nodeInfo, 4,
152 | // 8); // hidRoot
153 | this.hidRoot = (int) headerNodeInfo.seekAndReadLong(4, 4);
154 | // System.out.println(hidRoot);
155 | // System.exit(0);
156 | /*
157 | * System.out.printf("Table %s: hidRoot 0x%08X\n", tableType, hidRoot);
158 | * /
159 | **/
160 | this.description += "Table (" + this.tableType + ")\n" + "hidUserRoot: " + this.hidUserRoot + " - 0x"
161 | + Long.toHexString(this.hidUserRoot) + "\n" + "Size Of Keys: " + this.sizeOfItemKey + " - 0x"
162 | + Long.toHexString(this.sizeOfItemKey) + "\n" + "Size Of Values: " + this.sizeOfItemValue + " - 0x"
163 | + Long.toHexString(this.sizeOfItemValue) + "\n" + "hidRoot: " + this.hidRoot + " - 0x"
164 | + Long.toHexString(this.hidRoot) + "\n";
165 | }
166 |
167 | protected void releaseRawData() {
168 | this.subNodeDescriptorItems = null;
169 | }
170 |
171 | /**
172 | * get the number of items stored in this table.
173 | *
174 | * @return row count
175 | */
176 | public int getRowCount() {
177 | return this.numberOfKeys;
178 | }
179 |
180 | class NodeInfo {
181 | int startOffset;
182 | int endOffset;
183 | // byte[] data;
184 | PSTNodeInputStream in;
185 |
186 | NodeInfo(final int start, final int end, final PSTNodeInputStream in) throws PSTException {
187 | if (start > end) {
188 | throw new PSTException(
189 | String.format("Invalid NodeInfo parameters: start %1$d is greater than end %2$d", start, end));
190 | }
191 | this.startOffset = start;
192 | this.endOffset = end;
193 | this.in = in;
194 | // this.data = data;
195 | }
196 |
197 | int length() {
198 | return this.endOffset - this.startOffset;
199 | }
200 |
201 | long seekAndReadLong(final long offset, final int length) throws IOException, PSTException {
202 | return this.in.seekAndReadLong(this.startOffset + offset, length);
203 | }
204 | }
205 |
206 | protected NodeInfo getNodeInfo(final int hnid) throws PSTException, IOException {
207 |
208 | // Zero-length node?
209 | if (hnid == 0) {
210 | return new NodeInfo(0, 0, this.in);
211 | }
212 |
213 | // Is it a subnode ID?
214 | if (this.subNodeDescriptorItems != null && this.subNodeDescriptorItems.containsKey(hnid)) {
215 | final PSTDescriptorItem item = this.subNodeDescriptorItems.get(hnid);
216 | // byte[] data;
217 | NodeInfo subNodeInfo = null;
218 |
219 | try {
220 | // data = item.getData();
221 | final PSTNodeInputStream subNodeIn = new PSTNodeInputStream(this.in.getPSTFile(), item);
222 | subNodeInfo = new NodeInfo(0, (int) subNodeIn.length(), subNodeIn);
223 | } catch (final IOException e) {
224 | throw new PSTException(String.format("IOException reading subNode: 0x%08X", hnid));
225 | }
226 |
227 | // return new NodeInfo(0, data.length, data);
228 | return subNodeInfo;
229 | }
230 |
231 | if ((hnid & 0x1F) != 0) {
232 | // Some kind of external node
233 | return null;
234 | }
235 |
236 | final int whichBlock = (hnid >>> 16);
237 | if (whichBlock > this.arrayBlocks.length) {
238 | // Block doesn't exist!
239 | String err = String.format("getNodeInfo: block doesn't exist! hnid = 0x%08X\n", hnid);
240 | err += String.format("getNodeInfo: block doesn't exist! whichBlock = 0x%08X\n", whichBlock);
241 | err += "\n" + (this.arrayBlocks.length);
242 | throw new PSTException(err);
243 | // return null;
244 | }
245 |
246 | // A normal node in a local heap
247 | final int index = (hnid & 0xFFFF) >> 5;
248 | int blockOffset = 0;
249 | if (whichBlock > 0) {
250 | blockOffset = this.arrayBlocks[whichBlock - 1].intValue();
251 | }
252 | // Get offset of HN page map
253 | int iHeapNodePageMap = (int) this.in.seekAndReadLong(blockOffset, 2) + blockOffset;
254 | final int cAlloc = (int) this.in.seekAndReadLong(iHeapNodePageMap, 2);
255 | if (index >= cAlloc + 1) {
256 | throw new PSTException(String.format("getNodeInfo: node index doesn't exist! nid = 0x%08X\n", hnid));
257 | // return null;
258 | }
259 | iHeapNodePageMap += (2 * index) + 2;
260 | final int start = (int) this.in.seekAndReadLong(iHeapNodePageMap, 2) + blockOffset;
261 | final int end = (int) this.in.seekAndReadLong(iHeapNodePageMap + 2, 2) + blockOffset;
262 |
263 | final NodeInfo out = new NodeInfo(start, end, this.in);
264 | return out;
265 | }
266 |
267 | }
268 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTable7CItem.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | /**
37 | * Items found in the 7c tables
38 | *
39 | * @author Richard Johnson
40 | */
41 | class PSTTable7CItem extends PSTTableItem {
42 |
43 | @Override
44 | public String toString() {
45 | return "7c Table Item: " + super.toString() + "\n";
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTableBC.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.util.HashMap;
37 |
38 | /**
39 | * The BC Table type. (Property Context)
40 | * Used by pretty much everything.
41 | *
42 | * @author Richard Johnson
43 | */
44 | class PSTTableBC extends PSTTable {
45 |
46 | private final HashMap items = new HashMap<>();
47 |
48 | private final StringBuilder descBuffer = new StringBuilder();
49 | private boolean isDescNotYetInitiated = false;
50 |
51 | PSTTableBC(final PSTNodeInputStream in) throws PSTException, java.io.IOException {
52 | super(in, new HashMap());
53 | // data = null; // No direct access to data!
54 |
55 | if (this.tableTypeByte != 0xffffffbc) {
56 | // System.out.println(Long.toHexString(this.tableTypeByte));
57 | throw new PSTException("unable to create PSTTableBC, table does not appear to be a bc!");
58 | }
59 |
60 | // go through each of the entries.
61 | // byte[] keyTableInfo = getNodeInfo(hidRoot);
62 | final NodeInfo keyTableInfoNodeInfo = this.getNodeInfo(this.hidRoot);
63 | final byte[] keyTableInfo = new byte[keyTableInfoNodeInfo.length()];
64 | keyTableInfoNodeInfo.in.seek(keyTableInfoNodeInfo.startOffset);
65 | keyTableInfoNodeInfo.in.readCompletely(keyTableInfo);
66 |
67 | // PSTObject.printHexFormatted(keyTableInfo, true);
68 | // System.out.println(in.length());
69 | // System.exit(0);
70 | this.numberOfKeys = keyTableInfo.length / (this.sizeOfItemKey + this.sizeOfItemValue);
71 |
72 | this.descBuffer.append("Number of entries: " + this.numberOfKeys + "\n");
73 |
74 | // Read the key table
75 | int offset = 0;
76 | for (int x = 0; x < this.numberOfKeys; x++) {
77 |
78 | final PSTTableBCItem item = new PSTTableBCItem();
79 | item.itemIndex = x;
80 | item.entryType = (int) PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset + 0, offset + 2);
81 | // item.entryType =(int)in.seekAndReadLong(offset, 2);
82 | item.entryValueType = (int) PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset + 2, offset + 4);
83 | // item.entryValueType = (int)in.seekAndReadLong(offset+2, 2);
84 | item.entryValueReference = (int) PSTObject.convertLittleEndianBytesToLong(keyTableInfo, offset + 4,
85 | offset + 8);
86 | // item.entryValueReference = (int)in.seekAndReadLong(offset+4, 4);
87 |
88 | // Data is in entryValueReference for all types <= 4 bytes long
89 | switch (item.entryValueType) {
90 |
91 | case 0x0002: // 16bit integer
92 | item.entryValueReference &= 0xFFFF;
93 | case 0x0003: // 32bit integer
94 | case 0x000A: // 32bit error code
95 | /*
96 | * System.out.printf("Integer%s: 0x%04X:%04X, %d\n",
97 | * (item.entryValueType == 0x0002) ? "16" : "32",
98 | * item.entryType, item.entryValueType,
99 | * item.entryValueReference);
100 | * /
101 | **/
102 | case 0x0001: // Place-holder
103 | case 0x0004: // 32bit floating
104 | item.isExternalValueReference = true;
105 | break;
106 |
107 | case 0x000b: // Boolean - a single byte
108 | item.entryValueReference &= 0xFF;
109 | /*
110 | * System.out.printf("boolean: 0x%04X:%04X, %s\n",
111 | * item.entryType, item.entryValueType,
112 | * (item.entryValueReference == 0) ? "false" : "true");
113 | * /
114 | **/
115 | item.isExternalValueReference = true;
116 | break;
117 |
118 | case 0x000D:
119 | default:
120 | // Is it in the local heap?
121 | item.isExternalValueReference = true; // Assume not
122 | // System.out.println(item.entryValueReference);
123 | // byte[] nodeInfo = getNodeInfo(item.entryValueReference);
124 | final NodeInfo nodeInfoNodeInfo = this.getNodeInfo(item.entryValueReference);
125 | if (nodeInfoNodeInfo == null) {
126 | // It's an external reference that we don't deal with here.
127 | /*
128 | * System.out.printf("%s: %shid 0x%08X\n",
129 | * (item.entryValueType == 0x1f || item.entryValueType ==
130 | * 0x1e) ? "String" : "Other",
131 | * PSTFile.getPropertyDescription(item.entryType,
132 | * item.entryValueType),
133 | * item.entryValueReference);
134 | * /
135 | **/
136 | } else {
137 | // Make a copy of the data
138 | // item.data = new
139 | // byte[nodeInfo.endOffset-nodeInfo.startOffset];
140 | final byte[] nodeInfo = new byte[nodeInfoNodeInfo.length()];
141 | nodeInfoNodeInfo.in.seek(nodeInfoNodeInfo.startOffset);
142 | nodeInfoNodeInfo.in.readCompletely(nodeInfo);
143 | item.data = nodeInfo; // should be new array, so just use it
144 | // System.arraycopy(nodeInfo.data, nodeInfo.startOffset,
145 | // item.data, 0, item.data.length);
146 | item.isExternalValueReference = false;
147 | /*
148 | * if ( item.entryValueType == 0x1f ||
149 | * item.entryValueType == 0x1e )
150 | * {
151 | * try {
152 | * // if ( item.entryType == 0x0037 )
153 | * {
154 | * String temp = new String(item.data, item.entryValueType
155 | * == 0x1E ? "UTF8" : "UTF-16LE");
156 | * System.out.printf("String: 0x%04X:%04X, \"%s\"\n",
157 | * item.entryType, item.entryValueType, temp);
158 | * }
159 | * } catch (UnsupportedEncodingException e) {
160 | * e.printStackTrace();
161 | * }
162 | * }
163 | * else
164 | * {
165 | *
166 | * System.out.printf("Other: 0x%04X:%04X, %d bytes\n",
167 | * item.entryType, item.entryValueType, item.data.length);
168 | *
169 | * }
170 | * /
171 | **/
172 | }
173 | break;
174 | }
175 |
176 | offset = offset + 8;
177 |
178 | this.items.put(item.entryType, item);
179 | // description += item.toString()+"\n\n";
180 |
181 | }
182 |
183 | this.releaseRawData();
184 | }
185 |
186 | /**
187 | * get the items parsed out of this table.
188 | *
189 | * @return items
190 | */
191 | public HashMap getItems() {
192 | return this.items;
193 | }
194 |
195 | @Override
196 | public String toString() {
197 |
198 | if (this.isDescNotYetInitiated) {
199 | this.isDescNotYetInitiated = false;
200 |
201 | for (final Integer curItem : this.items.keySet()) {
202 | this.descBuffer.append(this.items.get(curItem).toString() + "\n\n");
203 | }
204 | // description += item.toString()+"\n\n";
205 | }
206 |
207 | return this.description + this.descBuffer.toString();
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTableBCItem.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | /**
37 | * Items within the BC Table
38 | *
39 | * @author Richard Johnson
40 | */
41 | class PSTTableBCItem extends PSTTableItem {
42 |
43 | @Override
44 | public String toString() {
45 | return "Table Item: " + super.toString() + "\n";
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTableItem.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.UnsupportedEncodingException;
37 | import java.nio.charset.Charset;
38 | import java.text.SimpleDateFormat;
39 | import java.util.Date;
40 | import java.util.SimpleTimeZone;
41 |
42 | /**
43 | * Generic table item.
44 | * Provides some basic string functions
45 | *
46 | * @author Richard Johnson
47 | */
48 | class PSTTableItem {
49 |
50 | public static final int VALUE_TYPE_PT_UNICODE = 0x1f;
51 | public static final int VALUE_TYPE_PT_STRING8 = 0x1e;
52 | public static final int VALUE_TYPE_PT_BIN = 0x102;
53 |
54 | public int itemIndex = 0;
55 | public int entryType = 0;
56 | public int entryValueType = 0;
57 | public int entryValueReference = 0;
58 | public byte[] data = new byte[0];
59 | public boolean isExternalValueReference = false;
60 |
61 | public long getLongValue() {
62 | if (this.data.length > 0) {
63 | return PSTObject.convertLittleEndianBytesToLong(this.data);
64 | }
65 | return -1;
66 | }
67 |
68 | /**
69 | * Gets a string value of the data for a given codepage (charset name)
70 | *
71 | * @param codepage the codepage
72 | * @return the string value
73 | */
74 | public String getStringValue(String codepage) {
75 | return this.getStringValue(this.entryValueType,codepage);
76 | }
77 |
78 | /**
79 | * Gets a string value of the data
80 | *
81 | * @param stringType the string type
82 | * @param codepage the codepage
83 | * @return string value
84 | */
85 | public String getStringValue(final int stringType,String codepage) {
86 |
87 | if (stringType == VALUE_TYPE_PT_UNICODE) {
88 | // we are a nice little-endian unicode string.
89 | try {
90 | if (this.isExternalValueReference) {
91 | return "External string reference!";
92 | }
93 | return new String(this.data, "UTF-16LE").trim();
94 | } catch (final UnsupportedEncodingException e) {
95 |
96 | System.err.println("Error decoding string: " + this.data.toString());
97 | return "";
98 | }
99 | }
100 |
101 | if (stringType == VALUE_TYPE_PT_STRING8) {
102 | // System.out.println("Warning! decoding string8 without charset:
103 | // "+this.entryType + " - "+ Integer.toHexString(this.entryType));
104 | return new String(this.data, Charset.forName(codepage)).trim();
105 | }
106 |
107 | final StringBuffer outputBuffer = new StringBuffer();
108 | /*
109 | * if ( stringType == VALUE_TYPE_PT_BIN) {
110 | * int theChar;
111 | * for (int x = 0; x < data.length; x++) {
112 | * theChar = data[x] & 0xFF;
113 | * outputBuffer.append((char)theChar);
114 | * }
115 | * }
116 | * else
117 | * /
118 | **/
119 | {
120 | // we are not a normal string, give a hexish sort of output
121 | final StringBuffer hexOut = new StringBuffer();
122 | for (final byte element : this.data) {
123 | final int valueChar = element & 0xff;
124 | if (Character.isLetterOrDigit((char) valueChar)) {
125 | outputBuffer.append((char) valueChar);
126 | outputBuffer.append(" ");
127 | } else {
128 | outputBuffer.append(". ");
129 | }
130 | final String hexValue = Long.toHexString(valueChar);
131 | hexOut.append(hexValue);
132 | hexOut.append(" ");
133 | if (hexValue.length() > 1) {
134 | outputBuffer.append(" ");
135 | }
136 | }
137 | outputBuffer.append("\n");
138 | outputBuffer.append(" ");
139 | outputBuffer.append(hexOut);
140 | }
141 |
142 | return new String(outputBuffer);
143 | }
144 |
145 | @Override
146 | public String toString() {
147 | final String ret = PSTFile.getPropertyDescription(this.entryType, this.entryValueType);
148 |
149 | if (this.entryValueType == 0x000B) {
150 | return ret + (this.entryValueReference == 0 ? "false" : "true");
151 | }
152 |
153 | if (this.isExternalValueReference) {
154 | // Either a true external reference, or entryValueReference contains
155 | // the actual data
156 | return ret + String.format("0x%08X (%d)", this.entryValueReference, this.entryValueReference);
157 | }
158 |
159 | if (this.entryValueType == 0x0005 || this.entryValueType == 0x0014) {
160 | // 64bit data
161 | if (this.data == null) {
162 | return ret + "no data";
163 | }
164 | if (this.data.length == 8) {
165 | final long l = PSTObject.convertLittleEndianBytesToLong(this.data, 0, 8);
166 | return String.format("%s0x%016X (%d)", ret, l, l);
167 | } else {
168 | return String.format("%s invalid data length: %d", ret, this.data.length);
169 | }
170 | }
171 |
172 | if (this.entryValueType == 0x0040) {
173 | // It's a date...
174 | final int high = (int) PSTObject.convertLittleEndianBytesToLong(this.data, 4, 8);
175 | final int low = (int) PSTObject.convertLittleEndianBytesToLong(this.data, 0, 4);
176 |
177 | final Date d = PSTObject.filetimeToDate(high, low);
178 | this.dateFormatter.setTimeZone(utcTimeZone);
179 | return ret + this.dateFormatter.format(d);
180 | }
181 |
182 | if (this.entryValueType == 0x001F) {
183 | // Unicode string
184 | String s;
185 | try {
186 | s = new String(this.data, "UTF-16LE");
187 | } catch (final UnsupportedEncodingException e) {
188 | System.err.println("Error decoding string: " + this.data.toString());
189 | s = "";
190 | }
191 |
192 | if (s.length() >= 2 && s.charAt(0) == 0x0001) {
193 | return String.format("%s [%04X][%04X]%s", ret, (short) s.charAt(0), (short) s.charAt(1),
194 | s.substring(2));
195 | }
196 |
197 | return ret + s;
198 | }
199 |
200 | return ret + this.getStringValue("UTF-8");
201 | }
202 |
203 | private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
204 | private static SimpleTimeZone utcTimeZone = new SimpleTimeZone(0, "UTC");
205 | }
206 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTask.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.io.IOException;
37 | import java.util.Date;
38 | import java.util.HashMap;
39 |
40 | /**
41 | * Object that represents Task items
42 | *
43 | * @author Richard Johnson
44 | */
45 | public class PSTTask extends PSTMessage {
46 |
47 | /**
48 | * Instantiates a new Pst task.
49 | *
50 | * @param theFile the the file
51 | * @param descriptorIndexNode the descriptor index node
52 | * @throws PSTException the pst exception
53 | * @throws IOException the io exception
54 | */
55 | public PSTTask(final PSTFile theFile, final DescriptorIndexNode descriptorIndexNode)
56 | throws PSTException, IOException {
57 | super(theFile, descriptorIndexNode);
58 | }
59 |
60 | /**
61 | * Instantiates a new Pst task.
62 | *
63 | * @param theFile the the file
64 | * @param folderIndexNode the folder index node
65 | * @param table the table
66 | * @param localDescriptorItems the local descriptor items
67 | */
68 | public PSTTask(final PSTFile theFile, final DescriptorIndexNode folderIndexNode, final PSTTableBC table,
69 | final HashMap localDescriptorItems) {
70 | super(theFile, folderIndexNode, table, localDescriptorItems);
71 | }
72 |
73 | /**
74 | * Status Integer 32-bit signed 0x0 => Not started
75 | *
76 | * @return the task status
77 | */
78 | public int getTaskStatus() {
79 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008101, PSTFile.PSETID_Task));
80 | }
81 |
82 | /**
83 | * Percent Complete Floating point double precision (64-bit)
84 | *
85 | * @return the percent complete
86 | */
87 | public double getPercentComplete() {
88 | return this.getDoubleItem(this.pstFile.getNameToIdMapItem(0x00008102, PSTFile.PSETID_Task));
89 | }
90 |
91 | /**
92 | * Is team task Boolean
93 | *
94 | * @return the boolean
95 | */
96 | public boolean isTeamTask() {
97 | return this.getBooleanItem(this.pstFile.getNameToIdMapItem(0x00008103, PSTFile.PSETID_Task));
98 | }
99 |
100 | /**
101 | * Date completed Filetime
102 | *
103 | * @return the task date completed
104 | */
105 | public Date getTaskDateCompleted() {
106 | return this.getDateItem(this.pstFile.getNameToIdMapItem(0x0000810f, PSTFile.PSETID_Task));
107 | }
108 |
109 | /**
110 | * Actual effort in minutes Integer 32-bit signed
111 | *
112 | * @return the task actual effort
113 | */
114 | public int getTaskActualEffort() {
115 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008110, PSTFile.PSETID_Task));
116 | }
117 |
118 | /**
119 | * Total effort in minutes Integer 32-bit signed
120 | *
121 | * @return the task estimated effort
122 | */
123 | public int getTaskEstimatedEffort() {
124 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008111, PSTFile.PSETID_Task));
125 | }
126 |
127 | /**
128 | * Task version Integer 32-bit signed FTK: Access count
129 | *
130 | * @return the task version
131 | */
132 | public int getTaskVersion() {
133 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008112, PSTFile.PSETID_Task));
134 | }
135 |
136 | /**
137 | * Complete Boolean
138 | *
139 | * @return the boolean
140 | */
141 | public boolean isTaskComplete() {
142 | return this.getBooleanItem(this.pstFile.getNameToIdMapItem(0x0000811c, PSTFile.PSETID_Task));
143 | }
144 |
145 | /**
146 | * Owner ASCII or Unicode string
147 | *
148 | * @return the task owner
149 | */
150 | public String getTaskOwner() {
151 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x0000811f, PSTFile.PSETID_Task));
152 | }
153 |
154 | /**
155 | * Delegator ASCII or Unicode string
156 | *
157 | * @return the task assigner
158 | */
159 | public String getTaskAssigner() {
160 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008121, PSTFile.PSETID_Task));
161 | }
162 |
163 | /**
164 | * Unknown ASCII or Unicode string
165 | *
166 | * @return the task last user
167 | */
168 | public String getTaskLastUser() {
169 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008122, PSTFile.PSETID_Task));
170 | }
171 |
172 | /**
173 | * Ordinal Integer 32-bit signed
174 | *
175 | * @return the task ordinal
176 | */
177 | public int getTaskOrdinal() {
178 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008123, PSTFile.PSETID_Task));
179 | }
180 |
181 | /**
182 | * Is recurring Boolean
183 | *
184 | * @return the boolean
185 | */
186 | public boolean isTaskFRecurring() {
187 | return this.getBooleanItem(this.pstFile.getNameToIdMapItem(0x00008126, PSTFile.PSETID_Task));
188 | }
189 |
190 | /**
191 | * Role ASCII or Unicode string
192 | *
193 | * @return the task role
194 | */
195 | public String getTaskRole() {
196 | return this.getStringItem(this.pstFile.getNameToIdMapItem(0x00008127, PSTFile.PSETID_Task));
197 | }
198 |
199 | /**
200 | * Ownership Integer 32-bit signed
201 | *
202 | * @return the task ownership
203 | */
204 | public int getTaskOwnership() {
205 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x00008129, PSTFile.PSETID_Task));
206 | }
207 |
208 | /**
209 | * Delegation State
210 | *
211 | * @return the acceptance state
212 | */
213 | public int getAcceptanceState() {
214 | return this.getIntItem(this.pstFile.getNameToIdMapItem(0x0000812a, PSTFile.PSETID_Task));
215 | }
216 |
217 | @Override
218 | public String toString() {
219 | return "Status Integer 32-bit signed 0x0 => Not started [TODO]: " + this.getTaskStatus() + "\n"
220 | + "Percent Complete Floating point double precision (64-bit): " + this.getPercentComplete() + "\n"
221 | + "Is team task Boolean: " + this.isTeamTask() + "\n" + "Start date Filetime: " + this.getTaskStartDate()
222 | + "\n" + "Due date Filetime: " + this.getTaskDueDate() + "\n" + "Date completed Filetime: "
223 | + this.getTaskDateCompleted() + "\n" + "Actual effort in minutes Integer 32-bit signed: "
224 | + this.getTaskActualEffort() + "\n" + "Total effort in minutes Integer 32-bit signed: "
225 | + this.getTaskEstimatedEffort() + "\n" + "Task version Integer 32-bit signed FTK: Access count: "
226 | + this.getTaskVersion() + "\n" + "Complete Boolean: " + this.isTaskComplete() + "\n"
227 | + "Owner ASCII or Unicode string: " + this.getTaskOwner() + "\n" + "Delegator ASCII or Unicode string: "
228 | + this.getTaskAssigner() + "\n" + "Unknown ASCII or Unicode string: " + this.getTaskLastUser() + "\n"
229 | + "Ordinal Integer 32-bit signed: " + this.getTaskOrdinal() + "\n" + "Is recurring Boolean: "
230 | + this.isTaskFRecurring() + "\n" + "Role ASCII or Unicode string: " + this.getTaskRole() + "\n"
231 | + "Ownership Integer 32-bit signed: " + this.getTaskOwnership() + "\n" + "Delegation State: "
232 | + this.getAcceptanceState();
233 |
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/java/com/pff/PSTTimeZone.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Richard Johnson & Orin Eman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * ---
17 | *
18 | * This file is part of java-libpst.
19 | *
20 | * java-libpst is free software: you can redistribute it and/or modify
21 | * it under the terms of the GNU Lesser General Public License as published by
22 | * the Free Software Foundation, either version 3 of the License, or
23 | * (at your option) any later version.
24 | *
25 | * java-libpst is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Lesser General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Lesser General Public License
31 | * along with java-libpst. If not, see .
32 | *
33 | */
34 | package com.pff;
35 |
36 | import java.util.Calendar;
37 | import java.util.SimpleTimeZone;
38 |
39 | /**
40 | * Class containing time zone information
41 | *
42 | * @author Orin Eman
43 | *
44 | *
45 | */
46 |
47 | public class PSTTimeZone {
48 | PSTTimeZone(final byte[] timeZoneData) {
49 | this.rule = null;
50 | this.name = "";
51 |
52 | try {
53 | final int headerLen = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, 2, 4);
54 | final int nameLen = 2 * (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, 6, 8);
55 | this.name = new String(timeZoneData, 8, nameLen, "UTF-16LE");
56 | int ruleOffset = 8 + nameLen;
57 | final int nRules = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, ruleOffset, ruleOffset + 2);
58 |
59 | ruleOffset = 4 + headerLen;
60 | for (int rule = 0; rule < nRules; ++rule) {
61 | // Is this rule the effective rule?
62 | final int flags = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, ruleOffset + 4,
63 | ruleOffset + 6);
64 | if ((flags & 0x0002) != 0) {
65 | this.rule = new TZRule(timeZoneData, ruleOffset + 6);
66 | break;
67 | }
68 | ruleOffset += 66;
69 | }
70 | } catch (final Exception e) {
71 | System.err.printf("Exception reading timezone: %s\n", e.toString());
72 | e.printStackTrace();
73 | this.rule = null;
74 | this.name = "";
75 | }
76 | }
77 |
78 | PSTTimeZone(String name, final byte[] timeZoneData) {
79 | this.name = name;
80 | this.rule = null;
81 |
82 | try {
83 | this.rule = new TZRule(new SYSTEMTIME(), timeZoneData, 0);
84 | } catch (final Exception e) {
85 | System.err.printf("Exception reading timezone: %s\n", e.toString());
86 | e.printStackTrace();
87 | this.rule = null;
88 | name = "";
89 | }
90 | }
91 |
92 | public String getName() {
93 | return this.name;
94 | }
95 |
96 | public SimpleTimeZone getSimpleTimeZone() {
97 | if (this.simpleTimeZone != null) {
98 | return this.simpleTimeZone;
99 | }
100 |
101 | if (this.rule.startStandard.wMonth == 0) {
102 | // A time zone with no daylight savings time
103 | this.simpleTimeZone = new SimpleTimeZone((this.rule.lBias + this.rule.lStandardBias) * 60 * 1000,
104 | this.name);
105 |
106 | return this.simpleTimeZone;
107 | }
108 |
109 | final int startMonth = (this.rule.startDaylight.wMonth - 1) + Calendar.JANUARY;
110 | final int startDayOfMonth = (this.rule.startDaylight.wDay == 5) ? -1
111 | : ((this.rule.startDaylight.wDay - 1) * 7) + 1;
112 | final int startDayOfWeek = this.rule.startDaylight.wDayOfWeek + Calendar.SUNDAY;
113 | final int endMonth = (this.rule.startStandard.wMonth - 1) + Calendar.JANUARY;
114 | final int endDayOfMonth = (this.rule.startStandard.wDay == 5) ? -1
115 | : ((this.rule.startStandard.wDay - 1) * 7) + 1;
116 | final int endDayOfWeek = this.rule.startStandard.wDayOfWeek + Calendar.SUNDAY;
117 | final int savings = (this.rule.lStandardBias - this.rule.lDaylightBias) * 60 * 1000;
118 |
119 | this.simpleTimeZone = new SimpleTimeZone(-((this.rule.lBias + this.rule.lStandardBias) * 60 * 1000), this.name,
120 | startMonth, startDayOfMonth, -startDayOfWeek,
121 | (((((this.rule.startDaylight.wHour * 60) + this.rule.startDaylight.wMinute) * 60)
122 | + this.rule.startDaylight.wSecond) * 1000) + this.rule.startDaylight.wMilliseconds,
123 | endMonth, endDayOfMonth, -endDayOfWeek,
124 | (((((this.rule.startStandard.wHour * 60) + this.rule.startStandard.wMinute) * 60)
125 | + this.rule.startStandard.wSecond) * 1000) + this.rule.startStandard.wMilliseconds,
126 | savings);
127 |
128 | return this.simpleTimeZone;
129 | }
130 |
131 | public boolean isEqual(final PSTTimeZone rhs) {
132 | if (this.name.equalsIgnoreCase(rhs.name)) {
133 | if (this.rule.isEqual(rhs.rule)) {
134 | return true;
135 | }
136 |
137 | System.err.printf("Warning: different timezones with the same name: %s\n", this.name);
138 | }
139 | return false;
140 | }
141 |
142 | public SYSTEMTIME getStart() {
143 | return this.rule.dtStart;
144 | }
145 |
146 | public int getBias() {
147 | return this.rule.lBias;
148 | }
149 |
150 | public int getStandardBias() {
151 | return this.rule.lStandardBias;
152 | }
153 |
154 | public int getDaylightBias() {
155 | return this.rule.lDaylightBias;
156 | }
157 |
158 | public SYSTEMTIME getDaylightStart() {
159 | return this.rule.startDaylight;
160 | }
161 |
162 | public SYSTEMTIME getStandardStart() {
163 | return this.rule.startStandard;
164 | }
165 |
166 | public class SYSTEMTIME {
167 |
168 | SYSTEMTIME() {
169 | this.wYear = 0;
170 | this.wMonth = 0;
171 | this.wDayOfWeek = 0;
172 | this.wDay = 0;
173 | this.wHour = 0;
174 | this.wMinute = 0;
175 | this.wSecond = 0;
176 | this.wMilliseconds = 0;
177 | }
178 |
179 | SYSTEMTIME(final byte[] timeZoneData, final int offset) {
180 | this.wYear = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset, offset + 2) & 0x7FFF);
181 | this.wMonth = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 2, offset + 4)
182 | & 0x7FFF);
183 | this.wDayOfWeek = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 4, offset + 6)
184 | & 0x7FFF);
185 | this.wDay = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 6, offset + 8)
186 | & 0x7FFF);
187 | this.wHour = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 8, offset + 10)
188 | & 0x7FFF);
189 | this.wMinute = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 10, offset + 12)
190 | & 0x7FFF);
191 | this.wSecond = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 12, offset + 14)
192 | & 0x7FFF);
193 | this.wMilliseconds = (short) (PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 14,
194 | offset + 16) & 0x7FFF);
195 | }
196 |
197 | boolean isEqual(final SYSTEMTIME rhs) {
198 | return this.wYear == rhs.wYear && this.wMonth == rhs.wMonth && this.wDayOfWeek == rhs.wDayOfWeek
199 | && this.wDay == rhs.wDay && this.wHour == rhs.wHour && this.wMinute == rhs.wMinute
200 | && this.wSecond == rhs.wSecond && this.wMilliseconds == rhs.wMilliseconds;
201 | }
202 |
203 | public short wYear;
204 | public short wMonth;
205 | public short wDayOfWeek;
206 | public short wDay;
207 | public short wHour;
208 | public short wMinute;
209 | public short wSecond;
210 | public short wMilliseconds;
211 | }
212 |
213 | /**
214 | * A static copy of the UTC time zone, available for others to use
215 | */
216 | public static SimpleTimeZone utcTimeZone = new SimpleTimeZone(0, "UTC");
217 |
218 | private class TZRule {
219 |
220 | TZRule(final SYSTEMTIME dtStart, final byte[] timeZoneData, final int offset) {
221 | this.dtStart = dtStart;
222 | this.InitBiases(timeZoneData, offset);
223 | @SuppressWarnings("unused")
224 | final short wStandardYear = (short) PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 12,
225 | offset + 14);
226 | this.startStandard = new SYSTEMTIME(timeZoneData, offset + 14);
227 | @SuppressWarnings("unused")
228 | final short wDaylightYear = (short) PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 30,
229 | offset + 32);
230 | this.startDaylight = new SYSTEMTIME(timeZoneData, offset + 32);
231 | }
232 |
233 | TZRule(final byte[] timeZoneData, final int offset) {
234 | this.dtStart = new SYSTEMTIME(timeZoneData, offset);
235 | this.InitBiases(timeZoneData, offset + 16);
236 | this.startStandard = new SYSTEMTIME(timeZoneData, offset + 28);
237 | this.startDaylight = new SYSTEMTIME(timeZoneData, offset + 44);
238 | }
239 |
240 | private void InitBiases(final byte[] timeZoneData, final int offset) {
241 | this.lBias = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset, offset + 4);
242 | this.lStandardBias = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 4, offset + 8);
243 | this.lDaylightBias = (int) PSTObject.convertLittleEndianBytesToLong(timeZoneData, offset + 8, offset + 12);
244 | }
245 |
246 | boolean isEqual(final TZRule rhs) {
247 | return this.dtStart.isEqual(rhs.dtStart) && this.lBias == rhs.lBias
248 | && this.lStandardBias == rhs.lStandardBias && this.lDaylightBias == rhs.lDaylightBias
249 | && this.startStandard.isEqual(rhs.startStandard) && this.startDaylight.isEqual(rhs.startDaylight);
250 | }
251 |
252 | SYSTEMTIME dtStart;
253 | int lBias;
254 | int lStandardBias;
255 | int lDaylightBias;
256 | SYSTEMTIME startStandard;
257 | SYSTEMTIME startDaylight;
258 | }
259 |
260 | private String name;
261 | private TZRule rule;
262 | private SimpleTimeZone simpleTimeZone = null;
263 | }
264 |
--------------------------------------------------------------------------------
/src/main/java/example/Test.java:
--------------------------------------------------------------------------------
1 | package example;
2 |
3 | import java.util.Vector;
4 |
5 | import com.pff.PSTException;
6 | import com.pff.PSTFile;
7 | import com.pff.PSTFolder;
8 | import com.pff.PSTMessage;
9 |
10 | public class Test {
11 | public static void main(final String[] args) {
12 | new Test(args[0]);
13 | }
14 |
15 | public Test(final String filename) {
16 | try {
17 | final PSTFile pstFile = new PSTFile(filename);
18 | System.out.println(pstFile.getMessageStore().getDisplayName());
19 | this.processFolder(pstFile.getRootFolder());
20 | } catch (final Exception err) {
21 | err.printStackTrace();
22 | }
23 | }
24 |
25 | int depth = -1;
26 |
27 | public void processFolder(final PSTFolder folder) throws PSTException, java.io.IOException {
28 | this.depth++;
29 | // the root folder doesn't have a display name
30 | if (this.depth > 0) {
31 | this.printDepth();
32 | System.out.println(folder.getDisplayName());
33 | }
34 |
35 | // go through the folders...
36 | if (folder.hasSubfolders()) {
37 | final Vector childFolders = folder.getSubFolders();
38 | for (final PSTFolder childFolder : childFolders) {
39 | this.processFolder(childFolder);
40 | }
41 | }
42 |
43 | // and now the emails for this folder
44 | if (folder.getContentCount() > 0) {
45 | this.depth++;
46 | PSTMessage email = (PSTMessage) folder.getNextChild();
47 | while (email != null) {
48 | if (!email.getMessageClass().equals("IPM.Note")) {
49 | this.printDepth();
50 | System.out.println("Email: [" + email.getMessageClass() + "]" + email.getDescriptorNodeId() + " - " + email.getSubject());
51 | }
52 | email = (PSTMessage) folder.getNextChild();
53 | }
54 | this.depth--;
55 | }
56 | this.depth--;
57 | }
58 |
59 | public void printDepth() {
60 | for (int x = 0; x < this.depth - 1; x++) {
61 | System.out.print(" | ");
62 | }
63 | System.out.print(" |- ");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/example/TestGui.rs:
--------------------------------------------------------------------------------
1 | EmailTableModel
2 | TestGui
3 |
--------------------------------------------------------------------------------
/src/main/resources/InternetCodepages.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2010 Richard Johnson & Orin Eman
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # ---
16 | #
17 | # This file is part of java-libpst.
18 | #
19 | # java-libpst is free software: you can redistribute it and/or modify
20 | # it under the terms of the GNU Lesser General Public License as published by
21 | # the Free Software Foundation, either version 3 of the License, or
22 | # (at your option) any later version.
23 | #
24 | # java-libpst is distributed in the hope that it will be useful,
25 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
26 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 | # GNU Lesser General Public License for more details.
28 | #
29 | # You should have received a copy of the GNU Lesser General Public License
30 | # along with java-libpst. If not, see .
31 |
32 | 28596=iso-8859-6
33 | 1256=windows-1256
34 | 28594=iso-8859-4
35 | 1257=windows-1257
36 | 28592=iso-8859-2
37 | 1250=windows-1250
38 | 936=gb2312
39 | 52936=hz-gb-2312
40 | 54936=gb18030
41 | 950=big5
42 | 28595=iso-8859-5
43 | 20866=koi8-r
44 | 21866=koi8-u
45 | 1251=windows-1251
46 | 28597=iso-8859-7
47 | 1253=windows-1253
48 | 38598=iso-8859-8-i
49 | 1255=windows-1255
50 | 51932=euc-jp
51 | 50220=iso-2022-jp
52 | 50221=csISO2022JP
53 | 932=iso-2022-jp
54 | 949=ks_c_5601-1987
55 | 51949=euc-kr
56 | 28593=iso-8859-3
57 | 28605=iso-8859-15
58 | 874=windows-874
59 | 28599=iso-8859-9
60 | 1254=windows-1254
61 | 65000=utf-7
62 | 65001=utf-8
63 | 20127=us-ascii
64 | 1258=windows-1258
65 | 28591=iso-8859-1
66 | 1252=Windows-1252
67 |
--------------------------------------------------------------------------------
/src/main/resources/PIDShortID.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjohnsondev/java-libpst/f158a64acf2a0e46ac3bd699bc7a5a8da6c40d26/src/main/resources/PIDShortID.csv
--------------------------------------------------------------------------------
/src/test/java/com/pff/AppointmentTest.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URISyntaxException;
6 | import java.net.URL;
7 | import java.util.Calendar;
8 | import java.util.Arrays;
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 | import org.junit.runners.JUnit4;
13 |
14 | /**
15 | * Tests for {@link PSTAppointment}.
16 | *
17 | * @author Richard Johnson
18 | */
19 | @RunWith(JUnit4.class)
20 | public class AppointmentTest {
21 |
22 | /**
23 | * Test we can access appointments from the PST.
24 | */
25 | @Test
26 | public final void testGetDistList()
27 | throws PSTException, IOException, URISyntaxException {
28 | URL dirUrl = ClassLoader.getSystemResource("dist-list.pst");
29 | PSTFile pstFile = new PSTFile(new File(dirUrl.toURI()));
30 | PSTAppointment appt = (PSTAppointment) PSTObject.detectAndLoadPSTObject(pstFile, 2097348);
31 | PSTAppointmentRecurrence r = new PSTAppointmentRecurrence(
32 | appt.getRecurrenceStructure(), appt, appt.getRecurrenceTimeZone());
33 |
34 |
35 | Assert.assertEquals(
36 | "Has 3 deleted items (1 removed, 2 changed)",
37 | 3,
38 | r.getDeletedInstanceDates().length);
39 |
40 | Assert.assertEquals(
41 | "Number of Exceptions",
42 | 2,
43 | r.getExceptionCount());
44 |
45 | String d = r.getException(0).getDescription().trim();
46 | Assert.assertEquals("correct app desc", "This is the appointment at 9", d);
47 |
48 | Calendar c = PSTObject.apptTimeToCalendar(
49 | r.getException(0).getStartDateTime());
50 | Assert.assertEquals(
51 | "First exception correct hour",
52 | 9,
53 | c.get(Calendar.HOUR));
54 |
55 | d = r.getException(1).getDescription().trim();
56 | Assert.assertEquals("correct app desc", "This is the one at 10", d);
57 |
58 | c = PSTObject.apptTimeToCalendar(
59 | r.getException(1).getStartDateTime());
60 | Assert.assertEquals(
61 | "Second exception correct hour",
62 | 10,
63 | c.get(Calendar.HOUR));
64 |
65 | //System.out.println(r.getExceptionCount());
66 | //System.out.println(r.getException(0).getDTStamp());
67 |
68 | //for (int x = 0; x < r.getDeletedInstanceDates().length; x++) {
69 | // System.out.println(r.getDeletedInstanceDates()[x]);
70 | //}
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/src/test/java/com/pff/DistListTest.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URISyntaxException;
6 | import java.net.URL;
7 | import java.util.HashSet;
8 | import java.util.Arrays;
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 | import org.junit.runners.JUnit4;
13 |
14 | /**
15 | * Tests for {@link PSTDistList}.
16 | *
17 | * @author Richard Johnson
18 | */
19 | @RunWith(JUnit4.class)
20 | public class DistListTest {
21 |
22 | /**
23 | * Test we can retrieve distribution lists from the PST.
24 | */
25 | @Test
26 | public final void testGetDistList()
27 | throws PSTException, IOException, URISyntaxException {
28 | URL dirUrl = ClassLoader.getSystemResource("dist-list.pst");
29 | PSTFile pstFile = new PSTFile(new File(dirUrl.toURI()));
30 | PSTDistList obj = (PSTDistList)PSTObject.detectAndLoadPSTObject(pstFile, 2097188);
31 | Object[] members = obj.getDistributionListMembers();
32 | Assert.assertEquals("Correct number of members", members.length, 3);
33 | int numberOfContacts = 0;
34 | int numberOfOneOffRecords = 0;
35 | HashSet emailAddresses = new HashSet();
36 | HashSet displayNames = new HashSet();
37 | for (Object member : members) {
38 | if (member instanceof PSTContact) {
39 | PSTContact contact = (PSTContact)member;
40 | Assert.assertEquals("Contact email address",
41 | contact.getEmail1EmailAddress(),
42 | "contact1@rjohnson.id.au");
43 | numberOfContacts++;
44 | } else {
45 | PSTDistList.OneOffEntry entry = (PSTDistList.OneOffEntry)member;
46 | emailAddresses.add(entry.getEmailAddress());
47 | displayNames.add(entry.getDisplayName());
48 | numberOfOneOffRecords++;
49 | }
50 | }
51 | Assert.assertEquals("Correct number of members", members.length, 3);
52 | Assert.assertEquals("Contains all display names",
53 | displayNames,
54 | new HashSet(Arrays.asList(
55 | new String[] {"dist name 2",
56 | "dist name 1"})));
57 | Assert.assertEquals("Contains all email addresses",
58 | emailAddresses,
59 | new HashSet(Arrays.asList(
60 | new String[] {"dist1@rjohnson.id.au",
61 | "dist2@rjohnson.id.au"})));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/test/java/com/pff/PSTGlobalObjectIdTest.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.util.Date;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.hamcrest.CoreMatchers.equalTo;
8 | import static org.junit.Assert.assertThat;
9 |
10 | /**
11 | * Author: Nick Buller
12 | */
13 | public class PSTGlobalObjectIdTest {
14 |
15 | @Test
16 | public void unpackValid() {
17 | // Global Object ID:
18 | // Byte Array ID = cb: 16 lpb: 040000008200E00074C5B7101A82E008
19 | // Year: 0x0000 = 0
20 | // Month: 0x00 = 0 = 0x0
21 | // Day: 0x00 = 0
22 | // Creation Time = 0x01D04F6F:0xA226A470 = 01:50:07.415 PM 23/02/2015
23 | // X: 0x00000000:0x00000000
24 | // Size: 0x10 = 16
25 | // Data = cb: 16 lpb: 086DFAD3919FD44089199898CDCF4DC2
26 | byte[] objectId = {
27 | 0x04, 0x00, 0x00, 0x00, (byte) 0x82, 0x00, (byte) 0xE0, 0x00, 0x74, (byte) 0xC5, (byte) 0xB7, 0x10, 0x1A, (byte) 0x82, (byte) 0xE0, 0x08, // Byte Array ID
28 | 0x07, // Year Hi
29 | (byte) 0xde, // Year Low
30 | 0x0b, // Month
31 | 0x14, // Day
32 | 0x70, (byte) 0xA4, 0x26, (byte) 0xA2, 0x6F, 0x4F, (byte) 0xD0, 0x01, // Creation Time
33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X
34 | 0x10, 0x00, 0x00, 0x00, // Size
35 | 0x08, 0x6D, (byte) 0xFA, (byte) 0xD3, (byte) 0x91, (byte) 0x9F, (byte) 0xD4, 0x40, (byte) 0x89, 0x19, (byte) 0x98, (byte) 0x98, (byte) 0xCD, (byte) 0xCF, 0x4D, (byte) 0xC2 // Data
36 | };
37 |
38 | PSTGlobalObjectId object = new PSTGlobalObjectId(objectId);
39 |
40 | assertThat("Validate YearHi is correct", object.getYearLow(), equalTo(0x00DE));
41 | assertThat("Validate YearLow is correct", object.getYearHigh(), equalTo(0x7));
42 | assertThat("Validate Year is correct", object.getYear(), equalTo(2014));
43 | assertThat("Validate Day is correct", object.getDay(), equalTo(20));
44 | assertThat("Validate Month is correct", object.getMonth(), equalTo(11));
45 | assertThat("Validate CreationTimeLow is correct", object.getCreationTimeLow(), equalTo(0xA226A470));
46 | assertThat("Validate CreationTimeHigh is correct", object.getCreationTimeHigh(), equalTo(0x01D04F6F));
47 | assertThat("Validate CreationTime is correct", object.getCreationTime().getTime(), equalTo(new Date(1424699407415L).getTime()));
48 | assertThat("Validate Size is correct", object.getDataSize(), equalTo(16));
49 | assertThat("Validate Size of date matches actual data size", object.getData().length, equalTo(object.getDataSize()));
50 | assertThat("Validate Date is correct", PSTGlobalObjectId.bytesToHex(object.getData()), equalTo("086DFAD3919FD44089199898CDCF4DC2"));
51 | }
52 |
53 | @Test(expected = AssertionError.class)
54 | public void unpackWithInvalidIdSignature() {
55 | byte[] objectId = {
56 | 0x04, 0x00, 0x00, 0x00, (byte) 0x82, 0x00, (byte) 0xE0, 0x00, 0x74, (byte) 0xC5, (byte) 0xB7, 0x10, 0x1A, (byte) 0x82, (byte) 0xE0, 0x00, // Byte Array ID (last byte 00 rather then 08
57 | 0x07, // Year Hi
58 | (byte) 0xde, // Year Low
59 | 0x0b, // Month
60 | 0x14, // Day
61 | 0x70, (byte) 0xA4, 0x26, (byte) 0xA2, 0x6F, 0x4F, (byte) 0xD0, 0x01, // Creation Time
62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X
63 | 0x10, 0x00, 0x00, 0x00, // Size
64 | 0x08, 0x6D, (byte) 0xFA, (byte) 0xD3, (byte) 0x91, (byte) 0x9F, (byte) 0xD4, 0x40, (byte) 0x89, 0x19, (byte) 0x98, (byte) 0x98, (byte) 0xCD, (byte) 0xCF, 0x4D, (byte) 0xC2 // Data
65 | };
66 |
67 | PSTGlobalObjectId object = new PSTGlobalObjectId(objectId);
68 | }
69 |
70 | @Test(expected = AssertionError.class)
71 | public void unpackWithInvalidIdData() {
72 | byte[] objectId = {
73 | 0x04, 0x00, 0x00, 0x00, (byte) 0x82, 0x00, (byte) 0xE0, 0x00, 0x74, (byte) 0xC5, (byte) 0xB7, 0x10, 0x1A, (byte) 0x82, (byte) 0xE0, 0x08, // Byte Array ID
74 | 0x07, // Year Hi
75 | (byte) 0xde, // Year Low
76 | 0x0b, // Month
77 | 0x14, // Day
78 | 0x70, (byte) 0xA4, 0x26, (byte) 0xA2, 0x6F, 0x4F, (byte) 0xD0, 0x01, // Creation Time
79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X
80 | 0x10, 0x00, 0x00, 0x00, // Size
81 | 0x08, 0x6D, (byte) 0xFA, (byte) 0xD3, (byte) 0x91, (byte) 0x9F, (byte) 0xD4, 0x40, (byte) 0x89, 0x19, (byte) 0x98, (byte) 0x98, (byte) 0xCD, (byte) 0xCF, 0x4D // Data (missing last byte)
82 | };
83 |
84 | PSTGlobalObjectId object = new PSTGlobalObjectId(objectId);
85 | }
86 |
87 | @Test(expected = AssertionError.class)
88 | public void unpackWithInvalidIdDataLength() {
89 | byte[] objectId = {
90 | 0x04, 0x00, 0x00, 0x00, (byte) 0x82, 0x00, (byte) 0xE0, 0x00, 0x74, (byte) 0xC5, (byte) 0xB7, 0x10, 0x1A, (byte) 0x82, (byte) 0xE0, 0x08, // Byte Array ID
91 | 0x07, // Year Hi
92 | (byte) 0xde, // Year Low
93 | 0x0b, // Month
94 | 0x14, // Day
95 | 0x70, (byte) 0xA4, 0x26, (byte) 0xA2, 0x6F, 0x4F, (byte) 0xD0, 0x01, // Creation Time
96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X
97 | 0x10, 0x10, 0x00, 0x00, // Size
98 | 0x08, 0x6D, (byte) 0xFA, (byte) 0xD3, (byte) 0x91, (byte) 0x9F, (byte) 0xD4, 0x40, (byte) 0x89, 0x19, (byte) 0x98, (byte) 0x98, (byte) 0xCD, (byte) 0xCF, 0x4D, (byte) 0xC2 // Data
99 | };
100 |
101 | PSTGlobalObjectId object = new PSTGlobalObjectId(objectId);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/test/java/com/pff/PasswordTest.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URISyntaxException;
6 | import java.net.URL;
7 | import java.util.HashSet;
8 | import java.util.Arrays;
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 | import org.junit.runners.JUnit4;
13 |
14 | /**
15 | * Tests for {@link PSTDistList}.
16 | *
17 | * @author Richard Johnson
18 | */
19 | @RunWith(JUnit4.class)
20 | public class PasswordTest {
21 |
22 | /**
23 | * Test for password protectedness.
24 | */
25 | @Test
26 | public final void testPasswordProtected()
27 | throws PSTException, IOException, URISyntaxException {
28 | URL dirUrl = ClassLoader.getSystemResource("passworded.pst");
29 | PSTFile pstFile = new PSTFile(new File(dirUrl.toURI()));
30 | Assert.assertEquals("Is password protected",
31 | pstFile.getMessageStore().isPasswordProtected(),
32 | true);
33 | }
34 |
35 | /**
36 | * Test for non-password protectedness.
37 | */
38 | @Test
39 | public final void testNotPasswordProtected()
40 | throws PSTException, IOException, URISyntaxException {
41 | URL dirUrl = ClassLoader.getSystemResource("dist-list.pst");
42 | PSTFile pstFile = new PSTFile(new File(dirUrl.toURI()));
43 | Assert.assertEquals("Is password protected",
44 | pstFile.getMessageStore().isPasswordProtected(),
45 | false);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/java/com/pff/Version36Test.java:
--------------------------------------------------------------------------------
1 | package com.pff;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URISyntaxException;
6 | import java.net.URL;
7 | import java.util.*;
8 | import org.junit.Assert;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.junit.runners.JUnit4;
12 |
13 | @RunWith(JUnit4.class)
14 | public class Version36Test {
15 |
16 | @Test
17 | public final void testVersion36()
18 | throws PSTException, IOException, URISyntaxException {
19 | URL dirUrl = ClassLoader.getSystemResource("example-2013.ost");
20 | PSTFile pstFile2 = new PSTFile(new File(dirUrl.toURI()));
21 | PSTFolder inbox = (PSTFolder)PSTObject.detectAndLoadPSTObject(pstFile2, 8578);
22 | Assert.assertEquals(
23 | "Number of emails in folder",
24 | inbox.getContentCount(),
25 | 2);
26 | PSTMessage msg = (PSTMessage)PSTObject.detectAndLoadPSTObject(pstFile2, 2097284);
27 | Assert.assertEquals(
28 | "correct email text.",
29 | "This is an e-mail message sent automatically by Microsoft "
30 | + "Outlook while testing the settings for your account.",
31 | msg.getBodyHTML().trim());
32 | //processFolder(pstFile2.getRootFolder());
33 | }
34 |
35 | int depth = -1;
36 | public void processFolder(PSTFolder folder)
37 | throws PSTException, java.io.IOException {
38 | depth++;
39 | // the root folder doesn't have a display name
40 | if (depth > 0) {
41 | printDepth();
42 | System.out.println("Folder: " + folder.getDescriptorNodeId() + " - " + folder.getDisplayName());
43 | }
44 |
45 | // go through the folders...
46 | if (folder.hasSubfolders()) {
47 | Vector childFolders = folder.getSubFolders();
48 | for (PSTFolder childFolder : childFolders) {
49 | processFolder(childFolder);
50 | }
51 | }
52 |
53 | // and now the emails for this folder
54 | if (folder.getContentCount() > 0) {
55 | depth++;
56 | PSTMessage email = (PSTMessage)folder.getNextChild();
57 | while (email != null) {
58 | printDepth();
59 | System.out.println("Email: "+ email.getDescriptorNodeId() + " - " + email.getSubject());
60 | email = (PSTMessage)folder.getNextChild();
61 | }
62 | depth--;
63 | }
64 | depth--;
65 | }
66 |
67 | public void printDepth() {
68 | for (int x = 0; x < depth-1; x++) {
69 | System.out.print(" | ");
70 | }
71 | System.out.print(" |- ");
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/test/resources/dist-list.pst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjohnsondev/java-libpst/f158a64acf2a0e46ac3bd699bc7a5a8da6c40d26/src/test/resources/dist-list.pst
--------------------------------------------------------------------------------
/src/test/resources/example-2013.ost:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjohnsondev/java-libpst/f158a64acf2a0e46ac3bd699bc7a5a8da6c40d26/src/test/resources/example-2013.ost
--------------------------------------------------------------------------------
/src/test/resources/passworded.pst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjohnsondev/java-libpst/f158a64acf2a0e46ac3bd699bc7a5a8da6c40d26/src/test/resources/passworded.pst
--------------------------------------------------------------------------------