diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in index b6dfc13..8cbd8a4 100644 --- a/dlls/msi/Makefile.in +++ b/dlls/msi/Makefile.in @@ -28,6 +28,7 @@ C_SRCS = \ insert.c \ install.c \ join.c \ + media.c \ msi.c \ msi_main.c \ msiquery.c \ diff --git a/dlls/msi/action.c b/dlls/msi/action.c index 142eaff..9e6c267 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -5665,6 +5665,18 @@ static UINT ACTION_MoveFiles( MSIPACKAGE *package ) return rc; } +typedef struct tagMSIASSEMBLY +{ + struct list entry; + MSICOMPONENT *component; + MSIFEATURE *feature; + MSIFILE *file; + LPWSTR manifest; + LPWSTR application; + DWORD attributes; + BOOL installed; +} MSIASSEMBLY; + static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache, DWORD dwReserved); static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion, @@ -5707,12 +5719,32 @@ static BOOL init_functionpointers(void) return TRUE; } -static UINT install_assembly(LPWSTR path) +static UINT install_assembly(MSIASSEMBLY *assembly, LPWSTR path) { IAssemblyCache *cache; HRESULT hr; UINT r = ERROR_FUNCTION_FAILED; + TRACE("installing assembly: %s\n", debugstr_w(path)); + + if (assembly->feature) + msi_feature_set_state(assembly->feature, INSTALLSTATE_LOCAL); + + if (assembly->manifest) + FIXME("Manifest unhandled\n"); + + if (assembly->application) + { + FIXME("Assembly should be privately installed\n"); + return ERROR_SUCCESS; + } + + if (assembly->attributes == msidbAssemblyAttributesWin32) + { + FIXME("Win32 assemblies not handled\n"); + return ERROR_SUCCESS; + } + if (!init_functionpointers() || !pCreateAssemblyCache) return ERROR_FUNCTION_FAILED; @@ -5731,106 +5763,207 @@ done: return r; } -static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param ) +typedef struct tagASSEMBLY_LIST { - MSIPACKAGE *package = param; - MSICOMPONENT *comp; - MSIFEATURE *feature; - MSIFILE *file; - WCHAR path[MAX_PATH]; - LPCWSTR app; - DWORD attr; - UINT r; + MSIPACKAGE *package; + struct list *assemblies; +} ASSEMBLY_LIST; - comp = get_loaded_component(package, MSI_RecordGetString(rec, 1)); - if (!comp || !comp->Enabled || - !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE))) +static UINT load_assembly(MSIRECORD *rec, LPVOID param) +{ + ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param; + MSIASSEMBLY *assembly; + + assembly = msi_alloc_zero(sizeof(MSIASSEMBLY)); + if (!assembly) + return ERROR_OUTOFMEMORY; + + assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1)); + + if (!assembly->component || !assembly->component->Enabled || + !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE))) { TRACE("Component not set for install, not publishing assembly\n"); + msi_free(assembly); return ERROR_SUCCESS; } - feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2)); - if (feature) - msi_feature_set_state(feature, INSTALLSTATE_LOCAL); - - if (MSI_RecordGetString(rec, 3)) - FIXME("Manifest unhandled\n"); + assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2)); + assembly->file = msi_find_file(list->package, assembly->component->KeyPath); - app = MSI_RecordGetString(rec, 4); - if (app) + if (!assembly->file) { - FIXME("Assembly should be privately installed\n"); - return ERROR_SUCCESS; + ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath)); + return ERROR_FUNCTION_FAILED; } - attr = MSI_RecordGetInteger(rec, 5); - if (attr == msidbAssemblyAttributesWin32) - { - FIXME("Win32 assemblies not handled\n"); + assembly->manifest = strdupW(MSI_RecordGetString(rec, 3)); + assembly->application = strdupW(MSI_RecordGetString(rec, 4)); + assembly->attributes = MSI_RecordGetInteger(rec, 5); + assembly->installed = FALSE; + + list_add_head(list->assemblies, &assembly->entry); + + return ERROR_SUCCESS; +} + +static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies) +{ + MSIQUERY *view; + ASSEMBLY_LIST list; + UINT r; + + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','s','i','A','s','s','e','m','b','l','y','`',0}; + + r = MSI_DatabaseOpenViewW(package->db, query, &view); + if (r != ERROR_SUCCESS) return ERROR_SUCCESS; - } - /* FIXME: extract all files belonging to this component */ - file = msi_find_file(package, comp->KeyPath); - if (!file) + list.package = package; + list.assemblies = assemblies; + + r = MSI_IterateRecords(view, NULL, load_assembly, &list); + msiobj_release(&view->hdr); + + return r; +} + +static void free_assemblies(struct list *assemblies) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE(item, cursor, assemblies) { - ERR("File %s not found\n", debugstr_w(comp->KeyPath)); - return ERROR_FUNCTION_FAILED; + MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry); + + list_remove(&assembly->entry); + msi_free(assembly->application); + msi_free(assembly->manifest); + msi_free(assembly); } +} - GetTempPathW(MAX_PATH, path); +static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out) +{ + MSIASSEMBLY *assembly; - if (file->IsCompressed) + LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry) { - r = msi_extract_file(package, file, path); - if (r != ERROR_SUCCESS) + if (!lstrcmpW(assembly->file->File, file)) { - ERR("Failed to extract temporary assembly\n"); - return r; + *out = assembly; + return TRUE; } - - PathAddBackslashW(path); - lstrcatW(path, file->FileName); } - else + + return FALSE; +} + +static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, + LPWSTR *path, DWORD *attrs, PVOID user) +{ + MSIASSEMBLY *assembly; + WCHAR temppath[MAX_PATH]; + struct list *assemblies = (struct list *)user; + UINT r; + + if (!find_assembly(assemblies, file, &assembly)) + return FALSE; + + GetTempPathW(MAX_PATH, temppath); + PathAddBackslashW(temppath); + lstrcatW(temppath, assembly->file->FileName); + + if (action == MSICABEXTRACT_BEGINEXTRACT) { - PathAddBackslashW(path); - lstrcatW(path, file->FileName); + if (assembly->installed) + return FALSE; - if (!CopyFileW(file->SourcePath, path, FALSE)) - { - ERR("Failed to copy temporary assembly: %d\n", GetLastError()); - return ERROR_FUNCTION_FAILED; - } + *path = strdupW(temppath); + *attrs = assembly->file->Attributes; } + else if (action == MSICABEXTRACT_FILEEXTRACTED) + { + assembly->installed = TRUE; - r = install_assembly(path); - if (r != ERROR_SUCCESS) - ERR("Failed to install assembly\n"); - - /* FIXME: write Installer assembly reg values */ + r = install_assembly(assembly, temppath); + if (r != ERROR_SUCCESS) + ERR("Failed to install assembly\n"); + } - return r; + return TRUE; } static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) { - UINT rc; - MSIQUERY *view; + UINT r; + struct list assemblies = LIST_INIT(assemblies); + MSIASSEMBLY *assembly; + MSIMEDIAINFO *mi; + WCHAR path[MAX_PATH]; - static const WCHAR ExecSeqQuery[] = - {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', - '`','M','s','i','A','s','s','e','m','b','l','y','`',0}; + r = load_assemblies(package, &assemblies); + if (r != ERROR_SUCCESS) + goto done; - rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); - if (rc != ERROR_SUCCESS) - return ERROR_SUCCESS; + if (list_empty(&assemblies)) + goto done; - rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package); - msiobj_release(&view->hdr); + mi = msi_alloc_zero(sizeof(MSIMEDIAINFO)); + if (!mi) + { + r = ERROR_OUTOFMEMORY; + goto done; + } - return rc; + LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry) + { + if (assembly->installed && !mi->is_continuous) + continue; + + if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous || + (assembly->file->IsCompressed && !mi->is_extracted)) + { + MSICABDATA data; + + r = ready_media(package, assembly->file, mi); + if (r != ERROR_SUCCESS) + { + ERR("Failed to ready media\n"); + break; + } + + data.mi = mi; + data.package = package; + data.cb = installassembly_cb; + data.user = &assemblies; + + if (assembly->file->IsCompressed && + !msi_cabextract(package, mi, &data)) + { + ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); + r = ERROR_FUNCTION_FAILED; + break; + } + } + + if (!assembly->file->IsCompressed) + { + lstrcpyW(path, assembly->file->SourcePath); + + r = install_assembly(assembly, path); + if (r != ERROR_SUCCESS) + ERR("Failed to install assembly\n"); + } + + /* FIXME: write Installer assembly reg values */ + } + +done: + free_assemblies(&assemblies); + return r; } static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, diff --git a/dlls/msi/files.c b/dlls/msi/files.c index 842a72d..08e8ec8 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -55,183 +55,6 @@ extern const WCHAR szPatchFiles[]; extern const WCHAR szRemoveDuplicateFiles[]; extern const WCHAR szRemoveFiles[]; -static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; - -static BOOL source_matches_volume(MSIMEDIAINFO *mi, LPWSTR source_root) -{ - WCHAR volume_name[MAX_PATH + 1]; - - if (!GetVolumeInformationW(source_root, volume_name, MAX_PATH + 1, - NULL, NULL, NULL, NULL, 0)) - { - ERR("Failed to get volume information\n"); - return FALSE; - } - - return !lstrcmpW(mi->volume_label, volume_name); -} - -static UINT msi_change_media( MSIPACKAGE *package, MSIMEDIAINFO *mi ) -{ - LPSTR msg; - LPWSTR error, error_dialog; - LPWSTR source_dir; - UINT r = ERROR_SUCCESS; - - static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0}; - static const WCHAR error_prop[] = {'E','r','r','o','r','D','i','a','l','o','g',0}; - - if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) == INSTALLUILEVEL_NONE && !gUIHandlerA ) - return ERROR_SUCCESS; - - error = generate_error_string( package, 1302, 1, mi->disk_prompt ); - error_dialog = msi_dup_property( package, error_prop ); - source_dir = msi_dup_property( package, cszSourceDir ); - PathStripToRootW(source_dir); - - while ( r == ERROR_SUCCESS && - !source_matches_volume(mi, source_dir) ) - { - r = msi_spawn_error_dialog( package, error_dialog, error ); - - if (gUIHandlerA) - { - msg = strdupWtoA( error ); - gUIHandlerA( gUIContext, MB_RETRYCANCEL | INSTALLMESSAGE_ERROR, msg ); - msi_free(msg); - } - } - - msi_free( error ); - msi_free( error_dialog ); - msi_free( source_dir ); - - return r; -} - -/* - * This is a helper function for handling embedded cabinet media - */ -static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name, - WCHAR* source) -{ - UINT rc; - USHORT* data; - UINT size; - DWORD write; - HANDLE the_file; - WCHAR tmp[MAX_PATH]; - - rc = read_raw_stream_data(package->db,stream_name,&data,&size); - if (rc != ERROR_SUCCESS) - return rc; - - write = MAX_PATH; - if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write)) - GetTempPathW(MAX_PATH,tmp); - - GetTempFileNameW(tmp,stream_name,0,source); - - track_tempfile(package, source); - the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL); - - if (the_file == INVALID_HANDLE_VALUE) - { - ERR("Unable to create file %s\n",debugstr_w(source)); - rc = ERROR_FUNCTION_FAILED; - goto end; - } - - WriteFile(the_file,data,size,&write,NULL); - CloseHandle(the_file); - TRACE("wrote %i bytes to %s\n",write,debugstr_w(source)); -end: - msi_free(data); - return rc; -} - - -/* Support functions for FDI functions */ -typedef struct -{ - MSIPACKAGE* package; - MSIMEDIAINFO *mi; -} CabData; - -static void * cabinet_alloc(ULONG cb) -{ - return msi_alloc(cb); -} - -static void cabinet_free(void *pv) -{ - msi_free(pv); -} - -static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode) -{ - HANDLE handle; - DWORD dwAccess = 0; - DWORD dwShareMode = 0; - DWORD dwCreateDisposition = OPEN_EXISTING; - switch (oflag & _O_ACCMODE) - { - case _O_RDONLY: - dwAccess = GENERIC_READ; - dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; - break; - case _O_WRONLY: - dwAccess = GENERIC_WRITE; - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - break; - case _O_RDWR: - dwAccess = GENERIC_READ | GENERIC_WRITE; - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - break; - } - if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) - dwCreateDisposition = CREATE_NEW; - else if (oflag & _O_CREAT) - dwCreateDisposition = CREATE_ALWAYS; - handle = CreateFileA( pszFile, dwAccess, dwShareMode, NULL, - dwCreateDisposition, 0, NULL ); - if (handle == INVALID_HANDLE_VALUE) - return 0; - return (INT_PTR) handle; -} - -static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb) -{ - HANDLE handle = (HANDLE) hf; - DWORD dwRead; - if (ReadFile(handle, pv, cb, &dwRead, NULL)) - return dwRead; - return 0; -} - -static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb) -{ - HANDLE handle = (HANDLE) hf; - DWORD dwWritten; - if (WriteFile(handle, pv, cb, &dwWritten, NULL)) - return dwWritten; - return 0; -} - -static int cabinet_close(INT_PTR hf) -{ - HANDLE handle = (HANDLE) hf; - return CloseHandle(handle) ? 0 : -1; -} - -static long cabinet_seek(INT_PTR hf, long dist, int seektype) -{ - HANDLE handle = (HANDLE) hf; - /* flags are compatible and so are passed straight through */ - return SetFilePointer(handle, dist, NULL, seektype); -} - static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action ) { MSIRECORD *uirow; @@ -252,213 +75,6 @@ static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *ac ui_progress( package, 2, f->FileSize, 0, 0); } -static UINT msi_media_get_disk_info( MSIPACKAGE *package, MSIMEDIAINFO *mi ) -{ - MSIRECORD *row; - LPWSTR ptr; - - static const WCHAR query[] = - {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', - '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', - '`','D','i','s','k','I','d','`',' ','=',' ','%','i',0}; - - row = MSI_QueryGetRecord(package->db, query, mi->disk_id); - if (!row) - { - TRACE("Unable to query row\n"); - return ERROR_FUNCTION_FAILED; - } - - mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); - mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); - mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); - - if (!mi->first_volume) - mi->first_volume = strdupW(mi->volume_label); - - ptr = strrchrW(mi->source, '\\') + 1; - lstrcpyW(ptr, mi->cabinet); - msiobj_release(&row->hdr); - - return ERROR_SUCCESS; -} - -static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) -{ - TRACE("(%d)\n", fdint); - - switch (fdint) - { - case fdintPARTIAL_FILE: - { - CabData *data = (CabData *)pfdin->pv; - data->mi->is_continuous = FALSE; - return 0; - } - case fdintNEXT_CABINET: - { - CabData *data = (CabData *)pfdin->pv; - MSIMEDIAINFO *mi = data->mi; - LPWSTR cab = strdupAtoW(pfdin->psz1); - UINT rc; - - msi_free(mi->disk_prompt); - msi_free(mi->cabinet); - msi_free(mi->volume_label); - mi->disk_prompt = NULL; - mi->cabinet = NULL; - mi->volume_label = NULL; - - mi->disk_id++; - mi->is_continuous = TRUE; - - rc = msi_media_get_disk_info(data->package, mi); - if (rc != ERROR_SUCCESS) - { - msi_free(cab); - ERR("Failed to get next cabinet information: %d\n", rc); - return -1; - } - - if (lstrcmpiW(mi->cabinet, cab)) - { - msi_free(cab); - ERR("Continuous cabinet does not match the next cabinet in the Media table\n"); - return -1; - } - - msi_free(cab); - - TRACE("Searching for %s\n", debugstr_w(mi->source)); - - if (GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES) - rc = msi_change_media(data->package, mi); - - if (rc != ERROR_SUCCESS) - return -1; - - return 0; - } - case fdintCOPY_FILE: - { - CabData *data = (CabData*) pfdin->pv; - HANDLE handle; - LPWSTR file; - MSIFILE *f; - DWORD attrs; - - file = strdupAtoW(pfdin->psz1); - f = get_loaded_file(data->package, file); - msi_free(file); - - if (!f) - { - WARN("unknown file in cabinet (%s)\n",debugstr_a(pfdin->psz1)); - return 0; - } - - if (f->state != msifs_missing && f->state != msifs_overwrite) - { - TRACE("Skipping extraction of %s\n",debugstr_a(pfdin->psz1)); - return 0; - } - - msi_file_update_ui( data->package, f, szInstallFiles ); - - TRACE("extracting %s\n", debugstr_w(f->TargetPath) ); - - attrs = f->Attributes & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM); - if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL; - - handle = CreateFileW( f->TargetPath, GENERIC_READ | GENERIC_WRITE, 0, - NULL, CREATE_ALWAYS, attrs, NULL ); - if ( handle == INVALID_HANDLE_VALUE ) - { - if ( GetFileAttributesW( f->TargetPath ) != INVALID_FILE_ATTRIBUTES ) - f->state = msifs_installed; - else - ERR("failed to create %s (error %d)\n", - debugstr_w( f->TargetPath ), GetLastError() ); - - return 0; - } - - f->state = msifs_installed; - return (INT_PTR) handle; - } - case fdintCLOSE_FILE_INFO: - { - CabData *data = (CabData*) pfdin->pv; - FILETIME ft; - FILETIME ftLocal; - HANDLE handle = (HANDLE) pfdin->hf; - - data->mi->is_continuous = FALSE; - - if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) - return -1; - if (!LocalFileTimeToFileTime(&ft, &ftLocal)) - return -1; - if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) - return -1; - CloseHandle(handle); - return 1; - } - default: - return 0; - } -} - -/*********************************************************************** - * msi_cabextract - * - * Extract files from a cab file. - */ -BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, - PFNFDINOTIFY notify, LPVOID data) -{ - LPSTR cabinet, cab_path = NULL; - LPWSTR ptr; - HFDI hfdi; - ERF erf; - BOOL ret = FALSE; - - TRACE("Extracting %s\n", debugstr_w(mi->source)); - - hfdi = FDICreate(cabinet_alloc, cabinet_free, cabinet_open, cabinet_read, - cabinet_write, cabinet_close, cabinet_seek, 0, &erf); - if (!hfdi) - { - ERR("FDICreate failed\n"); - return FALSE; - } - - ptr = strrchrW(mi->source, '\\') + 1; - cabinet = strdupWtoA(ptr); - if (!cabinet) - goto done; - - cab_path = strdupWtoA(mi->source); - if (!cab_path) - goto done; - - cab_path[ptr - mi->source] = '\0'; - - ret = FDICopy(hfdi, cabinet, cab_path, 0, notify, NULL, data); - if (!ret) - ERR("FDICopy failed\n"); - -done: - FDIDestroy(hfdi); - msi_free(cabinet); - msi_free(cab_path); - - if (ret) - mi->is_extracted = TRUE; - - return ret; -} - /* compares the version of a file read from the filesystem and * the version specified in the File table */ @@ -477,209 +93,6 @@ static int msi_compare_file_version(MSIFILE *file) return lstrcmpW(version, file->Version); } -void msi_free_media_info( MSIMEDIAINFO *mi ) -{ - msi_free( mi->disk_prompt ); - msi_free( mi->cabinet ); - msi_free( mi->volume_label ); - msi_free( mi->first_volume ); - msi_free( mi ); -} - -UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) -{ - MSIRECORD *row; - LPWSTR source_dir; - LPWSTR source; - DWORD options; - UINT r; - - static const WCHAR query[] = { - 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', - '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', - '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=', - ' ','%','i',' ','A','N','D',' ','`','D','i','s','k','I','d','`',' ','>','=', - ' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ', - '`','D','i','s','k','I','d','`',0 - }; - - row = MSI_QueryGetRecord(package->db, query, file->Sequence, mi->disk_id); - if (!row) - { - TRACE("Unable to query row\n"); - return ERROR_FUNCTION_FAILED; - } - - mi->is_extracted = FALSE; - mi->disk_id = MSI_RecordGetInteger(row, 1); - mi->last_sequence = MSI_RecordGetInteger(row, 2); - msi_free(mi->disk_prompt); - mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); - msi_free(mi->cabinet); - mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); - msi_free(mi->volume_label); - mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); - msiobj_release(&row->hdr); - - if (!mi->first_volume) - mi->first_volume = strdupW(mi->volume_label); - - source_dir = msi_dup_property(package, cszSourceDir); - lstrcpyW(mi->source, source_dir); - - PathStripToRootW(source_dir); - mi->type = GetDriveTypeW(source_dir); - - if (file->IsCompressed && mi->cabinet) - { - if (mi->cabinet[0] == '#') - { - r = writeout_cabinet_stream(package, &mi->cabinet[1], mi->source); - if (r != ERROR_SUCCESS) - { - ERR("Failed to extract cabinet stream\n"); - return ERROR_FUNCTION_FAILED; - } - } - else - lstrcatW(mi->source, mi->cabinet); - } - - options = MSICODE_PRODUCT; - if (mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE) - { - source = source_dir; - options |= MSISOURCETYPE_MEDIA; - } - else if (package->BaseURL && UrlIsW(package->BaseURL, URLIS_URL)) - { - source = package->BaseURL; - options |= MSISOURCETYPE_URL; - } - else - { - source = mi->source; - options |= MSISOURCETYPE_NETWORK; - } - - msi_package_add_media_disk(package, package->Context, - MSICODE_PRODUCT, mi->disk_id, - mi->volume_label, mi->disk_prompt); - - msi_package_add_info(package, package->Context, - options, INSTALLPROPERTY_LASTUSEDSOURCEW, source); - - msi_free(source_dir); - return ERROR_SUCCESS; -} - -/* FIXME: search NETWORK and URL sources as well */ -UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi) -{ - WCHAR source[MAX_PATH]; - WCHAR volume[MAX_PATH]; - WCHAR prompt[MAX_PATH]; - DWORD volumesz, promptsz; - DWORD index, size, id; - UINT r; - - size = MAX_PATH; - r = MsiSourceListGetInfoW(package->ProductCode, NULL, - package->Context, MSICODE_PRODUCT, - INSTALLPROPERTY_LASTUSEDSOURCEW, source, &size); - if (r != ERROR_SUCCESS) - return r; - - index = 0; - volumesz = MAX_PATH; - promptsz = MAX_PATH; - while (MsiSourceListEnumMediaDisksW(package->ProductCode, NULL, - package->Context, - MSICODE_PRODUCT, index++, &id, - volume, &volumesz, prompt, &promptsz) == ERROR_SUCCESS) - { - mi->disk_id = id; - mi->volume_label = msi_realloc(mi->volume_label, ++volumesz * sizeof(WCHAR)); - lstrcpyW(mi->volume_label, volume); - mi->disk_prompt = msi_realloc(mi->disk_prompt, ++promptsz * sizeof(WCHAR)); - lstrcpyW(mi->disk_prompt, prompt); - - if (source_matches_volume(mi, source)) - { - /* FIXME: what about SourceDir */ - lstrcpyW(mi->source, source); - lstrcatW(mi->source, mi->cabinet); - return ERROR_SUCCESS; - } - } - - return ERROR_FUNCTION_FAILED; -} - -static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) -{ - UINT rc = ERROR_SUCCESS; - - /* media info for continuous cabinet is already loaded */ - if (mi->is_continuous) - return ERROR_SUCCESS; - - rc = msi_load_media_info(package, file, mi); - if (rc != ERROR_SUCCESS) - { - ERR("Unable to load media info\n"); - return ERROR_FUNCTION_FAILED; - } - - /* cabinet is internal, no checks needed */ - if (!mi->cabinet || mi->cabinet[0] == '#') - return ERROR_SUCCESS; - - /* package should be downloaded */ - if (file->IsCompressed && - GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES && - package->BaseURL && UrlIsW(package->BaseURL, URLIS_URL)) - { - WCHAR temppath[MAX_PATH]; - - msi_download_file(mi->source, temppath); - lstrcpyW(mi->source, temppath); - return ERROR_SUCCESS; - } - - /* check volume matches, change media if not */ - if (mi->volume_label && mi->disk_id > 1 && - lstrcmpW(mi->first_volume, mi->volume_label)) - { - LPWSTR source = msi_dup_property(package, cszSourceDir); - BOOL matches; - - matches = source_matches_volume(mi, source); - msi_free(source); - - if ((mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE) && !matches) - { - rc = msi_change_media(package, mi); - if (rc != ERROR_SUCCESS) - return rc; - } - } - - if (file->IsCompressed && - GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES) - { - /* FIXME: this might be done earlier in the install process */ - rc = find_published_source(package, mi); - if (rc != ERROR_SUCCESS) - { - ERR("Cabinet not found: %s\n", debugstr_w(mi->source)); - return ERROR_INSTALL_FAILURE; - } - } - - return ERROR_SUCCESS; -} - static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, MSIFILE** file) { @@ -770,6 +183,40 @@ static BOOL check_dest_hash_matches(MSIFILE *file) return !memcmp(&hash, &file->hash, sizeof(MSIFILEHASHINFO)); } +static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, + LPWSTR *path, DWORD *attrs, PVOID user) +{ + static MSIFILE *f = NULL; + + if (action == MSICABEXTRACT_BEGINEXTRACT) + { + f = get_loaded_file(package, file); + if (!f) + { + WARN("unknown file in cabinet (%s)\n", debugstr_w(file)); + return FALSE; + } + + if (f->state != msifs_missing && f->state != msifs_overwrite) + { + TRACE("Skipping extraction of %s\n", debugstr_w(file)); + return FALSE; + } + + msi_file_update_ui(package, f, szInstallFiles); + + *path = strdupW(f->TargetPath); + *attrs = f->Attributes; + } + else if (action == MSICABEXTRACT_FILEEXTRACTED) + { + f->state = msifs_installed; + f = NULL; + } + + return TRUE; +} + /* * ACTION_InstallFiles() * @@ -819,7 +266,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) if (file->Sequence > mi->last_sequence || mi->is_continuous || (file->IsCompressed && !mi->is_extracted)) { - CabData data; + MSICABDATA data; rc = ready_media(package, file, mi); if (rc != ERROR_SUCCESS) @@ -830,9 +277,11 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) data.mi = mi; data.package = package; + data.cb = installfiles_cb; + data.user = NULL; if (file->IsCompressed && - !msi_cabextract(package, mi, cabinet_notify, &data)) + !msi_cabextract(package, mi, &data)) { ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); rc = ERROR_FUNCTION_FAILED; @@ -863,7 +312,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) } } - msi_free_media_info( mi ); + msi_free_media_info(mi); return rc; } diff --git a/dlls/msi/helpers.c b/dlls/msi/helpers.c index aba3bd9..4e20a27 100644 --- a/dlls/msi/helpers.c +++ b/dlls/msi/helpers.c @@ -26,13 +26,9 @@ #include #include "windef.h" -#include "winbase.h" -#include "winerror.h" #include "wine/debug.h" #include "msipriv.h" #include "winuser.h" -#include "winreg.h" -#include "shlwapi.h" #include "wine/unicode.h" #include "msidefs.h" @@ -1039,132 +1035,3 @@ void msi_ui_error( DWORD msg_id, DWORD type ) MessageBoxW( NULL, text, title, type ); } - -typedef struct -{ - MSIPACKAGE *package; - MSIMEDIAINFO *mi; - MSIFILE *file; - LPWSTR destination; -} CabData; - -static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) -{ - TRACE("(%d)\n", fdint); - - switch (fdint) - { - case fdintNEXT_CABINET: - { - ERR("continuous cabinets not handled\n"); - return 0; - } - - case fdintCOPY_FILE: - { - CabData *data = (CabData*) pfdin->pv; - LPWSTR file, path; - DWORD attrs, size; - HANDLE handle; - MSIFILE *f; - - file = strdupAtoW(pfdin->psz1); - f = get_loaded_file(data->package, file); - msi_free(file); - - if (!f) - { - WARN("unknown file in cabinet (%s)\n",debugstr_a(pfdin->psz1)); - return 0; - } - - if (lstrcmpW(f->File, data->file->File)) - return 0; - - size = lstrlenW(data->destination) + lstrlenW(data->file->FileName) + 2; - path = msi_alloc(size * sizeof(WCHAR)); - lstrcpyW(path, data->destination); - PathAddBackslashW(path); - lstrcatW(path, data->file->FileName); - - TRACE("extracting %s\n", debugstr_w(path)); - - attrs = f->Attributes & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM); - if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL; - - handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, - NULL, CREATE_ALWAYS, attrs, NULL); - if (handle == INVALID_HANDLE_VALUE) - { - if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) - ERR("failed to create %s (error %d)\n", - debugstr_w(path), GetLastError()); - - msi_free(path); - return 0; - } - - msi_free(path); - return (INT_PTR)handle; - } - - case fdintCLOSE_FILE_INFO: - { - FILETIME ft; - FILETIME ftLocal; - HANDLE handle = (HANDLE)pfdin->hf; - - if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) - return -1; - if (!LocalFileTimeToFileTime(&ft, &ftLocal)) - return -1; - if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) - return -1; - CloseHandle(handle); - return 1; - } - - default: - return 0; - } -} - -UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir) -{ - MSIMEDIAINFO *mi; - CabData data; - UINT r; - - mi = msi_alloc_zero(sizeof(MSIMEDIAINFO)); - if (!mi) - return ERROR_OUTOFMEMORY; - - r = msi_load_media_info(package, file, mi); - if (r != ERROR_SUCCESS) - goto done; - - if (GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES) - { - r = find_published_source(package, mi); - if (r != ERROR_SUCCESS) - { - ERR("Cabinet not found: %s\n", debugstr_w(mi->source)); - return ERROR_INSTALL_FAILURE; - } - } - - data.package = package; - data.mi = mi; - data.file = file; - data.destination = destdir; - - if (!msi_cabextract(package, mi, cabinet_notify, &data)) - { - ERR("Failed to extract cabinet file\n"); - r = ERROR_FUNCTION_FAILED; - } - -done: - msi_free_media_info(mi); - return r; -} diff --git a/dlls/msi/media.c b/dlls/msi/media.c new file mode 100644 index 0000000..bac801d --- /dev/null +++ b/dlls/msi/media.c @@ -0,0 +1,632 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2008 James Hawkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "windef.h" +#include "winerror.h" +#include "wine/debug.h" +#include "fdi.h" +#include "msvcrt/fcntl.h" +#include "msipriv.h" +#include "winuser.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static BOOL source_matches_volume(MSIMEDIAINFO *mi, LPWSTR source_root) +{ + WCHAR volume_name[MAX_PATH + 1]; + + if (!GetVolumeInformationW(source_root, volume_name, MAX_PATH + 1, + NULL, NULL, NULL, NULL, 0)) + { + ERR("Failed to get volume information\n"); + return FALSE; + } + + return !lstrcmpW(mi->volume_label, volume_name); +} + +static UINT msi_change_media(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + LPSTR msg; + LPWSTR error, error_dialog; + LPWSTR source_dir; + UINT r = ERROR_SUCCESS; + + static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0}; + static const WCHAR error_prop[] = {'E','r','r','o','r','D','i','a','l','o','g',0}; + + if ((msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) == + INSTALLUILEVEL_NONE && !gUIHandlerA) + return ERROR_SUCCESS; + + error = generate_error_string(package, 1302, 1, mi->disk_prompt); + error_dialog = msi_dup_property(package, error_prop); + source_dir = msi_dup_property(package, cszSourceDir); + PathStripToRootW(source_dir); + + while (r == ERROR_SUCCESS && + !source_matches_volume(mi, source_dir)) + { + r = msi_spawn_error_dialog(package, error_dialog, error); + + if (gUIHandlerA) + { + msg = strdupWtoA(error); + gUIHandlerA(gUIContext, MB_RETRYCANCEL | INSTALLMESSAGE_ERROR, msg); + msi_free(msg); + } + } + + msi_free(error); + msi_free(error_dialog); + msi_free(source_dir); + + return r; +} + +static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream, + WCHAR* source) +{ + UINT rc; + USHORT* data; + UINT size; + DWORD write; + HANDLE hfile; + WCHAR tmp[MAX_PATH]; + + static const WCHAR cszTempFolder[]= { + 'T','e','m','p','F','o','l','d','e','r',0}; + + rc = read_raw_stream_data(package->db, stream, &data, &size); + if (rc != ERROR_SUCCESS) + return rc; + + write = MAX_PATH; + if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write)) + GetTempPathW(MAX_PATH, tmp); + + GetTempFileNameW(tmp, stream, 0, source); + + track_tempfile(package, source); + hfile = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (hfile == INVALID_HANDLE_VALUE) + { + ERR("Unable to create file %s\n", debugstr_w(source)); + rc = ERROR_FUNCTION_FAILED; + goto end; + } + + WriteFile(hfile, data, size, &write, NULL); + CloseHandle(hfile); + TRACE("wrote %i bytes to %s\n", write, debugstr_w(source)); + +end: + msi_free(data); + return rc; +} + +static void *cabinet_alloc(ULONG cb) +{ + return msi_alloc(cb); +} + +static void cabinet_free(void *pv) +{ + msi_free(pv); +} + +static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode) +{ + HANDLE handle; + DWORD dwAccess = 0; + DWORD dwShareMode = 0; + DWORD dwCreateDisposition = OPEN_EXISTING; + + switch (oflag & _O_ACCMODE) + { + case _O_RDONLY: + dwAccess = GENERIC_READ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case _O_WRONLY: + dwAccess = GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _O_RDWR: + dwAccess = GENERIC_READ | GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + } + + if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) + dwCreateDisposition = CREATE_NEW; + else if (oflag & _O_CREAT) + dwCreateDisposition = CREATE_ALWAYS; + + handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL, + dwCreateDisposition, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) + return 0; + + return (INT_PTR)handle; +} + +static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE)hf; + DWORD read; + + if (ReadFile(handle, pv, cb, &read, NULL)) + return read; + + return 0; +} + +static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE)hf; + DWORD written; + + if (WriteFile(handle, pv, cb, &written, NULL)) + return written; + + return 0; +} + +static int cabinet_close(INT_PTR hf) +{ + HANDLE handle = (HANDLE)hf; + return CloseHandle(handle) ? 0 : -1; +} + +static long cabinet_seek(INT_PTR hf, long dist, int seektype) +{ + HANDLE handle = (HANDLE)hf; + /* flags are compatible and so are passed straight through */ + return SetFilePointer(handle, dist, NULL, seektype); +} + +static UINT msi_media_get_disk_info(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + MSIRECORD *row; + LPWSTR ptr; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','D','i','s','k','I','d','`',' ','=',' ','%','i',0}; + + row = MSI_QueryGetRecord(package->db, query, mi->disk_id); + if (!row) + { + TRACE("Unable to query row\n"); + return ERROR_FUNCTION_FAILED; + } + + mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); + mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); + mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); + + if (!mi->first_volume) + mi->first_volume = strdupW(mi->volume_label); + + ptr = strrchrW(mi->source, '\\') + 1; + lstrcpyW(ptr, mi->cabinet); + msiobj_release(&row->hdr); + + return ERROR_SUCCESS; +} + +static INT_PTR cabinet_partial_file(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = (MSICABDATA *)pfdin->pv; + data->mi->is_continuous = FALSE; + return 0; +} + +static INT_PTR cabinet_next_cabinet(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = (MSICABDATA *)pfdin->pv; + MSIMEDIAINFO *mi = data->mi; + LPWSTR cab = strdupAtoW(pfdin->psz1); + INT_PTR res = -1; + UINT rc; + + msi_free(mi->disk_prompt); + msi_free(mi->cabinet); + msi_free(mi->volume_label); + mi->disk_prompt = NULL; + mi->cabinet = NULL; + mi->volume_label = NULL; + + mi->disk_id++; + mi->is_continuous = TRUE; + + rc = msi_media_get_disk_info(data->package, mi); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get next cabinet information: %d\n", rc); + goto done; + } + + if (lstrcmpiW(mi->cabinet, cab)) + { + ERR("Continuous cabinet does not match the next cabinet in the Media table\n"); + goto done; + } + + TRACE("Searching for %s\n", debugstr_w(mi->source)); + + res = 0; + if (GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES) + { + if (msi_change_media(data->package, mi) != ERROR_SUCCESS) + res = -1; + } + +done: + msi_free(cab); + return res; +} + +static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = (MSICABDATA*)pfdin->pv; + HANDLE handle = 0; + LPWSTR path = NULL; + DWORD attrs; + + data->curfile = strdupAtoW(pfdin->psz1); + if (!data->cb(data->package, data->curfile, MSICABEXTRACT_BEGINEXTRACT, &path, + &attrs, data->user)) + goto done; + + TRACE("extracting %s\n", debugstr_w(path)); + + attrs = attrs & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM); + if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL; + + handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, attrs, NULL); + if (handle == INVALID_HANDLE_VALUE) + { + if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) + ERR("failed to create %s (error %d)\n", + debugstr_w(path), GetLastError()); + + goto done; + } + +done: + msi_free(path); + + return (INT_PTR)handle; +} + +static INT_PTR cabinet_close_file_info(FDINOTIFICATIONTYPE fdint, + PFDINOTIFICATION pfdin) +{ + MSICABDATA *data = (MSICABDATA*)pfdin->pv; + FILETIME ft; + FILETIME ftLocal; + HANDLE handle = (HANDLE)pfdin->hf; + + data->mi->is_continuous = FALSE; + + if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) + return -1; + if (!LocalFileTimeToFileTime(&ft, &ftLocal)) + return -1; + if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) + return -1; + + CloseHandle(handle); + + data->cb(data->package, data->curfile, MSICABEXTRACT_FILEEXTRACTED, NULL, NULL, + data->user); + + msi_free(data->curfile); + data->curfile = NULL; + + return 1; +} + +static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + TRACE("(%d)\n", fdint); + + switch (fdint) + { + case fdintPARTIAL_FILE: + return cabinet_partial_file(fdint, pfdin); + + case fdintNEXT_CABINET: + return cabinet_next_cabinet(fdint, pfdin); + + case fdintCOPY_FILE: + return cabinet_copy_file(fdint, pfdin); + + case fdintCLOSE_FILE_INFO: + return cabinet_close_file_info(fdint, pfdin); + + default: + return 0; + } +} + +/*********************************************************************** + * msi_cabextract + * + * Extract files from a cab file. + */ +BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data) +{ + LPSTR cabinet, cab_path = NULL; + LPWSTR ptr; + HFDI hfdi; + ERF erf; + BOOL ret = FALSE; + + TRACE("Extracting %s\n", debugstr_w(mi->source)); + + hfdi = FDICreate(cabinet_alloc, cabinet_free, cabinet_open, cabinet_read, + cabinet_write, cabinet_close, cabinet_seek, 0, &erf); + if (!hfdi) + { + ERR("FDICreate failed\n"); + return FALSE; + } + + ptr = strrchrW(mi->source, '\\') + 1; + cabinet = strdupWtoA(ptr); + if (!cabinet) + goto done; + + cab_path = strdupWtoA(mi->source); + if (!cab_path) + goto done; + + cab_path[ptr - mi->source] = '\0'; + + ret = FDICopy(hfdi, cabinet, cab_path, 0, cabinet_notify, NULL, data); + if (!ret) + ERR("FDICopy failed\n"); + +done: + FDIDestroy(hfdi); + msi_free(cabinet); + msi_free(cab_path); + + if (ret) + mi->is_extracted = TRUE; + + return ret; +} + +void msi_free_media_info(MSIMEDIAINFO *mi) +{ + msi_free(mi->disk_prompt); + msi_free(mi->cabinet); + msi_free(mi->volume_label); + msi_free(mi->first_volume); + msi_free(mi); +} + +UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) +{ + MSIRECORD *row; + LPWSTR source_dir; + LPWSTR source; + DWORD options; + UINT r; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=', + ' ','%','i',' ','A','N','D',' ','`','D','i','s','k','I','d','`',' ','>','=', + ' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ', + '`','D','i','s','k','I','d','`',0}; + + row = MSI_QueryGetRecord(package->db, query, file->Sequence, mi->disk_id); + if (!row) + { + TRACE("Unable to query row\n"); + return ERROR_FUNCTION_FAILED; + } + + mi->is_extracted = FALSE; + mi->disk_id = MSI_RecordGetInteger(row, 1); + mi->last_sequence = MSI_RecordGetInteger(row, 2); + msi_free(mi->disk_prompt); + mi->disk_prompt = strdupW(MSI_RecordGetString(row, 3)); + msi_free(mi->cabinet); + mi->cabinet = strdupW(MSI_RecordGetString(row, 4)); + msi_free(mi->volume_label); + mi->volume_label = strdupW(MSI_RecordGetString(row, 5)); + msiobj_release(&row->hdr); + + if (!mi->first_volume) + mi->first_volume = strdupW(mi->volume_label); + + source_dir = msi_dup_property(package, cszSourceDir); + lstrcpyW(mi->source, source_dir); + + PathStripToRootW(source_dir); + mi->type = GetDriveTypeW(source_dir); + + if (file->IsCompressed && mi->cabinet) + { + if (mi->cabinet[0] == '#') + { + r = writeout_cabinet_stream(package, &mi->cabinet[1], mi->source); + if (r != ERROR_SUCCESS) + { + ERR("Failed to extract cabinet stream\n"); + return ERROR_FUNCTION_FAILED; + } + } + else + lstrcatW(mi->source, mi->cabinet); + } + + options = MSICODE_PRODUCT; + if (mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE) + { + source = source_dir; + options |= MSISOURCETYPE_MEDIA; + } + else if (package->BaseURL && UrlIsW(package->BaseURL, URLIS_URL)) + { + source = package->BaseURL; + options |= MSISOURCETYPE_URL; + } + else + { + source = mi->source; + options |= MSISOURCETYPE_NETWORK; + } + + msi_package_add_media_disk(package, package->Context, + MSICODE_PRODUCT, mi->disk_id, + mi->volume_label, mi->disk_prompt); + + msi_package_add_info(package, package->Context, + options, INSTALLPROPERTY_LASTUSEDSOURCEW, source); + + msi_free(source_dir); + return ERROR_SUCCESS; +} + +/* FIXME: search NETWORK and URL sources as well */ +UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi) +{ + WCHAR source[MAX_PATH]; + WCHAR volume[MAX_PATH]; + WCHAR prompt[MAX_PATH]; + DWORD volumesz, promptsz; + DWORD index, size, id; + UINT r; + + size = MAX_PATH; + r = MsiSourceListGetInfoW(package->ProductCode, NULL, + package->Context, MSICODE_PRODUCT, + INSTALLPROPERTY_LASTUSEDSOURCEW, source, &size); + if (r != ERROR_SUCCESS) + return r; + + index = 0; + volumesz = MAX_PATH; + promptsz = MAX_PATH; + while (MsiSourceListEnumMediaDisksW(package->ProductCode, NULL, + package->Context, + MSICODE_PRODUCT, index++, &id, + volume, &volumesz, prompt, &promptsz) == ERROR_SUCCESS) + { + mi->disk_id = id; + mi->volume_label = msi_realloc(mi->volume_label, ++volumesz * sizeof(WCHAR)); + lstrcpyW(mi->volume_label, volume); + mi->disk_prompt = msi_realloc(mi->disk_prompt, ++promptsz * sizeof(WCHAR)); + lstrcpyW(mi->disk_prompt, prompt); + + if (source_matches_volume(mi, source)) + { + /* FIXME: what about SourceDir */ + lstrcpyW(mi->source, source); + lstrcatW(mi->source, mi->cabinet); + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi) +{ + UINT rc = ERROR_SUCCESS; + + /* media info for continuous cabinet is already loaded */ + if (mi->is_continuous) + return ERROR_SUCCESS; + + rc = msi_load_media_info(package, file, mi); + if (rc != ERROR_SUCCESS) + { + ERR("Unable to load media info\n"); + return ERROR_FUNCTION_FAILED; + } + + /* cabinet is internal, no checks needed */ + if (!mi->cabinet || mi->cabinet[0] == '#') + return ERROR_SUCCESS; + + /* package should be downloaded */ + if (file->IsCompressed && + GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES && + package->BaseURL && UrlIsW(package->BaseURL, URLIS_URL)) + { + WCHAR temppath[MAX_PATH]; + + msi_download_file(mi->source, temppath); + lstrcpyW(mi->source, temppath); + return ERROR_SUCCESS; + } + + /* check volume matches, change media if not */ + if (mi->volume_label && mi->disk_id > 1 && + lstrcmpW(mi->first_volume, mi->volume_label)) + { + LPWSTR source = msi_dup_property(package, cszSourceDir); + BOOL matches; + + matches = source_matches_volume(mi, source); + msi_free(source); + + if ((mi->type == DRIVE_CDROM || mi->type == DRIVE_REMOVABLE) && !matches) + { + rc = msi_change_media(package, mi); + if (rc != ERROR_SUCCESS) + return rc; + } + } + + if (file->IsCompressed && + GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES) + { + rc = find_published_source(package, mi); + if (rc != ERROR_SUCCESS) + { + ERR("Cabinet not found: %s\n", debugstr_w(mi->source)); + return ERROR_INSTALL_FAILURE; + } + } + + return ERROR_SUCCESS; +} diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 05df705..d35bd3f 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -908,10 +908,27 @@ extern UINT msi_create_component_directories( MSIPACKAGE *package ); extern void msi_ui_error( DWORD msg_id, DWORD type ); extern UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid, MSIINSTALLCONTEXT context, DWORD options, LPCWSTR value); + +/* media */ + +typedef BOOL (*PMSICABEXTRACTCB)(MSIPACKAGE *, LPCWSTR, DWORD, LPWSTR *, DWORD *, PVOID); + +#define MSICABEXTRACT_BEGINEXTRACT 0x01 +#define MSICABEXTRACT_FILEEXTRACTED 0x02 + +typedef struct +{ + MSIPACKAGE* package; + MSIMEDIAINFO *mi; + PMSICABEXTRACTCB cb; + LPWSTR curfile; + PVOID user; +} MSICABDATA; + +extern UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi); extern UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi); extern void msi_free_media_info(MSIMEDIAINFO *mi); -extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, PFNFDINOTIFY notify, LPVOID data); -extern UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir); +extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data); extern UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi); /* control event stuff */ -- 1.5.4.3