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