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