Set the out buffer count to zero on read 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
36 #include "query.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(msi);
39
40 #define MSIFIELD_NULL   0
41 #define MSIFIELD_INT    1
42 #define MSIFIELD_STR    2
43 #define MSIFIELD_WSTR   3
44 #define MSIFIELD_STREAM 4
45
46 void MSI_FreeField( MSIFIELD *field )
47 {
48     switch( field->type )
49     {
50     case MSIFIELD_NULL:
51     case MSIFIELD_INT:
52         break;
53     case MSIFIELD_WSTR:
54         HeapFree( GetProcessHeap(), 0, field->u.szwVal);
55         break;
56     case MSIFIELD_STREAM:
57         IStream_Release( field->u.stream );
58         break;
59     default:
60         ERR("Invalid field type %d\n", field->type);
61     }
62 }
63
64 void MSI_CloseRecord( MSIOBJECTHDR *arg )
65 {
66     MSIRECORD *rec = (MSIRECORD *) arg;
67     UINT i;
68
69     for( i=0; i<=rec->count; i++ )
70         MSI_FreeField( &rec->fields[i] );
71 }
72
73 MSIRECORD *MSI_CreateRecord( unsigned int cParams )
74 {
75     MSIRECORD *rec;
76     UINT len;
77
78     TRACE("%d\n", cParams);
79
80     len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
81     rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
82     if( rec )
83     rec->count = cParams;
84     return rec;
85 }
86
87 MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
88 {
89     MSIRECORD *rec;
90     MSIHANDLE ret = 0;
91
92     TRACE("%d\n", cParams);
93
94     rec = MSI_CreateRecord( cParams );
95     if( rec )
96         ret = alloc_msihandle( &rec->hdr );
97     return ret;
98 }
99
100 unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec )
101 {
102     return rec->count;
103 }
104
105 unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
106 {
107     MSIRECORD *rec;
108     UINT ret;
109
110     TRACE("%ld\n", handle );
111
112     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
113     if( !rec )
114     {
115         ERR("Record not found!\n");
116         return 0;
117     }
118
119     ret = MSI_RecordGetFieldCount( rec );
120     msiobj_release( &rec->hdr );
121
122     return ret;
123 }
124
125 static BOOL string2intW( LPCWSTR str, int *out )
126 {
127     int x = 0;
128     LPCWSTR p = str;
129
130     if( *p == '-' ) /* skip the minus sign */
131         p++;
132     while ( *p )
133     {
134         if( (*p < '0') || (*p > '9') )
135             return FALSE;
136         x *= 10;
137         x += (*p - '0');
138         p++;
139     }
140
141     if( str[0] == '-' ) /* check if it's negative */
142         x = -x;
143     *out = x; 
144
145     return TRUE;
146 }
147
148 int MSI_RecordGetInteger( MSIRECORD *rec, unsigned int iField)
149 {
150     int ret = 0;
151
152     TRACE("%p %d\n", rec, iField );
153
154     if( iField > rec->count )
155         return MSI_NULL_INTEGER;
156
157     switch( rec->fields[iField].type )
158     {
159     case MSIFIELD_INT:
160         return rec->fields[iField].u.iVal;
161     case MSIFIELD_WSTR:
162         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
163             return ret;
164         return MSI_NULL_INTEGER;
165     default:
166         break;
167     }
168
169     return MSI_NULL_INTEGER;
170 }
171
172 int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField)
173 {
174     MSIRECORD *rec;
175     UINT ret;
176
177     TRACE("%ld %d\n", handle, iField );
178
179     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
180     if( !rec )
181         return MSI_NULL_INTEGER;
182
183     ret = MSI_RecordGetInteger( rec, iField );
184     msiobj_release( &rec->hdr );
185
186     return ret;
187 }
188
189 UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
190 {
191     MSIRECORD *rec;
192     UINT i;
193
194     TRACE("%ld\n", handle );
195
196     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
197     if( !rec )
198         return ERROR_INVALID_HANDLE;
199
200     for( i=0; i<=rec->count; i++)
201     {
202         MSI_FreeField( &rec->fields[i] );
203         rec->fields[i].type = MSIFIELD_NULL;
204         rec->fields[i].u.iVal = 0;
205     }
206
207     return ERROR_SUCCESS;
208 }
209
210 UINT MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal )
211 {
212     TRACE("%p %u %d\n", rec, iField, iVal);
213
214     if( iField <= rec->count )
215     {
216         MSI_FreeField( &rec->fields[iField] );
217         rec->fields[iField].type = MSIFIELD_INT;
218         rec->fields[iField].u.iVal = iVal;
219     }
220
221     return ERROR_SUCCESS;
222 }
223
224 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal )
225 {
226     MSIRECORD *rec;
227     UINT ret;
228
229     TRACE("%ld %u %d\n", handle, iField, iVal);
230
231     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
232     if( !rec )
233         return ERROR_INVALID_HANDLE;
234
235     ret = MSI_RecordSetInteger( rec, iField, iVal );
236     msiobj_release( &rec->hdr );
237     return ret;
238 }
239
240 BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int iField )
241 {
242     BOOL r = TRUE;
243
244     TRACE("%p %d\n", rec, iField );
245
246     r = ( iField > rec->count ) ||
247         ( rec->fields[iField].type == MSIFIELD_NULL );
248
249     return r;
250 }
251
252 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField )
253 {
254     MSIRECORD *rec;
255     UINT ret;
256
257     TRACE("%ld %d\n", handle, iField );
258
259     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
260     if( !rec )
261         return ERROR_INVALID_HANDLE;
262     ret = MSI_RecordIsNull( rec, iField );
263     msiobj_release( &rec->hdr );
264     return ret;
265
266 }
267
268 UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField, 
269                LPSTR szValue, DWORD *pcchValue)
270 {
271     UINT len=0, ret;
272     CHAR buffer[16];
273
274     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
275
276     if( iField > rec->count )
277         return ERROR_INVALID_PARAMETER;
278
279     ret = ERROR_SUCCESS;
280     switch( rec->fields[iField].type )
281     {
282     case MSIFIELD_INT:
283         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
284         len = lstrlenA( buffer );
285         lstrcpynA(szValue, buffer, *pcchValue);
286         break;
287     case MSIFIELD_WSTR:
288         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
289                              NULL, 0 , NULL, NULL);
290         WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
291                              szValue, *pcchValue, NULL, NULL);
292         break;
293     case MSIFIELD_NULL:
294         len = 1;
295         if( *pcchValue > 0 )
296             szValue[0] = 0;
297         break;
298     default:
299         ret = ERROR_INVALID_PARAMETER;
300         break;
301     }
302
303     if( *pcchValue < len )
304         ret = ERROR_MORE_DATA;
305     *pcchValue = len;
306
307     return ret;
308 }
309
310 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, 
311                LPSTR szValue, DWORD *pcchValue)
312 {
313     MSIRECORD *rec;
314     UINT ret;
315
316     TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
317
318     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
319     if( !rec )
320         return ERROR_INVALID_HANDLE;
321     ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
322     msiobj_release( &rec->hdr );
323     return ret;
324 }
325
326 const WCHAR *MSI_RecordGetString( MSIRECORD *rec, unsigned int iField )
327 {
328     if( iField > rec->count )
329         return NULL;
330
331     if( rec->fields[iField].type != MSIFIELD_WSTR )
332         return NULL;
333
334     return rec->fields[iField].u.szwVal;
335 }
336
337 UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField,
338                LPWSTR szValue, DWORD *pcchValue)
339 {
340     UINT len=0, ret;
341     WCHAR buffer[16];
342     static const WCHAR szFormat[] = { '%','d',0 };
343
344     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
345
346     if( iField > rec->count )
347         return ERROR_INVALID_PARAMETER;
348
349     ret = ERROR_SUCCESS;
350     switch( rec->fields[iField].type )
351     {
352     case MSIFIELD_INT:
353         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
354         len = lstrlenW( buffer );
355         lstrcpynW(szValue, buffer, *pcchValue);
356         break;
357     case MSIFIELD_WSTR:
358         len = lstrlenW( rec->fields[iField].u.szwVal );
359         lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
360         break;
361     case MSIFIELD_NULL:
362         len = 1;
363         if( *pcchValue > 0 )
364             szValue[0] = 0;
365     default:
366         break;
367     }
368
369     if( *pcchValue < len )
370         ret = ERROR_MORE_DATA;
371     *pcchValue = len;
372
373     return ret;
374 }
375
376 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
377                LPWSTR szValue, DWORD *pcchValue)
378 {
379     MSIRECORD *rec;
380     UINT ret;
381
382     TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
383
384     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
385     if( !rec )
386         return ERROR_INVALID_HANDLE;
387
388     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
389     msiobj_release( &rec->hdr );
390     return ret;
391 }
392
393 UINT WINAPI MsiRecordDataSize(MSIHANDLE hRecord, unsigned int iField)
394 {
395     FIXME("%ld %d\n", hRecord, iField);
396     return 0;
397 }
398
399 UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
400 {
401     LPWSTR str;
402     UINT len;
403
404     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
405
406     if( iField > rec->count )
407         return ERROR_INVALID_FIELD;
408
409     len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
410     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
411     MultiByteToWideChar( CP_ACP, 0, szValue, -1, str, len );
412     MSI_FreeField( &rec->fields[iField] );
413     rec->fields[iField].type = MSIFIELD_WSTR;
414     rec->fields[iField].u.szwVal = str;
415
416     return 0;
417 }
418
419 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
420 {
421     MSIRECORD *rec;
422     UINT ret;
423
424     TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
425
426     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
427     if( !rec )
428         return ERROR_INVALID_HANDLE;
429     ret = MSI_RecordSetStringA( rec, iField, szValue );
430     msiobj_release( &rec->hdr );
431     return ret;
432 }
433
434 UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
435 {
436     LPWSTR str;
437     UINT len;
438
439     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
440
441     if( iField > rec->count )
442         return ERROR_INVALID_FIELD;
443
444     len = lstrlenW(szValue) + 1;
445     str = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR));
446     lstrcpyW( str, szValue );
447
448     MSI_FreeField( &rec->fields[iField] );
449     rec->fields[iField].type = MSIFIELD_WSTR;
450     rec->fields[iField].u.szwVal = str;
451
452     return 0;
453 }
454
455 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
456 {
457     MSIRECORD *rec;
458     UINT ret;
459
460     TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
461
462     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
463     if( !rec )
464         return ERROR_INVALID_HANDLE;
465
466     ret = MSI_RecordSetStringW( rec, iField, szValue );
467     msiobj_release( &rec->hdr );
468     return ret;
469 }
470
471 UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR szResult, DWORD *sz)
472 {
473     FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
474     return ERROR_CALL_NOT_IMPLEMENTED;
475 }
476
477 UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, LPWSTR szResult, DWORD *sz)
478 {
479     FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
480     return ERROR_CALL_NOT_IMPLEMENTED;
481 }
482
483 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
484 {
485     FIXME("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
486     return ERROR_CALL_NOT_IMPLEMENTED;
487 }
488
489 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE hRecord, unsigned int iField, LPCWSTR szFilename)
490 {
491     FIXME("%ld %d %s\n", hRecord, iField, debugstr_w(szFilename));
492     return ERROR_CALL_NOT_IMPLEMENTED;
493 }
494
495 UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
496 {
497     ULONG count;
498     HRESULT r;
499     IStream *stm;
500
501     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
502
503     if( iField > rec->count )
504         return ERROR_INVALID_FIELD;
505
506     if( rec->fields[iField].type != MSIFIELD_STREAM )
507     {
508         *sz = 0;
509         return ERROR_INVALID_FIELD;
510     }
511
512     stm = rec->fields[iField].u.stream;
513     if( !stm )
514         return ERROR_INVALID_FIELD;
515
516     /* if there's no buffer pointer, calculate the length to the end */
517     if( !buf )
518     {
519         LARGE_INTEGER ofs;
520         ULARGE_INTEGER end, cur;
521
522         ofs.QuadPart = cur.QuadPart = 0;
523         end.QuadPart = 0;
524         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
525         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
526         ofs.QuadPart = cur.QuadPart;
527         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
528         *sz = end.QuadPart - cur.QuadPart;
529
530         return ERROR_SUCCESS;
531     }
532
533     /* read the data */
534     count = 0;
535     r = IStream_Read( stm, buf, *sz, &count );
536     if( FAILED( r ) )
537     {
538         *sz = 0;
539         return ERROR_FUNCTION_FAILED;
540     }
541
542     *sz = count;
543
544     return ERROR_SUCCESS;
545 }
546
547 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
548 {
549     MSIRECORD *rec;
550     UINT ret;
551
552     TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
553
554     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
555     if( !rec )
556         return ERROR_INVALID_HANDLE;
557     ret = MSI_RecordReadStream( rec, iField, buf, sz );
558     msiobj_release( &rec->hdr );
559     return ret;
560 }
561
562 UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
563 {
564     TRACE("%p %d %p\n", rec, iField, stm);
565
566     if( iField > rec->count )
567         return ERROR_INVALID_FIELD;
568
569     MSI_FreeField( &rec->fields[iField] );
570
571     rec->fields[iField].type = MSIFIELD_STREAM;
572     rec->fields[iField].u.stream = stm;
573     IStream_AddRef( stm );
574
575     return ERROR_SUCCESS;
576 }