msi/tests: Windows Installer 3.0 often returns ERROR_INVALID_PARAMETER.
[wine] / dlls / msi / classes.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart 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 /* actions handled in this module
22  * RegisterClassInfo
23  * RegisterProgIdInfo
24  * RegisterExtensionInfo
25  * RegisterMIMEInfo
26  * UnRegisterClassInfo (TODO)
27  * UnRegisterProgIdInfo (TODO)
28  * UnRegisterExtensionInfo (TODO)
29  * UnRegisterMIMEInfo (TODO)
30  */
31
32 #include <stdarg.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winreg.h"
38 #include "wine/debug.h"
39 #include "msipriv.h"
40 #include "winuser.h"
41 #include "wine/unicode.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44
45
46 extern const WCHAR szRegisterClassInfo[];
47 extern const WCHAR szRegisterProgIdInfo[];
48 extern const WCHAR szRegisterExtensionInfo[];
49 extern const WCHAR szRegisterMIMEInfo[];
50
51 extern const WCHAR szUnregisterClassInfo[];
52 extern const WCHAR szUnregisterExtensionInfo[];
53 extern const WCHAR szUnregisterMIMEInfo[];
54 extern const WCHAR szUnregisterProgIdInfo[];
55
56 static MSIAPPID *load_appid( MSIPACKAGE* package, MSIRECORD *row )
57 {
58     LPCWSTR buffer;
59     MSIAPPID *appid;
60
61     /* fill in the data */
62
63     appid = msi_alloc_zero( sizeof(MSIAPPID) );
64     if (!appid)
65         return NULL;
66     
67     appid->AppID = msi_dup_record_field( row, 1 );
68     TRACE("loading appid %s\n", debugstr_w( appid->AppID ));
69
70     buffer = MSI_RecordGetString(row,2);
71     deformat_string( package, buffer, &appid->RemoteServerName );
72
73     appid->LocalServer = msi_dup_record_field(row,3);
74     appid->ServiceParameters = msi_dup_record_field(row,4);
75     appid->DllSurrogate = msi_dup_record_field(row,5);
76
77     appid->ActivateAtStorage = !MSI_RecordIsNull(row,6);
78     appid->RunAsInteractiveUser = !MSI_RecordIsNull(row,7);
79
80     list_add_tail( &package->appids, &appid->entry );
81     
82     return appid;
83 }
84
85 static MSIAPPID *load_given_appid( MSIPACKAGE *package, LPCWSTR name )
86 {
87     MSIRECORD *row;
88     MSIAPPID *appid;
89     static const WCHAR ExecSeqQuery[] =
90         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
91          '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ',
92          '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0};
93
94     if (!name)
95         return NULL;
96
97     /* check for appids already loaded */
98     LIST_FOR_EACH_ENTRY( appid, &package->appids, MSIAPPID, entry )
99     {
100         if (lstrcmpiW( appid->AppID, name )==0)
101         {
102             TRACE("found appid %s %p\n", debugstr_w(name), appid);
103             return appid;
104         }
105     }
106     
107     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, name);
108     if (!row)
109         return NULL;
110
111     appid = load_appid(package, row);
112     msiobj_release(&row->hdr);
113
114     return appid;
115 }
116
117 static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR progid);
118 static MSICLASS *load_given_class( MSIPACKAGE *package, LPCWSTR classid );
119
120 static MSIPROGID *load_progid( MSIPACKAGE* package, MSIRECORD *row )
121 {
122     MSIPROGID *progid;
123     LPCWSTR buffer;
124
125     /* fill in the data */
126
127     progid = msi_alloc_zero( sizeof(MSIPROGID) );
128     if (!progid)
129         return NULL;
130
131     list_add_tail( &package->progids, &progid->entry );
132
133     progid->ProgID = msi_dup_record_field(row,1);
134     TRACE("loading progid %s\n",debugstr_w(progid->ProgID));
135
136     buffer = MSI_RecordGetString(row,2);
137     progid->Parent = load_given_progid(package,buffer);
138     if (progid->Parent == NULL && buffer)
139         FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer));
140
141     buffer = MSI_RecordGetString(row,3);
142     progid->Class = load_given_class(package,buffer);
143     if (progid->Class == NULL && buffer)
144         FIXME("Unknown class %s\n",debugstr_w(buffer));
145
146     progid->Description = msi_dup_record_field(row,4);
147
148     if (!MSI_RecordIsNull(row,6))
149     {
150         INT icon_index = MSI_RecordGetInteger(row,6); 
151         LPCWSTR FileName = MSI_RecordGetString(row,5);
152         LPWSTR FilePath;
153         static const WCHAR fmt[] = {'%','s',',','%','i',0};
154
155         FilePath = build_icon_path(package,FileName);
156        
157         progid->IconPath = msi_alloc( (strlenW(FilePath)+10)* sizeof(WCHAR) );
158
159         sprintfW(progid->IconPath,fmt,FilePath,icon_index);
160
161         msi_free(FilePath);
162     }
163     else
164     {
165         buffer = MSI_RecordGetString(row,5);
166         if (buffer)
167             progid->IconPath = build_icon_path(package,buffer);
168     }
169
170     progid->CurVer = NULL;
171     progid->VersionInd = NULL;
172
173     /* if we have a parent then we may be that parents CurVer */
174     if (progid->Parent && progid->Parent != progid)
175     {
176         MSIPROGID *parent = progid->Parent;
177
178         while (parent->Parent && parent->Parent != parent)
179             parent = parent->Parent;
180
181         /* FIXME: need to determine if we are really the CurVer */
182
183         progid->CurVer = parent;
184         parent->VersionInd = progid;
185     }
186     
187     return progid;
188 }
189
190 static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR name)
191 {
192     MSIPROGID *progid;
193     MSIRECORD *row;
194     static const WCHAR ExecSeqQuery[] =
195         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
196          '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ',
197          '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0};
198
199     if (!name)
200         return NULL;
201
202     /* check for progids already loaded */
203     LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
204     {
205         if (strcmpiW( progid->ProgID,name )==0)
206         {
207             TRACE("found progid %s (%p)\n",debugstr_w(name), progid );
208             return progid;
209         }
210     }
211     
212     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name );
213     if (!row)
214         return NULL;
215
216     progid = load_progid(package, row);
217     msiobj_release(&row->hdr);
218
219     return progid;
220 }
221
222 static MSICLASS *load_class( MSIPACKAGE* package, MSIRECORD *row )
223 {
224     MSICLASS *cls;
225     DWORD i;
226     LPCWSTR buffer;
227
228     /* fill in the data */
229
230     cls = msi_alloc_zero( sizeof(MSICLASS) );
231     if (!cls)
232         return NULL;
233
234     list_add_tail( &package->classes, &cls->entry );
235
236     cls->clsid = msi_dup_record_field( row, 1 );
237     TRACE("loading class %s\n",debugstr_w(cls->clsid));
238     cls->Context = msi_dup_record_field( row, 2 );
239     buffer = MSI_RecordGetString(row,3);
240     cls->Component = get_loaded_component(package, buffer);
241
242     cls->ProgIDText = msi_dup_record_field(row,4);
243     cls->ProgID = load_given_progid(package, cls->ProgIDText);
244
245     cls->Description = msi_dup_record_field(row,5);
246
247     buffer = MSI_RecordGetString(row,6);
248     if (buffer)
249         cls->AppID = load_given_appid(package, buffer);
250
251     cls->FileTypeMask = msi_dup_record_field(row,7);
252
253     if (!MSI_RecordIsNull(row,9))
254     {
255
256         INT icon_index = MSI_RecordGetInteger(row,9); 
257         LPCWSTR FileName = MSI_RecordGetString(row,8);
258         LPWSTR FilePath;
259         static const WCHAR fmt[] = {'%','s',',','%','i',0};
260
261         FilePath = build_icon_path(package,FileName);
262        
263         cls->IconPath = msi_alloc( (strlenW(FilePath)+5)* sizeof(WCHAR) );
264
265         sprintfW(cls->IconPath,fmt,FilePath,icon_index);
266
267         msi_free(FilePath);
268     }
269     else
270     {
271         buffer = MSI_RecordGetString(row,8);
272         if (buffer)
273             cls->IconPath = build_icon_path(package,buffer);
274     }
275
276     if (!MSI_RecordIsNull(row,10))
277     {
278         i = MSI_RecordGetInteger(row,10);
279         if (i != MSI_NULL_INTEGER && i > 0 &&  i < 4)
280         {
281             static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0};
282             static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0};
283
284             switch(i)
285             {
286                 case 1:
287                     cls->DefInprocHandler = strdupW(ole2);
288                     break;
289                 case 2:
290                     cls->DefInprocHandler32 = strdupW(ole32);
291                     break;
292                 case 3:
293                     cls->DefInprocHandler = strdupW(ole2);
294                     cls->DefInprocHandler32 = strdupW(ole32);
295                     break;
296             }
297         }
298         else
299         {
300             cls->DefInprocHandler32 = msi_dup_record_field( row, 10);
301             reduce_to_longfilename(cls->DefInprocHandler32);
302         }
303     }
304     buffer = MSI_RecordGetString(row,11);
305     deformat_string(package,buffer,&cls->Argument);
306
307     buffer = MSI_RecordGetString(row,12);
308     cls->Feature = get_loaded_feature(package,buffer);
309
310     cls->Attributes = MSI_RecordGetInteger(row,13);
311     
312     return cls;
313 }
314
315 /*
316  * the Class table has 3 primary keys. Generally it is only 
317  * referenced through the first CLSID key. However when loading
318  * all of the classes we need to make sure we do not ignore rows
319  * with other Context and ComponentIndexs 
320  */
321 static MSICLASS *load_given_class(MSIPACKAGE *package, LPCWSTR classid)
322 {
323     MSICLASS *cls;
324     MSIRECORD *row;
325     static const WCHAR ExecSeqQuery[] =
326         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
327          '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ',
328          '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0};
329
330     if (!classid)
331         return NULL;
332     
333     /* check for classes already loaded */
334     LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
335     {
336         if (lstrcmpiW( cls->clsid, classid )==0)
337         {
338             TRACE("found class %s (%p)\n",debugstr_w(classid), cls);
339             return cls;
340         }
341     }
342
343     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, classid);
344     if (!row)
345         return NULL;
346
347     cls = load_class(package, row);
348     msiobj_release(&row->hdr);
349
350     return cls;
351 }
352
353 static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR extension );
354
355 static MSIMIME *load_mime( MSIPACKAGE* package, MSIRECORD *row )
356 {
357     LPCWSTR buffer;
358     MSIMIME *mt;
359
360     /* fill in the data */
361
362     mt = msi_alloc_zero( sizeof(MSIMIME) );
363     if (!mt)
364         return mt;
365
366     mt->ContentType = msi_dup_record_field( row, 1 ); 
367     TRACE("loading mime %s\n", debugstr_w(mt->ContentType));
368
369     buffer = MSI_RecordGetString( row, 2 );
370     mt->Extension = load_given_extension( package, buffer );
371
372     mt->clsid = msi_dup_record_field( row, 3 );
373     mt->Class = load_given_class( package, mt->clsid );
374
375     list_add_tail( &package->mimes, &mt->entry );
376
377     return mt;
378 }
379
380 static MSIMIME *load_given_mime( MSIPACKAGE *package, LPCWSTR mime )
381 {
382     MSIRECORD *row;
383     static const WCHAR ExecSeqQuery[] =
384         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
385          '`','M','I','M','E','`',' ','W','H','E','R','E',' ',
386          '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ',
387          '\'','%','s','\'',0};
388     MSIMIME *mt;
389
390     if (!mime)
391         return NULL;
392     
393     /* check for mime already loaded */
394     LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry )
395     {
396         if (strcmpiW(mt->ContentType,mime)==0)
397         {
398             TRACE("found mime %s (%p)\n",debugstr_w(mime), mt);
399             return mt;
400         }
401     }
402     
403     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, mime);
404     if (!row)
405         return NULL;
406
407     mt = load_mime(package, row);
408     msiobj_release(&row->hdr);
409
410     return mt;
411 }
412
413 static MSIEXTENSION *load_extension( MSIPACKAGE* package, MSIRECORD *row )
414 {
415     MSIEXTENSION *ext;
416     LPCWSTR buffer;
417
418     /* fill in the data */
419
420     ext = msi_alloc_zero( sizeof(MSIEXTENSION) );
421     if (!ext)
422         return NULL;
423
424     list_init( &ext->verbs );
425
426     list_add_tail( &package->extensions, &ext->entry );
427
428     ext->Extension = msi_dup_record_field( row, 1 );
429     TRACE("loading extension %s\n", debugstr_w(ext->Extension));
430
431     buffer = MSI_RecordGetString( row, 2 );
432     ext->Component = get_loaded_component( package,buffer );
433
434     ext->ProgIDText = msi_dup_record_field( row, 3 );
435     ext->ProgID = load_given_progid( package, ext->ProgIDText );
436
437     buffer = MSI_RecordGetString( row, 4 );
438     ext->Mime = load_given_mime( package, buffer );
439
440     buffer = MSI_RecordGetString(row,5);
441     ext->Feature = get_loaded_feature( package, buffer );
442
443     return ext;
444 }
445
446 /*
447  * While the extension table has 2 primary keys, this function is only looking
448  * at the Extension key which is what is referenced as a foreign key
449  */
450 static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR name )
451 {
452     MSIRECORD *row;
453     MSIEXTENSION *ext;
454     static const WCHAR ExecSeqQuery[] =
455         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
456          '`','E','x','t','e','n','s','i','o','n','`',' ',
457          'W','H','E','R','E',' ',
458          '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ',
459          '\'','%','s','\'',0};
460
461     if (!name)
462         return NULL;
463
464     if (name[0] == '.')
465         name++;
466
467     /* check for extensions already loaded */
468     LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
469     {
470         if (strcmpiW( ext->Extension, name )==0)
471         {
472             TRACE("extension %s already loaded %p\n", debugstr_w(name), ext);
473             return ext;
474         }
475     }
476     
477     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name );
478     if (!row)
479         return NULL;
480
481     ext = load_extension(package, row);
482     msiobj_release(&row->hdr);
483
484     return ext;
485 }
486
487 static UINT iterate_load_verb(MSIRECORD *row, LPVOID param)
488 {
489     MSIPACKAGE* package = param;
490     MSIVERB *verb;
491     LPCWSTR buffer;
492     MSIEXTENSION *extension;
493
494     buffer = MSI_RecordGetString(row,1);
495     extension = load_given_extension( package, buffer );
496     if (!extension)
497     {
498         ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer));
499         return ERROR_SUCCESS;
500     }
501
502     /* fill in the data */
503
504     verb = msi_alloc_zero( sizeof(MSIVERB) );
505     if (!verb)
506         return ERROR_OUTOFMEMORY;
507
508     verb->Verb = msi_dup_record_field(row,2);
509     TRACE("loading verb %s\n",debugstr_w(verb->Verb));
510     verb->Sequence = MSI_RecordGetInteger(row,3);
511
512     buffer = MSI_RecordGetString(row,4);
513     deformat_string(package,buffer,&verb->Command);
514
515     buffer = MSI_RecordGetString(row,5);
516     deformat_string(package,buffer,&verb->Argument);
517
518     /* associate the verb with the correct extension */
519     list_add_tail( &extension->verbs, &verb->entry );
520     
521     return ERROR_SUCCESS;
522 }
523
524 static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param)
525 {
526     MSICOMPONENT *comp;
527     LPCWSTR clsid;
528     LPCWSTR context;
529     LPCWSTR buffer;
530     MSIPACKAGE* package = param;
531     MSICLASS *cls;
532     BOOL match = FALSE;
533
534     clsid = MSI_RecordGetString(rec,1);
535     context = MSI_RecordGetString(rec,2);
536     buffer = MSI_RecordGetString(rec,3);
537     comp = get_loaded_component(package,buffer);
538
539     LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
540     {
541         if (strcmpiW( clsid, cls->clsid ))
542             continue;
543         if (strcmpW( context, cls->Context ))
544             continue;
545         if (comp == cls->Component)
546         {
547             match = TRUE;
548             break;
549         }
550     }
551     
552     if (!match)
553         load_class(package, rec);
554
555     return ERROR_SUCCESS;
556 }
557
558 static VOID load_all_classes(MSIPACKAGE *package)
559 {
560     UINT rc = ERROR_SUCCESS;
561     MSIQUERY *view;
562
563     static const WCHAR ExecSeqQuery[] =
564         {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
565          '`','C','l','a','s','s','`',0};
566
567     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
568     if (rc != ERROR_SUCCESS)
569         return;
570
571     rc = MSI_IterateRecords(view, NULL, iterate_all_classes, package);
572     msiobj_release(&view->hdr);
573 }
574
575 static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param)
576 {
577     MSICOMPONENT *comp;
578     LPCWSTR buffer;
579     LPCWSTR extension;
580     MSIPACKAGE* package = param;
581     BOOL match = FALSE;
582     MSIEXTENSION *ext;
583
584     extension = MSI_RecordGetString(rec,1);
585     buffer = MSI_RecordGetString(rec,2);
586     comp = get_loaded_component(package,buffer);
587
588     LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
589     {
590         if (strcmpiW(extension,ext->Extension))
591             continue;
592         if (comp == ext->Component)
593         {
594             match = TRUE;
595             break;
596         }
597     }
598
599     if (!match)
600         load_extension(package, rec);
601
602     return ERROR_SUCCESS;
603 }
604
605 static VOID load_all_extensions(MSIPACKAGE *package)
606 {
607     UINT rc = ERROR_SUCCESS;
608     MSIQUERY *view;
609
610     static const WCHAR ExecSeqQuery[] =
611         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
612          '`','E','x','t','e','n','s','i','o','n','`',0};
613
614     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
615     if (rc != ERROR_SUCCESS)
616         return;
617
618     rc = MSI_IterateRecords(view, NULL, iterate_all_extensions, package);
619     msiobj_release(&view->hdr);
620 }
621
622 static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param)
623 {
624     LPCWSTR buffer;
625     MSIPACKAGE* package = param;
626
627     buffer = MSI_RecordGetString(rec,1);
628     load_given_progid(package,buffer);
629     return ERROR_SUCCESS;
630 }
631
632 static VOID load_all_progids(MSIPACKAGE *package)
633 {
634     UINT rc = ERROR_SUCCESS;
635     MSIQUERY *view;
636
637     static const WCHAR ExecSeqQuery[] =
638         {'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ',
639          'F','R','O','M',' ', '`','P','r','o','g','I','d','`',0};
640
641     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
642     if (rc != ERROR_SUCCESS)
643         return;
644
645     rc = MSI_IterateRecords(view, NULL, iterate_all_progids, package);
646     msiobj_release(&view->hdr);
647 }
648
649 static VOID load_all_verbs(MSIPACKAGE *package)
650 {
651     UINT rc = ERROR_SUCCESS;
652     MSIQUERY *view;
653
654     static const WCHAR ExecSeqQuery[] =
655         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
656          '`','V','e','r','b','`',0};
657
658     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
659     if (rc != ERROR_SUCCESS)
660         return;
661
662     rc = MSI_IterateRecords(view, NULL, iterate_load_verb, package);
663     msiobj_release(&view->hdr);
664 }
665
666 static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param)
667 {
668     LPCWSTR buffer;
669     MSIPACKAGE* package = param;
670
671     buffer = MSI_RecordGetString(rec,1);
672     load_given_mime(package,buffer);
673     return ERROR_SUCCESS;
674 }
675
676 static VOID load_all_mimes(MSIPACKAGE *package)
677 {
678     UINT rc = ERROR_SUCCESS;
679     MSIQUERY *view;
680
681     static const WCHAR ExecSeqQuery[] =
682         {'S','E','L','E','C','T',' ',
683          '`','C','o','n','t','e','n','t','T','y','p','e','`',
684          ' ','F','R','O','M',' ',
685          '`','M','I','M','E','`',0};
686
687     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
688     if (rc != ERROR_SUCCESS)
689         return;
690
691     rc = MSI_IterateRecords(view, NULL, iterate_all_mimes, package);
692     msiobj_release(&view->hdr);
693 }
694
695 static void load_classes_and_such(MSIPACKAGE *package)
696 {
697     TRACE("Loading all the class info and related tables\n");
698
699     /* check if already loaded */
700     if (!list_empty( &package->classes ) ||
701         !list_empty( &package->mimes ) ||
702         !list_empty( &package->extensions ) ||
703         !list_empty( &package->progids ) )
704         return;
705
706     load_all_classes(package);
707     load_all_extensions(package);
708     load_all_progids(package);
709     /* these loads must come after the other loads */
710     load_all_verbs(package);
711     load_all_mimes(package);
712 }
713
714 static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid )
715 {
716     MSIPROGID *child;
717
718     if (!progid)
719         return;
720
721     if (progid->InstallMe)
722         return;
723
724     progid->InstallMe = TRUE;
725
726     /* all children if this is a parent also install */
727     LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry )
728     {
729         if (child->Parent == progid)
730             mark_progid_for_install( package, child );
731     }
732 }
733
734 static void mark_mime_for_install( MSIMIME *mime )
735 {
736     if (!mime)
737         return;
738     mime->InstallMe = TRUE;
739 }
740
741 static UINT register_appid(const MSIAPPID *appid, LPCWSTR app )
742 {
743     static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
744     static const WCHAR szRemoteServerName[] =
745          {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
746     static const WCHAR szLocalService[] =
747          {'L','o','c','a','l','S','e','r','v','i','c','e',0};
748     static const WCHAR szService[] =
749          {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
750     static const WCHAR szDLL[] =
751          {'D','l','l','S','u','r','r','o','g','a','t','e',0};
752     static const WCHAR szActivate[] =
753          {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0};
754     static const WCHAR szY[] = {'Y',0};
755     static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
756     static const WCHAR szUser[] = 
757          {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0};
758
759     HKEY hkey2,hkey3;
760
761     RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
762     RegCreateKeyW( hkey2, appid->AppID, &hkey3 );
763     RegCloseKey(hkey2);
764     msi_reg_set_val_str( hkey3, NULL, app );
765
766     if (appid->RemoteServerName)
767         msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName );
768
769     if (appid->LocalServer)
770         msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer );
771
772     if (appid->ServiceParameters)
773         msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters );
774
775     if (appid->DllSurrogate)
776         msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate );
777
778     if (appid->ActivateAtStorage)
779         msi_reg_set_val_str( hkey3, szActivate, szY );
780
781     if (appid->RunAsInteractiveUser)
782         msi_reg_set_val_str( hkey3, szRunAs, szUser );
783
784     RegCloseKey(hkey3);
785     return ERROR_SUCCESS;
786 }
787
788 UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
789 {
790     /* 
791      * Again I am assuming the words, "Whose key file represents" when referring
792      * to a Component as to meaning that Components KeyPath file
793      */
794     
795     UINT rc;
796     MSIRECORD *uirow;
797     static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
798     static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 };
799     static const WCHAR szVIProgID[] = { 'V','e','r','s','i','o','n','I','n','d','e','p','e','n','d','e','n','t','P','r','o','g','I','D',0 };
800     static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
801     static const WCHAR szSpace[] = {' ',0};
802     static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0};
803     HKEY hkey,hkey2,hkey3;
804     MSICLASS *cls;
805
806     load_classes_and_such(package);
807     rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
808     if (rc != ERROR_SUCCESS)
809         return ERROR_FUNCTION_FAILED;
810
811     LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
812     {
813         MSICOMPONENT *comp;
814         MSIFILE *file;
815         DWORD size;
816         LPWSTR argument;
817         MSIFEATURE *feature;
818
819         comp = cls->Component;
820         if ( !comp )
821             continue;
822
823         feature = cls->Feature;
824
825         /*
826          * MSDN says that these are based on Feature not on Component.
827          */
828         if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
829             !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
830         {
831             TRACE("Skipping class %s reg due to disabled feature %s\n",
832                   debugstr_w(cls->clsid), debugstr_w(feature->Feature));
833
834             continue;
835         }
836
837         TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls);
838
839         cls->Installed = TRUE;
840         mark_progid_for_install( package, cls->ProgID );
841
842         RegCreateKeyW( hkey, cls->clsid, &hkey2 );
843
844         if (cls->Description)
845             msi_reg_set_val_str( hkey2, NULL, cls->Description );
846
847         RegCreateKeyW( hkey2, cls->Context, &hkey3 );
848         file = get_loaded_file( package, comp->KeyPath );
849         if (!file)
850         {
851             TRACE("COM server not provided, skipping class %s\n", debugstr_w(cls->clsid));
852             continue;
853         }
854
855         /*
856          * FIXME: Implement install on demand (advertised components).
857          *
858          * ole32.dll should call msi.MsiProvideComponentFromDescriptor()
859          *  when it needs an InProcServer that doesn't exist.
860          * The component advertise string should be in the "InProcServer" value.
861          */
862         size = lstrlenW( file->TargetPath )+1;
863         if (cls->Argument)
864             size += lstrlenW(cls->Argument)+1;
865
866         argument = msi_alloc( size * sizeof(WCHAR) );
867         lstrcpyW( argument, file->TargetPath );
868
869         if (cls->Argument)
870         {
871             lstrcatW( argument, szSpace );
872             lstrcatW( argument, cls->Argument );
873         }
874
875         msi_reg_set_val_str( hkey3, NULL, argument );
876         msi_free(argument);
877
878         RegCloseKey(hkey3);
879
880         if (cls->ProgID || cls->ProgIDText)
881         {
882             LPCWSTR progid;
883
884             if (cls->ProgID)
885                 progid = cls->ProgID->ProgID;
886             else
887                 progid = cls->ProgIDText;
888
889             msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid );
890
891             if (cls->ProgID && cls->ProgID->VersionInd)
892             {
893                 msi_reg_set_subkey_val( hkey2, szVIProgID, NULL, 
894                                         cls->ProgID->VersionInd->ProgID );
895             }
896         }
897
898         if (cls->AppID)
899         {
900             MSIAPPID *appid = cls->AppID;
901
902             msi_reg_set_val_str( hkey2, szAppID, appid->AppID );
903
904             register_appid( appid, cls->Description );
905         }
906
907         if (cls->IconPath)
908         {
909             static const WCHAR szDefaultIcon[] = 
910                 {'D','e','f','a','u','l','t','I','c','o','n',0};
911
912             msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath );
913         }
914
915         if (cls->DefInprocHandler)
916         {
917             static const WCHAR szInproc[] =
918                 {'I','n','p','r','o','c','H','a','n','d','l','e','r',0};
919
920             msi_reg_set_subkey_val( hkey2, szInproc, NULL, cls->DefInprocHandler );
921         }
922
923         if (cls->DefInprocHandler32)
924         {
925             static const WCHAR szInproc32[] =
926                 {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
927
928             msi_reg_set_subkey_val( hkey2, szInproc32, NULL, cls->DefInprocHandler32 );
929         }
930         
931         RegCloseKey(hkey2);
932
933         /* if there is a FileTypeMask, register the FileType */
934         if (cls->FileTypeMask)
935         {
936             LPWSTR ptr, ptr2;
937             LPWSTR keyname;
938             INT index = 0;
939             ptr = cls->FileTypeMask;
940             while (ptr && *ptr)
941             {
942                 ptr2 = strchrW(ptr,';');
943                 if (ptr2)
944                     *ptr2 = 0;
945                 keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR));
946                 sprintfW( keyname, szFileType_fmt, cls->clsid, index );
947
948                 msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr );
949                 msi_free(keyname);
950
951                 if (ptr2)
952                     ptr = ptr2+1;
953                 else
954                     ptr = NULL;
955
956                 index ++;
957             }
958         }
959         
960         uirow = MSI_CreateRecord(1);
961
962         MSI_RecordSetStringW( uirow, 1, cls->clsid );
963         ui_actiondata(package,szRegisterClassInfo,uirow);
964         msiobj_release(&uirow->hdr);
965     }
966
967     RegCloseKey(hkey);
968     return rc;
969 }
970
971 static LPCWSTR get_clsid_of_progid( const MSIPROGID *progid )
972 {
973     while (progid)
974     {
975         if (progid->Class)
976             return progid->Class->clsid;
977         if (progid->Parent == progid)
978             break;
979         progid = progid->Parent;
980     }
981     return NULL;
982 }
983
984 static UINT register_progid( const MSIPROGID* progid )
985 {
986     static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
987     static const WCHAR szDefaultIcon[] =
988         {'D','e','f','a','u','l','t','I','c','o','n',0};
989     static const WCHAR szCurVer[] =
990         {'C','u','r','V','e','r',0};
991     HKEY hkey = 0;
992     UINT rc;
993
994     rc = RegCreateKeyW( HKEY_CLASSES_ROOT, progid->ProgID, &hkey );
995     if (rc == ERROR_SUCCESS)
996     {
997         LPCWSTR clsid = get_clsid_of_progid( progid );
998
999         if (clsid)
1000             msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid );
1001         else
1002             ERR("%s has no class\n", debugstr_w( progid->ProgID ) );
1003
1004         if (progid->Description)
1005             msi_reg_set_val_str( hkey, NULL, progid->Description );
1006
1007         if (progid->IconPath)
1008             msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath );
1009
1010         /* write out the current version */
1011         if (progid->CurVer)
1012             msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID );
1013
1014         RegCloseKey(hkey);
1015     }
1016     else
1017         ERR("failed to create key %s\n", debugstr_w( progid->ProgID ) );
1018
1019     return rc;
1020 }
1021
1022 UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
1023 {
1024     MSIPROGID *progid;
1025     MSIRECORD *uirow;
1026
1027     load_classes_and_such(package);
1028
1029     LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
1030     {
1031         /* check if this progid is to be installed */
1032         if (progid->Class && progid->Class->Installed)
1033             progid->InstallMe = TRUE;
1034
1035         if (!progid->InstallMe)
1036         {
1037             TRACE("progid %s not scheduled to be installed\n",
1038                              debugstr_w(progid->ProgID));
1039             continue;
1040         }
1041        
1042         TRACE("Registering progid %s\n", debugstr_w(progid->ProgID));
1043
1044         register_progid( progid );
1045
1046         uirow = MSI_CreateRecord( 1 );
1047         MSI_RecordSetStringW( uirow, 1, progid->ProgID );
1048         ui_actiondata( package, szRegisterProgIdInfo, uirow );
1049         msiobj_release( &uirow->hdr );
1050     }
1051
1052     return ERROR_SUCCESS;
1053 }
1054
1055 static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid, 
1056                 MSICOMPONENT* component, const MSIEXTENSION* extension,
1057                 MSIVERB* verb, INT* Sequence )
1058 {
1059     LPWSTR keyname;
1060     HKEY key;
1061     static const WCHAR szShell[] = {'s','h','e','l','l',0};
1062     static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0};
1063     static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0};
1064     static const WCHAR fmt2[] = {'\"','%','s','\"',0};
1065     LPWSTR command;
1066     DWORD size;
1067     LPWSTR advertise;
1068
1069     keyname = build_directory_name(4, progid, szShell, verb->Verb, szCommand);
1070
1071     TRACE("Making Key %s\n",debugstr_w(keyname));
1072     RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
1073     size = strlenW(component->FullKeypath);
1074     if (verb->Argument)
1075         size += strlenW(verb->Argument);
1076      size += 4;
1077
1078      command = msi_alloc(size * sizeof (WCHAR));
1079      if (verb->Argument)
1080         sprintfW(command, fmt, component->FullKeypath, verb->Argument);
1081      else
1082         sprintfW(command, fmt2, component->FullKeypath);
1083
1084      msi_reg_set_val_str( key, NULL, command );
1085      msi_free(command);
1086
1087      advertise = create_component_advertise_string(package, component, 
1088                                                    extension->Feature->Feature);
1089
1090      size = strlenW(advertise);
1091
1092      if (verb->Argument)
1093          size += strlenW(verb->Argument);
1094      size += 4;
1095
1096      command = msi_alloc_zero(size * sizeof (WCHAR));
1097
1098      strcpyW(command,advertise);
1099      if (verb->Argument)
1100      {
1101          static const WCHAR szSpace[] = {' ',0};
1102          strcatW(command,szSpace);
1103          strcatW(command,verb->Argument);
1104      }
1105
1106      msi_reg_set_val_multi_str( key, szCommand, command );
1107      
1108      RegCloseKey(key);
1109      msi_free(keyname);
1110      msi_free(advertise);
1111      msi_free(command);
1112
1113      if (verb->Command)
1114      {
1115         keyname = build_directory_name(3, progid, szShell, verb->Verb);
1116         msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command );
1117         msi_free(keyname);
1118      }
1119
1120      if (verb->Sequence != MSI_NULL_INTEGER)
1121      {
1122         if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence)
1123         {
1124             *Sequence = verb->Sequence;
1125             keyname = build_directory_name(2, progid, szShell);
1126             msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb );
1127             msi_free(keyname);
1128         }
1129     }
1130     return ERROR_SUCCESS;
1131 }
1132
1133 UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package)
1134 {
1135     static const WCHAR szContentType[] = 
1136         {'C','o','n','t','e','n','t',' ','T','y','p','e',0 };
1137     HKEY hkey;
1138     MSIEXTENSION *ext;
1139     MSIRECORD *uirow;
1140     BOOL install_on_demand = TRUE;
1141
1142     load_classes_and_such(package);
1143
1144     /* We need to set install_on_demand based on if the shell handles advertised
1145      * shortcuts and the like. Because Mike McCormack is working on this i am
1146      * going to default to TRUE
1147      */
1148     
1149     LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
1150     {
1151         LPWSTR extension;
1152         MSIFEATURE *feature;
1153      
1154         if (!ext->Component)
1155             continue;
1156
1157         feature = ext->Feature;
1158
1159         /* 
1160          * yes. MSDN says that these are based on _Feature_ not on
1161          * Component.  So verify the feature is to be installed
1162          */
1163         if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) &&
1164              !(install_on_demand &&
1165                ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )))
1166         {
1167             TRACE("Skipping extension %s reg due to disabled feature %s\n",
1168                    debugstr_w(ext->Extension), debugstr_w(feature->Feature));
1169
1170             continue;
1171         }
1172
1173         TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext);
1174
1175         ext->Installed = TRUE;
1176
1177         /* this is only registered if the extension has at least 1 verb
1178          * according to MSDN
1179          */
1180         if (ext->ProgID && !list_empty( &ext->verbs ) )
1181             mark_progid_for_install( package, ext->ProgID );
1182
1183         mark_mime_for_install(ext->Mime);
1184
1185         extension = msi_alloc( (lstrlenW( ext->Extension ) + 2)*sizeof(WCHAR) );
1186         extension[0] = '.';
1187         lstrcpyW(extension+1,ext->Extension);
1188
1189         RegCreateKeyW(HKEY_CLASSES_ROOT,extension,&hkey);
1190         msi_free( extension );
1191
1192         if (ext->Mime)
1193             msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType );
1194
1195         if (ext->ProgID || ext->ProgIDText)
1196         {
1197             static const WCHAR szSN[] = 
1198                 {'\\','S','h','e','l','l','N','e','w',0};
1199             HKEY hkey2;
1200             LPWSTR newkey;
1201             LPCWSTR progid;
1202             MSIVERB *verb;
1203             INT Sequence = MSI_NULL_INTEGER;
1204             
1205             if (ext->ProgID)
1206                 progid = ext->ProgID->ProgID;
1207             else
1208                 progid = ext->ProgIDText;
1209
1210             msi_reg_set_val_str( hkey, NULL, progid );
1211
1212             newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR)); 
1213
1214             strcpyW(newkey,progid);
1215             strcatW(newkey,szSN);
1216             RegCreateKeyW(hkey,newkey,&hkey2);
1217             RegCloseKey(hkey2);
1218
1219             msi_free(newkey);
1220
1221             /* do all the verbs */
1222             LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry )
1223             {
1224                 register_verb( package, progid, ext->Component,
1225                                ext, verb, &Sequence);
1226             }
1227         }
1228         
1229         RegCloseKey(hkey);
1230
1231         uirow = MSI_CreateRecord(1);
1232         MSI_RecordSetStringW( uirow, 1, ext->Extension );
1233         ui_actiondata(package,szRegisterExtensionInfo,uirow);
1234         msiobj_release(&uirow->hdr);
1235     }
1236
1237     return ERROR_SUCCESS;
1238 }
1239
1240 UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package)
1241 {
1242     static const WCHAR szExten[] = 
1243         {'E','x','t','e','n','s','i','o','n',0 };
1244     MSIRECORD *uirow;
1245     MSIMIME *mt;
1246
1247     load_classes_and_such(package);
1248
1249     LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry )
1250     {
1251         LPWSTR extension;
1252         LPCWSTR exten;
1253         LPCWSTR mime;
1254         static const WCHAR fmt[] = 
1255             {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\',
1256              'C','o','n','t','e','n','t',' ','T','y','p','e','\\', '%','s',0};
1257         LPWSTR key;
1258
1259         /* 
1260          * check if the MIME is to be installed. Either as requested by an
1261          * extension or Class
1262          */
1263         mt->InstallMe = (mt->InstallMe ||
1264               (mt->Class && mt->Class->Installed) ||
1265               (mt->Extension && mt->Extension->Installed));
1266
1267         if (!mt->InstallMe)
1268         {
1269             TRACE("MIME %s not scheduled to be installed\n",
1270                              debugstr_w(mt->ContentType));
1271             continue;
1272         }
1273         
1274         mime = mt->ContentType;
1275         exten = mt->Extension->Extension;
1276
1277         extension = msi_alloc( (lstrlenW( exten ) + 2)*sizeof(WCHAR) );
1278         extension[0] = '.';
1279         lstrcpyW(extension+1,exten);
1280
1281         key = msi_alloc( (strlenW(mime)+strlenW(fmt)+1) * sizeof(WCHAR) );
1282         sprintfW(key,fmt,mime);
1283         msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExten, extension );
1284
1285         msi_free(extension);
1286         msi_free(key);
1287
1288         if (mt->clsid)
1289             FIXME("Handle non null for field 3\n");
1290
1291         uirow = MSI_CreateRecord(2);
1292         MSI_RecordSetStringW(uirow,1,mt->ContentType);
1293         MSI_RecordSetStringW(uirow,2,exten);
1294         ui_actiondata(package,szRegisterMIMEInfo,uirow);
1295         msiobj_release(&uirow->hdr);
1296     }
1297
1298     return ERROR_SUCCESS;
1299 }