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