diff options
author | Eilidh McAdam <eilidh@lanedo.com> | 2012-07-02 18:48:02 +0100 |
---|---|---|
committer | Fridrich Štrba <fridrich.strba@bluewin.ch> | 2012-07-18 10:59:09 +0200 |
commit | 5d4a9127a4af92c49a8c73dfd7597db4772c58c4 (patch) | |
tree | 0ac0fba5252714a0ef5219b95794d63495ca597a /setup_native | |
parent | 456dd17399883d4ade4c863a5c37268a8aa51fce (diff) |
Initial crossmsi branch with first msidb implementation.
Msidb is a subset of MS msidb. Currently unsupported options are:
-m, -t, -j, -k, -w, -s
Change-Id: Ice5f646f70b2f797266ce3fabce12ae9f689b1c8
Signed-off-by: Fridrich Štrba <fridrich.strba@bluewin.ch>
Diffstat (limited to 'setup_native')
-rw-r--r-- | setup_native/source/win32/wintools/msidb/msidb.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/setup_native/source/win32/wintools/msidb/msidb.c b/setup_native/source/win32/wintools/msidb/msidb.c new file mode 100644 index 000000000000..c8d9c6f5dbfc --- /dev/null +++ b/setup_native/source/win32/wintools/msidb/msidb.c @@ -0,0 +1,589 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/*********************************************************************** + * msidb.exe + * + * Needs -mconsole. See ipconfig.c in wine/programs/ipconfig + * + * Copyright 2012 Eilidh McAdam (eilidh@lanedo.com) + * + **********************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msi.h> +#include <msiquery.h> +#include <msidefs.h> + +#include "wine/unicode.h" + +#define MAX_TABLES 50 +#define MAX_STREAMS 10 +#define MAX_STORAGES 10 +#define MAX_TABLE_NAME 50 +#define MAX_STREAM_NAME 62 +#define MAX_STORAGE_NAME 31 + +static const WCHAR PATH_DELIMITERW[] = {'/', 0}; +static const char PATH_DELIMITER[] = {'/', 0}; + + +static WCHAR *strdupAtoW( UINT cp, const char *str ) +{ + WCHAR *ret = NULL; + if (str) + { + DWORD len = MultiByteToWideChar( cp, 0, str, -1, NULL, 0 ); + if ((ret = malloc( len * sizeof(WCHAR) ))) + MultiByteToWideChar( cp, 0, str, -1, ret, len ); + } + return ret; +} + +static char *strdupWtoA( UINT cp, const WCHAR *str ) +{ + char *ret = NULL; + if (str) + { + DWORD len = WideCharToMultiByte(cp, 0, str, -1, NULL, 0, NULL, NULL); + if ( (ret = malloc(len)) ) + WideCharToMultiByte(cp, 0, str, -1, ret, len, NULL, NULL); + } + return ret; +} + +static BOOL msidbImportStorages(LPCWSTR dbfile, LPCWSTR wdir, LPWSTR storageNames[]) +{ + static const WCHAR delim[] = {'/', 0}; + UINT r, len, i; + MSIHANDLE dbhandle, view, rec; + WCHAR *storagePath = 0; + char *query = "INSERT INTO _Storages (Name, Data) VALUES(?, ?)"; + + r = MsiOpenDatabaseW(dbfile, (LPWSTR) MSIDBOPEN_TRANSACT, &dbhandle); + if (r != ERROR_SUCCESS) return FALSE; + + r = MsiDatabaseOpenView(dbhandle, query, &view); + if (r != ERROR_SUCCESS) return FALSE; + MsiViewExecute(view, 0); + r = MsiViewFetch(view, &rec); + if (r != ERROR_SUCCESS) return FALSE; + + for (i = 0; i < MAX_STORAGES && storageNames[i] != 0; ++i) + { + len = lstrlenW(wdir) + lstrlenW(storageNames[i]) + 2; + storagePath = malloc(len * sizeof(WCHAR)); + if (storagePath == NULL) return FALSE; + + lstrcpyW(storagePath, wdir); + lstrcatW(storagePath, delim); + lstrcatW(storagePath, storageNames[i]); + + /* WINE_MESSAGE("Importing from %s\n", wine_dbgstr_w(storagePath)); */ + + rec = MsiCreateRecord(2); + MsiRecordSetStringW(rec, 1, storageNames[i]); + r = MsiRecordSetStreamW(rec, 2, storagePath); + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Error setting storage %d\n", r); */ + return FALSE; + } + + r = MsiViewExecute(view, rec); + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Unable to update with storage record %d\n", r); */ + return FALSE; + } + + MsiCloseHandle(rec); + } + + MsiViewClose(view); + MsiCloseHandle(view); + MsiDatabaseCommit(dbhandle); + MsiCloseHandle(dbhandle); + return TRUE; +} + +static BOOL msidbExportStorage(LPCWSTR dbfile, LPCWSTR wdir, LPWSTR storageName) +{ + UINT r, len; + MSIHANDLE dbhandle, view, rec; + char queryBuffer[100]; + char *storageNameA = strdupWtoA(CP_ACP, storageName); + char *wdirA = strdupWtoA(CP_ACP, wdir); + char *storagePath = NULL; + char *dataBuffer; + DWORD dataLen = 0; + FILE *fp = NULL; + sprintf(queryBuffer, "SELECT * FROM _Storages WHERE Name = '%s'", storageNameA); + + r = MsiOpenDatabaseW(dbfile, (LPWSTR) MSIDBOPEN_READONLY, &dbhandle); + if (r != ERROR_SUCCESS) return FALSE; + + MsiDatabaseOpenView(dbhandle, queryBuffer, &view); + MsiViewExecute(view, 0); + r = MsiViewFetch(view, &rec); + if (r != ERROR_SUCCESS) return FALSE; + + if ((r = MsiRecordReadStream(rec, 2, 0, &dataLen)) != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Problem reading stream, error %d %d %d (%d)\n", r, r == ERROR_INVALID_HANDLE, r == ERROR_INVALID_PARAMETER, dataLen ); */ + return FALSE; + } + /* WINE_MESSAGE("Exporting storage %s using query %s\n", storageNameA, queryBuffer); */ + if ((dataBuffer = malloc(dataLen)) == NULL) return FALSE; + if (MsiRecordReadStream(rec, 2, dataBuffer, &dataLen) != ERROR_SUCCESS) return FALSE; + + len = strlen(wdirA) + strlen(storageNameA) + 2; + storagePath = malloc(len * sizeof(WCHAR)); + if (storagePath == NULL) return FALSE; + + strcpy(storagePath, wdirA); + strcat(storagePath, "/"); + strcat(storagePath, storageNameA); + + fp = fopen(storagePath , "wb"); + if (fp != NULL) + { + fwrite(dataBuffer, 1, dataLen, fp); + } + fclose(fp); + + free(storagePath); + MsiCloseHandle(rec); + + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(dbhandle); + free(storageNameA); + free(wdirA); + return TRUE; +} + +static BOOL msidbImportStreams(LPCWSTR dbfile, LPCWSTR wdir, LPWSTR streamNames[]) +{ + UINT r, len; + MSIHANDLE dbhandle, view, rec; + int i = 0; + WCHAR *streamPath = 0; + char *query = "INSERT INTO _Streams (Name, Data) VALUES(?, ?)"; + + r = MsiOpenDatabaseW(dbfile, (LPWSTR) MSIDBOPEN_TRANSACT, &dbhandle); + if (r != ERROR_SUCCESS) return FALSE; + + r = MsiDatabaseOpenView(dbhandle, query, &view); + if (r != ERROR_SUCCESS) return FALSE; + + for (i = 0; i < MAX_STREAMS && streamNames[i] != NULL; i++) + { + len = lstrlenW(wdir) + lstrlenW(streamNames[i]) + 2; + streamPath = malloc(len * sizeof(WCHAR)); + if (streamPath == NULL) return FALSE; + + lstrcpyW(streamPath, wdir); + lstrcatW(streamPath, PATH_DELIMITERW); + lstrcatW(streamPath, streamNames[i]); + + rec = MsiCreateRecord(2); + MsiRecordSetStringW(rec, 1, streamNames[i]); + r = MsiRecordSetStreamW(rec, 2, streamPath); + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Error setting stream %d (%s)\n", r, wine_dbgstr_w(streamNames[i])); */ + return FALSE; + } + + r = MsiViewExecute(view, rec); + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Unable to update with stream record %d\n", r); */ + return FALSE; + } + + MsiCloseHandle(rec); + } + + MsiViewClose(view); + MsiCloseHandle(view); + MsiDatabaseCommit(dbhandle); + MsiCloseHandle(dbhandle); + return TRUE; +} + +/*********************************************************************** + * msidbExportStream + * + * Exports a stream to a file with an .idb extension. + * + * Examples (note wildcard escape for *nix/bash): + * msidb -d <pathtomsi>.msi -f <workdir> -x <streamname> + * msidb -d <pathtomsi>.msi -f <workdir> -x data.cab + **********************************************************************/ +static BOOL msidbExportStream(LPCWSTR dbfile, LPCWSTR wdir, LPCWSTR streamName) +{ + static const char ext[] = {'.', 'i', 'd', 'b', 0}; + UINT r, len; + MSIHANDLE dbhandle, streamListView, rec; + char queryBuffer[100]; + FILE *fp = 0; + char *streamNameA = strdupWtoA(CP_ACP, streamName); + char *wdirA = strdupWtoA(CP_ACP, wdir); + char *streamFileA = 0; + char streamPath[MAX_PATH]; + char *dataBuffer; + DWORD dataLen = 0; + + r = MsiOpenDatabaseW(dbfile, (LPCWSTR) MSIDBOPEN_READONLY, &dbhandle); + if (r != ERROR_SUCCESS) return FALSE; + + sprintf(queryBuffer, "SELECT * FROM _Streams WHERE Name = '%s'", streamNameA); + MsiDatabaseOpenView(dbhandle, queryBuffer, &streamListView); + MsiViewExecute(streamListView, 0); + r = MsiViewFetch(streamListView, &rec); + if (r != ERROR_SUCCESS) return FALSE; + + if (MsiRecordReadStream(rec, 2, 0, &dataLen) != ERROR_SUCCESS) + return FALSE; + dataBuffer = malloc(dataLen); + if (!dataBuffer) return FALSE; + if (MsiRecordReadStream(rec, 2, dataBuffer, &dataLen) != ERROR_SUCCESS) + return FALSE; + + len = strlen(streamNameA) + 5; + streamFileA = malloc(len); + if (streamFileA == NULL) return FALSE; + + strcpy(streamFileA, streamNameA); + strcat(streamFileA, ext); + + strcpy(streamPath, wdirA); + strcat(streamPath, PATH_DELIMITER); + strcat(streamPath, streamFileA); + + fp = fopen(streamPath , "wb"); + if (fp != NULL) + { + fwrite(dataBuffer, 1, dataLen, fp); + } + fclose(fp); + + free(streamFileA); + MsiCloseHandle(rec); + + MsiViewClose(streamListView); + MsiCloseHandle(streamListView); + MsiCloseHandle(dbhandle); + free(wdirA); + free(streamNameA); + return TRUE; +} + +/*********************************************************************** + * msidbImportTables + * + * Takes a list of tables or '*' (for all) to export to text archive + * files in specified folder + * + * For each table, a file called <tablename>.idt is exported containing + * tab separated ASCII. + * + * Examples (note wildcard escape for *nix/bash): + * msidb -d <pathtomsi>.msi -f <workdir> -e \* + * msidb -d <pathtomsi>.msi -f <workdir> -e File Directory Binary + **********************************************************************/ +static BOOL msidbImportTables(LPCWSTR dbfile, LPCWSTR wdir, LPWSTR tables[], BOOL create) +{ + static const WCHAR ext[] = {'.', 'i', 'd', 't', 0}; + static const WCHAR all[] = {'*', 0}; + + UINT r, len; + char *dirNameA; + char *fileName; + DIR *dir; + struct dirent *ent; + + int i = 0; + MSIHANDLE dbhandle; + + LPWSTR tableFile = 0; + LPCWSTR oFlag = (LPCWSTR) MSIDBOPEN_TRANSACT; + + if (create == TRUE) oFlag = (LPCWSTR) MSIDBOPEN_CREATE; + r = MsiOpenDatabaseW(dbfile, oFlag, &dbhandle); + + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Error while opening db: %d %d %d %d\n", r == ERROR_BAD_PATHNAME, r == ERROR_FUNCTION_FAILED, r == ERROR_INVALID_HANDLE, r == ERROR_INVALID_PARAMETER); */ + return FALSE; + } + + if (strcmpW(tables[0], all) == 0) + { + dirNameA = strdupWtoA(CP_ACP, wdir); + dir = opendir(dirNameA); + + if (dir) + { + while ((ent = readdir(dir)) != NULL) + { + fileName = ent->d_name; + if (strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0) continue; + tableFile = strdupAtoW(CP_ACP, fileName); + r = MsiDatabaseImportW(dbhandle, wdir, tableFile); + + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Failed to import %s from %s\n", wine_dbgstr_w(tableFile), wine_dbgstr_w(wdir)); */ + } + + free(tableFile); + } + } + closedir(dir); + free(dirNameA); + } + else + { + for (i = 0; i < MAX_TABLES && tables[i] != 0; ++i) + { + len = lstrlenW(tables[i]) + 5; + tableFile = malloc(len * sizeof (WCHAR)); + if (tableFile == NULL) return FALSE; + + lstrcpyW(tableFile, tables[i]); + lstrcatW(tableFile, ext); + + r = MsiDatabaseImportW(dbhandle, wdir, tableFile); + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Error while importing %s: %d %d %d %d\n", wine_dbgstr_w(tableFile), r == ERROR_BAD_PATHNAME, r == ERROR_FUNCTION_FAILED, r == ERROR_INVALID_HANDLE, r == ERROR_INVALID_PARAMETER); */ + } + + free(tableFile); + + if (r != ERROR_SUCCESS) + { + return FALSE; + } + } + } + MsiDatabaseCommit(dbhandle); + MsiCloseHandle(dbhandle); + return TRUE; +} + +/*********************************************************************** + * msidbExportTables + * + * Takes a list of tables or '*' (for all) to export to text archive + * files in specified folder + * + * For each table, a file called <tablename>.idt is exported containing + * tab separated ASCII. + * + * Examples (note wildcard escape for *nix/bash): + * msidb -d <pathtomsi>.msi -f <workdir> -e \* + * msidb -d <pathtomsi>.msi -f <workdir> -e File Directory Binary + **********************************************************************/ +static BOOL msidbExportTables(LPCWSTR dbfile, LPCWSTR wdir, LPWSTR tables[]) +{ + static const WCHAR ext[] = {'.', 'i', 'd', 't', 0}; + static const WCHAR all[] = {'*', 0}; + UINT r, len; + MSIHANDLE dbhandle, tableListView, rec; + LPWSTR tableFile = 0; + WCHAR tableName[MAX_TABLE_NAME]; + DWORD size = sizeof(tableName) / sizeof(tableName[0]); + int i = 0; + + r = MsiOpenDatabaseW(dbfile, (LPCWSTR) MSIDBOPEN_READONLY, &dbhandle); + + if (r != ERROR_SUCCESS) return FALSE; + + if (strcmpW(tables[0], all) == 0) + { + r = MsiDatabaseOpenView(dbhandle, "SELECT Name FROM _Tables", &tableListView); + r = MsiViewExecute(tableListView, 0); + r = MsiViewFetch(tableListView, &rec); + + while (r == ERROR_SUCCESS) + { + size = sizeof(tableName) / sizeof(tableName[0]); + r = MsiRecordGetStringW(rec, 1, tableName, &size); + if (r == ERROR_SUCCESS) + { + len = lstrlenW(tableName) + 5; + tableFile = malloc(len * sizeof (WCHAR)); + if (tableFile == NULL) return FALSE; + + lstrcpyW(tableFile, tableName); + lstrcatW(tableFile, ext); + + r = MsiDatabaseExportW(dbhandle, tableName, wdir, tableFile); +#ifdef DEBUG + if (r != ERROR_SUCCESS) + { + /* WINE_MESSAGE("Failed to export %s. Reason: functionfailed(%d) invalidparam(%d) badpath(%d)\n",wine_dbgstr_w(tableFile), r==ERROR_FUNCTION_FAILED, r==ERROR_INVALID_PARAMETER, r==ERROR_BAD_PATHNAME ); */ + } +#endif + free(tableFile); + MsiCloseHandle(rec); + } +#ifdef DEBUG + else + { + /* WINE_MESSAGE("Failed with error. Size is now %d. Error: %d %d %d\n", size, r == ERROR_MORE_DATA, r == ERROR_INVALID_HANDLE, r == ERROR_INVALID_PARAMETER); */ + } +#endif + r = MsiViewFetch(tableListView, &rec); + } + + MsiViewClose(tableListView); + MsiCloseHandle(tableListView); + } + else + { + for (i = 0; i < MAX_TABLES && tables[i] != 0; ++i) + { + len = lstrlenW(tables[i]) + 5; + tableFile = malloc(len * sizeof (WCHAR)); + if (tableFile == NULL) return FALSE; + + lstrcpyW(tableFile, tables[i]); + lstrcatW(tableFile, ext); + r = MsiDatabaseExportW(dbhandle, tables[i], wdir, tableFile); + + free(tableFile); + } + } + + MsiCloseHandle(dbhandle); + return TRUE; +} + +static void usage( void ) +{ + printf( + "Usage: msidb [options] [tables]\n" + "\nOptions:\n" + " -d <path> Fully qualified path to MSI database file\n" + " -f <wdir> Path to the text archive folder\n" + " -c Create or overwrite with new database and import tables\n" + " -i <tables> Import tables from text archive files - " + "use * for all\n" + " -e <tables> Export tables to files archive in directory - " + "use * for all\n" + " -x <stream> Saves stream as <stream>.idb in <wdir>\n" + " -a <file> Adds stream from file to database\n" + " -r <storage> Adds storage to database as substorage\n"); +} + +int wmain(int argc, WCHAR *argv[]) +{ + LPWSTR dbfile, streamName, storageName; + LPWSTR wdir = (LPWSTR) L"."; + LPWSTR streamFiles[MAX_STREAMS] = {0}; + LPWSTR storageNames[MAX_STORAGES] = {0}; + LPWSTR iTables[MAX_TABLES] = {0}; + LPWSTR oTables[MAX_TABLES] = {0}; + BOOL createdb = FALSE; + int i = 0; + int currStream = 0; + int currStorage = 0; + dbfile = streamName = storageName = NULL; + + while (argv[1] && (argv[1][0] == '-' || argv[1][0] == '/')) + { + switch (tolower(argv[1][1])) + { + case 'i': /* Import tables */ + i = 0; + while (argv[2] && argv[2][0] != '-' && i < 10) + { + argv++; argc--; i++; + iTables[i] = argv[2]; + } + + break; + case 'e': /* Export tables */ + i = 0; + while (argv[2] && argv[2][0] != '-' && i < 10) + { + oTables[i] = argv[2]; + argv++; argc--; i++; + } + break; + case 'c': /* Create/ovrwrt .msi db */ + createdb = TRUE; + break; + case 'f': /* Specify working dir */ + argv++; argc--; + wdir = argv[1]; + break; + case 'd': /* Specify .msi db */ + argv++; argc--; + dbfile = argv[1]; + break; + case 'x': /* Save stream to .ibd */ + argv++; argc--; + streamName = argv[1]; + break; + case 'a': /* Add stream(s) to db */ + argv++; argc--; + if (currStream < MAX_STREAMS) + streamFiles[currStream++] = argv[1]; + break; + case 'w': + argv++; argc--; + storageName = argv[1]; + break; + case 'r': /* Add strg as substrg to db */ + argv++; argc--; + if (currStorage < MAX_STORAGES) + storageNames[currStorage++] = argv[1]; + break; + case '?': + case 'h': + usage(); + return 0; + } + argv++; argc--; + } + + if (iTables[0]) + if (!msidbImportTables(dbfile, wdir, iTables, createdb)) + return 1; + + if (oTables[0]) + if (!msidbExportTables(dbfile, wdir, oTables)) + return 2; + + if (streamName) + if (!msidbExportStream(dbfile, wdir, streamName)) + return 3; + + if (streamFiles[0]) + if (!msidbImportStreams(dbfile, wdir, streamFiles)) + return 4; + + if(storageNames[0]) + if (!msidbImportStorages(dbfile, wdir, storageNames)) + return 5; + + if (storageName) + if (!msidbExportStorage(dbfile, wdir, storageName)) + return 6; + + /*for (i = 0; i < 10; i++) + { + WINE_MESSAGE("%s\t%s\t%s\t%s\t%s\n", wine_dbgstr_w(iTables[i]), wine_dbgstr_w(oTables[i]), wine_dbgstr_w(streamName), wine_dbgstr_w(streamFiles[i]), wine_dbgstr_w(storageNames[i])); + }*/ + + return 0; +} |