xref: /reactos/dll/win32/msi/classes.c (revision 12e94103)
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     cls->action = INSTALLSTATE_UNKNOWN;
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     ext->action = INSTALLSTATE_UNKNOWN;
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 UINT register_appid(const MSIAPPID *appid, LPCWSTR app )
702 {
703     static const WCHAR szRemoteServerName[] =
704          {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
705     static const WCHAR szLocalService[] =
706          {'L','o','c','a','l','S','e','r','v','i','c','e',0};
707     static const WCHAR szService[] =
708          {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
709     static const WCHAR szDLL[] =
710          {'D','l','l','S','u','r','r','o','g','a','t','e',0};
711     static const WCHAR szActivate[] =
712          {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0};
713     static const WCHAR szY[] = {'Y',0};
714     static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
715     static const WCHAR szUser[] =
716          {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0};
717 
718     HKEY hkey2,hkey3;
719 
720     RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
721     RegCreateKeyW( hkey2, appid->AppID, &hkey3 );
722     RegCloseKey(hkey2);
723     msi_reg_set_val_str( hkey3, NULL, app );
724 
725     if (appid->RemoteServerName)
726         msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName );
727 
728     if (appid->LocalServer)
729         msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer );
730 
731     if (appid->ServiceParameters)
732         msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters );
733 
734     if (appid->DllSurrogate)
735         msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate );
736 
737     if (appid->ActivateAtStorage)
738         msi_reg_set_val_str( hkey3, szActivate, szY );
739 
740     if (appid->RunAsInteractiveUser)
741         msi_reg_set_val_str( hkey3, szRunAs, szUser );
742 
743     RegCloseKey(hkey3);
744     return ERROR_SUCCESS;
745 }
746 
747 UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
748 {
749     static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0};
750     const WCHAR *keypath;
751     MSIRECORD *uirow;
752     HKEY hkey, hkey2, hkey3;
753     MSICLASS *cls;
754     UINT r;
755 
756     r = load_classes_and_such( package );
757     if (r != ERROR_SUCCESS)
758         return r;
759 
760     if (is_64bit && package->platform == PLATFORM_INTEL)
761         keypath = szWow6432NodeCLSID;
762     else
763         keypath = szCLSID;
764 
765     if (RegCreateKeyW(HKEY_CLASSES_ROOT, keypath, &hkey) != ERROR_SUCCESS)
766         return ERROR_FUNCTION_FAILED;
767 
768     LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
769     {
770         MSICOMPONENT *comp;
771         MSIFILE *file;
772         DWORD size;
773         LPWSTR argument;
774         MSIFEATURE *feature;
775 
776         comp = cls->Component;
777         if ( !comp )
778             continue;
779 
780         if (!comp->Enabled)
781         {
782             TRACE("component is disabled\n");
783             continue;
784         }
785 
786         feature = cls->Feature;
787         if (!feature)
788             continue;
789 
790         feature->Action = msi_get_feature_action( package, feature );
791         if (feature->Action != INSTALLSTATE_LOCAL &&
792             feature->Action != INSTALLSTATE_ADVERTISED )
793         {
794             TRACE("feature %s not scheduled for installation, skipping registration of class %s\n",
795                   debugstr_w(feature->Feature), debugstr_w(cls->clsid));
796             continue;
797         }
798 
799         if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
800         {
801             TRACE("COM server not provided, skipping class %s\n", debugstr_w(cls->clsid));
802             continue;
803         }
804         TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls);
805 
806         cls->action = INSTALLSTATE_LOCAL;
807 
808         RegCreateKeyW( hkey, cls->clsid, &hkey2 );
809 
810         if (cls->Description)
811             msi_reg_set_val_str( hkey2, NULL, cls->Description );
812 
813         RegCreateKeyW( hkey2, cls->Context, &hkey3 );
814 
815         /*
816          * FIXME: Implement install on demand (advertised components).
817          *
818          * ole32.dll should call msi.MsiProvideComponentFromDescriptor()
819          *  when it needs an InProcServer that doesn't exist.
820          * The component advertise string should be in the "InProcServer" value.
821          */
822         size = lstrlenW( file->TargetPath )+1;
823         if (cls->Argument)
824             size += lstrlenW(cls->Argument)+1;
825 
826         argument = msi_alloc( size * sizeof(WCHAR) );
827         lstrcpyW( argument, file->TargetPath );
828 
829         if (cls->Argument)
830         {
831             lstrcatW( argument, szSpace );
832             lstrcatW( argument, cls->Argument );
833         }
834 
835         msi_reg_set_val_str( hkey3, NULL, argument );
836         msi_free(argument);
837 
838         RegCloseKey(hkey3);
839 
840         if (cls->ProgID || cls->ProgIDText)
841         {
842             LPCWSTR progid;
843 
844             if (cls->ProgID)
845                 progid = cls->ProgID->ProgID;
846             else
847                 progid = cls->ProgIDText;
848 
849             msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid );
850 
851             if (cls->ProgID && cls->ProgID->VersionInd)
852             {
853                 msi_reg_set_subkey_val( hkey2, szVIProgID, NULL,
854                                         cls->ProgID->VersionInd->ProgID );
855             }
856         }
857 
858         if (cls->AppID)
859         {
860             MSIAPPID *appid = cls->AppID;
861             msi_reg_set_val_str( hkey2, szAppID, appid->AppID );
862             register_appid( appid, cls->Description );
863         }
864 
865         if (cls->IconPath)
866             msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath );
867 
868         if (cls->DefInprocHandler)
869             msi_reg_set_subkey_val( hkey2, szInprocHandler, NULL, cls->DefInprocHandler );
870 
871         if (cls->DefInprocHandler32)
872             msi_reg_set_subkey_val( hkey2, szInprocHandler32, NULL, cls->DefInprocHandler32 );
873 
874         RegCloseKey(hkey2);
875 
876         /* if there is a FileTypeMask, register the FileType */
877         if (cls->FileTypeMask)
878         {
879             LPWSTR ptr, ptr2;
880             LPWSTR keyname;
881             INT index = 0;
882             ptr = cls->FileTypeMask;
883             while (ptr && *ptr)
884             {
885                 ptr2 = strchrW(ptr,';');
886                 if (ptr2)
887                     *ptr2 = 0;
888                 keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR));
889                 sprintfW( keyname, szFileType_fmt, cls->clsid, index );
890 
891                 msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr );
892                 msi_free(keyname);
893 
894                 if (ptr2)
895                     ptr = ptr2+1;
896                 else
897                     ptr = NULL;
898 
899                 index ++;
900             }
901         }
902 
903         uirow = MSI_CreateRecord(1);
904         MSI_RecordSetStringW( uirow, 1, cls->clsid );
905         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
906         msiobj_release(&uirow->hdr);
907     }
908     RegCloseKey(hkey);
909     return ERROR_SUCCESS;
910 }
911 
912 UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
913 {
914     static const WCHAR szFileType[] = {'F','i','l','e','T','y','p','e','\\',0};
915     const WCHAR *keypath;
916     MSIRECORD *uirow;
917     MSICLASS *cls;
918     HKEY hkey, hkey2;
919     UINT r;
920 
921     r = load_classes_and_such( package );
922     if (r != ERROR_SUCCESS)
923         return r;
924 
925     if (is_64bit && package->platform == PLATFORM_INTEL)
926         keypath = szWow6432NodeCLSID;
927     else
928         keypath = szCLSID;
929 
930     if (RegOpenKeyW( HKEY_CLASSES_ROOT, keypath, &hkey ) != ERROR_SUCCESS)
931         return ERROR_SUCCESS;
932 
933     LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
934     {
935         MSIFEATURE *feature;
936         MSICOMPONENT *comp;
937         LPWSTR filetype;
938         LONG res;
939 
940         comp = cls->Component;
941         if (!comp)
942             continue;
943 
944         if (!comp->Enabled)
945         {
946             TRACE("component is disabled\n");
947             continue;
948         }
949 
950         feature = cls->Feature;
951         if (!feature)
952             continue;
953 
954         feature->Action = msi_get_feature_action( package, feature );
955         if (feature->Action != INSTALLSTATE_ABSENT)
956         {
957             TRACE("feature %s not scheduled for removal, skipping unregistration of class %s\n",
958                   debugstr_w(feature->Feature), debugstr_w(cls->clsid));
959             continue;
960         }
961         TRACE("Unregistering class %s (%p)\n", debugstr_w(cls->clsid), cls);
962 
963         cls->action = INSTALLSTATE_ABSENT;
964 
965         res = RegDeleteTreeW( hkey, cls->clsid );
966         if (res != ERROR_SUCCESS)
967             WARN("Failed to delete class key %d\n", res);
968 
969         if (cls->AppID)
970         {
971             res = RegOpenKeyW( HKEY_CLASSES_ROOT, szAppID, &hkey2 );
972             if (res == ERROR_SUCCESS)
973             {
974                 res = RegDeleteKeyW( hkey2, cls->AppID->AppID );
975                 if (res != ERROR_SUCCESS)
976                     WARN("Failed to delete appid key %d\n", res);
977                 RegCloseKey( hkey2 );
978             }
979         }
980         if (cls->FileTypeMask)
981         {
982             filetype = msi_alloc( (strlenW( szFileType ) + strlenW( cls->clsid ) + 1) * sizeof(WCHAR) );
983             if (filetype)
984             {
985                 strcpyW( filetype, szFileType );
986                 strcatW( filetype, cls->clsid );
987                 res = RegDeleteTreeW( HKEY_CLASSES_ROOT, filetype );
988                 msi_free( filetype );
989 
990                 if (res != ERROR_SUCCESS)
991                     WARN("Failed to delete file type %d\n", res);
992             }
993         }
994 
995         uirow = MSI_CreateRecord( 1 );
996         MSI_RecordSetStringW( uirow, 1, cls->clsid );
997         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
998         msiobj_release( &uirow->hdr );
999     }
1000     RegCloseKey( hkey );
1001     return ERROR_SUCCESS;
1002 }
1003 
1004 static LPCWSTR get_clsid_of_progid( const MSIPROGID *progid )
1005 {
1006     while (progid)
1007     {
1008         if (progid->Class)
1009             return progid->Class->clsid;
1010         if (progid->Parent == progid)
1011             break;
1012         progid = progid->Parent;
1013     }
1014     return NULL;
1015 }
1016 
1017 static UINT register_progid( const MSIPROGID* progid )
1018 {
1019     static const WCHAR szCurVer[] = {'C','u','r','V','e','r',0};
1020     HKEY hkey = 0;
1021     UINT rc;
1022 
1023     rc = RegCreateKeyW( HKEY_CLASSES_ROOT, progid->ProgID, &hkey );
1024     if (rc == ERROR_SUCCESS)
1025     {
1026         LPCWSTR clsid = get_clsid_of_progid( progid );
1027 
1028         if (clsid)
1029             msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid );
1030         else
1031             TRACE("%s has no class\n", debugstr_w( progid->ProgID ) );
1032 
1033         if (progid->Description)
1034             msi_reg_set_val_str( hkey, NULL, progid->Description );
1035 
1036         if (progid->IconPath)
1037             msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath );
1038 
1039         /* write out the current version */
1040         if (progid->CurVer)
1041             msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID );
1042 
1043         RegCloseKey(hkey);
1044     }
1045     else
1046         ERR("failed to create key %s\n", debugstr_w( progid->ProgID ) );
1047 
1048     return rc;
1049 }
1050 
1051 static const MSICLASS *get_progid_class( const MSIPROGID *progid )
1052 {
1053     while (progid)
1054     {
1055         if (progid->Parent) progid = progid->Parent;
1056         if (progid->Class) return progid->Class;
1057         if (!progid->Parent || progid->Parent == progid) break;
1058     }
1059     return NULL;
1060 }
1061 
1062 static BOOL has_class_installed( const MSIPROGID *progid )
1063 {
1064     const MSICLASS *class = get_progid_class( progid );
1065     if (!class || !class->ProgID) return FALSE;
1066     return (class->action == INSTALLSTATE_LOCAL);
1067 }
1068 
1069 static BOOL has_one_extension_installed( const MSIPACKAGE *package, const MSIPROGID *progid )
1070 {
1071     const MSIEXTENSION *extension;
1072     LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry )
1073     {
1074         if (extension->ProgID == progid && !list_empty( &extension->verbs ) &&
1075             extension->action == INSTALLSTATE_LOCAL) return TRUE;
1076     }
1077     return FALSE;
1078 }
1079 
1080 UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
1081 {
1082     MSIPROGID *progid;
1083     MSIRECORD *uirow;
1084     UINT r;
1085 
1086     r = load_classes_and_such( package );
1087     if (r != ERROR_SUCCESS)
1088         return r;
1089 
1090     LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
1091     {
1092         if (!has_class_installed( progid ) && !has_one_extension_installed( package, progid ))
1093         {
1094             TRACE("progid %s not scheduled to be installed\n", debugstr_w(progid->ProgID));
1095             continue;
1096         }
1097         TRACE("Registering progid %s\n", debugstr_w(progid->ProgID));
1098 
1099         register_progid( progid );
1100 
1101         uirow = MSI_CreateRecord( 1 );
1102         MSI_RecordSetStringW( uirow, 1, progid->ProgID );
1103         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1104         msiobj_release( &uirow->hdr );
1105     }
1106     return ERROR_SUCCESS;
1107 }
1108 
1109 static BOOL has_class_removed( const MSIPROGID *progid )
1110 {
1111     const MSICLASS *class = get_progid_class( progid );
1112     if (!class || !class->ProgID) return FALSE;
1113     return (class->action == INSTALLSTATE_ABSENT);
1114 }
1115 
1116 static BOOL has_extensions( const MSIPACKAGE *package, const MSIPROGID *progid )
1117 {
1118     const MSIEXTENSION *extension;
1119     LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry )
1120     {
1121         if (extension->ProgID == progid && !list_empty( &extension->verbs )) return TRUE;
1122     }
1123     return FALSE;
1124 }
1125 
1126 static BOOL has_all_extensions_removed( const MSIPACKAGE *package, const MSIPROGID *progid )
1127 {
1128     BOOL ret = FALSE;
1129     const MSIEXTENSION *extension;
1130     LIST_FOR_EACH_ENTRY( extension, &package->extensions, MSIEXTENSION, entry )
1131     {
1132         if (extension->ProgID == progid && !list_empty( &extension->verbs ) &&
1133             extension->action == INSTALLSTATE_ABSENT) ret = TRUE;
1134         else ret = FALSE;
1135     }
1136     return ret;
1137 }
1138 
1139 UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
1140 {
1141     MSIPROGID *progid;
1142     MSIRECORD *uirow;
1143     LONG res;
1144     UINT r;
1145 
1146     r = load_classes_and_such( package );
1147     if (r != ERROR_SUCCESS)
1148         return r;
1149 
1150     LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
1151     {
1152         if (!has_class_removed( progid ) ||
1153             (has_extensions( package, progid ) && !has_all_extensions_removed( package, progid )))
1154         {
1155             TRACE("progid %s not scheduled to be removed\n", debugstr_w(progid->ProgID));
1156             continue;
1157         }
1158         TRACE("Unregistering progid %s\n", debugstr_w(progid->ProgID));
1159 
1160         res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid->ProgID );
1161         if (res != ERROR_SUCCESS)
1162             TRACE("Failed to delete progid key %d\n", res);
1163 
1164         uirow = MSI_CreateRecord( 1 );
1165         MSI_RecordSetStringW( uirow, 1, progid->ProgID );
1166         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1167         msiobj_release( &uirow->hdr );
1168     }
1169     return ERROR_SUCCESS;
1170 }
1171 
1172 static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid,
1173                 MSICOMPONENT* component, const MSIEXTENSION* extension,
1174                 MSIVERB* verb, INT* Sequence )
1175 {
1176     LPWSTR keyname;
1177     HKEY key;
1178     static const WCHAR szShell[] = {'s','h','e','l','l',0};
1179     static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0};
1180     static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0};
1181     static const WCHAR fmt2[] = {'\"','%','s','\"',0};
1182     LPWSTR command;
1183     DWORD size;
1184     LPWSTR advertise;
1185 
1186     keyname = msi_build_directory_name(4, progid, szShell, verb->Verb, szCommand);
1187 
1188     TRACE("Making Key %s\n",debugstr_w(keyname));
1189     RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
1190     size = strlenW(component->FullKeypath);
1191     if (verb->Argument)
1192         size += strlenW(verb->Argument);
1193      size += 4;
1194 
1195      command = msi_alloc(size * sizeof (WCHAR));
1196      if (verb->Argument)
1197         sprintfW(command, fmt, component->FullKeypath, verb->Argument);
1198      else
1199         sprintfW(command, fmt2, component->FullKeypath);
1200 
1201      msi_reg_set_val_str( key, NULL, command );
1202      msi_free(command);
1203 
1204      advertise = msi_create_component_advertise_string(package, component,
1205                                                        extension->Feature->Feature);
1206      size = strlenW(advertise);
1207 
1208      if (verb->Argument)
1209          size += strlenW(verb->Argument);
1210      size += 4;
1211 
1212      command = msi_alloc_zero(size * sizeof (WCHAR));
1213 
1214      strcpyW(command,advertise);
1215      if (verb->Argument)
1216      {
1217          strcatW(command,szSpace);
1218          strcatW(command,verb->Argument);
1219      }
1220 
1221      msi_reg_set_val_multi_str( key, szCommand, command );
1222 
1223      RegCloseKey(key);
1224      msi_free(keyname);
1225      msi_free(advertise);
1226      msi_free(command);
1227 
1228      if (verb->Command)
1229      {
1230         keyname = msi_build_directory_name( 3, progid, szShell, verb->Verb );
1231         msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command );
1232         msi_free(keyname);
1233      }
1234 
1235      if (verb->Sequence != MSI_NULL_INTEGER)
1236      {
1237         if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence)
1238         {
1239             *Sequence = verb->Sequence;
1240             keyname = msi_build_directory_name( 2, progid, szShell );
1241             msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb );
1242             msi_free(keyname);
1243         }
1244     }
1245     return ERROR_SUCCESS;
1246 }
1247 
1248 UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package)
1249 {
1250     static const WCHAR szContentType[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0};
1251     HKEY hkey = NULL;
1252     MSIEXTENSION *ext;
1253     MSIRECORD *uirow;
1254     BOOL install_on_demand = TRUE;
1255     LONG res;
1256     UINT r;
1257 
1258     r = load_classes_and_such( package );
1259     if (r != ERROR_SUCCESS)
1260         return r;
1261 
1262     /* We need to set install_on_demand based on if the shell handles advertised
1263      * shortcuts and the like. Because Mike McCormack is working on this i am
1264      * going to default to TRUE
1265      */
1266 
1267     LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
1268     {
1269         LPWSTR extension;
1270         MSIFEATURE *feature;
1271 
1272         if (!ext->Component)
1273             continue;
1274 
1275         if (!ext->Component->Enabled)
1276         {
1277             TRACE("component is disabled\n");
1278             continue;
1279         }
1280 
1281         feature = ext->Feature;
1282         if (!feature)
1283             continue;
1284 
1285         /*
1286          * yes. MSDN says that these are based on _Feature_ not on
1287          * Component.  So verify the feature is to be installed
1288          */
1289         feature->Action = msi_get_feature_action( package, feature );
1290         if (feature->Action != INSTALLSTATE_LOCAL &&
1291             !(install_on_demand && feature->Action == INSTALLSTATE_ADVERTISED))
1292         {
1293             TRACE("feature %s not scheduled for installation, skipping registration of extension %s\n",
1294                   debugstr_w(feature->Feature), debugstr_w(ext->Extension));
1295             continue;
1296         }
1297         TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext);
1298 
1299         ext->action = INSTALLSTATE_LOCAL;
1300 
1301         extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) );
1302         if (extension)
1303         {
1304             extension[0] = '.';
1305             strcpyW( extension + 1, ext->Extension );
1306             res = RegCreateKeyW( HKEY_CLASSES_ROOT, extension, &hkey );
1307             msi_free( extension );
1308             if (res != ERROR_SUCCESS)
1309                 WARN("Failed to create extension key %d\n", res);
1310         }
1311 
1312         if (ext->Mime)
1313             msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType );
1314 
1315         if (ext->ProgID || ext->ProgIDText)
1316         {
1317             static const WCHAR szSN[] =
1318                 {'\\','S','h','e','l','l','N','e','w',0};
1319             HKEY hkey2;
1320             LPWSTR newkey;
1321             LPCWSTR progid;
1322             MSIVERB *verb;
1323             INT Sequence = MSI_NULL_INTEGER;
1324 
1325             if (ext->ProgID)
1326                 progid = ext->ProgID->ProgID;
1327             else
1328                 progid = ext->ProgIDText;
1329 
1330             msi_reg_set_val_str( hkey, NULL, progid );
1331 
1332             newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR));
1333 
1334             strcpyW(newkey,progid);
1335             strcatW(newkey,szSN);
1336             RegCreateKeyW(hkey,newkey,&hkey2);
1337             RegCloseKey(hkey2);
1338 
1339             msi_free(newkey);
1340 
1341             /* do all the verbs */
1342             LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry )
1343             {
1344                 register_verb( package, progid, ext->Component,
1345                                ext, verb, &Sequence);
1346             }
1347         }
1348 
1349         RegCloseKey(hkey);
1350 
1351         uirow = MSI_CreateRecord(1);
1352         MSI_RecordSetStringW( uirow, 1, ext->Extension );
1353         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1354         msiobj_release(&uirow->hdr);
1355     }
1356     return ERROR_SUCCESS;
1357 }
1358 
1359 UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
1360 {
1361     MSIEXTENSION *ext;
1362     MSIRECORD *uirow;
1363     LONG res;
1364     UINT r;
1365 
1366     r = load_classes_and_such( package );
1367     if (r != ERROR_SUCCESS)
1368         return r;
1369 
1370     LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
1371     {
1372         LPWSTR extension;
1373         MSIFEATURE *feature;
1374 
1375         if (!ext->Component)
1376             continue;
1377 
1378         if (!ext->Component->Enabled)
1379         {
1380             TRACE("component is disabled\n");
1381             continue;
1382         }
1383 
1384         feature = ext->Feature;
1385         if (!feature)
1386             continue;
1387 
1388         feature->Action = msi_get_feature_action( package, feature );
1389         if (feature->Action != INSTALLSTATE_ABSENT)
1390         {
1391             TRACE("feature %s not scheduled for removal, skipping unregistration of extension %s\n",
1392                   debugstr_w(feature->Feature), debugstr_w(ext->Extension));
1393             continue;
1394         }
1395         TRACE("Unregistering extension %s\n", debugstr_w(ext->Extension));
1396 
1397         ext->action = INSTALLSTATE_ABSENT;
1398 
1399         extension = msi_alloc( (strlenW( ext->Extension ) + 2) * sizeof(WCHAR) );
1400         if (extension)
1401         {
1402             extension[0] = '.';
1403             strcpyW( extension + 1, ext->Extension );
1404             res = RegDeleteTreeW( HKEY_CLASSES_ROOT, extension );
1405             msi_free( extension );
1406             if (res != ERROR_SUCCESS)
1407                 WARN("Failed to delete extension key %d\n", res);
1408         }
1409 
1410         if (ext->ProgID || ext->ProgIDText)
1411         {
1412             static const WCHAR shellW[] = {'\\','s','h','e','l','l',0};
1413             LPCWSTR progid;
1414             LPWSTR progid_shell;
1415 
1416             if (ext->ProgID)
1417                 progid = ext->ProgID->ProgID;
1418             else
1419                 progid = ext->ProgIDText;
1420 
1421             progid_shell = msi_alloc( (strlenW( progid ) + strlenW( shellW ) + 1) * sizeof(WCHAR) );
1422             if (progid_shell)
1423             {
1424                 strcpyW( progid_shell, progid );
1425                 strcatW( progid_shell, shellW );
1426                 res = RegDeleteTreeW( HKEY_CLASSES_ROOT, progid_shell );
1427                 msi_free( progid_shell );
1428                 if (res != ERROR_SUCCESS)
1429                     WARN("Failed to delete shell key %d\n", res);
1430                 RegDeleteKeyW( HKEY_CLASSES_ROOT, progid );
1431             }
1432         }
1433 
1434         uirow = MSI_CreateRecord( 1 );
1435         MSI_RecordSetStringW( uirow, 1, ext->Extension );
1436         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1437         msiobj_release( &uirow->hdr );
1438     }
1439     return ERROR_SUCCESS;
1440 }
1441 
1442 UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package)
1443 {
1444     static const WCHAR szExtension[] = {'E','x','t','e','n','s','i','o','n',0};
1445     MSIRECORD *uirow;
1446     MSIMIME *mt;
1447     UINT r;
1448 
1449     r = load_classes_and_such( package );
1450     if (r != ERROR_SUCCESS)
1451         return r;
1452 
1453     LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry )
1454     {
1455         LPWSTR extension = NULL, key;
1456 
1457         /*
1458          * check if the MIME is to be installed. Either as requested by an
1459          * extension or Class
1460          */
1461         if ((!mt->Class || mt->Class->action != INSTALLSTATE_LOCAL) &&
1462             (!mt->Extension || mt->Extension->action != INSTALLSTATE_LOCAL))
1463         {
1464             TRACE("MIME %s not scheduled to be installed\n", debugstr_w(mt->ContentType));
1465             continue;
1466         }
1467 
1468         TRACE("Registering MIME type %s\n", debugstr_w(mt->ContentType));
1469 
1470         if (mt->Extension) extension = msi_alloc( (strlenW( mt->Extension->Extension ) + 2) * sizeof(WCHAR) );
1471         key = msi_alloc( (strlenW( mt->ContentType ) + strlenW( szMIMEDatabase ) + 1) * sizeof(WCHAR) );
1472 
1473         if (extension && key)
1474         {
1475             extension[0] = '.';
1476             strcpyW( extension + 1, mt->Extension->Extension );
1477 
1478             strcpyW( key, szMIMEDatabase );
1479             strcatW( key, mt->ContentType );
1480             msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExtension, extension );
1481 
1482             if (mt->clsid)
1483                 msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szCLSID, mt->clsid );
1484         }
1485         msi_free( extension );
1486         msi_free( key );
1487 
1488         uirow = MSI_CreateRecord( 2 );
1489         MSI_RecordSetStringW( uirow, 1, mt->ContentType );
1490         MSI_RecordSetStringW( uirow, 2, mt->suffix );
1491         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1492         msiobj_release( &uirow->hdr );
1493     }
1494     return ERROR_SUCCESS;
1495 }
1496 
1497 UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
1498 {
1499     MSIRECORD *uirow;
1500     MSIMIME *mime;
1501     UINT r;
1502 
1503     r = load_classes_and_such( package );
1504     if (r != ERROR_SUCCESS)
1505         return r;
1506 
1507     LIST_FOR_EACH_ENTRY( mime, &package->mimes, MSIMIME, entry )
1508     {
1509         LONG res;
1510         LPWSTR mime_key;
1511 
1512         if ((!mime->Class || mime->Class->action != INSTALLSTATE_ABSENT) &&
1513             (!mime->Extension || mime->Extension->action != INSTALLSTATE_ABSENT))
1514         {
1515             TRACE("MIME %s not scheduled to be removed\n", debugstr_w(mime->ContentType));
1516             continue;
1517         }
1518 
1519         TRACE("Unregistering MIME type %s\n", debugstr_w(mime->ContentType));
1520 
1521         mime_key = msi_alloc( (strlenW( szMIMEDatabase ) + strlenW( mime->ContentType ) + 1) * sizeof(WCHAR) );
1522         if (mime_key)
1523         {
1524             strcpyW( mime_key, szMIMEDatabase );
1525             strcatW( mime_key, mime->ContentType );
1526             res = RegDeleteKeyW( HKEY_CLASSES_ROOT, mime_key );
1527             if (res != ERROR_SUCCESS)
1528                 WARN("Failed to delete MIME key %d\n", res);
1529             msi_free( mime_key );
1530         }
1531 
1532         uirow = MSI_CreateRecord( 2 );
1533         MSI_RecordSetStringW( uirow, 1, mime->ContentType );
1534         MSI_RecordSetStringW( uirow, 2, mime->suffix );
1535         MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1536         msiobj_release( &uirow->hdr );
1537     }
1538     return ERROR_SUCCESS;
1539 }
1540