1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Clipboard Viewer
4  * FILE:            base/applications/clipbrd/fileutils.c
5  * PURPOSE:         Clipboard file format helper functions.
6  * PROGRAMMERS:     Ricardo Hanke
7  *                  Hermes Belusca-Maito
8  */
9 
10 #include "precomp.h"
11 
12 static HGLOBAL ClipboardReadMemoryBlock(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
13 {
14     HGLOBAL hData;
15     LPVOID lpData;
16     DWORD dwBytesRead;
17 
18     hData = GlobalAlloc(GHND, dwLength);
19     if (!hData)
20         return NULL;
21 
22     lpData = GlobalLock(hData);
23     if (!lpData)
24     {
25         GlobalFree(hData);
26         return NULL;
27     }
28 
29     if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
30     {
31         GlobalUnlock(hData);
32         GlobalFree(hData);
33         return NULL;
34     }
35 
36     if (!ReadFile(hFile, lpData, dwLength, &dwBytesRead, NULL))
37     {
38         GlobalUnlock(hData);
39         GlobalFree(hData);
40         return NULL;
41     }
42 
43     GlobalUnlock(hData);
44 
45     return hData;
46 }
47 
48 static BOOL ClipboardReadMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, DWORD dwLength, WORD FileIdentifier, PVOID lpFormatName)
49 {
50     HGLOBAL hData;
51     DWORD dwTemp = 0;
52 
53     hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
54     if (!hData)
55         return FALSE;
56 
57     if ((dwFormat >= 0xC000) && (dwFormat <= 0xFFFF))
58     {
59         if (FileIdentifier == CLIP_FMT_31)
60             dwTemp = RegisterClipboardFormatA((LPCSTR)lpFormatName);
61         else if ((FileIdentifier == CLIP_FMT_NT) || (FileIdentifier == CLIP_FMT_BK))
62             dwTemp = RegisterClipboardFormatW((LPCWSTR)lpFormatName);
63 
64         if (!dwTemp)
65         {
66             GlobalFree(hData);
67             return FALSE;
68         }
69     }
70     else
71     {
72         dwTemp = dwFormat;
73     }
74 
75     if (!SetClipboardData(dwTemp, hData))
76     {
77         GlobalFree(hData);
78         return FALSE;
79     }
80 
81     return TRUE;
82 }
83 
84 static BOOL ClipboardWriteMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, PDWORD pdwLength)
85 {
86     HGLOBAL hData;
87     LPVOID lpData;
88     DWORD dwBytesWritten;
89 
90     hData = GetClipboardData(dwFormat);
91     if (!hData)
92         return FALSE;
93 
94     lpData = GlobalLock(hData);
95     if (!lpData)
96         return FALSE;
97 
98     *pdwLength = GlobalSize(hData);
99 
100     if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
101     {
102         GlobalUnlock(hData);
103         return FALSE;
104     }
105 
106     if (!WriteFile(hFile, lpData, *pdwLength, &dwBytesWritten, NULL))
107     {
108         GlobalUnlock(hData);
109         return FALSE;
110     }
111 
112     GlobalUnlock(hData);
113 
114     return TRUE;
115 }
116 
117 static BOOL ClipboardReadPalette(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
118 {
119     LPLOGPALETTE lpPalette;
120     HPALETTE hPalette;
121     HGLOBAL hData;
122 
123     hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
124     if (!hData)
125     {
126         return FALSE;
127     }
128 
129     lpPalette = GlobalLock(hData);
130     if (!lpPalette)
131     {
132         GlobalFree(hData);
133         return FALSE;
134     }
135 
136     hPalette = CreatePalette(lpPalette);
137     if (!hPalette)
138     {
139         GlobalUnlock(hData);
140         GlobalFree(hData);
141         SetLastError(ERROR_OUTOFMEMORY);
142         return FALSE;
143     }
144 
145     GlobalUnlock(hData);
146     GlobalFree(hData);
147 
148     if (!SetClipboardData(CF_PALETTE, hPalette))
149     {
150         DeleteObject(hPalette);
151         return FALSE;
152     }
153 
154     return TRUE;
155 }
156 
157 static BOOL ClipboardReadMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
158 {
159     HMETAFILE hMf;
160     HGLOBAL hData;
161     LPVOID lpData;
162 
163     hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
164     if (!hData)
165     {
166         return FALSE;
167     }
168 
169     lpData = GlobalLock(hData);
170     if (!lpData)
171     {
172         GlobalFree(hData);
173         return FALSE;
174     }
175 
176     hMf = SetMetaFileBitsEx(dwLength, lpData);
177 
178     GlobalUnlock(hData);
179     GlobalFree(hData);
180 
181     if (!hMf)
182     {
183         SetLastError(ERROR_OUTOFMEMORY);
184         return FALSE;
185     }
186 
187     if (!SetClipboardData(CF_METAFILEPICT, hMf))
188     {
189         DeleteMetaFile(hMf);
190         return FALSE;
191     }
192 
193     return TRUE;
194 }
195 
196 static BOOL ClipboardReadEnhMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
197 {
198     HENHMETAFILE hEmf;
199     HGLOBAL hData;
200     LPVOID lpData;
201 
202     hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
203     if (!hData)
204     {
205         return FALSE;
206     }
207 
208     lpData = GlobalLock(hData);
209     if (!lpData)
210     {
211         GlobalFree(hData);
212         return FALSE;
213     }
214 
215     hEmf = SetEnhMetaFileBits(dwLength, lpData);
216 
217     GlobalUnlock(hData);
218     GlobalFree(hData);
219 
220     if (!hEmf)
221     {
222         SetLastError(ERROR_OUTOFMEMORY);
223         return FALSE;
224     }
225 
226     if (!SetClipboardData(CF_ENHMETAFILE, hEmf))
227     {
228         DeleteEnhMetaFile(hEmf);
229         return FALSE;
230     }
231 
232     return TRUE;
233 }
234 
235 static BOOL ClipboardReadBitmap(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
236 {
237     HGLOBAL hData;
238     HBITMAP hBitmap;
239     LPBITMAP lpBitmap;
240 
241     hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
242     if (!hData)
243     {
244         return FALSE;
245     }
246 
247     lpBitmap = GlobalLock(hData);
248     if (!lpBitmap)
249     {
250         GlobalFree(hData);
251         return FALSE;
252     }
253 
254     lpBitmap->bmBits = lpBitmap + sizeof(BITMAP) + 1;
255 
256     hBitmap = CreateBitmapIndirect(lpBitmap);
257 
258     GlobalUnlock(hData);
259     GlobalFree(hData);
260 
261     if (!hBitmap)
262     {
263         SetLastError(ERROR_OUTOFMEMORY);
264         return FALSE;
265     }
266 
267     if (!SetClipboardData(CF_BITMAP, hBitmap))
268     {
269         DeleteObject(hBitmap);
270         return FALSE;
271     }
272 
273     return TRUE;
274 }
275 
276 void ReadClipboardFile(LPCWSTR lpFileName)
277 {
278     CLIPFILEHEADER ClipFileHeader;
279     CLIPFORMATHEADER ClipFormatArray;
280     NTCLIPFILEHEADER NtClipFileHeader;
281     NTCLIPFORMATHEADER NtClipFormatArray;
282     PVOID pClipFileHeader;
283     PVOID pClipFormatArray;
284     DWORD SizeOfFileHeader, SizeOfFormatHeader;
285 
286     WORD wFileIdentifier;
287     WORD wFormatCount;
288     DWORD dwFormatID;
289     DWORD dwLenData;
290     DWORD dwOffData;
291     PVOID szName;
292 
293     HANDLE hFile;
294     DWORD dwBytesRead;
295     BOOL bResult;
296     int i;
297 
298     /* Open the file for read access */
299     hFile = CreateFileW(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
300                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
301     if (hFile == INVALID_HANDLE_VALUE)
302     {
303         ShowLastWin32Error(Globals.hMainWnd);
304         goto done;
305     }
306 
307     /* Just read enough bytes to get the clipboard file format ID */
308     if (!ReadFile(hFile, &wFileIdentifier, sizeof(wFileIdentifier), &dwBytesRead, NULL))
309     {
310         ShowLastWin32Error(Globals.hMainWnd);
311         goto done;
312     }
313 
314     /* Set data according to the clipboard file format ID */
315     switch (wFileIdentifier)
316     {
317         case CLIP_FMT_31:
318             SizeOfFileHeader   = sizeof(CLIPFILEHEADER);
319             SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
320             pClipFileHeader    = &ClipFileHeader;
321             pClipFormatArray   = &ClipFormatArray;
322             break;
323 
324         case CLIP_FMT_NT:
325         case CLIP_FMT_BK:
326             SizeOfFileHeader   = sizeof(NTCLIPFILEHEADER);
327             SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
328             pClipFileHeader    = &NtClipFileHeader;
329             pClipFormatArray   = &NtClipFormatArray;
330             break;
331 
332         default:
333             MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
334             goto done;
335     }
336 
337     /* Completely read the header */
338     SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
339     if (!ReadFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesRead, NULL) ||
340         dwBytesRead != SizeOfFileHeader)
341     {
342         ShowLastWin32Error(Globals.hMainWnd);
343         goto done;
344     }
345 
346     /* Get header data */
347     switch (wFileIdentifier)
348     {
349         case CLIP_FMT_31:
350             assert(wFileIdentifier == ((CLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
351             wFormatCount = ((CLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
352             break;
353 
354         case CLIP_FMT_NT:
355         case CLIP_FMT_BK:
356             assert(wFileIdentifier == ((NTCLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
357             wFormatCount = ((NTCLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
358             break;
359     }
360 
361     /* Loop through the format data array */
362     for (i = 0; i < wFormatCount; i++)
363     {
364         if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
365         {
366             ShowLastWin32Error(Globals.hMainWnd);
367             goto done;
368         }
369 
370         if (!ReadFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesRead, NULL))
371         {
372             ShowLastWin32Error(Globals.hMainWnd);
373             goto done;
374         }
375 
376         /* Get format data */
377         switch (wFileIdentifier)
378         {
379             case CLIP_FMT_31:
380                 dwFormatID = ((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
381                 dwLenData  = ((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
382                 dwOffData  = ((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
383                 szName     = ((CLIPFORMATHEADER*)pClipFormatArray)->szName;
384                 break;
385 
386             case CLIP_FMT_NT:
387             case CLIP_FMT_BK:
388                 dwFormatID = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
389                 dwLenData  = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
390                 dwOffData  = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
391                 szName     = ((NTCLIPFORMATHEADER*)pClipFormatArray)->szName;
392                 break;
393         }
394 
395         switch (dwFormatID)
396         {
397             case CF_OWNERDISPLAY:
398             {
399                 break;
400             }
401 
402             case CF_BITMAP:
403             case CF_DSPBITMAP:
404             {
405                 bResult = ClipboardReadBitmap(hFile, dwOffData, dwLenData);
406                 break;
407             }
408 
409             case CF_METAFILEPICT:
410             case CF_DSPMETAFILEPICT:
411             {
412                 bResult = ClipboardReadMetafile(hFile, dwOffData, dwLenData);
413                 break;
414             }
415 
416             case CF_ENHMETAFILE:
417             case CF_DSPENHMETAFILE:
418             {
419                 bResult = ClipboardReadEnhMetafile(hFile, dwOffData, dwLenData);
420                 break;
421             }
422 
423             case CF_PALETTE:
424             {
425                 bResult = ClipboardReadPalette(hFile, dwOffData, dwLenData);
426                 break;
427             }
428 
429             default:
430             {
431                 if ((dwFormatID < CF_PRIVATEFIRST) || (dwFormatID > CF_PRIVATELAST))
432                 {
433                     bResult = ClipboardReadMemory(hFile, dwFormatID, dwOffData, dwLenData, wFileIdentifier, szName);
434                 }
435                 break;
436             }
437         }
438 
439         if (!bResult)
440             ShowLastWin32Error(Globals.hMainWnd);
441     }
442 
443 done:
444     if (hFile != INVALID_HANDLE_VALUE)
445         CloseHandle(hFile);
446 
447     return;
448 }
449 
450 void WriteClipboardFile(LPCWSTR lpFileName, WORD wFileIdentifier)
451 {
452     CLIPFILEHEADER ClipFileHeader;
453     CLIPFORMATHEADER ClipFormatArray;
454     NTCLIPFILEHEADER NtClipFileHeader;
455     NTCLIPFORMATHEADER NtClipFormatArray;
456     PVOID pClipFileHeader;
457     PVOID pClipFormatArray;
458     DWORD SizeOfFileHeader, SizeOfFormatHeader;
459 
460     WORD wFormatCount;
461     DWORD dwFormatID;
462     DWORD dwLenData;
463     DWORD dwOffData;
464     // PVOID szName;
465 
466     HANDLE hFile;
467     DWORD dwBytesWritten;
468     int i;
469 
470     /* Create the file for write access */
471     hFile = CreateFileW(lpFileName, GENERIC_WRITE, 0, NULL,
472                         CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
473     if (hFile == INVALID_HANDLE_VALUE)
474     {
475         ShowLastWin32Error(Globals.hMainWnd);
476         goto done;
477     }
478 
479     wFormatCount = CountClipboardFormats();
480 
481     /* Select the file format and setup the header according to the clipboard file format ID */
482     switch (wFileIdentifier)
483     {
484         case CLIP_FMT_31:
485             SizeOfFileHeader   = sizeof(CLIPFILEHEADER);
486             SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
487             pClipFileHeader    = &ClipFileHeader;
488             pClipFormatArray   = &ClipFormatArray;
489 
490             ClipFileHeader.wFileIdentifier = CLIP_FMT_31; // wFileIdentifier
491             ClipFileHeader.wFormatCount    = wFormatCount;
492             break;
493 
494         case CLIP_FMT_NT:
495         case CLIP_FMT_BK:
496             SizeOfFileHeader   = sizeof(NTCLIPFILEHEADER);
497             SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
498             pClipFileHeader    = &NtClipFileHeader;
499             pClipFormatArray   = &NtClipFormatArray;
500 
501             NtClipFileHeader.wFileIdentifier = CLIP_FMT_NT; // wFileIdentifier
502             NtClipFileHeader.wFormatCount    = wFormatCount;
503             break;
504 
505         default:
506             MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
507             goto done;
508     }
509 
510     /* Write the header */
511     SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
512     if (!WriteFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesWritten, NULL) ||
513         dwBytesWritten != SizeOfFileHeader)
514     {
515         ShowLastWin32Error(Globals.hMainWnd);
516         goto done;
517     }
518 
519     /* Compute where the data should start (after the file header and the format array) */
520     dwOffData = SizeOfFileHeader + wFormatCount * SizeOfFormatHeader;
521 
522     /* Loop through each format and save the data */
523     i = 0;
524     dwFormatID = EnumClipboardFormats(0);
525     while (dwFormatID)
526     {
527         if (i >= wFormatCount)
528         {
529             /* Must never happen! */
530             assert(FALSE);
531             break;
532         }
533 
534         /* Write the clipboard data at the specified offset, and retrieve its length */
535         if (!ClipboardWriteMemory(hFile, dwFormatID, dwOffData, &dwLenData))
536             goto Cont;
537 
538         /* Write the format data header */
539         switch (wFileIdentifier)
540         {
541             case CLIP_FMT_31:
542                 ZeroMemory(pClipFormatArray, sizeof(CLIPFORMATHEADER));
543                 ((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
544                 ((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData  = dwLenData;
545                 ((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData  = dwOffData;
546                 RetrieveClipboardFormatName(Globals.hInstance,
547                                             dwFormatID,
548                                             FALSE,
549                                             ((CLIPFORMATHEADER*)pClipFormatArray)->szName,
550                                             ARRAYSIZE(((CLIPFORMATHEADER*)pClipFormatArray)->szName));
551                 break;
552 
553             case CLIP_FMT_NT:
554             case CLIP_FMT_BK:
555                 ZeroMemory(pClipFormatArray, sizeof(NTCLIPFORMATHEADER));
556                 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
557                 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData  = dwLenData;
558                 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData  = dwOffData;
559                 RetrieveClipboardFormatName(Globals.hInstance,
560                                             dwFormatID,
561                                             TRUE,
562                                             ((NTCLIPFORMATHEADER*)pClipFormatArray)->szName,
563                                             ARRAYSIZE(((NTCLIPFORMATHEADER*)pClipFormatArray)->szName));
564                 break;
565         }
566 
567         if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
568         {
569             ShowLastWin32Error(Globals.hMainWnd);
570             goto done;
571         }
572 
573         if (!WriteFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesWritten, NULL))
574         {
575             ShowLastWin32Error(Globals.hMainWnd);
576             goto done;
577         }
578 
579         /* Adjust the offset for the next data stream */
580         dwOffData += dwLenData;
581 
582 Cont:
583         i++;
584         dwFormatID = EnumClipboardFormats(dwFormatID);
585     }
586 
587 done:
588     if (hFile != INVALID_HANDLE_VALUE)
589         CloseHandle(hFile);
590 
591     return;
592 }
593