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