main.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * winemsibuilder - tool to build MSI packages
  3. *
  4. * Copyright 2010 Hans Leidekker 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. #define WIN32_LEAN_AND_MEAN
  21. #define COBJMACROS
  22. #include <stdio.h>
  23. #include <windows.h>
  24. #include <msi.h>
  25. #include <msiquery.h>
  26. #include <objbase.h>
  27. #include "wine/debug.h"
  28. WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder);
  29. static UINT open_database( const WCHAR *msifile, MSIHANDLE *handle )
  30. {
  31. UINT r;
  32. MSIHANDLE hdb;
  33. if (GetFileAttributesW( msifile ) == INVALID_FILE_ATTRIBUTES)
  34. {
  35. r = MsiOpenDatabaseW( msifile, MSIDBOPEN_CREATE, &hdb );
  36. if (r != ERROR_SUCCESS)
  37. {
  38. WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile), r );
  39. return r;
  40. }
  41. r = MsiDatabaseCommit( hdb );
  42. if (r != ERROR_SUCCESS)
  43. {
  44. WINE_ERR( "failed to commit database (%u)\n", r );
  45. MsiCloseHandle( hdb );
  46. return r;
  47. }
  48. }
  49. else
  50. {
  51. r = MsiOpenDatabaseW( msifile, MSIDBOPEN_TRANSACT, &hdb );
  52. if (r != ERROR_SUCCESS)
  53. {
  54. WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile), r );
  55. return r;
  56. }
  57. }
  58. *handle = hdb;
  59. return ERROR_SUCCESS;
  60. }
  61. static int import_tables( const WCHAR *msifile, WCHAR **tables )
  62. {
  63. UINT r;
  64. MSIHANDLE hdb;
  65. WCHAR *dir;
  66. DWORD len;
  67. r = open_database( msifile, &hdb );
  68. if (r != ERROR_SUCCESS) return 1;
  69. len = GetCurrentDirectoryW( 0, NULL );
  70. if (!(dir = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
  71. {
  72. MsiCloseHandle( hdb );
  73. return 1;
  74. }
  75. GetCurrentDirectoryW( len + 1, dir );
  76. while (*tables)
  77. {
  78. r = MsiDatabaseImportW( hdb, dir, *tables );
  79. if (r != ERROR_SUCCESS)
  80. {
  81. WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables), r );
  82. break;
  83. }
  84. tables++;
  85. }
  86. if (r == ERROR_SUCCESS)
  87. {
  88. r = MsiDatabaseCommit( hdb );
  89. if (r != ERROR_SUCCESS)
  90. WINE_ERR( "failed to commit changes (%u)\n", r );
  91. }
  92. HeapFree( GetProcessHeap(), 0, dir );
  93. MsiCloseHandle( hdb );
  94. return (r != ERROR_SUCCESS);
  95. }
  96. /* taken from dlls/msi/table.c */
  97. static int utf2mime( int x )
  98. {
  99. if (x >= '0' && x <= '9')
  100. return x - '0';
  101. if (x >= 'A' && x <= 'Z')
  102. return x - 'A' + 10;
  103. if (x >= 'a' && x <= 'z')
  104. return x - 'a' + 10 + 26;
  105. if (x == '.')
  106. return 10 + 26 + 26;
  107. if (x == '_')
  108. return 10 + 26 + 26 + 1;
  109. return -1;
  110. }
  111. #define MAX_STREAM_NAME 0x1f
  112. static WCHAR *encode_stream( const WCHAR *in )
  113. {
  114. DWORD c, next, count;
  115. WCHAR *out, *p;
  116. count = lstrlenW( in );
  117. if (count > MAX_STREAM_NAME)
  118. return NULL;
  119. count += 2;
  120. if (!(out = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) ))) return NULL;
  121. p = out;
  122. while (count--)
  123. {
  124. c = *in++;
  125. if (!c)
  126. {
  127. *p = c;
  128. return out;
  129. }
  130. if (c < 0x80 && utf2mime( c ) >= 0)
  131. {
  132. c = utf2mime( c ) + 0x4800;
  133. next = *in;
  134. if (next && next < 0x80)
  135. {
  136. next = utf2mime( next );
  137. if (next != -1)
  138. {
  139. next += 0x3ffffc0;
  140. c += next << 6;
  141. in++;
  142. }
  143. }
  144. }
  145. *p++ = c;
  146. }
  147. HeapFree( GetProcessHeap(), 0, out );
  148. return NULL;
  149. }
  150. static int add_stream( const WCHAR *msifile, const WCHAR *stream, const WCHAR *file )
  151. {
  152. UINT r;
  153. HRESULT hr;
  154. MSIHANDLE hdb;
  155. IStorage *stg;
  156. IStream *stm = NULL;
  157. HANDLE handle;
  158. char buffer[4096];
  159. ULARGE_INTEGER size;
  160. DWORD low, high, read;
  161. WCHAR *encname;
  162. int ret = 1;
  163. /* make sure we have the right type of file */
  164. r = open_database( msifile, &hdb );
  165. if (r != ERROR_SUCCESS) return 1;
  166. MsiCloseHandle( hdb );
  167. hr = StgOpenStorage( msifile, NULL, STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
  168. if (hr != S_OK)
  169. {
  170. WINE_WARN( "failed to open storage %s (0x%08x)\n", wine_dbgstr_w(msifile), hr );
  171. return 1;
  172. }
  173. encname = encode_stream( stream );
  174. if (!encname)
  175. {
  176. WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream) );
  177. goto done;
  178. }
  179. hr = IStorage_CreateStream( stg, encname, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
  180. if (hr != S_OK)
  181. {
  182. WINE_WARN( "failed to create stream %s (0x%08x)\n", wine_dbgstr_w(encname), hr );
  183. goto done;
  184. }
  185. handle = CreateFileW( file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
  186. if (handle == INVALID_HANDLE_VALUE)
  187. {
  188. WINE_WARN( "failed to open file %s (%u)\n", wine_dbgstr_w(file), GetLastError() );
  189. goto done;
  190. }
  191. low = GetFileSize( handle, &high );
  192. if (low == INVALID_FILE_SIZE || high)
  193. {
  194. WINE_WARN( "file %s too big\n", wine_dbgstr_w(file) );
  195. CloseHandle( handle );
  196. goto done;
  197. }
  198. size.QuadPart = low;
  199. hr = IStream_SetSize( stm, size );
  200. if (hr != S_OK) goto done;
  201. while (ReadFile( handle, buffer, sizeof(buffer), &read, NULL ) && read)
  202. {
  203. hr = IStream_Write( stm, buffer, read, NULL );
  204. if (hr != S_OK) break;
  205. size.QuadPart -= read;
  206. }
  207. CloseHandle( handle );
  208. if (size.QuadPart)
  209. {
  210. WINE_WARN( "failed to write stream contents\n" );
  211. goto done;
  212. }
  213. IStorage_Commit( stg, 0 );
  214. ret = 0;
  215. done:
  216. HeapFree( GetProcessHeap(), 0, encname );
  217. if (stm) IStream_Release( stm );
  218. IStorage_Release( stg );
  219. return ret;
  220. }
  221. static void show_usage( void )
  222. {
  223. WINE_MESSAGE(
  224. "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
  225. "Options:\n"
  226. " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
  227. " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
  228. "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
  229. "will be created with an empty database.\n"
  230. );
  231. }
  232. int __cdecl wmain( int argc, WCHAR *argv[] )
  233. {
  234. if (argc < 3 || argv[1][0] != '-')
  235. {
  236. show_usage();
  237. return 1;
  238. }
  239. switch (argv[1][1])
  240. {
  241. case 'i':
  242. if (argc < 4) break;
  243. return import_tables( argv[2], argv + 3 );
  244. case 'a':
  245. if (argc < 5) break;
  246. return add_stream( argv[2], argv[3], argv[4] );
  247. default:
  248. WINE_WARN( "unknown option\n" );
  249. break;
  250. }
  251. show_usage();
  252. return 1;
  253. }