Implement MsiQueryFeatureStateW.
[wine] / dlls / msi / format.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Aric Stewart for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /*
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp 
24  */
25
26 #include <stdarg.h>
27 #include <stdio.h>
28
29 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winerror.h"
34 #include "winreg.h"
35 #include "wine/debug.h"
36 #include "fdi.h"
37 #include "msi.h"
38 #include "msiquery.h"
39 #include "msvcrt/fcntl.h"
40 #include "objbase.h"
41 #include "objidl.h"
42 #include "msipriv.h"
43 #include "winnls.h"
44 #include "winuser.h"
45 #include "shlobj.h"
46 #include "wine/unicode.h"
47 #include "winver.h"
48 #include "action.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51
52
53 LPWSTR build_default_format(MSIRECORD* record)
54 {
55     int i;  
56     int count;
57     LPWSTR rc;
58     static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
59     WCHAR buf[11];
60
61     count = MSI_RecordGetFieldCount(record);
62
63     rc = HeapAlloc(GetProcessHeap(),0,(11*count)*sizeof(WCHAR));
64     rc[0] = 0;
65     for (i = 1; i <= count; i++)
66     {
67         sprintfW(buf,fmt,i,i); 
68         strcatW(rc,buf);
69     }
70     return rc;
71 }
72
73 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
74 {
75     DWORD i;
76     for (i = 0; i < len; i++)
77         if (buf[i] == token)
78             return &buf[i];
79     return NULL;
80 }
81
82 /* break out helper functions for deformating */
83 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
84 {
85     LPWSTR value = NULL;
86     INT index;
87
88     *sz = 0;
89     if (!package)
90         return NULL;
91
92     ERR("POORLY HANDLED DEFORMAT.. [$componentkey] \n");
93     index = get_loaded_component(package,key);
94     if (index >= 0)
95     {
96         value = resolve_folder(package, package->components[index].Directory, 
97                                        FALSE, FALSE, NULL);
98         *sz = (strlenW(value)) * sizeof(WCHAR);
99     }
100
101     return value;
102 }
103
104 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
105 {
106     LPWSTR value = NULL;
107     INT index;
108
109     *sz = 0;
110
111     if (!package)
112         return NULL;
113
114     index = get_loaded_file(package,key);
115     if (index >=0)
116     {
117         value = strdupW(package->files[index].TargetPath);
118         *sz = (strlenW(value)) * sizeof(WCHAR);
119     }
120
121     return value;
122 }
123
124 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key, 
125                                    DWORD* chunk)
126 {
127     LPWSTR value = NULL;
128     DWORD sz;
129
130     sz  = GetEnvironmentVariableW(key,NULL,0);
131     if (sz > 0)
132     {
133         sz++;
134         value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
135         GetEnvironmentVariableW(&key[1],value,sz);
136         *chunk = (strlenW(value)) * sizeof(WCHAR);
137     }
138     else
139     {
140         ERR("Unknown environment variable %s\n", debugstr_w(key));
141         *chunk = 0;
142         value = NULL;
143     }
144     return value;
145 }
146
147  
148 static LPWSTR deformat_NULL(DWORD* chunk)
149 {
150     LPWSTR value;
151
152     value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
153     value[0] =  0;
154     *chunk = sizeof(WCHAR);
155     return value;
156 }
157
158 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
159 {
160     LPWSTR value;
161
162     value = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*2);
163     value[0] =  key[0];
164     *chunk = sizeof(WCHAR);
165
166     return value;
167 }
168
169
170 static BOOL is_key_number(LPCWSTR key)
171 {
172     INT index = 0;
173     if (key[0] == 0)
174         return FALSE;
175
176     while (isdigitW(key[index])) index++;
177     if (key[index] == 0)
178         return TRUE;
179     else
180         return FALSE;
181 }
182
183 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
184 {
185     INT index;
186     LPWSTR value; 
187
188     index = atoiW(key);
189     TRACE("record index %i\n",index);
190     value = load_dynamic_stringW(record,index);
191     if (value)
192         *chunk = strlenW(value) * sizeof(WCHAR);
193     else
194     {
195         value = NULL;
196         *chunk = 0;
197     }
198     return value;
199 }
200
201 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
202 {
203     UINT rc;
204     LPWSTR value;
205
206     if (!package)
207         return NULL;
208
209     value = load_dynamic_property(package,key, &rc);
210
211     if (rc == ERROR_SUCCESS)
212         *chunk = (strlenW(value)) * sizeof(WCHAR);
213
214     return value;
215 }
216
217 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
218                                     LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2, 
219                                     BOOL *nested)
220 {
221     INT count = 0;
222     INT total_count = 0;
223     int i;
224
225     *mark = scanW(source,'[',len_remaining);
226     if (!*mark)
227         return FALSE;
228
229     count = 1;
230     total_count = 1;
231     *nested = FALSE;
232     for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
233     {
234         if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
235         {
236             count ++;
237             total_count ++;
238             *nested = TRUE;
239         }
240         else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
241         {
242             count --;
243         }
244     }
245
246     if (count > 0)
247         return FALSE;
248
249     *mark2 = &(*mark)[i-1]; 
250
251     i = *mark2 - *mark;
252     *key = HeapAlloc(GetProcessHeap(),0,i*sizeof(WCHAR));
253     /* do not have the [] in the key */
254     i -= 1;
255     memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
256     (*key)[i] = 0;
257
258     TRACE("Found Key %s\n",debugstr_w(*key));
259     return TRUE;
260 }
261
262
263 /*
264  * len is in WCHARs
265  * return is also in WCHARs
266  */
267 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
268                                      WCHAR** data, DWORD len, MSIRECORD* record)
269 {
270     LPCWSTR mark = NULL;
271     LPCWSTR mark2 = NULL;
272     DWORD size=0;
273     DWORD chunk=0;
274     LPWSTR key;
275     LPWSTR value = NULL;
276     DWORD sz;
277     LPBYTE newdata = NULL;
278     const WCHAR* progress = NULL;
279     BOOL nested;
280
281     if (ptr==NULL)
282     {
283         TRACE("Deformatting NULL string\n");
284         *data = NULL;
285         return 0;
286     }
287
288     TRACE("Starting with %s\n",debugstr_w(ptr));
289
290     /* scan for special characters... fast exit */
291     if (!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len)))
292     {
293         /* not formatted */
294         *data = HeapAlloc(GetProcessHeap(),0,(len*sizeof(WCHAR)));
295         memcpy(*data,ptr,len*sizeof(WCHAR));
296         TRACE("Returning %s\n",debugstr_w(*data));
297         return len;
298     }
299   
300     progress = ptr;
301
302     while (progress - ptr < len)
303     {
304         /* formatted string located */
305         if (!find_next_outermost_key(progress, len - (progress - ptr), &key,
306                                      &mark, &mark2, &nested))
307         {
308             LPBYTE nd2;
309
310             TRACE("after value %s .. %s\n",debugstr_w((LPWSTR)newdata),
311                                        debugstr_w(mark));
312             chunk = (len - (progress - ptr)) * sizeof(WCHAR);
313             TRACE("after chunk is %li + %li\n",size,chunk);
314             if (size)
315                 nd2 = HeapReAlloc(GetProcessHeap(),0,newdata,(size+chunk));
316             else
317                 nd2 = HeapAlloc(GetProcessHeap(),0,chunk);
318
319             newdata = nd2;
320             memcpy(&newdata[size],progress,chunk);
321             size+=chunk;
322             break;
323         }
324
325         if (mark != progress)
326         {
327             LPBYTE tgt;
328             DWORD old_size = size;
329             INT cnt = (mark - progress);
330             TRACE("%i  (%i) characters before marker\n",cnt,(mark-progress));
331             size += cnt * sizeof(WCHAR);
332             if (!old_size)
333                 tgt = HeapAlloc(GetProcessHeap(),0,size);
334             else
335                 tgt = HeapReAlloc(GetProcessHeap(),0,newdata,size);
336             newdata  = tgt;
337             memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));  
338         }
339
340         progress = mark;
341
342         if (nested)
343         {
344             TRACE("Nested key... %s\n",debugstr_w(key));
345             deformat_string_internal(package, key, &value, strlenW(key)+1,
346                                      record);
347
348             HeapFree(GetProcessHeap(),0,key);
349             key = value;
350         }
351
352         TRACE("Current %s .. %s\n",debugstr_w((LPWSTR)newdata),debugstr_w(key));
353
354         if (!package)
355         {
356             /* only deformat number indexs */
357             if (is_key_number(key))
358                 value = deformat_index(record,key,&chunk);  
359             else
360             {
361                 DWORD keylen = strlenW(key);
362                 chunk = (keylen + 2)*sizeof(WCHAR);
363                 value = HeapAlloc(GetProcessHeap(),0,chunk);
364                 value[0] = '[';
365                 memcpy(&value[1],key,keylen*sizeof(WCHAR));
366                 value[1+keylen] = ']';
367             }
368         }
369         else
370         {
371             sz = 0;
372             switch (key[0])
373             {
374                 case '~':
375                     value = deformat_NULL(&chunk);
376                 break;
377                 case '$':
378                     value = deformat_component(package,&key[1],&chunk);
379                 break;
380                 case '#':
381                 case '!': /* should be short path */
382                     value = deformat_file(package,&key[1], &chunk);
383                 break;
384                 case '\\':
385                     value = deformat_escape(&key[1],&chunk);
386                 break;
387                 case '%':
388                     value = deformat_environment(package,&key[1],&chunk);
389                 break;
390                 default:
391                     if (is_key_number(key))
392                         value = deformat_index(record,key,&chunk);
393                     else
394                         value = deformat_property(package,key,&chunk);
395                 break;      
396             }
397         }
398
399         HeapFree(GetProcessHeap(),0,key);
400
401         if (value!=NULL)
402         {
403             LPBYTE nd2;
404             TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
405                     chunk, size);
406             if (size)
407                 nd2= HeapReAlloc(GetProcessHeap(),0,newdata,(size + chunk));
408             else
409                 nd2= HeapAlloc(GetProcessHeap(),0,chunk);
410             newdata = nd2;
411             memcpy(&newdata[size],value,chunk);
412             size+=chunk;   
413             HeapFree(GetProcessHeap(),0,value);
414         }
415
416         progress = mark2+1;
417     }
418
419     TRACE("after everything %s\n",debugstr_w((LPWSTR)newdata));
420
421     *data = (LPWSTR)newdata;
422     return size / sizeof(WCHAR);
423 }
424
425
426 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
427                         DWORD *size )
428 {
429     LPWSTR deformated;
430     LPWSTR rec;
431     DWORD len;
432     UINT rc = ERROR_INVALID_PARAMETER;
433
434     TRACE("%p %p %p %li\n",package, record ,buffer, *size);
435
436     rec = load_dynamic_stringW(record,0);
437     if (!rec)
438         rec = build_default_format(record);
439
440     TRACE("(%s)\n",debugstr_w(rec));
441
442     len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
443                                    record);
444
445     if (buffer)
446     {
447         if (*size>len)
448         {
449             memcpy(buffer,deformated,len*sizeof(WCHAR));
450             rc = ERROR_SUCCESS;
451             buffer[len] = 0;
452         }
453         else
454         {
455             if (*size > 0)
456             {
457                 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
458                 buffer[(*size)-1] = 0;
459             }
460             rc = ERROR_MORE_DATA;
461         }
462     }
463     else
464         rc = ERROR_SUCCESS;
465
466     *size = len;
467
468     HeapFree(GetProcessHeap(),0,rec);
469     HeapFree(GetProcessHeap(),0,deformated);
470     return rc;
471 }
472
473 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
474                         DWORD *size )
475 {
476     LPWSTR deformated;
477     LPWSTR rec;
478     DWORD len,lenA;
479     UINT rc = ERROR_INVALID_PARAMETER;
480
481     TRACE("%p %p %p %li\n",package, record ,buffer, *size);
482
483     rec = load_dynamic_stringW(record,0);
484     if (!rec)
485         rec = build_default_format(record);
486
487     TRACE("(%s)\n",debugstr_w(rec));
488
489     len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
490                                    record);
491     lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
492
493     if (buffer)
494     {
495         WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
496         if (*size>lenA)
497         {
498             rc = ERROR_SUCCESS;
499             buffer[lenA] = 0;
500         }
501         else
502         {
503             rc = ERROR_MORE_DATA;
504             buffer[(*size)-1] = 0;    
505         }
506     }
507     else
508         rc = ERROR_SUCCESS;
509
510     *size = lenA;
511
512     HeapFree(GetProcessHeap(),0,rec);
513     HeapFree(GetProcessHeap(),0,deformated);
514     return rc;
515 }
516
517
518 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 
519                               LPWSTR szResult, DWORD *sz )
520 {
521     UINT r = ERROR_INVALID_HANDLE;
522     MSIPACKAGE *package;
523     MSIRECORD *record;
524
525     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
526
527     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
528
529     if (!record)
530         return ERROR_INVALID_HANDLE;
531     if (!sz)
532     {
533         msiobj_release( &record->hdr );
534         if (szResult)
535             return ERROR_INVALID_PARAMETER;
536         else
537             return ERROR_SUCCESS;
538     }
539
540     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
541
542     r = MSI_FormatRecordW( package, record, szResult, sz );
543     msiobj_release( &record->hdr );
544     if (package)
545         msiobj_release( &package->hdr );
546     return r;
547 }
548
549 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
550                               LPSTR szResult, DWORD *sz )
551 {
552     UINT r = ERROR_INVALID_HANDLE;
553     MSIPACKAGE *package = NULL;
554     MSIRECORD *record = NULL;
555
556     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
557
558     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
559
560     if (!record)
561         return ERROR_INVALID_HANDLE;
562     if (!sz)
563     {
564         msiobj_release( &record->hdr );
565         if (szResult)
566             return ERROR_INVALID_PARAMETER;
567         else
568             return ERROR_SUCCESS;
569     }
570
571     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
572
573     r = MSI_FormatRecordA( package, record, szResult, sz );
574     msiobj_release( &record->hdr );
575     if (package)
576         msiobj_release( &package->hdr );
577     return r;
578 }