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