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