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