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