xref: /reactos/dll/win32/msi/classes.c (revision 65ce1461)
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 }
1565