Fixed MSI buffer overflow.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002 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 #include "windef.h"
24 #include "winbase.h"
25 #include "winerror.h"
26 #include "wine/debug.h"
27 #include "msi.h"
28 #include "msiquery.h"
29 #include "objbase.h"
30 #include "objidl.h"
31 #include "msipriv.h"
32 #include "winnls.h"
33
34 #include "query.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msi);
37
38 typedef struct tagMSICOLUMNINFO
39 {
40     LPWSTR tablename;
41     UINT   number;
42     LPWSTR colname;
43     UINT   type;
44     UINT   offset;
45 } MSICOLUMNINFO;
46
47 struct tagMSITABLE
48 {
49     USHORT *data;
50     UINT size;
51     UINT ref_count;
52     /* MSICOLUMNINFO *columns; */
53     /* UINT num_cols; */
54     struct tagMSITABLE *next;
55     struct tagMSITABLE *prev;
56     WCHAR name[1];
57 } ;
58
59 #define MAX_STREAM_NAME 0x1f
60
61 static int utf2mime(int x)
62 {
63     if( (x>='0') && (x<='9') )
64         return x-'0';
65     if( (x>='A') && (x<='Z') )
66         return x-'A'+10;
67     if( (x>='a') && (x<='z') )
68         return x-'a'+10+26;
69     if( x=='.' )
70         return 10+26+26;
71     if( x=='_' )
72         return 10+26+26+1;
73     return -1;
74 }
75
76 static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out)
77 {
78     DWORD count = MAX_STREAM_NAME;
79     DWORD ch, next;
80
81     if( bTable )
82     {
83          *out++ = 0x4840;
84          count --;
85     }
86     while( count -- ) 
87     {
88         ch = *in++;
89         if( !ch )
90         {
91             *out = ch;
92             return TRUE;
93         }
94         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
95         {
96             ch = utf2mime(ch) + 0x4800;
97             next = *in;
98             if( next && (next<0x80) )
99             {
100                 next = utf2mime(next);
101                 if( next >= 0  )
102                 {
103                      next += 0x3ffffc0;
104                      ch += (next<<6);
105                      in++;
106                 }
107             }
108         }
109         *out++ = ch;
110     }
111     return FALSE;
112 }
113
114 #if 0
115 static int mime2utf(int x)
116 {
117     if( x<10 )
118         return x + '0';
119     if( x<(10+26))
120         return x - 10 + 'A';
121     if( x<(10+26+26))
122         return x - 10 - 26 + 'a';
123     if( x == (10+26+26) )
124         return '.';
125     return '_';
126 }
127
128 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
129 {
130     WCHAR ch;
131     DWORD count = 0;
132
133     while ( (ch = *in++) )
134     {
135         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
136         {
137             if( ch >= 0x4800 )
138                 ch = mime2utf(ch-0x4800);
139             else
140             {
141                 ch -= 0x3800;
142                 *out++ = mime2utf(ch&0x3f);
143                 count++;
144                 ch = mime2utf((ch>>6)&0x3f);
145             }
146         }
147         *out++ = ch;
148         count++;
149     }
150     *out = 0;
151     return count;
152 }
153 #endif
154
155 UINT read_table_from_storage(IStorage *stg, LPCWSTR name, MSITABLE **ptable)
156 {
157     WCHAR buffer[0x20];
158     HRESULT r;
159     IStream *stm = NULL;
160     STATSTG stat;
161     UINT ret = ERROR_FUNCTION_FAILED;
162     VOID *data;
163     ULONG sz, count;
164     MSITABLE *t;
165
166     encode_streamname(TRUE, name, buffer);
167
168     TRACE("%s -> %s\n",debugstr_w(name),debugstr_w(buffer));
169
170     r = IStorage_OpenStream(stg, buffer, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
171     if( FAILED( r ) )
172     {
173         ERR("open stream failed r = %08lx!\n",r);
174         return r;
175     }
176
177     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
178     if( FAILED( r ) )
179     {
180         ERR("open stream failed r = %08lx!\n",r);
181         goto end;
182     }
183
184     if( stat.cbSize.QuadPart >> 32 )
185     {
186         ERR("Too big!\n");
187         goto end;
188     }
189         
190     sz = stat.cbSize.QuadPart;
191     data = HeapAlloc( GetProcessHeap(), 0, sz );
192     if( !data )
193     {
194         ERR("couldn't allocate memory r=%08lx!\n",r);
195         goto end;
196     }
197         
198     r = IStream_Read(stm, data, sz, &count );
199     if( FAILED( r ) )
200     {
201         HeapFree( GetProcessHeap(), 0, data );
202         ERR("read stream failed r = %08lx!\n",r);
203         goto end;
204     }
205
206     t = HeapAlloc( GetProcessHeap(), 0, sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
207     if( !t )
208     {
209         HeapFree( GetProcessHeap(), 0, data );
210         ERR("malloc failed!\n");
211         goto end;
212     }
213
214     if( count == sz )
215     {
216         ret = ERROR_SUCCESS;
217         t->size = sz;
218         t->data = data;
219         lstrcpyW( t->name, name );
220         t->ref_count = 1;
221         *ptable = t;
222     }
223     else
224     {
225         HeapFree( GetProcessHeap(), 0, data );
226         ERR("Count != sz\n");
227     }
228
229 end:
230     if( stm )
231         IStream_Release( stm );
232
233     return ret;
234 }
235
236 /* add this table to the list of cached tables in the database */
237 void add_table(MSIDATABASE *db, MSITABLE *table)
238 {
239     table->next = db->first_table;
240     table->prev = NULL;
241     if( db->first_table )
242         db->first_table->prev = table;
243     else
244         db->last_table = table;
245     db->first_table = table;
246 }
247  
248 /* remove from the list of cached tables */
249 void remove_table( MSIDATABASE *db, MSITABLE *table )
250 {
251     if( table->next )
252         table->next->prev = table->prev;
253     else
254         db->last_table = table->prev;
255     if( table->prev )
256         table->prev->next = table->next;
257     else
258         db->first_table = table->next;
259     table->next = NULL;
260     table->prev = NULL;
261 }
262
263 void release_table( MSIDATABASE *db, MSITABLE *table )
264 {
265     if( !table->ref_count )
266         ERR("Trying to destroy table with refcount 0\n");
267     table->ref_count --;
268     if( !table->ref_count )
269     {
270         remove_table( db, table );
271         HeapFree( GetProcessHeap(), 0, table->data );
272         HeapFree( GetProcessHeap(), 0, table );
273         TRACE("Destroyed table %s\n", debugstr_w(table->name));
274     }
275 }
276
277 void free_cached_tables( MSIDATABASE *db )
278 {
279     while( db->first_table )
280     {
281         MSITABLE *t = db->first_table;
282
283         if ( --t->ref_count )
284             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
285         remove_table( db, t );
286         HeapFree( GetProcessHeap(), 0, t->data );
287         HeapFree( GetProcessHeap(), 0, t );
288     }
289 }
290
291 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
292 {
293     MSITABLE *t;
294
295     for( t = db->first_table; t; t=t->next )
296     {
297         if( !lstrcmpW( name, t->name ) )
298         {
299             *ptable = t;
300             return ERROR_SUCCESS;
301         }
302     }
303
304     return ERROR_FUNCTION_FAILED;
305 }
306
307 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
308 {
309     UINT r;
310
311     *ptable = NULL;
312
313     /* first, see if the table is cached */
314     r = find_cached_table( db, name, ptable );
315     if( r == ERROR_SUCCESS )
316     {
317         (*ptable)->ref_count++;
318         return r;
319     }
320
321     r = read_table_from_storage( db->storage, name, ptable );
322     if( r != ERROR_SUCCESS )
323         return r;
324
325     /* add the table to the list */
326     add_table( db, *ptable );
327     (*ptable)->ref_count++;
328
329     return ERROR_SUCCESS;
330 }
331
332 UINT dump_string_table(MSIDATABASE *db)
333 {
334     DWORD i, count, offset, len;
335     string_table *st = &db->strings;
336
337     MESSAGE("%d,%d bytes\n",st->pool.size,st->info.size);
338
339     count = st->pool.size/4;
340
341     offset = 0;
342     for(i=0; i<count; i++)
343     {
344         len = st->pool.data[i*2];
345         MESSAGE("[%2ld] = %s\n",i, debugstr_an(st->info.data+offset,len));
346         offset += len;
347     }
348
349     return ERROR_SUCCESS;
350 }
351
352 UINT load_string_table( MSIDATABASE *db, string_table *pst)
353 {
354     MSITABLE *pool = NULL, *info = NULL;
355     UINT r, ret = ERROR_FUNCTION_FAILED;
356     const WCHAR szStringData[] = { 
357         '_','S','t','r','i','n','g','D','a','t','a',0 };
358     const WCHAR szStringPool[] = { 
359         '_','S','t','r','i','n','g','P','o','o','l',0 };
360
361     r = get_table( db, szStringPool, &pool );
362     if( r != ERROR_SUCCESS)
363         goto end;
364     r = get_table( db, szStringData, &info );
365     if( r != ERROR_SUCCESS)
366         goto end;
367
368     pst->pool.size = pool->size;
369     pst->pool.data = pool->data;
370     pst->info.size = info->size;
371     pst->info.data = (CHAR *)info->data;
372
373     TRACE("Loaded %d,%d bytes\n",pst->pool.size,pst->info.size);
374
375     ret = ERROR_SUCCESS;
376
377 end:
378     if( info )
379         release_table( db, info );
380     if( pool )
381         release_table( db, pool );
382
383     return ret;
384 }
385
386 UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz )
387 {
388     DWORD i, count, offset, len;
389
390     count = st->pool.size/4;
391     TRACE("Finding string %d of %ld\n", string_no, count);
392     if(string_no >= count)
393         return ERROR_FUNCTION_FAILED;
394
395     offset = 0;
396     for(i=0; i<string_no; i++)
397     {
398         len = st->pool.data[i*2];
399         offset += len;
400     }
401
402     len = st->pool.data[i*2];
403
404     if( !buffer )
405     {
406         *sz = len;
407         return ERROR_SUCCESS;
408     }
409
410     if( (offset+len) > st->info.size )
411         return ERROR_FUNCTION_FAILED;
412
413     len = MultiByteToWideChar(CP_ACP,0,&st->info.data[offset],len,buffer,*sz-1); 
414     buffer[len] = 0;
415     *sz = len+1;
416
417     return ERROR_SUCCESS;
418 }
419
420 static UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id )
421 {
422     DWORD i, count, offset, len, sz;
423     UINT r = ERROR_INVALID_PARAMETER;
424     LPSTR str;
425
426     count = st->pool.size/4;
427     TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer), count);
428
429     sz = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
430     if( sz <= 0 )
431         return r;
432     str = HeapAlloc( GetProcessHeap(), 0, sz );
433     if( !str )
434         return ERROR_NOT_ENOUGH_MEMORY;
435     WideCharToMultiByte( CP_ACP, 0, buffer, -1, str, sz, NULL, NULL );
436
437     offset = 0;
438     sz--;  /* nul terminated */
439     for(i=0; i<count; i++)
440     {
441         len = st->pool.data[i*2];
442         if ( ( sz == len ) && !memcmp( str, st->info.data+offset, sz ) )
443         {
444             TRACE("%ld <- %s\n",i,debugstr_an(st->info.data+offset, len) );
445             *id = i;
446             r = ERROR_SUCCESS;
447             break;
448         }
449         offset += len;
450     }
451
452     if( str )
453         HeapFree( GetProcessHeap(), 0, str );
454
455     return r;
456 }
457
458 static LPWSTR strdupW( LPCWSTR str )
459 {
460     UINT len = lstrlenW( str ) + 1;
461     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
462     if( ret )
463         lstrcpyW( ret, str );
464     return ret;
465 }
466
467 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
468 {
469     if( col->type & MSITYPE_STRING )
470         return 2;
471     if( (col->type & 0xff) > 4 )
472         ERR("Invalid column size!\n");
473     return col->type & 0xff;
474 }
475
476 /* information for default tables */
477 const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
478 const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
479 const WCHAR szName[]    = { 'N','a','m','e',0 };
480 const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
481 const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
482 const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
483 const WCHAR szType[]    = { 'T','y','p','e',0 };
484
485 struct standard_table {
486     LPCWSTR tablename;
487     LPCWSTR columnname;
488     UINT number;
489     UINT type;
490 } MSI_standard_tables[] =
491 {
492   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
493   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
494   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
495   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
496   { szColumns, szType,   4, MSITYPE_VALID | 2},
497 };
498
499 #define STANDARD_TABLE_COUNT \
500      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
501
502 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
503 {
504     DWORD i, n=0;
505
506     for(i=0; i<STANDARD_TABLE_COUNT; i++)
507     {
508         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
509             continue;
510         if(colinfo && (n < *sz) )
511         {
512             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
513             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
514             colinfo[n].number = MSI_standard_tables[i].number;
515             colinfo[n].type = MSI_standard_tables[i].type;
516             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
517                     debugstr_w(colinfo[n].colname)); */
518             if( n )
519                 colinfo[n].offset = colinfo[n-1].offset
520                                   + bytes_per_column( &colinfo[n-1] );
521             else
522                 colinfo[n].offset = 0;
523         }
524         n++;
525         if( colinfo && (n >= *sz) )
526             break;
527     }
528     *sz = n;
529     return ERROR_SUCCESS;
530 }
531
532 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
533 {
534     UINT sz=0, r;
535     LPWSTR str;
536
537     r = msi_id2string( &db->strings, stringid, NULL, &sz );
538     if( r != ERROR_SUCCESS )
539         return NULL;
540     sz ++; /* space for NUL char */
541     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
542     if( !str )
543         return str;
544     r = msi_id2string( &db->strings, stringid, str, &sz );
545     if( r == ERROR_SUCCESS )
546         return str;
547     HeapFree(  GetProcessHeap(), 0, str );
548     return NULL;
549 }
550
551 UINT get_tablecolumns( MSIDATABASE *db, 
552        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
553 {
554     UINT r, i, n=0, table_id, count, maxcount = *sz;
555     MSITABLE *table = NULL;
556     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
557
558     /* first check if there is a default table with that name */
559     r = get_defaulttablecolumns( szTableName, colinfo, sz );
560     if( ( r == ERROR_SUCCESS ) && *sz )
561         return r;
562
563     r = get_table( db, szColumns, &table);
564     if( r != ERROR_SUCCESS )
565     {
566         ERR("table %s not available\n", debugstr_w(szColumns));
567         return r;
568     }
569
570     /* convert table and column names to IDs from the string table */
571     r = msi_string2id( &db->strings, szTableName, &table_id );
572     if( r != ERROR_SUCCESS )
573     {
574         release_table( db, table );
575         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
576         return r;
577     }
578
579     TRACE("Table id is %d\n", table_id);
580
581     count = table->size/8;
582     for( i=0; i<count; i++ )
583     {
584         if( table->data[ i ] != table_id )
585             continue;
586         if( colinfo )
587         {
588             UINT id = table->data[ i + count*2 ];
589             colinfo[n].tablename = MSI_makestring( db, table_id );
590             colinfo[n].number = table->data[ i + count ] - (1<<15);
591             colinfo[n].colname = MSI_makestring( db, id );
592             colinfo[n].type = table->data[ i + count*3 ];
593             /* this assumes that columns are in order in the table */
594             if( n )
595                 colinfo[n].offset = colinfo[n-1].offset
596                                   + bytes_per_column( &colinfo[n-1] );
597             else
598                 colinfo[n].offset = 0;
599             TRACE("table %s column %d is [%s] (%d) with type %08x "
600                   "offset %d at row %d\n", debugstr_w(szTableName),
601                    colinfo[n].number, debugstr_w(colinfo[n].colname),
602                    id, colinfo[n].type, colinfo[n].offset, i);
603             if( n != (colinfo[n].number-1) )
604             {
605                 ERR("oops. data in the _Columns table isn't in the right "
606                     "order for table %s\n", debugstr_w(szTableName));
607                 return ERROR_FUNCTION_FAILED;
608             }
609         }
610         n++;
611         if( colinfo && ( n >= maxcount ) )
612             break;
613     }
614     *sz = n;
615
616     release_table( db, table );
617
618     return ERROR_SUCCESS;
619 }
620
621 /* try to find the table name in the _Tables table */
622 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
623 {
624     const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
625     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
626     UINT r, table_id = 0, i, count;
627     MSITABLE *table = NULL;
628
629     if( !lstrcmpW( name, szTables ) )
630         return TRUE;
631     if( !lstrcmpW( name, szColumns ) )
632         return TRUE;
633
634     r = msi_string2id( &db->strings, name, &table_id );
635     if( r != ERROR_SUCCESS )
636     {
637         ERR("Couldn't find id for %s\n", debugstr_w(name));
638         return FALSE;
639     }
640
641     r = get_table( db, szTables, &table);
642     if( r != ERROR_SUCCESS )
643     {
644         ERR("table %s not available\n", debugstr_w(szTables));
645         return FALSE;
646     }
647
648     count = table->size/2;
649     for( i=0; i<count; i++ )
650         if( table->data[ i ] == table_id )
651             break;
652
653     release_table( db, table );
654
655     if (i!=count)
656         return TRUE;
657
658     return FALSE;
659 }
660
661 /* below is the query interface to a table */
662
663 typedef struct tagMSITABLEVIEW
664 {
665     MSIVIEW        view;
666     MSIDATABASE   *db;
667     MSITABLE      *table;
668     MSICOLUMNINFO *columns;
669     UINT           num_cols;
670     UINT           row_size;
671     WCHAR          name[1];
672 } MSITABLEVIEW;
673
674
675 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
676 {
677     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
678     UINT offset, num_rows, n;
679
680     if( !tv->table )
681         return ERROR_INVALID_PARAMETER;
682
683     if( (col==0) || (col>tv->num_cols) )
684         return ERROR_INVALID_PARAMETER;
685
686     /* how many rows are there ? */
687     num_rows = tv->table->size / tv->row_size;
688     if( row >= num_rows )
689         return ERROR_NO_MORE_ITEMS;
690
691     if( tv->columns[col-1].offset >= tv->row_size )
692     {
693         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
694         ERR("%p %p\n", tv, tv->columns );
695         return ERROR_FUNCTION_FAILED;
696     }
697
698     offset = row + (tv->columns[col-1].offset/2) * num_rows;
699     n = bytes_per_column( &tv->columns[col-1] );
700     switch( n )
701     {
702     case 4:
703         offset = row*2 + (tv->columns[col-1].offset/2) * num_rows;
704         *val = tv->table->data[offset] + (tv->table->data[offset + 1] << 16);
705         break;
706     case 2:
707         offset = row + (tv->columns[col-1].offset/2) * num_rows;
708         *val = tv->table->data[offset];
709         break;
710     default:
711         ERR("oops! what is %d bytes per column?\n", n );
712         return ERROR_FUNCTION_FAILED;
713     }
714
715     TRACE("Data [%d][%d] = %d \n", row, col, *val );
716
717     return ERROR_SUCCESS;
718 }
719
720 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
721 {
722     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
723     UINT r;
724
725     TRACE("%p %ld\n", tv, record);
726
727     if( tv->table )
728         return ERROR_FUNCTION_FAILED;
729
730     r = get_table( tv->db, tv->name, &tv->table );
731     if( r != ERROR_SUCCESS )
732         return r;
733     
734     return ERROR_SUCCESS;
735 }
736
737 static UINT TABLE_close( struct tagMSIVIEW *view )
738 {
739     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
740
741     TRACE("%p\n", view );
742
743     if( !tv->table )
744         return ERROR_FUNCTION_FAILED;
745
746     release_table( tv->db, tv->table );
747     tv->table = NULL;
748     
749     return ERROR_SUCCESS;
750 }
751
752 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
753 {
754     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
755
756     TRACE("%p %p %p\n", view, rows, cols );
757
758     if( cols )
759         *cols = tv->num_cols;
760     if( rows )
761     {
762         if( !tv->table )
763             return ERROR_INVALID_PARAMETER;
764         *rows = tv->table->size / tv->row_size;
765     }
766
767     return ERROR_SUCCESS;
768 }
769
770 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
771                 UINT n, LPWSTR *name, UINT *type )
772 {
773     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
774
775     TRACE("%p %d %p %p\n", tv, n, name, type );
776
777     if( ( n == 0 ) || ( n > tv->num_cols ) )
778         return ERROR_INVALID_PARAMETER;
779
780     if( name )
781     {
782         *name = strdupW( tv->columns[n-1].colname );
783         if( !*name )
784             return ERROR_FUNCTION_FAILED;
785     }
786     if( type )
787         *type = tv->columns[n-1].type;
788
789     return ERROR_SUCCESS;
790 }
791
792 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
793 {
794     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
795     return ERROR_CALL_NOT_IMPLEMENTED;
796 }
797
798 static UINT TABLE_delete( struct tagMSIVIEW *view )
799 {
800     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
801
802     TRACE("%p\n", view );
803
804     if( tv->table )
805         release_table( tv->db, tv->table );
806     tv->table = NULL;
807
808     if( tv->columns )
809     {
810         UINT i;
811         for( i=0; i<tv->num_cols; i++)
812         {
813             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
814             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
815         }
816         HeapFree( GetProcessHeap(), 0, tv->columns );
817     }
818     tv->columns = NULL;
819
820     HeapFree( GetProcessHeap(), 0, tv );
821
822     return ERROR_SUCCESS;
823 }
824
825
826 MSIVIEWOPS table_ops =
827 {
828     TABLE_fetch_int,
829     TABLE_execute,
830     TABLE_close,
831     TABLE_get_dimensions,
832     TABLE_get_column_info,
833     TABLE_modify,
834     TABLE_delete
835 };
836
837 UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view )
838 {
839     MSITABLEVIEW *tv ;
840     UINT r, sz, column_count;
841     MSICOLUMNINFO *columns, *last_col;
842
843     TRACE("%p %s %p\n", db, debugstr_w(name), view );
844
845     /* get the number of columns in this table */
846     column_count = 0;
847     r = get_tablecolumns( db, name, NULL, &column_count );
848     if( r != ERROR_SUCCESS )
849         return r;
850
851     /* if there's no columns, there's no table */
852     if( column_count == 0 )
853         return ERROR_INVALID_PARAMETER;
854
855     TRACE("Table found\n");
856
857     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
858     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
859     if( !tv )
860         return ERROR_FUNCTION_FAILED;
861     
862     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
863     if( !columns )
864     {
865         HeapFree( GetProcessHeap(), 0, tv );
866         return ERROR_FUNCTION_FAILED;
867     }
868
869     r = get_tablecolumns( db, name, columns, &column_count );
870     if( r != ERROR_SUCCESS )
871     {
872         HeapFree( GetProcessHeap(), 0, columns );
873         HeapFree( GetProcessHeap(), 0, tv );
874         return ERROR_FUNCTION_FAILED;
875     }
876
877     TRACE("Table has %d columns\n", column_count);
878
879     last_col = &columns[column_count-1];
880
881     /* fill the structure */
882     tv->view.ops = &table_ops;
883     tv->db = db;
884     tv->columns = columns;
885     tv->num_cols = column_count;
886     tv->table = NULL;
887     tv->row_size = last_col->offset + bytes_per_column( last_col );
888
889     TRACE("one row is %d bytes\n", tv->row_size );
890
891     *view = (MSIVIEW*) tv;
892     lstrcpyW( tv->name, name );
893
894     return ERROR_SUCCESS;
895 }