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