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