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