123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /*
- * winemsibuilder - tool to build MSI packages
- *
- * Copyright 2010 Hans Leidekker for CodeWeavers
- *
- * 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
- */
- #define WIN32_LEAN_AND_MEAN
- #define COBJMACROS
- #include <stdio.h>
- #include <windows.h>
- #include <msi.h>
- #include <msiquery.h>
- #include <objbase.h>
- #include "wine/debug.h"
- WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder);
- static UINT open_database( const WCHAR *msifile, MSIHANDLE *handle )
- {
- UINT r;
- MSIHANDLE hdb;
- if (GetFileAttributesW( msifile ) == INVALID_FILE_ATTRIBUTES)
- {
- r = MsiOpenDatabaseW( msifile, MSIDBOPEN_CREATE, &hdb );
- if (r != ERROR_SUCCESS)
- {
- WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile), r );
- return r;
- }
- r = MsiDatabaseCommit( hdb );
- if (r != ERROR_SUCCESS)
- {
- WINE_ERR( "failed to commit database (%u)\n", r );
- MsiCloseHandle( hdb );
- return r;
- }
- }
- else
- {
- r = MsiOpenDatabaseW( msifile, MSIDBOPEN_TRANSACT, &hdb );
- if (r != ERROR_SUCCESS)
- {
- WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile), r );
- return r;
- }
- }
- *handle = hdb;
- return ERROR_SUCCESS;
- }
- static int import_tables( const WCHAR *msifile, WCHAR **tables )
- {
- UINT r;
- MSIHANDLE hdb;
- WCHAR *dir;
- DWORD len;
- r = open_database( msifile, &hdb );
- if (r != ERROR_SUCCESS) return 1;
- len = GetCurrentDirectoryW( 0, NULL );
- if (!(dir = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
- {
- MsiCloseHandle( hdb );
- return 1;
- }
- GetCurrentDirectoryW( len + 1, dir );
- while (*tables)
- {
- r = MsiDatabaseImportW( hdb, dir, *tables );
- if (r != ERROR_SUCCESS)
- {
- WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables), r );
- break;
- }
- tables++;
- }
- if (r == ERROR_SUCCESS)
- {
- r = MsiDatabaseCommit( hdb );
- if (r != ERROR_SUCCESS)
- WINE_ERR( "failed to commit changes (%u)\n", r );
- }
- HeapFree( GetProcessHeap(), 0, dir );
- MsiCloseHandle( hdb );
- return (r != ERROR_SUCCESS);
- }
- /* taken from dlls/msi/table.c */
- static int utf2mime( int x )
- {
- if (x >= '0' && x <= '9')
- return x - '0';
- if (x >= 'A' && x <= 'Z')
- return x - 'A' + 10;
- if (x >= 'a' && x <= 'z')
- return x - 'a' + 10 + 26;
- if (x == '.')
- return 10 + 26 + 26;
- if (x == '_')
- return 10 + 26 + 26 + 1;
- return -1;
- }
- #define MAX_STREAM_NAME 0x1f
- static WCHAR *encode_stream( const WCHAR *in )
- {
- DWORD c, next, count;
- WCHAR *out, *p;
- count = lstrlenW( in );
- if (count > MAX_STREAM_NAME)
- return NULL;
- count += 2;
- if (!(out = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) ))) return NULL;
- p = out;
- while (count--)
- {
- c = *in++;
- if (!c)
- {
- *p = c;
- return out;
- }
- if (c < 0x80 && utf2mime( c ) >= 0)
- {
- c = utf2mime( c ) + 0x4800;
- next = *in;
- if (next && next < 0x80)
- {
- next = utf2mime( next );
- if (next != -1)
- {
- next += 0x3ffffc0;
- c += next << 6;
- in++;
- }
- }
- }
- *p++ = c;
- }
- HeapFree( GetProcessHeap(), 0, out );
- return NULL;
- }
- static int add_stream( const WCHAR *msifile, const WCHAR *stream, const WCHAR *file )
- {
- UINT r;
- HRESULT hr;
- MSIHANDLE hdb;
- IStorage *stg;
- IStream *stm = NULL;
- HANDLE handle;
- char buffer[4096];
- ULARGE_INTEGER size;
- DWORD low, high, read;
- WCHAR *encname;
- int ret = 1;
- /* make sure we have the right type of file */
- r = open_database( msifile, &hdb );
- if (r != ERROR_SUCCESS) return 1;
- MsiCloseHandle( hdb );
- hr = StgOpenStorage( msifile, NULL, STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
- if (hr != S_OK)
- {
- WINE_WARN( "failed to open storage %s (0x%08x)\n", wine_dbgstr_w(msifile), hr );
- return 1;
- }
- encname = encode_stream( stream );
- if (!encname)
- {
- WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream) );
- goto done;
- }
- hr = IStorage_CreateStream( stg, encname, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
- if (hr != S_OK)
- {
- WINE_WARN( "failed to create stream %s (0x%08x)\n", wine_dbgstr_w(encname), hr );
- goto done;
- }
- handle = CreateFileW( file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
- if (handle == INVALID_HANDLE_VALUE)
- {
- WINE_WARN( "failed to open file %s (%u)\n", wine_dbgstr_w(file), GetLastError() );
- goto done;
- }
- low = GetFileSize( handle, &high );
- if (low == INVALID_FILE_SIZE || high)
- {
- WINE_WARN( "file %s too big\n", wine_dbgstr_w(file) );
- CloseHandle( handle );
- goto done;
- }
- size.QuadPart = low;
- hr = IStream_SetSize( stm, size );
- if (hr != S_OK) goto done;
- while (ReadFile( handle, buffer, sizeof(buffer), &read, NULL ) && read)
- {
- hr = IStream_Write( stm, buffer, read, NULL );
- if (hr != S_OK) break;
- size.QuadPart -= read;
- }
- CloseHandle( handle );
- if (size.QuadPart)
- {
- WINE_WARN( "failed to write stream contents\n" );
- goto done;
- }
- IStorage_Commit( stg, 0 );
- ret = 0;
- done:
- HeapFree( GetProcessHeap(), 0, encname );
- if (stm) IStream_Release( stm );
- IStorage_Release( stg );
- return ret;
- }
- static void show_usage( void )
- {
- WINE_MESSAGE(
- "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
- "Options:\n"
- " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
- " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
- "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
- "will be created with an empty database.\n"
- );
- }
- int __cdecl wmain( int argc, WCHAR *argv[] )
- {
- if (argc < 3 || argv[1][0] != '-')
- {
- show_usage();
- return 1;
- }
- switch (argv[1][1])
- {
- case 'i':
- if (argc < 4) break;
- return import_tables( argv[2], argv + 3 );
- case 'a':
- if (argc < 5) break;
- return add_stream( argv[2], argv[3], argv[4] );
- default:
- WINE_WARN( "unknown option\n" );
- break;
- }
- show_usage();
- return 1;
- }
|