xref: /reactos/dll/win32/msi/font.c (revision 9d3c3a75)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winerror.h"
25 #include "winreg.h"
26 #include "wine/debug.h"
27 #include "msipriv.h"
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(msi);
30 
31 typedef struct _tagTT_OFFSET_TABLE {
32     USHORT uMajorVersion;
33     USHORT uMinorVersion;
34     USHORT uNumOfTables;
35     USHORT uSearchRange;
36     USHORT uEntrySelector;
37     USHORT uRangeShift;
38 } TT_OFFSET_TABLE;
39 
40 typedef struct _tagTT_TABLE_DIRECTORY {
41     char szTag[4]; /* table name */
42     ULONG uCheckSum; /* Check sum */
43     ULONG uOffset; /* Offset from beginning of file */
44     ULONG uLength; /* length of the table in bytes */
45 } TT_TABLE_DIRECTORY;
46 
47 typedef struct _tagTT_NAME_TABLE_HEADER {
48     USHORT uFSelector; /* format selector. Always 0 */
49     USHORT uNRCount; /* Name Records count */
50     USHORT uStorageOffset; /* Offset for strings storage,
51                             * from start of the table */
52 } TT_NAME_TABLE_HEADER;
53 
54 #define NAME_ID_FULL_FONT_NAME  4
55 #define NAME_ID_VERSION         5
56 
57 typedef struct _tagTT_NAME_RECORD {
58     USHORT uPlatformID;
59     USHORT uEncodingID;
60     USHORT uLanguageID;
61     USHORT uNameID;
62     USHORT uStringLength;
63     USHORT uStringOffset; /* from start of storage area */
64 } TT_NAME_RECORD;
65 
66 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
67 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
68 
69 /*
70  * Code based off of code located here
71  * http://www.codeproject.com/gdi/fontnamefromfile.asp
72  */
73 static WCHAR *load_ttf_name_id( MSIPACKAGE *package, const WCHAR *filename, DWORD id )
74 {
75     TT_TABLE_DIRECTORY tblDir;
76     BOOL bFound = FALSE;
77     TT_OFFSET_TABLE ttOffsetTable;
78     TT_NAME_TABLE_HEADER ttNTHeader;
79     TT_NAME_RECORD ttRecord;
80     DWORD dwRead;
81     HANDLE handle;
82     LPWSTR ret = NULL;
83     int i;
84 
85     if (package)
86         handle = msi_create_file( package, filename, GENERIC_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL );
87     else
88         handle = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
89     if (handle == INVALID_HANDLE_VALUE)
90     {
91         ERR("Unable to open font file %s\n", debugstr_w(filename));
92         return NULL;
93     }
94 
95     if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
96         goto end;
97 
98     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
99     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
100     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
101 
102     if ((ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) &&
103         (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f))
104         goto end;
105 
106     for (i=0; i< ttOffsetTable.uNumOfTables; i++)
107     {
108         if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
109             break;
110         if (memcmp(tblDir.szTag,"name",4)==0)
111         {
112             bFound = TRUE;
113             tblDir.uLength = SWAPLONG(tblDir.uLength);
114             tblDir.uOffset = SWAPLONG(tblDir.uOffset);
115             break;
116         }
117     }
118 
119     if (!bFound)
120         goto end;
121 
122     SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
123     if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
124         goto end;
125 
126     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
127     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
128     for(i=0; i<ttNTHeader.uNRCount; i++)
129     {
130         if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
131             break;
132 
133         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
134         ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
135         ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
136         if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 &&
137             (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1))
138         {
139             WCHAR *buf;
140             unsigned int i;
141 
142             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
143             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
144             SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset,
145                            NULL, FILE_BEGIN);
146             if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end;
147             dwRead = 0;
148             ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
149             if (dwRead % sizeof(WCHAR))
150             {
151                 msi_free(buf);
152                 goto end;
153             }
154             for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]);
155             ret = strdupW(buf);
156             msi_free(buf);
157             break;
158         }
159     }
160 
161 end:
162     CloseHandle(handle);
163     return ret;
164 }
165 
166 static WCHAR *font_name_from_file( MSIPACKAGE *package, const WCHAR *filename )
167 {
168     WCHAR *name, *ret = NULL;
169 
170     if ((name = load_ttf_name_id( package, filename, NAME_ID_FULL_FONT_NAME )))
171     {
172         if (!name[0])
173         {
174             WARN("empty font name\n");
175             msi_free( name );
176             return NULL;
177         }
178         ret = msi_alloc( (lstrlenW( name ) + lstrlenW( L" (TrueType)" ) + 1 ) * sizeof(WCHAR) );
179         lstrcpyW( ret, name );
180         lstrcatW( ret, L" (TrueType)" );
181         msi_free( name );
182     }
183     return ret;
184 }
185 
186 WCHAR *msi_get_font_file_version( MSIPACKAGE *package, const WCHAR *filename )
187 {
188     WCHAR *version, *p, *q, *ret = NULL;
189 
190     if ((version = load_ttf_name_id( package, filename, NAME_ID_VERSION )))
191     {
192         int len, major = 0, minor = 0;
193         if ((p = wcschr( version, ';' ))) *p = 0;
194         p = version;
195         while (*p && !iswdigit( *p )) p++;
196         if ((q = wcschr( p, '.' )))
197         {
198             major = wcstol( p, NULL, 10 );
199             p = ++q;
200             while (*q && iswdigit( *q )) q++;
201             if (!*q || *q == ' ') minor = wcstol( p, NULL, 10 );
202             else major = 0;
203         }
204         len = lstrlenW( L"%u.%u.0.0" ) + 20;
205         ret = msi_alloc( len * sizeof(WCHAR) );
206         swprintf( ret, len, L"%u.%u.0.0", major, minor );
207         msi_free( version );
208     }
209     return ret;
210 }
211 
212 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
213 {
214     MSIPACKAGE *package = param;
215     LPWSTR name;
216     LPCWSTR filename;
217     MSIFILE *file;
218     MSICOMPONENT *comp;
219     HKEY hkey1, hkey2;
220     MSIRECORD *uirow;
221     LPWSTR uipath, p;
222 
223     filename = MSI_RecordGetString( row, 1 );
224     file = msi_get_loaded_file( package, filename );
225     if (!file)
226     {
227         WARN("unable to find file %s\n", debugstr_w(filename));
228         return ERROR_SUCCESS;
229     }
230     comp = msi_get_loaded_component( package, file->Component->Component );
231     if (!comp)
232     {
233         WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
234         return ERROR_SUCCESS;
235     }
236     comp->Action = msi_get_component_action( package, comp );
237     if (comp->Action != INSTALLSTATE_LOCAL)
238     {
239         TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
240         return ERROR_SUCCESS;
241     }
242 
243     RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" ,&hkey1 );
244     RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2 );
245 
246     if (MSI_RecordIsNull(row,2))
247         name = font_name_from_file( package, file->TargetPath );
248     else
249         name = msi_dup_record_field(row,2);
250 
251     if (name)
252     {
253         msi_reg_set_val_str( hkey1, name, file->TargetPath);
254         msi_reg_set_val_str( hkey2, name, file->TargetPath);
255     }
256 
257     msi_free(name);
258     RegCloseKey(hkey1);
259     RegCloseKey(hkey2);
260 
261     /* the UI chunk */
262     uirow = MSI_CreateRecord( 1 );
263     uipath = strdupW( file->TargetPath );
264     p = wcsrchr(uipath,'\\');
265     if (p) p++;
266     else p = uipath;
267     MSI_RecordSetStringW( uirow, 1, p );
268     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
269     msiobj_release( &uirow->hdr );
270     msi_free( uipath );
271     /* FIXME: call msi_ui_progress? */
272 
273     return ERROR_SUCCESS;
274 }
275 
276 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
277 {
278     MSIQUERY *view;
279     UINT rc;
280 
281     if (package->script == SCRIPT_NONE)
282         return msi_schedule_action(package, SCRIPT_INSTALL, L"RegisterFonts");
283 
284     rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Font`", &view);
285     if (rc != ERROR_SUCCESS)
286         return ERROR_SUCCESS;
287 
288     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
289     msiobj_release(&view->hdr);
290     return rc;
291 }
292 
293 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
294 {
295     MSIPACKAGE *package = param;
296     LPWSTR name;
297     LPCWSTR filename;
298     MSIFILE *file;
299     MSICOMPONENT *comp;
300     HKEY hkey1, hkey2;
301     MSIRECORD *uirow;
302     LPWSTR uipath, p;
303 
304     filename = MSI_RecordGetString( row, 1 );
305     file = msi_get_loaded_file( package, filename );
306     if (!file)
307     {
308         WARN("unable to find file %s\n", debugstr_w(filename));
309         return ERROR_SUCCESS;
310     }
311     comp = msi_get_loaded_component( package, file->Component->Component );
312     if (!comp)
313     {
314         WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
315         return ERROR_SUCCESS;
316     }
317     comp->Action = msi_get_component_action( package, comp );
318     if (comp->Action != INSTALLSTATE_ABSENT)
319     {
320         TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
321         return ERROR_SUCCESS;
322     }
323 
324     RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", &hkey1 );
325     RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2 );
326 
327     if (MSI_RecordIsNull( row, 2 ))
328         name = font_name_from_file( package, file->TargetPath );
329     else
330         name = msi_dup_record_field( row, 2 );
331 
332     if (name)
333     {
334         RegDeleteValueW( hkey1, name );
335         RegDeleteValueW( hkey2, name );
336     }
337 
338     msi_free( name );
339     RegCloseKey( hkey1 );
340     RegCloseKey( hkey2 );
341 
342     /* the UI chunk */
343     uirow = MSI_CreateRecord( 1 );
344     uipath = strdupW( file->TargetPath );
345     p = wcsrchr( uipath,'\\' );
346     if (p) p++;
347     else p = uipath;
348     MSI_RecordSetStringW( uirow, 1, p );
349     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
350     msiobj_release( &uirow->hdr );
351     msi_free( uipath );
352     /* FIXME: call msi_ui_progress? */
353 
354     return ERROR_SUCCESS;
355 }
356 
357 UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
358 {
359     MSIQUERY *view;
360     UINT r;
361 
362     if (package->script == SCRIPT_NONE)
363         return msi_schedule_action(package, SCRIPT_INSTALL, L"UnregisterFonts");
364 
365     r = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `Font`", &view );
366     if (r != ERROR_SUCCESS)
367         return ERROR_SUCCESS;
368 
369     r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
370     msiobj_release( &view->hdr );
371     return r;
372 }
373