oleacc: Add Swedish translation.
[wine] / dlls / msi / record.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 Mike McCormack 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
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "wine/debug.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "msipriv.h"
33 #include "objidl.h"
34 #include "winnls.h"
35 #include "ole2.h"
36
37 #include "winreg.h"
38 #include "shlwapi.h"
39
40 #include "query.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
43
44 #define MSIFIELD_NULL   0
45 #define MSIFIELD_INT    1
46 #define MSIFIELD_WSTR   3
47 #define MSIFIELD_STREAM 4
48 #define MSIFIELD_INTPTR 5
49
50 static void MSI_FreeField( MSIFIELD *field )
51 {
52     switch( field->type )
53     {
54     case MSIFIELD_NULL:
55     case MSIFIELD_INT:
56     case MSIFIELD_INTPTR:
57         break;
58     case MSIFIELD_WSTR:
59         msi_free( field->u.szwVal);
60         break;
61     case MSIFIELD_STREAM:
62         IStream_Release( field->u.stream );
63         break;
64     default:
65         ERR("Invalid field type %d\n", field->type);
66     }
67 }
68
69 void MSI_CloseRecord( MSIOBJECTHDR *arg )
70 {
71     MSIRECORD *rec = (MSIRECORD *) arg;
72     UINT i;
73
74     for( i=0; i<=rec->count; i++ )
75         MSI_FreeField( &rec->fields[i] );
76 }
77
78 MSIRECORD *MSI_CreateRecord( UINT cParams )
79 {
80     MSIRECORD *rec;
81     UINT len;
82
83     TRACE("%d\n", cParams);
84
85     if( cParams>65535 )
86         return NULL;
87
88     len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
89     rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
90     if( rec )
91         rec->count = cParams;
92     return rec;
93 }
94
95 MSIHANDLE WINAPI MsiCreateRecord( UINT cParams )
96 {
97     MSIRECORD *rec;
98     MSIHANDLE ret = 0;
99
100     TRACE("%d\n", cParams);
101
102     rec = MSI_CreateRecord( cParams );
103     if( rec )
104     {
105         ret = alloc_msihandle( &rec->hdr );
106         msiobj_release( &rec->hdr );
107     }
108     return ret;
109 }
110
111 UINT MSI_RecordGetFieldCount( const MSIRECORD *rec )
112 {
113     return rec->count;
114 }
115
116 UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
117 {
118     MSIRECORD *rec;
119     UINT ret;
120
121     TRACE("%d\n", handle );
122
123     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
124     if( !rec )
125         return -1;
126
127     msiobj_lock( &rec->hdr );
128     ret = MSI_RecordGetFieldCount( rec );
129     msiobj_unlock( &rec->hdr );
130     msiobj_release( &rec->hdr );
131
132     return ret;
133 }
134
135 static BOOL string2intW( LPCWSTR str, int *out )
136 {
137     int x = 0;
138     LPCWSTR p = str;
139
140     if( *p == '-' ) /* skip the minus sign */
141         p++;
142     while ( *p )
143     {
144         if( (*p < '0') || (*p > '9') )
145             return FALSE;
146         x *= 10;
147         x += (*p - '0');
148         p++;
149     }
150
151     if( str[0] == '-' ) /* check if it's negative */
152         x = -x;
153     *out = x; 
154
155     return TRUE;
156 }
157
158 UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n,
159                           MSIRECORD *out_rec, UINT out_n )
160 {
161     UINT r = ERROR_SUCCESS;
162
163     msiobj_lock( &in_rec->hdr );
164
165     if ( in_n > in_rec->count || out_n > out_rec->count )
166         r = ERROR_FUNCTION_FAILED;
167     else if ( in_rec != out_rec || in_n != out_n )
168     {
169         LPWSTR str;
170         MSIFIELD *in, *out;
171
172         in = &in_rec->fields[in_n];
173         out = &out_rec->fields[out_n];
174
175         switch ( in->type )
176         {
177         case MSIFIELD_NULL:
178             break;
179         case MSIFIELD_INT:
180             out->u.iVal = in->u.iVal;
181             break;
182         case MSIFIELD_INTPTR:
183             out->u.pVal = in->u.pVal;
184             break;
185         case MSIFIELD_WSTR:
186             str = strdupW( in->u.szwVal );
187             if ( !str )
188                 r = ERROR_OUTOFMEMORY;
189             else
190                 out->u.szwVal = str;
191             break;
192         case MSIFIELD_STREAM:
193             IStream_AddRef( in->u.stream );
194             out->u.stream = in->u.stream;
195             break;
196         default:
197             ERR("invalid field type %d\n", in->type);
198         }
199         if (r == ERROR_SUCCESS)
200             out->type = in->type;
201     }
202
203     msiobj_unlock( &in_rec->hdr );
204
205     return r;
206 }
207
208 INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField )
209 {
210     int ret;
211
212     TRACE( "%p %d\n", rec, iField );
213
214     if( iField > rec->count )
215         return MININT_PTR;
216
217     switch( rec->fields[iField].type )
218     {
219     case MSIFIELD_INT:
220         return rec->fields[iField].u.iVal;
221     case MSIFIELD_INTPTR:
222         return rec->fields[iField].u.pVal;
223     case MSIFIELD_WSTR:
224         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
225             return ret;
226         return MININT_PTR;
227     default:
228         break;
229     }
230
231     return MININT_PTR;
232 }
233
234 int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField)
235 {
236     int ret = 0;
237
238     TRACE("%p %d\n", rec, iField );
239
240     if( iField > rec->count )
241         return MSI_NULL_INTEGER;
242
243     switch( rec->fields[iField].type )
244     {
245     case MSIFIELD_INT:
246         return rec->fields[iField].u.iVal;
247     case MSIFIELD_INTPTR:
248         return rec->fields[iField].u.pVal;
249     case MSIFIELD_WSTR:
250         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
251             return ret;
252         return MSI_NULL_INTEGER;
253     default:
254         break;
255     }
256
257     return MSI_NULL_INTEGER;
258 }
259
260 int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField)
261 {
262     MSIRECORD *rec;
263     UINT ret;
264
265     TRACE("%d %d\n", handle, iField );
266
267     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
268     if( !rec )
269         return MSI_NULL_INTEGER;
270
271     msiobj_lock( &rec->hdr );
272     ret = MSI_RecordGetInteger( rec, iField );
273     msiobj_unlock( &rec->hdr );
274     msiobj_release( &rec->hdr );
275
276     return ret;
277 }
278
279 UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
280 {
281     MSIRECORD *rec;
282     UINT i;
283
284     TRACE("%d\n", handle );
285
286     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
287     if( !rec )
288         return ERROR_INVALID_HANDLE;
289
290     msiobj_lock( &rec->hdr );
291     for( i=0; i<=rec->count; i++)
292     {
293         MSI_FreeField( &rec->fields[i] );
294         rec->fields[i].type = MSIFIELD_NULL;
295         rec->fields[i].u.iVal = 0;
296     }
297     msiobj_unlock( &rec->hdr );
298     msiobj_release( &rec->hdr );
299
300     return ERROR_SUCCESS;
301 }
302
303 UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal )
304 {
305     TRACE("%p %u %ld\n", rec, iField, pVal);
306
307     if( iField > rec->count )
308         return ERROR_INVALID_PARAMETER;
309
310     MSI_FreeField( &rec->fields[iField] );
311     rec->fields[iField].type = MSIFIELD_INTPTR;
312     rec->fields[iField].u.pVal = pVal;
313
314     return ERROR_SUCCESS;
315 }
316
317 UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal )
318 {
319     TRACE("%p %u %d\n", rec, iField, iVal);
320
321     if( iField > rec->count )
322         return ERROR_INVALID_PARAMETER;
323
324     MSI_FreeField( &rec->fields[iField] );
325     rec->fields[iField].type = MSIFIELD_INT;
326     rec->fields[iField].u.iVal = iVal;
327
328     return ERROR_SUCCESS;
329 }
330
331 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
332 {
333     MSIRECORD *rec;
334     UINT ret;
335
336     TRACE("%d %u %d\n", handle, iField, iVal);
337
338     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
339     if( !rec )
340         return ERROR_INVALID_HANDLE;
341
342     msiobj_lock( &rec->hdr );
343     ret = MSI_RecordSetInteger( rec, iField, iVal );
344     msiobj_unlock( &rec->hdr );
345     msiobj_release( &rec->hdr );
346     return ret;
347 }
348
349 BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
350 {
351     BOOL r = TRUE;
352
353     TRACE("%p %d\n", rec, iField );
354
355     r = ( iField > rec->count ) ||
356         ( rec->fields[iField].type == MSIFIELD_NULL );
357
358     return r;
359 }
360
361 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
362 {
363     MSIRECORD *rec;
364     UINT ret;
365
366     TRACE("%d %d\n", handle, iField );
367
368     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
369     if( !rec )
370         return 0;
371     msiobj_lock( &rec->hdr );
372     ret = MSI_RecordIsNull( rec, iField );
373     msiobj_unlock( &rec->hdr );
374     msiobj_release( &rec->hdr );
375     return ret;
376
377 }
378
379 UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
380                LPSTR szValue, LPDWORD pcchValue)
381 {
382     UINT len=0, ret;
383     CHAR buffer[16];
384
385     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
386
387     if( iField > rec->count )
388     {
389         if ( szValue && *pcchValue > 0 )
390             szValue[0] = 0;
391
392         *pcchValue = 0;
393         return ERROR_SUCCESS;
394     }
395
396     ret = ERROR_SUCCESS;
397     switch( rec->fields[iField].type )
398     {
399     case MSIFIELD_INT:
400         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
401         len = lstrlenA( buffer );
402         if (szValue)
403             lstrcpynA(szValue, buffer, *pcchValue);
404         break;
405     case MSIFIELD_WSTR:
406         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
407                              NULL, 0 , NULL, NULL);
408         if (szValue)
409             WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
410                                  szValue, *pcchValue, NULL, NULL);
411         if( szValue && *pcchValue && len>*pcchValue )
412             szValue[*pcchValue-1] = 0;
413         if( len )
414             len--;
415         break;
416     case MSIFIELD_NULL:
417         if( szValue && *pcchValue > 0 )
418             szValue[0] = 0;
419         break;
420     default:
421         ret = ERROR_INVALID_PARAMETER;
422         break;
423     }
424
425     if( szValue && *pcchValue <= len )
426         ret = ERROR_MORE_DATA;
427     *pcchValue = len;
428
429     return ret;
430 }
431
432 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
433                LPSTR szValue, LPDWORD pcchValue)
434 {
435     MSIRECORD *rec;
436     UINT ret;
437
438     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
439
440     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
441     if( !rec )
442         return ERROR_INVALID_HANDLE;
443     msiobj_lock( &rec->hdr );
444     ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
445     msiobj_unlock( &rec->hdr );
446     msiobj_release( &rec->hdr );
447     return ret;
448 }
449
450 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
451 {
452     if( iField > rec->count )
453         return NULL;
454
455     if( rec->fields[iField].type != MSIFIELD_WSTR )
456         return NULL;
457
458     return rec->fields[iField].u.szwVal;
459 }
460
461 UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
462                LPWSTR szValue, LPDWORD pcchValue)
463 {
464     UINT len=0, ret;
465     WCHAR buffer[16];
466     static const WCHAR szFormat[] = { '%','d',0 };
467
468     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
469
470     if( iField > rec->count )
471     {
472         if ( szValue && *pcchValue > 0 )
473             szValue[0] = 0;
474
475         *pcchValue = 0;
476         return ERROR_SUCCESS;
477     }
478
479     ret = ERROR_SUCCESS;
480     switch( rec->fields[iField].type )
481     {
482     case MSIFIELD_INT:
483         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
484         len = lstrlenW( buffer );
485         if (szValue)
486             lstrcpynW(szValue, buffer, *pcchValue);
487         break;
488     case MSIFIELD_WSTR:
489         len = lstrlenW( rec->fields[iField].u.szwVal );
490         if (szValue)
491             lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
492         break;
493     case MSIFIELD_NULL:
494         if( szValue && *pcchValue > 0 )
495             szValue[0] = 0;
496     default:
497         break;
498     }
499
500     if( szValue && *pcchValue <= len )
501         ret = ERROR_MORE_DATA;
502     *pcchValue = len;
503
504     return ret;
505 }
506
507 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
508                LPWSTR szValue, LPDWORD pcchValue)
509 {
510     MSIRECORD *rec;
511     UINT ret;
512
513     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
514
515     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
516     if( !rec )
517         return ERROR_INVALID_HANDLE;
518
519     msiobj_lock( &rec->hdr );
520     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
521     msiobj_unlock( &rec->hdr );
522     msiobj_release( &rec->hdr );
523     return ret;
524 }
525
526 static UINT msi_get_stream_size( IStream *stm )
527 {
528     STATSTG stat;
529     HRESULT r;
530
531     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
532     if( FAILED(r) )
533         return 0;
534     return stat.cbSize.QuadPart;
535 }
536
537 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
538 {
539     TRACE("%p %d\n", rec, iField);
540
541     if( iField > rec->count )
542         return 0;
543
544     switch( rec->fields[iField].type )
545     {
546     case MSIFIELD_INT:
547         return sizeof (INT);
548     case MSIFIELD_WSTR:
549         return lstrlenW( rec->fields[iField].u.szwVal );
550     case MSIFIELD_NULL:
551         break;
552     case MSIFIELD_STREAM:
553         return msi_get_stream_size( rec->fields[iField].u.stream );
554     }
555     return 0;
556 }
557
558 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
559 {
560     MSIRECORD *rec;
561     UINT ret;
562
563     TRACE("%d %d\n", handle, iField);
564
565     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
566     if( !rec )
567         return 0;
568     msiobj_lock( &rec->hdr );
569     ret = MSI_RecordDataSize( rec, iField);
570     msiobj_unlock( &rec->hdr );
571     msiobj_release( &rec->hdr );
572     return ret;
573 }
574
575 static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue )
576 {
577     LPWSTR str;
578
579     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
580
581     if( iField > rec->count )
582         return ERROR_INVALID_FIELD;
583
584     MSI_FreeField( &rec->fields[iField] );
585     if( szValue && szValue[0] )
586     {
587         str = strdupAtoW( szValue );
588         rec->fields[iField].type = MSIFIELD_WSTR;
589         rec->fields[iField].u.szwVal = str;
590     }
591     else
592     {
593         rec->fields[iField].type = MSIFIELD_NULL;
594         rec->fields[iField].u.szwVal = NULL;
595     }
596
597     return 0;
598 }
599
600 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
601 {
602     MSIRECORD *rec;
603     UINT ret;
604
605     TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
606
607     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
608     if( !rec )
609         return ERROR_INVALID_HANDLE;
610     msiobj_lock( &rec->hdr );
611     ret = MSI_RecordSetStringA( rec, iField, szValue );
612     msiobj_unlock( &rec->hdr );
613     msiobj_release( &rec->hdr );
614     return ret;
615 }
616
617 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
618 {
619     LPWSTR str;
620
621     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
622
623     if( iField > rec->count )
624         return ERROR_INVALID_FIELD;
625
626     MSI_FreeField( &rec->fields[iField] );
627
628     if( szValue && szValue[0] )
629     {
630         str = strdupW( szValue );
631         rec->fields[iField].type = MSIFIELD_WSTR;
632         rec->fields[iField].u.szwVal = str;
633     }
634     else
635     {
636         rec->fields[iField].type = MSIFIELD_NULL;
637         rec->fields[iField].u.szwVal = NULL;
638     }
639
640     return 0;
641 }
642
643 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
644 {
645     MSIRECORD *rec;
646     UINT ret;
647
648     TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
649
650     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
651     if( !rec )
652         return ERROR_INVALID_HANDLE;
653
654     msiobj_lock( &rec->hdr );
655     ret = MSI_RecordSetStringW( rec, iField, szValue );
656     msiobj_unlock( &rec->hdr );
657     msiobj_release( &rec->hdr );
658     return ret;
659 }
660
661 /* read the data in a file into an IStream */
662 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
663 {
664     DWORD sz, szHighWord = 0, read;
665     HANDLE handle;
666     HGLOBAL hGlob = 0;
667     HRESULT hr;
668     ULARGE_INTEGER ulSize;
669
670     TRACE("reading %s\n", debugstr_w(szFile));
671
672     /* read the file into memory */
673     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
674     if( handle == INVALID_HANDLE_VALUE )
675         return GetLastError();
676     sz = GetFileSize(handle, &szHighWord);
677     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
678     {
679         hGlob = GlobalAlloc(GMEM_FIXED, sz);
680         if( hGlob )
681         {
682             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
683             if( !r )
684             {
685                 GlobalFree(hGlob);
686                 hGlob = 0;
687             }
688         }
689     }
690     CloseHandle(handle);
691     if( !hGlob )
692         return ERROR_FUNCTION_FAILED;
693
694     /* make a stream out of it, and set the correct file size */
695     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
696     if( FAILED( hr ) )
697     {
698         GlobalFree(hGlob);
699         return ERROR_FUNCTION_FAILED;
700     }
701
702     /* set the correct size - CreateStreamOnHGlobal screws it up */
703     ulSize.QuadPart = sz;
704     IStream_SetSize(*pstm, ulSize);
705
706     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
707
708     return ERROR_SUCCESS;
709 }
710
711 UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
712 {
713     if ( (iField == 0) || (iField > rec->count) )
714         return ERROR_INVALID_PARAMETER;
715
716     MSI_FreeField( &rec->fields[iField] );
717     rec->fields[iField].type = MSIFIELD_STREAM;
718     rec->fields[iField].u.stream = stream;
719
720     return ERROR_SUCCESS;
721 }
722
723 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
724 {
725     IStream *stm = NULL;
726     HRESULT r;
727
728     if( (iField == 0) || (iField > rec->count) )
729         return ERROR_INVALID_PARAMETER;
730
731     /* no filename means we should seek back to the start of the stream */
732     if( !szFilename )
733     {
734         LARGE_INTEGER ofs;
735         ULARGE_INTEGER cur;
736
737         if( rec->fields[iField].type != MSIFIELD_STREAM )
738             return ERROR_INVALID_FIELD;
739
740         stm = rec->fields[iField].u.stream;
741         if( !stm )
742             return ERROR_INVALID_FIELD;
743
744         ofs.QuadPart = 0;
745         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
746         if( FAILED( r ) )
747             return ERROR_FUNCTION_FAILED;
748     }
749     else
750     {
751         /* read the file into a stream and save the stream in the record */
752         r = RECORD_StreamFromFile(szFilename, &stm);
753         if( r != ERROR_SUCCESS )
754             return r;
755
756         /* if all's good, store it in the record */
757         MSI_RecordSetStream(rec, iField, stm);
758     }
759
760     return ERROR_SUCCESS;
761 }
762
763 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
764 {
765     LPWSTR wstr = NULL;
766     UINT ret;
767
768     TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
769
770     if( szFilename )
771     {
772         wstr = strdupAtoW( szFilename );
773         if( !wstr )
774              return ERROR_OUTOFMEMORY;
775     }
776     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
777     msi_free(wstr);
778
779     return ret;
780 }
781
782 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
783 {
784     MSIRECORD *rec;
785     UINT ret;
786
787     TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
788
789     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
790     if( !rec )
791         return ERROR_INVALID_HANDLE;
792
793     msiobj_lock( &rec->hdr );
794     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
795     msiobj_unlock( &rec->hdr );
796     msiobj_release( &rec->hdr );
797     return ret;
798 }
799
800 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
801 {
802     ULONG count;
803     HRESULT r;
804     IStream *stm;
805
806     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
807
808     if( !sz )
809         return ERROR_INVALID_PARAMETER;
810
811     if( iField > rec->count)
812         return ERROR_INVALID_PARAMETER;
813
814     if ( rec->fields[iField].type == MSIFIELD_NULL )
815     {
816         *sz = 0;
817         return ERROR_INVALID_DATA;
818     }
819
820     if( rec->fields[iField].type != MSIFIELD_STREAM )
821         return ERROR_INVALID_DATATYPE;
822
823     stm = rec->fields[iField].u.stream;
824     if( !stm )
825         return ERROR_INVALID_PARAMETER;
826
827     /* if there's no buffer pointer, calculate the length to the end */
828     if( !buf )
829     {
830         LARGE_INTEGER ofs;
831         ULARGE_INTEGER end, cur;
832
833         ofs.QuadPart = cur.QuadPart = 0;
834         end.QuadPart = 0;
835         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
836         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
837         ofs.QuadPart = cur.QuadPart;
838         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
839         *sz = end.QuadPart - cur.QuadPart;
840
841         return ERROR_SUCCESS;
842     }
843
844     /* read the data */
845     count = 0;
846     r = IStream_Read( stm, buf, *sz, &count );
847     if( FAILED( r ) )
848     {
849         *sz = 0;
850         return ERROR_FUNCTION_FAILED;
851     }
852
853     *sz = count;
854
855     return ERROR_SUCCESS;
856 }
857
858 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
859 {
860     MSIRECORD *rec;
861     UINT ret;
862
863     TRACE("%d %d %p %p\n", handle, iField, buf, sz);
864
865     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
866     if( !rec )
867         return ERROR_INVALID_HANDLE;
868     msiobj_lock( &rec->hdr );
869     ret = MSI_RecordReadStream( rec, iField, buf, sz );
870     msiobj_unlock( &rec->hdr );
871     msiobj_release( &rec->hdr );
872     return ret;
873 }
874
875 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
876 {
877     TRACE("%p %d %p\n", rec, iField, stm);
878
879     if( iField > rec->count )
880         return ERROR_INVALID_FIELD;
881
882     MSI_FreeField( &rec->fields[iField] );
883
884     rec->fields[iField].type = MSIFIELD_STREAM;
885     rec->fields[iField].u.stream = stm;
886     IStream_AddRef( stm );
887
888     return ERROR_SUCCESS;
889 }
890
891 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
892 {
893     TRACE("%p %d %p\n", rec, iField, pstm);
894
895     if( iField > rec->count )
896         return ERROR_INVALID_FIELD;
897
898     if( rec->fields[iField].type != MSIFIELD_STREAM )
899         return ERROR_INVALID_FIELD;
900
901     *pstm = rec->fields[iField].u.stream;
902     IStream_AddRef( *pstm );
903
904     return ERROR_SUCCESS;
905 }
906
907 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
908 {
909     ULARGE_INTEGER size;
910     LARGE_INTEGER pos;
911     IStream *out;
912     DWORD stgm;
913     HRESULT r;
914
915     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
916     r = SHCreateStreamOnFileW( name, stgm, &out );
917     if( FAILED( r ) )
918         return ERROR_FUNCTION_FAILED;
919
920     pos.QuadPart = 0;
921     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
922     if( FAILED( r ) )
923         goto end;
924
925     pos.QuadPart = 0;
926     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
927     if( FAILED( r ) )
928         goto end;
929
930     r = IStream_CopyTo( stm, out, size, NULL, NULL );
931
932 end:
933     IStream_Release( out );
934     if( FAILED( r ) )
935         return ERROR_FUNCTION_FAILED;
936     return ERROR_SUCCESS;
937 }
938
939 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
940 {
941     IStream *stm = NULL;
942     UINT r;
943
944     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
945
946     msiobj_lock( &rec->hdr );
947
948     r = MSI_RecordGetIStream( rec, iField, &stm );
949     if( r == ERROR_SUCCESS )
950     {
951         r = msi_dump_stream_to_file( stm, name );
952         IStream_Release( stm );
953     }
954
955     msiobj_unlock( &rec->hdr );
956
957     return r;
958 }
959
960 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
961 {
962     MSIRECORD *clone;
963     UINT r, i, count;
964
965     count = MSI_RecordGetFieldCount(rec);
966     clone = MSI_CreateRecord(count);
967     if (!clone)
968         return NULL;
969
970     for (i = 0; i <= count; i++)
971     {
972         if (rec->fields[i].type == MSIFIELD_STREAM)
973         {
974             if (FAILED(IStream_Clone(rec->fields[i].u.stream,
975                                      &clone->fields[i].u.stream)))
976             {
977                 msiobj_release(&clone->hdr);
978                 return NULL;
979             }
980             clone->fields[i].type = MSIFIELD_STREAM;
981         }
982         else
983         {
984             r = MSI_RecordCopyField(rec, i, clone, i);
985             if (r != ERROR_SUCCESS)
986             {
987                 msiobj_release(&clone->hdr);
988                 return NULL;
989             }
990         }
991     }
992
993     return clone;
994 }
995
996 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
997 {
998     UINT i;
999
1000     if (a->count != b->count)
1001         return FALSE;
1002
1003     for (i = 0; i <= a->count; i++)
1004     {
1005         if (a->fields[i].type != b->fields[i].type)
1006             return FALSE;
1007
1008         switch (a->fields[i].type)
1009         {
1010             case MSIFIELD_NULL:
1011                 break;
1012
1013             case MSIFIELD_INT:
1014                 if (a->fields[i].u.iVal != b->fields[i].u.iVal)
1015                     return FALSE;
1016                 break;
1017
1018             case MSIFIELD_WSTR:
1019                 if (lstrcmpW(a->fields[i].u.szwVal, b->fields[i].u.szwVal))
1020                     return FALSE;
1021                 break;
1022
1023             case MSIFIELD_STREAM:
1024             default:
1025                 return FALSE;
1026         }
1027     }
1028
1029     return TRUE;
1030 }