1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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