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