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