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