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