1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsDecodeAppleFile.h"
7 #include "nsCRT.h"
8
9 NS_IMPL_ADDREF(nsDecodeAppleFile)
NS_IMPL_RELEASE(nsDecodeAppleFile)10 NS_IMPL_RELEASE(nsDecodeAppleFile)
11
12 NS_INTERFACE_MAP_BEGIN(nsDecodeAppleFile)
13 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
14 NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
15 NS_INTERFACE_MAP_END
16
17 nsDecodeAppleFile::nsDecodeAppleFile() {
18 m_state = parseHeaders;
19 m_dataBufferLength = 0;
20 m_dataBuffer = (unsigned char *)malloc(MAX_BUFFERSIZE);
21 m_entries = nullptr;
22 m_rfRefNum = -1;
23 m_totalDataForkWritten = 0;
24 m_totalResourceForkWritten = 0;
25 m_headerOk = false;
26
27 m_comment[0] = 0;
28 memset(&m_dates, 0, sizeof(m_dates));
29 memset(&m_finderInfo, 0, sizeof(m_dates));
30 memset(&m_finderExtraInfo, 0, sizeof(m_dates));
31 }
32
~nsDecodeAppleFile()33 nsDecodeAppleFile::~nsDecodeAppleFile() {
34 free(m_dataBuffer);
35 m_dataBuffer = nullptr;
36 if (m_entries) delete[] m_entries;
37 }
38
Initialize(nsIOutputStream * output,nsIFile * file)39 NS_IMETHODIMP nsDecodeAppleFile::Initialize(nsIOutputStream *output,
40 nsIFile *file) {
41 m_output = output;
42
43 nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(file);
44 macFile->GetTargetFSSpec(&m_fsFileSpec);
45
46 m_offset = 0;
47 m_dataForkOffset = 0;
48
49 return NS_OK;
50 }
51
Close(void)52 NS_IMETHODIMP nsDecodeAppleFile::Close(void) {
53 nsresult rv;
54 rv = m_output->Close();
55
56 int32_t i;
57
58 if (m_rfRefNum != -1) FSClose(m_rfRefNum);
59
60 /* Check if the file is complete and if it's the case, write file attributes
61 */
62 if (m_headerOk) {
63 bool dataOk = true; /* It's ok if the file doesn't have a datafork,
64 therefore set it to true by default. */
65 if (m_headers.magic == APPLESINGLE_MAGIC) {
66 for (i = 0; i < m_headers.entriesCount; i++)
67 if (ENT_DFORK == m_entries[i].id) {
68 dataOk = (bool)(m_totalDataForkWritten == m_entries[i].length);
69 break;
70 }
71 }
72
73 bool resourceOk = FALSE;
74 for (i = 0; i < m_headers.entriesCount; i++)
75 if (ENT_RFORK == m_entries[i].id) {
76 resourceOk = (bool)(m_totalResourceForkWritten == m_entries[i].length);
77 break;
78 }
79
80 if (dataOk && resourceOk) {
81 HFileInfo *fpb;
82 CInfoPBRec cipbr;
83
84 fpb = (HFileInfo *)&cipbr;
85 fpb->ioVRefNum = m_fsFileSpec.vRefNum;
86 fpb->ioDirID = m_fsFileSpec.parID;
87 fpb->ioNamePtr = m_fsFileSpec.name;
88 fpb->ioFDirIndex = 0;
89 PBGetCatInfoSync(&cipbr);
90
91 /* set finder info */
92 memcpy(&fpb->ioFlFndrInfo, &m_finderInfo, sizeof(FInfo));
93 memcpy(&fpb->ioFlXFndrInfo, &m_finderExtraInfo, sizeof(FXInfo));
94 fpb->ioFlFndrInfo.fdFlags &=
95 0xfc00; /* clear flags maintained by finder */
96
97 /* set file dates */
98 fpb->ioFlCrDat = m_dates.create - CONVERT_TIME;
99 fpb->ioFlMdDat = m_dates.modify - CONVERT_TIME;
100 fpb->ioFlBkDat = m_dates.backup - CONVERT_TIME;
101
102 /* update file info */
103 fpb->ioDirID = fpb->ioFlParID;
104 PBSetCatInfoSync(&cipbr);
105
106 /* set comment */
107 IOParam vinfo;
108 GetVolParmsInfoBuffer vp;
109 DTPBRec dtp;
110
111 memset((void *)&vinfo, 0, sizeof(vinfo));
112 vinfo.ioVRefNum = fpb->ioVRefNum;
113 vinfo.ioBuffer = (Ptr)&vp;
114 vinfo.ioReqCount = sizeof(vp);
115 if (PBHGetVolParmsSync((HParmBlkPtr)&vinfo) == noErr &&
116 ((vp.vMAttrib >> bHasDesktopMgr) & 1)) {
117 memset((void *)&dtp, 0, sizeof(dtp));
118 dtp.ioVRefNum = fpb->ioVRefNum;
119 if (PBDTGetPath(&dtp) == noErr) {
120 dtp.ioDTBuffer = (Ptr)&m_comment[1];
121 dtp.ioNamePtr = fpb->ioNamePtr;
122 dtp.ioDirID = fpb->ioDirID;
123 dtp.ioDTReqCount = m_comment[0];
124 if (PBDTSetCommentSync(&dtp) == noErr) PBDTFlushSync(&dtp);
125 }
126 }
127 }
128 }
129
130 return rv;
131 }
132
Flush(void)133 NS_IMETHODIMP nsDecodeAppleFile::Flush(void) { return m_output->Flush(); }
134
WriteFrom(nsIInputStream * inStr,uint32_t count,uint32_t * _retval)135 NS_IMETHODIMP nsDecodeAppleFile::WriteFrom(nsIInputStream *inStr,
136 uint32_t count, uint32_t *_retval) {
137 return m_output->WriteFrom(inStr, count, _retval);
138 }
139
WriteSegments(nsReadSegmentFun reader,void * closure,uint32_t count,uint32_t * _retval)140 NS_IMETHODIMP nsDecodeAppleFile::WriteSegments(nsReadSegmentFun reader,
141 void *closure, uint32_t count,
142 uint32_t *_retval) {
143 return m_output->WriteSegments(reader, closure, count, _retval);
144 }
145
IsNonBlocking(bool * aNonBlocking)146 NS_IMETHODIMP nsDecodeAppleFile::IsNonBlocking(bool *aNonBlocking) {
147 return m_output->IsNonBlocking(aNonBlocking);
148 }
149
Write(const char * buffer,uint32_t bufferSize,uint32_t * writeCount)150 NS_IMETHODIMP nsDecodeAppleFile::Write(const char *buffer, uint32_t bufferSize,
151 uint32_t *writeCount) {
152 /* WARNING: to simplify my life, I presume that I should get all appledouble
153 headers in the first block, else I would have to implement a buffer */
154
155 const char *buffPtr = buffer;
156 uint32_t dataCount;
157 int32_t i;
158 nsresult rv = NS_OK;
159
160 *writeCount = 0;
161
162 while (bufferSize > 0 && NS_SUCCEEDED(rv)) {
163 switch (m_state) {
164 case parseHeaders:
165 dataCount = sizeof(ap_header) - m_dataBufferLength;
166 if (dataCount > bufferSize) dataCount = bufferSize;
167 memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
168 m_dataBufferLength += dataCount;
169
170 if (m_dataBufferLength == sizeof(ap_header)) {
171 memcpy(&m_headers, m_dataBuffer, sizeof(ap_header));
172
173 /* Check header to be sure we are dealing with the right kind of data,
174 * else just write it to the data fork. */
175 if ((m_headers.magic == APPLEDOUBLE_MAGIC ||
176 m_headers.magic == APPLESINGLE_MAGIC) &&
177 m_headers.version == VERSION && m_headers.entriesCount) {
178 /* Just to be sure, the filler must contains only 0 */
179 for (i = 0; i < 4 && m_headers.fill[i] == 0L; i++)
180 ;
181 if (i == 4) m_state = parseEntries;
182 }
183 m_dataBufferLength = 0;
184
185 if (m_state == parseHeaders) {
186 dataCount = 0;
187 m_state = parseWriteThrough;
188 }
189 }
190 break;
191
192 case parseEntries:
193 if (!m_entries) {
194 m_entries = new ap_entry[m_headers.entriesCount];
195 if (!m_entries) return NS_ERROR_OUT_OF_MEMORY;
196 }
197 uint32_t entriesSize = sizeof(ap_entry) * m_headers.entriesCount;
198 dataCount = entriesSize - m_dataBufferLength;
199 if (dataCount > bufferSize) dataCount = bufferSize;
200 memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
201 m_dataBufferLength += dataCount;
202
203 if (m_dataBufferLength == entriesSize) {
204 for (i = 0; i < m_headers.entriesCount; i++) {
205 memcpy(&m_entries[i], &m_dataBuffer[i * sizeof(ap_entry)],
206 sizeof(ap_entry));
207 if (m_headers.magic == APPLEDOUBLE_MAGIC) {
208 uint32_t offset = m_entries[i].offset + m_entries[i].length;
209 if (offset > m_dataForkOffset) m_dataForkOffset = offset;
210 }
211 }
212 m_headerOk = true;
213 m_state = parseLookupPart;
214 }
215 break;
216
217 case parseLookupPart:
218 /* which part are we parsing? */
219 m_currentPartID = -1;
220 for (i = 0; i < m_headers.entriesCount; i++)
221 if (m_offset == m_entries[i].offset && m_entries[i].length) {
222 m_currentPartID = m_entries[i].id;
223 m_currentPartLength = m_entries[i].length;
224 m_currentPartCount = 0;
225
226 switch (m_currentPartID) {
227 case ENT_DFORK:
228 m_state = parseDataFork;
229 break;
230 case ENT_RFORK:
231 m_state = parseResourceFork;
232 break;
233
234 case ENT_COMMENT:
235 case ENT_DATES:
236 case ENT_FINFO:
237 m_dataBufferLength = 0;
238 m_state = parsePart;
239 break;
240
241 default:
242 m_state = parseSkipPart;
243 break;
244 }
245 break;
246 }
247
248 if (m_currentPartID == -1) {
249 /* maybe is the datafork of an appledouble file? */
250 if (m_offset == m_dataForkOffset) {
251 m_currentPartID = ENT_DFORK;
252 m_currentPartLength = -1;
253 m_currentPartCount = 0;
254 m_state = parseDataFork;
255 } else
256 dataCount = 1;
257 }
258 break;
259
260 case parsePart:
261 dataCount = m_currentPartLength - m_dataBufferLength;
262 if (dataCount > bufferSize) dataCount = bufferSize;
263 memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
264 m_dataBufferLength += dataCount;
265
266 if (m_dataBufferLength == m_currentPartLength) {
267 switch (m_currentPartID) {
268 case ENT_COMMENT:
269 m_comment[0] =
270 m_currentPartLength > 255 ? 255 : m_currentPartLength;
271 memcpy(&m_comment[1], buffPtr, m_comment[0]);
272 break;
273 case ENT_DATES:
274 if (m_currentPartLength == sizeof(m_dates))
275 memcpy(&m_dates, buffPtr, m_currentPartLength);
276 break;
277 case ENT_FINFO:
278 if (m_currentPartLength ==
279 (sizeof(m_finderInfo) + sizeof(m_finderExtraInfo))) {
280 memcpy(&m_finderInfo, buffPtr, sizeof(m_finderInfo));
281 memcpy(&m_finderExtraInfo, buffPtr + sizeof(m_finderInfo),
282 sizeof(m_finderExtraInfo));
283 }
284 break;
285 }
286 m_state = parseLookupPart;
287 }
288 break;
289
290 case parseSkipPart:
291 dataCount = m_currentPartLength - m_currentPartCount;
292 if (dataCount > bufferSize)
293 dataCount = bufferSize;
294 else
295 m_state = parseLookupPart;
296 break;
297
298 case parseDataFork:
299 if (m_headers.magic == APPLEDOUBLE_MAGIC)
300 dataCount = bufferSize;
301 else {
302 dataCount = m_currentPartLength - m_currentPartCount;
303 if (dataCount > bufferSize)
304 dataCount = bufferSize;
305 else
306 m_state = parseLookupPart;
307 }
308
309 if (m_output) {
310 uint32_t writeCount;
311 rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
312 if (dataCount != writeCount) rv = NS_ERROR_FAILURE;
313 m_totalDataForkWritten += dataCount;
314 }
315
316 break;
317
318 case parseResourceFork:
319 dataCount = m_currentPartLength - m_currentPartCount;
320 if (dataCount > bufferSize)
321 dataCount = bufferSize;
322 else
323 m_state = parseLookupPart;
324
325 if (m_rfRefNum == -1) {
326 if (noErr != FSpOpenRF(&m_fsFileSpec, fsWrPerm, &m_rfRefNum))
327 return NS_ERROR_FAILURE;
328 }
329
330 long count = dataCount;
331 if (noErr != FSWrite(m_rfRefNum, &count, buffPtr) || count != dataCount)
332 return NS_ERROR_FAILURE;
333 m_totalResourceForkWritten += dataCount;
334 break;
335
336 case parseWriteThrough:
337 dataCount = bufferSize;
338 if (m_output) {
339 uint32_t writeCount;
340 rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
341 if (dataCount != writeCount) rv = NS_ERROR_FAILURE;
342 }
343 break;
344 }
345
346 if (dataCount) {
347 *writeCount += dataCount;
348 bufferSize -= dataCount;
349 buffPtr += dataCount;
350 m_currentPartCount += dataCount;
351 m_offset += dataCount;
352 dataCount = 0;
353 }
354 }
355
356 return rv;
357 }
358