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 (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f)) 116 goto end; 117 118 for (i=0; i< ttOffsetTable.uNumOfTables; i++) 119 { 120 if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL)) 121 break; 122 if (memcmp(tblDir.szTag,"name",4)==0) 123 { 124 bFound = TRUE; 125 tblDir.uLength = SWAPLONG(tblDir.uLength); 126 tblDir.uOffset = SWAPLONG(tblDir.uOffset); 127 break; 128 } 129 } 130 131 if (!bFound) 132 goto end; 133 134 SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN); 135 if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL)) 136 goto end; 137 138 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount); 139 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset); 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 ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID); 147 ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID); 148 if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 && 149 (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1)) 150 { 151 WCHAR *buf; 152 unsigned int i; 153 154 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength); 155 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset); 156 SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, 157 NULL, FILE_BEGIN); 158 if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end; 159 dwRead = 0; 160 ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL); 161 if (dwRead % sizeof(WCHAR)) 162 { 163 msi_free(buf); 164 goto end; 165 } 166 for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]); 167 ret = strdupW(buf); 168 msi_free(buf); 169 break; 170 } 171 } 172 173 end: 174 CloseHandle(handle); 175 return ret; 176 } 177 178 static WCHAR *font_name_from_file( const WCHAR *filename ) 179 { 180 static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0}; 181 WCHAR *name, *ret = NULL; 182 183 if ((name = load_ttf_name_id( filename, NAME_ID_FULL_FONT_NAME ))) 184 { 185 if (!name[0]) 186 { 187 WARN("empty font name\n"); 188 msi_free( name ); 189 return NULL; 190 } 191 ret = msi_alloc( (strlenW( name ) + strlenW( truetypeW ) + 1 ) * sizeof(WCHAR) ); 192 strcpyW( ret, name ); 193 strcatW( ret, truetypeW ); 194 msi_free( name ); 195 } 196 return ret; 197 } 198 199 WCHAR *msi_font_version_from_file( const WCHAR *filename ) 200 { 201 static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0}; 202 WCHAR *version, *p, *q, *ret = NULL; 203 204 if ((version = load_ttf_name_id( filename, NAME_ID_VERSION ))) 205 { 206 int len, major = 0, minor = 0; 207 if ((p = strchrW( version, ';' ))) *p = 0; 208 p = version; 209 while (*p && !isdigitW( *p )) p++; 210 if ((q = strchrW( p, '.' ))) 211 { 212 major = atoiW( p ); 213 p = ++q; 214 while (*q && isdigitW( *q )) q++; 215 if (!*q || *q == ' ') minor = atoiW( p ); 216 else major = 0; 217 } 218 len = strlenW( fmtW ) + 20; 219 ret = msi_alloc( len * sizeof(WCHAR) ); 220 sprintfW( ret, fmtW, major, minor ); 221 msi_free( version ); 222 } 223 return ret; 224 } 225 226 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param) 227 { 228 MSIPACKAGE *package = param; 229 LPWSTR name; 230 LPCWSTR filename; 231 MSIFILE *file; 232 MSICOMPONENT *comp; 233 HKEY hkey1, hkey2; 234 MSIRECORD *uirow; 235 LPWSTR uipath, p; 236 237 filename = MSI_RecordGetString( row, 1 ); 238 file = msi_get_loaded_file( package, filename ); 239 if (!file) 240 { 241 WARN("unable to find file %s\n", debugstr_w(filename)); 242 return ERROR_SUCCESS; 243 } 244 comp = msi_get_loaded_component( package, file->Component->Component ); 245 if (!comp) 246 { 247 WARN("unable to find component %s\n", debugstr_w(file->Component->Component)); 248 return ERROR_SUCCESS; 249 } 250 comp->Action = msi_get_component_action( package, comp ); 251 if (comp->Action != INSTALLSTATE_LOCAL) 252 { 253 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component)); 254 return ERROR_SUCCESS; 255 } 256 257 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1); 258 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2); 259 260 if (MSI_RecordIsNull(row,2)) 261 name = font_name_from_file( file->TargetPath ); 262 else 263 name = msi_dup_record_field(row,2); 264 265 if (name) 266 { 267 msi_reg_set_val_str( hkey1, name, file->TargetPath); 268 msi_reg_set_val_str( hkey2, name, file->TargetPath); 269 } 270 271 msi_free(name); 272 RegCloseKey(hkey1); 273 RegCloseKey(hkey2); 274 275 /* the UI chunk */ 276 uirow = MSI_CreateRecord( 1 ); 277 uipath = strdupW( file->TargetPath ); 278 p = strrchrW(uipath,'\\'); 279 if (p) p++; 280 else p = uipath; 281 MSI_RecordSetStringW( uirow, 1, p ); 282 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 283 msiobj_release( &uirow->hdr ); 284 msi_free( uipath ); 285 /* FIXME: call msi_ui_progress? */ 286 287 return ERROR_SUCCESS; 288 } 289 290 UINT ACTION_RegisterFonts(MSIPACKAGE *package) 291 { 292 static const WCHAR query[] = { 293 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0}; 294 MSIQUERY *view; 295 UINT rc; 296 297 rc = MSI_DatabaseOpenViewW(package->db, query, &view); 298 if (rc != ERROR_SUCCESS) 299 return ERROR_SUCCESS; 300 301 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package); 302 msiobj_release(&view->hdr); 303 return rc; 304 } 305 306 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param ) 307 { 308 MSIPACKAGE *package = param; 309 LPWSTR name; 310 LPCWSTR filename; 311 MSIFILE *file; 312 MSICOMPONENT *comp; 313 HKEY hkey1, hkey2; 314 MSIRECORD *uirow; 315 LPWSTR uipath, p; 316 317 filename = MSI_RecordGetString( row, 1 ); 318 file = msi_get_loaded_file( package, filename ); 319 if (!file) 320 { 321 WARN("unable to find file %s\n", debugstr_w(filename)); 322 return ERROR_SUCCESS; 323 } 324 comp = msi_get_loaded_component( package, file->Component->Component ); 325 if (!comp) 326 { 327 WARN("unable to find component %s\n", debugstr_w(file->Component->Component)); 328 return ERROR_SUCCESS; 329 } 330 comp->Action = msi_get_component_action( package, comp ); 331 if (comp->Action != INSTALLSTATE_ABSENT) 332 { 333 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component)); 334 return ERROR_SUCCESS; 335 } 336 337 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 ); 338 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 ); 339 340 if (MSI_RecordIsNull( row, 2 )) 341 name = font_name_from_file( file->TargetPath ); 342 else 343 name = msi_dup_record_field( row, 2 ); 344 345 if (name) 346 { 347 RegDeleteValueW( hkey1, name ); 348 RegDeleteValueW( hkey2, name ); 349 } 350 351 msi_free( name ); 352 RegCloseKey( hkey1 ); 353 RegCloseKey( hkey2 ); 354 355 /* the UI chunk */ 356 uirow = MSI_CreateRecord( 1 ); 357 uipath = strdupW( file->TargetPath ); 358 p = strrchrW( uipath,'\\' ); 359 if (p) p++; 360 else p = uipath; 361 MSI_RecordSetStringW( uirow, 1, p ); 362 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 363 msiobj_release( &uirow->hdr ); 364 msi_free( uipath ); 365 /* FIXME: call msi_ui_progress? */ 366 367 return ERROR_SUCCESS; 368 } 369 370 UINT ACTION_UnregisterFonts( MSIPACKAGE *package ) 371 { 372 static const WCHAR query[] = { 373 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0}; 374 MSIQUERY *view; 375 UINT r; 376 377 r = MSI_DatabaseOpenViewW( package->db, query, &view ); 378 if (r != ERROR_SUCCESS) 379 return ERROR_SUCCESS; 380 381 r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package ); 382 msiobj_release( &view->hdr ); 383 return r; 384 } 385