1
2 /* ====================================================================
3 Licensed to the Apache Software Foundation (ASF) under one or more
4 contributor license agreements. See the NOTICE file distributed with
5 this work for additional information regarding copyright ownership.
6 The ASF licenses this file to You under the Apache License, Version 2.0
7 (the "License"); you may not use this file except in compliance with
8 the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================== */
18
19
20
21 package org.apache.poi.hslf.record;
22
23 import java.io;
24 import org.apache.poi.poifs.filesystem;
25 import org.apache.poi.util.LittleEndian;
26 import org.apache.poi.util.StringUtil;
27 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
28
29
30 /**
31 * This is a special kind of Atom, becauase it doesn't live inside the
32 * PowerPoint document. Instead, it lives in a seperate stream in the
33 * document. As such, it has to be treaded specially
34 *
35 * @author Nick Burch
36 */
37
38 public class CurrentUserAtom
39 {
40 /** Standard Atom header */
41 public static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
42 /** The Powerpoint magic numer */
43 public static final byte[] magicNumber = new byte[] { 95, -64, -111, -29 };
44 /** The Powerpoint 97 version, major and minor numbers */
45 public static final byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
46
47 /** The version, major and minor numbers */
48 private int docFinalVersionA;
49 private int docFinalVersionB;
50 private byte docMajorNo;
51 private byte docMinorNo;
52
53 /** The Offset into the file for the current edit */
54 private long currentEditOffset;
55 /** The Username of the last person to edit the file */
56 private String lastEditUser;
57 /** The document release version */
58 private long releaseVersion;
59
60 /** Only correct after reading in or writing out */
61 private byte[] _contents;
62
63
64 /* ********************* getter/setter follows *********************** */
65
66 public int getDocFinalVersionA() { return docFinalVersionA; }
67 public int getDocFinalVersionB() { return docFinalVersionB; }
68 public byte getDocMajorNo() { return docMajorNo; }
69 public byte getDocMinorNo() { return docMinorNo; }
70
71 public long getReleaseVersion() { return releaseVersion; }
72 public void setReleaseVersion(long rv) { releaseVersion = rv; }
73
74 /** Points to the UserEditAtom */
75 public long getCurrentEditOffset() { return currentEditOffset; }
76 public void setCurrentEditOffset(long id ) { currentEditOffset = id; }
77
78 public String getLastEditUsername() { return lastEditUser; }
79 public void setLastEditUsername(String u) { lastEditUser = u; }
80
81
82 /* ********************* real code follows *************************** */
83
84 /**
85 * Create a new Current User Atom
86 */
87 public CurrentUserAtom() {
88 _contents = new byte[0];
89 throw new RuntimeException("Creation support for Current User Atom not complete");
90 }
91
92 /**
93 * Find the Current User in the filesystem, and create from that
94 */
95 public CurrentUserAtom(POIFSFileSystem fs) throws IOException {
96 this(fs.getRoot());
97 }
98 /**
99 * Find the Current User in the filesystem, and create from that
100 */
101 public CurrentUserAtom(DirectoryNode dir) throws IOException {
102 // Decide how big it is
103 DocumentEntry docProps =
104 (DocumentEntry)dir.getEntry("Current User");
105 _contents = new byte[docProps.getSize()];
106
107 // Check it's big enough - if it's not at least 28 bytes long, then
108 // the record is corrupt
109 if(_contents.length < 28) {
110 throw new CorruptPowerPointFileException("The Current User stream must be at least 28 bytes long, but was only " + _contents.length);
111 }
112
113 // Grab the contents
114 InputStream in = dir.createDocumentInputStream("Current User");
115 in.read(_contents);
116
117 // Set everything up
118 init();
119 }
120
121 /**
122 * Create things from the bytes
123 */
124 public CurrentUserAtom(byte[] b) {
125 _contents = b;
126 init();
127 }
128
129 /**
130 * Actually do the creation from a block of bytes
131 */
132 private void init() {
133 // Grab the edit offset
134 currentEditOffset = LittleEndian.getUInt(_contents,16);
135
136 // Grab the versions
137 docFinalVersionA = LittleEndian.getUShort(_contents,20);
138 docFinalVersionB = LittleEndian.getUShort(_contents,22);
139 docMajorNo = _contents[24];
140 docMinorNo = _contents[25];
141
142 // Get the username length
143 long usernameLen = LittleEndian.getUShort(_contents,20);
144 if(usernameLen > 512) {
145 // Handle the case of it being garbage
146 System.err.println("Warning - invalid username length " + usernameLen + " found, treating as if there was no username set");
147 usernameLen = 0;
148 }
149
150 // Now we know the length of the username,
151 // use this to grab the revision
152 if(_contents.length >= 28+(int)usernameLen + 4) {
153 releaseVersion = LittleEndian.getUInt(_contents,28+(int)usernameLen);
154 } else {
155 // No revision given, as not enough data. Odd
156 releaseVersion = 0;
157 }
158
159 // Grab the unicode username, if stored
160 int start = 28+(int)usernameLen+4;
161 int len = 2*(int)usernameLen;
162
163 if(_contents.length >= start+len) {
164 byte[] textBytes = new byte[len];
165 System.arraycopy(_contents,start,textBytes,0,len);
166 lastEditUser = StringUtil.getFromUnicodeLE(textBytes);
167 } else {
168 // Fake from the 8 bit version
169 byte[] textBytes = new byte[(int)usernameLen];
170 System.arraycopy(_contents,28,textBytes,0,(int)usernameLen);
171 lastEditUser = StringUtil.getFromCompressedUnicode(textBytes,0,(int)usernameLen);
172 }
173 }
174
175
176 /**
177 * Writes ourselves back out
178 */
179 public void writeOut(OutputStream out) throws IOException {
180 // Decide on the size
181 // 8 = atom header
182 // 20 = up to name
183 // 4 = revision
184 // 3 * len = ascii + unicode
185 int size = 8 + 20 + 4 + (3 * lastEditUser.length());
186 _contents = new byte[size];
187
188 // First we have a 8 byte atom header
189 System.arraycopy(atomHeader,0,_contents,0,4);
190 // Size is 20+user len + revision len(4)
191 int atomSize = 20+4+lastEditUser.length();
192 LittleEndian.putInt(_contents,4,atomSize);
193
194 // Now we have the size of the details, which is 20
195 LittleEndian.putInt(_contents,8,20);
196
197 // Now the ppt magic number (4 bytes)
198 System.arraycopy(magicNumber,0,_contents,12,4);
199
200 // Now the current edit offset
201 LittleEndian.putInt(_contents,16,(int)currentEditOffset);
202
203 // Now the file versions, 2+2+1+1
204 LittleEndian.putShort(_contents,20,(short)docFinalVersionA);
205 LittleEndian.putShort(_contents,22,(short)docFinalVersionB);
206 _contents[24] = docMajorNo;
207 _contents[25] = docMinorNo;
208
209 // 2 bytes blank
210 _contents[26] = 0;
211 _contents[27] = 0;
212
213 // username in bytes in us ascii
214 byte[] asciiUN = new byte[lastEditUser.length()];
215 StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
216 System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
217
218 // 4 byte release version
219 LittleEndian.putInt(_contents,28+asciiUN.length,(int)releaseVersion);
220
221 // username in unicode
222 byte [] ucUN = new byte[lastEditUser.length()*2];
223 StringUtil.putUnicodeLE(lastEditUser,ucUN,0);
224 System.arraycopy(ucUN,0,_contents,28+asciiUN.length+4,ucUN.length);
225
226 // Write out
227 out.write(_contents);
228 }
229
230 /**
231 * Writes ourselves back out to a filesystem
232 */
233 public void writeToFS(POIFSFileSystem fs) throws IOException {
234 // Grab contents
235 ByteArrayOutputStream baos = new ByteArrayOutputStream();
236 writeOut(baos);
237 ByteArrayInputStream bais =
238 new ByteArrayInputStream(baos.toByteArray());
239
240 // Write out
241 fs.createDocument(bais,"Current User");
242 }
243 }