ole32: Don't fail in OleCreate if created object doesn't implement IRunnableObject...
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "wine/debug.h"
35 #include "msi.h"
36 #include "msipriv.h"
37 #include "winnls.h"
38 #include "wine/unicode.h"
39 #include "action.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43
44 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
45                                      WCHAR** data, DWORD len, MSIRECORD* record,
46                                      BOOL* in_group);
47
48
49 static LPWSTR build_default_format(MSIRECORD* record)
50 {
51     int i;  
52     int count;
53     LPWSTR rc, buf;
54     static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
55     static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
56     static const WCHAR fmt_index[] = {'%','i',0};
57     LPCWSTR str;
58     WCHAR index[10];
59     DWORD size, max_len, len;
60
61     count = MSI_RecordGetFieldCount(record);
62
63     max_len = MAX_PATH;
64     buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
65
66     rc = NULL;
67     size = 1;
68     for (i = 1; i <= count; i++)
69     {
70         sprintfW(index,fmt_index,i);
71         str = MSI_RecordGetString(record, i);
72         len = (str) ? lstrlenW(str) : 0;
73         len += (sizeof(fmt_null) - 3) + lstrlenW(index);
74         size += len;
75
76         if (len > max_len)
77         {
78             max_len = len;
79             buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
80             if (!buf) return NULL;
81         }
82
83         if (str)
84             sprintfW(buf,fmt,i,str);
85         else
86             sprintfW(buf,fmt_null,i);
87
88         if (!rc)
89         {
90             rc = msi_alloc(size * sizeof(WCHAR));
91             lstrcpyW(rc, buf);
92         }
93         else
94         {
95             rc = msi_realloc(rc, size * sizeof(WCHAR));
96             lstrcatW(rc, buf);
97         }
98     }
99     msi_free(buf);
100     return rc;
101 }
102
103 static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
104 {
105     DWORD i;
106     for (i = 0; i < len; i++)
107         if (buf[i] == token)
108             return &buf[i];
109     return NULL;
110 }
111
112 /* break out helper functions for deformating */
113 static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
114 {
115     LPWSTR value = NULL;
116     MSICOMPONENT *comp;
117
118     *sz = 0;
119     if (!package)
120         return NULL;
121
122     FIXME("component key %s\n", debugstr_w(key));
123     comp = get_loaded_component(package,key);
124     if (comp)
125     {
126         value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
127         *sz = (strlenW(value)) * sizeof(WCHAR);
128     }
129
130     return value;
131 }
132
133 static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz, 
134                             BOOL shortname)
135 {
136     LPWSTR value = NULL;
137     MSIFILE *file;
138
139     *sz = 0;
140
141     if (!package)
142         return NULL;
143
144     file = get_loaded_file( package, key );
145     if (file)
146     {
147         if (!shortname)
148         {
149             value = strdupW( file->TargetPath );
150             *sz = (strlenW(value)) * sizeof(WCHAR);
151         }
152         else
153         {
154             DWORD size = 0;
155             size = GetShortPathNameW( file->TargetPath, NULL, 0 );
156
157             if (size > 0)
158             {
159                 *sz = (size-1) * sizeof (WCHAR);
160                 size ++;
161                 value = msi_alloc(size * sizeof(WCHAR));
162                 GetShortPathNameW( file->TargetPath, value, size );
163             }
164             else
165             {
166                 ERR("Unable to get ShortPath size (%s)\n",
167                     debugstr_w( file->TargetPath) );
168                 value = strdupW( file->TargetPath );
169                 *sz = (lstrlenW(value)) * sizeof(WCHAR);
170             }
171         }
172     }
173
174     return value;
175 }
176
177 static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key, 
178                                    DWORD* chunk)
179 {
180     LPWSTR value = NULL;
181     DWORD sz;
182
183     sz  = GetEnvironmentVariableW(key,NULL,0);
184     if (sz > 0)
185     {
186         sz++;
187         value = msi_alloc(sz * sizeof(WCHAR));
188         GetEnvironmentVariableW(key,value,sz);
189         *chunk = (strlenW(value)) * sizeof(WCHAR);
190     }
191     else
192     {
193         ERR("Unknown environment variable %s\n", debugstr_w(key));
194         *chunk = 0;
195         value = NULL;
196     }
197     return value;
198 }
199
200  
201 static LPWSTR deformat_NULL(DWORD* chunk)
202 {
203     LPWSTR value;
204
205     value = msi_alloc(sizeof(WCHAR)*2);
206     value[0] =  0;
207     *chunk = sizeof(WCHAR);
208     return value;
209 }
210
211 static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
212 {
213     LPWSTR value;
214
215     value = msi_alloc(sizeof(WCHAR)*2);
216     value[0] =  key[0];
217     *chunk = sizeof(WCHAR);
218
219     return value;
220 }
221
222
223 static BOOL is_key_number(LPCWSTR key)
224 {
225     INT index = 0;
226     if (key[0] == 0)
227         return FALSE;
228
229     while (isdigitW(key[index])) index++;
230     if (key[index] == 0)
231         return TRUE;
232     else
233         return FALSE;
234 }
235
236 static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
237 {
238     INT index;
239     LPWSTR value; 
240
241     index = atoiW(key);
242     TRACE("record index %i\n",index);
243     value = msi_dup_record_field(record,index);
244     if (value)
245         *chunk = strlenW(value) * sizeof(WCHAR);
246     else
247     {
248         value = NULL;
249         *chunk = 0;
250     }
251     return value;
252 }
253
254 static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
255 {
256     LPWSTR value;
257
258     if (!package)
259         return NULL;
260
261     value = msi_dup_property( package, key );
262
263     if (value)
264         *chunk = (strlenW(value)) * sizeof(WCHAR);
265
266     return value;
267 }
268
269 /*
270  * Groups cannot be nested. They are just treated as from { to next } 
271  */
272 static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
273                                     LPWSTR *group, LPCWSTR *mark, 
274                                     LPCWSTR* mark2)
275 {
276     int i;
277     BOOL found = FALSE;
278
279     *mark = scanW(source,'{',len_remaining);
280     if (!*mark)
281         return FALSE;
282
283     for (i = 1; (*mark - source) + i < len_remaining; i++)
284     {
285         if ((*mark)[i] == '}')
286         {
287             found = TRUE;
288             break;
289         }
290     }
291     if (! found)
292         return FALSE;
293
294     *mark2 = &(*mark)[i]; 
295
296     i = *mark2 - *mark;
297     *group = msi_alloc(i*sizeof(WCHAR));
298
299     i -= 1;
300     memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
301     (*group)[i] = 0;
302
303     TRACE("Found group %s\n",debugstr_w(*group));
304     return TRUE;
305 }
306
307
308 static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
309                                     LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2, 
310                                     BOOL *nested)
311 {
312     INT count = 0;
313     INT total_count = 0;
314     int i;
315
316     *mark = scanW(source,'[',len_remaining);
317     if (!*mark)
318         return FALSE;
319
320     count = 1;
321     total_count = 1;
322     *nested = FALSE;
323     for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
324     {
325         if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
326         {
327             count ++;
328             total_count ++;
329             *nested = TRUE;
330         }
331         else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
332         {
333             count --;
334         }
335     }
336
337     if (count > 0)
338         return FALSE;
339
340     *mark2 = &(*mark)[i-1]; 
341
342     i = *mark2 - *mark;
343     *key = msi_alloc(i*sizeof(WCHAR));
344     /* do not have the [] in the key */
345     i -= 1;
346     memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
347     (*key)[i] = 0;
348
349     TRACE("Found Key %s\n",debugstr_w(*key));
350     return TRUE;
351 }
352
353 static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len, 
354                       MSIRECORD* record, DWORD* size)
355 {
356     LPWSTR value = NULL;
357     LPCWSTR mark, mark2;
358     LPWSTR key;
359     BOOL nested;
360     INT failcount;
361     static const WCHAR fmt[] = {'{','%','s','}',0};
362     UINT sz;
363
364     if (!group || group[0] == 0) 
365     {
366         *size = 0;
367         return NULL;
368     }
369     /* if no [] then group is returned as is */
370
371      if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
372      {
373          *size = (len+2)*sizeof(WCHAR);
374          value = msi_alloc(*size);
375          sprintfW(value,fmt,group);
376          /* do not return size of the null at the end */
377          *size = (len+1)*sizeof(WCHAR);
378          return value;
379      }
380
381      msi_free(key);
382      failcount = 0;
383      sz = deformat_string_internal(package, group, &value, strlenW(group),
384                                      record, &failcount);
385      if (failcount==0)
386      {
387         *size = sz * sizeof(WCHAR);
388         return value;
389      }
390      else if (failcount < 0)
391      {
392          LPWSTR v2;
393
394          v2 = msi_alloc((sz+2)*sizeof(WCHAR));
395          v2[0] = '{';
396          memcpy(&v2[1],value,sz*sizeof(WCHAR));
397          v2[sz+1]='}';
398          msi_free(value);
399
400          *size = (sz+2)*sizeof(WCHAR);
401          return v2;
402      }
403      else
404      {
405          msi_free(value);
406          *size = 0;
407          return NULL;
408      }
409 }
410
411
412 /*
413  * len is in WCHARs
414  * return is also in WCHARs
415  */
416 static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
417                                      WCHAR** data, DWORD len, MSIRECORD* record,
418                                      INT* failcount)
419 {
420     LPCWSTR mark = NULL;
421     LPCWSTR mark2 = NULL;
422     DWORD size=0;
423     DWORD chunk=0;
424     LPWSTR key;
425     LPWSTR value = NULL;
426     DWORD sz;
427     LPBYTE newdata = NULL;
428     const WCHAR* progress = NULL;
429     BOOL nested;
430
431     if (ptr==NULL)
432     {
433         TRACE("Deformatting NULL string\n");
434         *data = NULL;
435         return 0;
436     }
437
438     TRACE("Starting with %s\n",debugstr_wn(ptr,len));
439
440     /* scan for special characters... fast exit */
441     if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) && 
442         (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
443     {
444         /* not formatted */
445         *data = msi_alloc((len*sizeof(WCHAR)));
446         memcpy(*data,ptr,len*sizeof(WCHAR));
447         TRACE("Returning %s\n",debugstr_wn(*data,len));
448         return len;
449     }
450   
451     progress = ptr;
452
453     while (progress - ptr < len)
454     {
455         /* seek out first group if existing */
456         if (find_next_group(progress, len - (progress - ptr), &key,
457                                 &mark, &mark2))
458         {
459             value = deformat_group(package, key, strlenW(key)+1, record, 
460                             &chunk);
461             msi_free( key );
462             key = NULL;
463             nested = FALSE;
464         }
465         /* formatted string located */
466         else if (!find_next_outermost_key(progress, len - (progress - ptr), 
467                                 &key, &mark, &mark2, &nested))
468         {
469             LPBYTE nd2;
470
471             TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
472                                     size/sizeof(WCHAR)));
473             chunk = (len - (progress - ptr)) * sizeof(WCHAR);
474             TRACE("after chunk is %i + %i\n",size,chunk);
475             if (size)
476                 nd2 = msi_realloc(newdata,(size+chunk));
477             else
478                 nd2 = msi_alloc(chunk);
479
480             newdata = nd2;
481             memcpy(&newdata[size],progress,chunk);
482             size+=chunk;
483             break;
484         }
485
486         if (mark != progress)
487         {
488             LPBYTE tgt;
489             DWORD old_size = size;
490             INT cnt = (mark - progress);
491             TRACE("%i  (%i) characters before marker\n",cnt,(mark-progress));
492             size += cnt * sizeof(WCHAR);
493             if (!old_size)
494                 tgt = msi_alloc(size);
495             else
496                 tgt = msi_realloc(newdata,size);
497             newdata  = tgt;
498             memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));  
499         }
500
501         progress = mark;
502
503         if (nested)
504         {
505             TRACE("Nested key... %s\n",debugstr_w(key));
506             deformat_string_internal(package, key, &value, strlenW(key)+1,
507                                      record, failcount);
508
509             msi_free(key);
510             key = value;
511         }
512
513         TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata, 
514                                 size/sizeof(WCHAR)),debugstr_w(key));
515
516         if (!package)
517         {
518             /* only deformat number indexs */
519             if (key && is_key_number(key))
520             {
521                 value = deformat_index(record,key,&chunk);  
522                 if (!chunk && failcount && *failcount >= 0)
523                     (*failcount)++;
524             }
525             else
526             {
527                 if (failcount)
528                     *failcount = -1;
529                 if(key)
530                 {
531                     DWORD keylen = strlenW(key);
532                     chunk = (keylen + 2)*sizeof(WCHAR);
533                     value = msi_alloc(chunk);
534                     value[0] = '[';
535                     memcpy(&value[1],key,keylen*sizeof(WCHAR));
536                     value[1+keylen] = ']';
537                 }
538             }
539         }
540         else
541         {
542             sz = 0;
543             if (key) switch (key[0])
544             {
545                 case '~':
546                     value = deformat_NULL(&chunk);
547                 break;
548                 case '$':
549                     value = deformat_component(package,&key[1],&chunk);
550                 break;
551                 case '#':
552                     value = deformat_file(package,&key[1], &chunk, FALSE);
553                 break;
554                 case '!': /* should be short path */
555                     value = deformat_file(package,&key[1], &chunk, TRUE);
556                 break;
557                 case '\\':
558                     value = deformat_escape(&key[1],&chunk);
559                 break;
560                 case '%':
561                     value = deformat_environment(package,&key[1],&chunk);
562                 break;
563                 default:
564                     /* index keys cannot be nested */
565                     if (is_key_number(key))
566                         if (!nested)
567                             value = deformat_index(record,key,&chunk);
568                         else
569                         {
570                             static const WCHAR fmt[] = {'[','%','s',']',0};
571                             value = msi_alloc(10);
572                             sprintfW(value,fmt,key);
573                             chunk = strlenW(value)*sizeof(WCHAR);
574                         }
575                     else
576                         value = deformat_property(package,key,&chunk);
577                 break;      
578             }
579         }
580
581         msi_free(key);
582
583         if (value!=NULL)
584         {
585             LPBYTE nd2;
586             TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
587                     chunk, size);
588             if (size)
589                 nd2= msi_realloc(newdata,(size + chunk));
590             else
591                 nd2= msi_alloc(chunk);
592             newdata = nd2;
593             memcpy(&newdata[size],value,chunk);
594             size+=chunk;   
595             msi_free(value);
596         }
597         else if (failcount && *failcount >=0 )
598             (*failcount)++;
599
600         progress = mark2+1;
601     }
602
603     TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata, 
604                             size/sizeof(WCHAR)));
605
606     *data = (LPWSTR)newdata;
607     return size / sizeof(WCHAR);
608 }
609
610
611 UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
612                         DWORD *size )
613 {
614     LPWSTR deformated;
615     LPWSTR rec;
616     DWORD len;
617     UINT rc = ERROR_INVALID_PARAMETER;
618
619     TRACE("%p %p %p %i\n", package, record ,buffer, *size);
620
621     rec = msi_dup_record_field(record,0);
622     if (!rec)
623         rec = build_default_format(record);
624
625     TRACE("(%s)\n",debugstr_w(rec));
626
627     len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
628                                    record, NULL);
629
630     if (buffer)
631     {
632         if (*size>len)
633         {
634             memcpy(buffer,deformated,len*sizeof(WCHAR));
635             rc = ERROR_SUCCESS;
636             buffer[len] = 0;
637         }
638         else
639         {
640             if (*size > 0)
641             {
642                 memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
643                 buffer[(*size)-1] = 0;
644             }
645             rc = ERROR_MORE_DATA;
646         }
647     }
648     else
649         rc = ERROR_SUCCESS;
650
651     *size = len;
652
653     msi_free(rec);
654     msi_free(deformated);
655     return rc;
656 }
657
658 UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
659                         DWORD *size )
660 {
661     LPWSTR deformated;
662     LPWSTR rec;
663     DWORD len,lenA;
664     UINT rc = ERROR_INVALID_PARAMETER;
665
666     TRACE("%p %p %p %i\n", package, record ,buffer, *size);
667
668     rec = msi_dup_record_field(record,0);
669     if (!rec)
670         rec = build_default_format(record);
671
672     TRACE("(%s)\n",debugstr_w(rec));
673
674     len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
675                                    record, NULL);
676     /* If len is zero then WideCharToMultiByte will return 0 indicating 
677      * failure, but that will do just as well since we are ignoring
678      * possible errors.
679      */
680     lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
681
682     if (buffer)
683     {
684         /* Ditto above */
685         WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
686         if (*size>lenA)
687         {
688             rc = ERROR_SUCCESS;
689             buffer[lenA] = 0;
690         }
691         else
692         {
693             rc = ERROR_MORE_DATA;
694             if (*size)
695                 buffer[(*size)-1] = 0;
696         }
697     }
698     else
699         rc = ERROR_SUCCESS;
700
701     *size = lenA;
702
703     msi_free(rec);
704     msi_free(deformated);
705     return rc;
706 }
707
708
709 UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 
710                               LPWSTR szResult, DWORD *sz )
711 {
712     UINT r = ERROR_INVALID_HANDLE;
713     MSIPACKAGE *package;
714     MSIRECORD *record;
715
716     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
717
718     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
719
720     if (!record)
721         return ERROR_INVALID_HANDLE;
722     if (!sz)
723     {
724         msiobj_release( &record->hdr );
725         if (szResult)
726             return ERROR_INVALID_PARAMETER;
727         else
728             return ERROR_SUCCESS;
729     }
730
731     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
732
733     r = MSI_FormatRecordW( package, record, szResult, sz );
734     msiobj_release( &record->hdr );
735     if (package)
736         msiobj_release( &package->hdr );
737     return r;
738 }
739
740 UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
741                               LPSTR szResult, DWORD *sz )
742 {
743     UINT r = ERROR_INVALID_HANDLE;
744     MSIPACKAGE *package = NULL;
745     MSIRECORD *record = NULL;
746
747     TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
748
749     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
750
751     if (!record)
752         return ERROR_INVALID_HANDLE;
753     if (!sz)
754     {
755         msiobj_release( &record->hdr );
756         if (szResult)
757             return ERROR_INVALID_PARAMETER;
758         else
759             return ERROR_SUCCESS;
760     }
761
762     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
763
764     r = MSI_FormatRecordA( package, record, szResult, sz );
765     msiobj_release( &record->hdr );
766     if (package)
767         msiobj_release( &package->hdr );
768     return r;
769 }