1 // FileFolderPluginOpen.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "resource.h"
6 
7 #include "../../../Windows/FileName.h"
8 #include "../../../Windows/Thread.h"
9 
10 #include "../Agent/Agent.h"
11 #include "../GUI/ExtractRes.h"
12 
13 #include "FileFolderPluginOpen.h"
14 #include "FormatUtils.h"
15 #include "LangUtils.h"
16 #include "OpenCallback.h"
17 #include "PluginLoader.h"
18 #include "PropertyName.h"
19 #include "RegistryPlugins.h"
20 
21 using namespace NWindows;
22 
23 struct CThreadArchiveOpen
24 {
25   UString Path;
26   UString ArcFormat;
27   CMyComPtr<IInStream> InStream;
28   CMyComPtr<IFolderManager> FolderManager;
29   CMyComPtr<IProgress> OpenCallback;
30   COpenArchiveCallback *OpenCallbackSpec;
31 
32   CMyComPtr<IFolderFolder> Folder;
33   HRESULT Result;
34 
ProcessCThreadArchiveOpen35   void Process()
36   {
37     try
38     {
39       CProgressCloser closer(OpenCallbackSpec->ProgressDialog);
40       Result = FolderManager->OpenFolderFile(InStream, Path, ArcFormat, &Folder, OpenCallback);
41     }
42     catch(...) { Result = E_FAIL; }
43   }
44 
MyThreadFunctionCThreadArchiveOpen45   static THREAD_FUNC_DECL MyThreadFunction(void *param)
46   {
47     ((CThreadArchiveOpen *)param)->Process();
48     return 0;
49   }
50 };
51 
52 /*
53 static int FindPlugin(const CObjectVector<CPluginInfo> &plugins, const UString &pluginName)
54 {
55   for (int i = 0; i < plugins.Size(); i++)
56     if (plugins[i].Name.CompareNoCase(pluginName) == 0)
57       return i;
58   return -1;
59 }
60 */
61 
SplitNameToPureNameAndExtension(const FString & fullName,FString & pureName,FString & extensionDelimiter,FString & extension)62 static void SplitNameToPureNameAndExtension(const FString &fullName,
63     FString &pureName, FString &extensionDelimiter, FString &extension)
64 {
65   int index = fullName.ReverseFind_Dot();
66   if (index < 0)
67   {
68     pureName = fullName;
69     extensionDelimiter.Empty();
70     extension.Empty();
71   }
72   else
73   {
74     pureName.SetFrom(fullName, index);
75     extensionDelimiter = '.';
76     extension = fullName.Ptr((unsigned)index + 1);
77   }
78 }
79 
80 
81 struct CArcLevelInfo
82 {
83   UString Error;
84   UString Path;
85   UString Type;
86   UString ErrorType;
87   UString ErrorFlags;
88 };
89 
90 
91 struct CArcLevelsInfo
92 {
93   CObjectVector<CArcLevelInfo> Levels; // LastLevel Is NON-OPEN
94 };
95 
96 
97 UString GetOpenArcErrorMessage(UInt32 errorFlags);
98 
99 
GetFolderLevels(CMyComPtr<IFolderFolder> & folder,CArcLevelsInfo & levels)100 static void GetFolderLevels(CMyComPtr<IFolderFolder> &folder, CArcLevelsInfo &levels)
101 {
102   levels.Levels.Clear();
103 
104   CMyComPtr<IGetFolderArcProps> getFolderArcProps;
105   folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
106 
107   if (!getFolderArcProps)
108     return;
109   CMyComPtr<IFolderArcProps> arcProps;
110   getFolderArcProps->GetFolderArcProps(&arcProps);
111   if (!arcProps)
112     return;
113 
114   UInt32 numLevels;
115   if (arcProps->GetArcNumLevels(&numLevels) != S_OK)
116     numLevels = 0;
117 
118   for (UInt32 level = 0; level <= numLevels; level++)
119   {
120     const PROPID propIDs[] = { kpidError, kpidPath, kpidType, kpidErrorType };
121 
122     CArcLevelInfo lev;
123 
124     for (Int32 i = 0; i < 4; i++)
125     {
126       CMyComBSTR name;
127       NCOM::CPropVariant prop;
128       if (arcProps->GetArcProp(level, propIDs[i], &prop) != S_OK)
129         continue;
130       if (prop.vt != VT_EMPTY)
131       {
132         UString *s = NULL;
133         switch (propIDs[i])
134         {
135           case kpidError: s = &lev.Error; break;
136           case kpidPath: s = &lev.Path; break;
137           case kpidType: s = &lev.Type; break;
138           case kpidErrorType: s = &lev.ErrorType; break;
139         }
140         *s = (prop.vt == VT_BSTR) ? prop.bstrVal : L"?";
141       }
142     }
143 
144     {
145       NCOM::CPropVariant prop;
146       if (arcProps->GetArcProp(level, kpidErrorFlags, &prop) == S_OK)
147       {
148         UInt32 flags = GetOpenArcErrorFlags(prop);
149         if (flags != 0)
150           lev.ErrorFlags = GetOpenArcErrorMessage(flags);
151       }
152     }
153 
154     levels.Levels.Add(lev);
155   }
156 }
157 
GetBracedType(const wchar_t * type)158 static UString GetBracedType(const wchar_t *type)
159 {
160   UString s ('[');
161   s += type;
162   s += ']';
163   return s;
164 }
165 
GetFolderError(CMyComPtr<IFolderFolder> & folder,UString & open_Errors,UString & nonOpen_Errors)166 static void GetFolderError(CMyComPtr<IFolderFolder> &folder, UString &open_Errors, UString &nonOpen_Errors)
167 {
168   CArcLevelsInfo levs;
169   GetFolderLevels(folder, levs);
170   open_Errors.Empty();
171   nonOpen_Errors.Empty();
172 
173   FOR_VECTOR (i, levs.Levels)
174   {
175     bool isNonOpenLevel = (i == 0);
176     const CArcLevelInfo &lev = levs.Levels[levs.Levels.Size() - 1 - i];
177 
178     UString m;
179 
180     if (!lev.ErrorType.IsEmpty())
181     {
182       m = MyFormatNew(IDS_CANT_OPEN_AS_TYPE, GetBracedType(lev.ErrorType));
183       if (!isNonOpenLevel)
184       {
185         m.Add_LF();
186         m += MyFormatNew(IDS_IS_OPEN_AS_TYPE, GetBracedType(lev.Type));
187       }
188     }
189 
190     if (!lev.Error.IsEmpty())
191     {
192       if (!m.IsEmpty())
193         m.Add_LF();
194       m += GetBracedType(lev.Type);
195       m += " : ";
196       m += GetNameOfProperty(kpidError, L"Error");
197       m += " : ";
198       m += lev.Error;
199     }
200 
201     if (!lev.ErrorFlags.IsEmpty())
202     {
203       if (!m.IsEmpty())
204         m.Add_LF();
205       m += GetNameOfProperty(kpidErrorFlags, L"Errors");
206       m += ": ";
207       m += lev.ErrorFlags;
208     }
209 
210     if (!m.IsEmpty())
211     {
212       if (isNonOpenLevel)
213       {
214         UString &s = nonOpen_Errors;
215         s += lev.Path;
216         s.Add_LF();
217         s += m;
218       }
219       else
220       {
221         UString &s = open_Errors;
222         if (!s.IsEmpty())
223           s += "--------------------\n";
224         s += lev.Path;
225         s.Add_LF();
226         s += m;
227       }
228     }
229   }
230 }
231 
232 
OpenFileFolderPlugin(IInStream * inStream,const FString & path,const UString & arcFormat,HWND parentWindow)233 HRESULT CFfpOpen::OpenFileFolderPlugin(IInStream *inStream,
234     const FString &path, const UString &arcFormat, HWND parentWindow)
235 {
236   CObjectVector<CPluginInfo> plugins;
237   ReadFileFolderPluginInfoList(plugins);
238 
239   FString extension, name, pureName, dot;
240 
241   int slashPos = path.ReverseFind_PathSepar();
242   FString dirPrefix;
243   FString fileName;
244   if (slashPos >= 0)
245   {
246     dirPrefix.SetFrom(path, (unsigned)(slashPos + 1));
247     fileName = path.Ptr((unsigned)(slashPos + 1));
248   }
249   else
250     fileName = path;
251 
252   SplitNameToPureNameAndExtension(fileName, pureName, dot, extension);
253 
254   /*
255   if (!extension.IsEmpty())
256   {
257     CExtInfo extInfo;
258     if (ReadInternalAssociation(extension, extInfo))
259     {
260       for (int i = extInfo.Plugins.Size() - 1; i >= 0; i--)
261       {
262         int pluginIndex = FindPlugin(plugins, extInfo.Plugins[i]);
263         if (pluginIndex >= 0)
264         {
265           const CPluginInfo plugin = plugins[pluginIndex];
266           plugins.Delete(pluginIndex);
267           plugins.Insert(0, plugin);
268         }
269       }
270     }
271   }
272   */
273 
274   ErrorMessage.Empty();
275 
276   FOR_VECTOR (i, plugins)
277   {
278     const CPluginInfo &plugin = plugins[i];
279     if (!plugin.ClassIDDefined)
280       continue;
281     CPluginLibrary library;
282 
283     CThreadArchiveOpen t;
284 
285     if (plugin.FilePath.IsEmpty())
286       t.FolderManager = new CArchiveFolderManager;
287     else if (library.LoadAndCreateManager(plugin.FilePath, plugin.ClassID, &t.FolderManager) != S_OK)
288       continue;
289 
290     t.OpenCallbackSpec = new COpenArchiveCallback;
291     t.OpenCallback = t.OpenCallbackSpec;
292     t.OpenCallbackSpec->PasswordIsDefined = Encrypted;
293     t.OpenCallbackSpec->Password = Password;
294     t.OpenCallbackSpec->ParentWindow = parentWindow;
295 
296     if (inStream)
297       t.OpenCallbackSpec->SetSubArchiveName(fs2us(fileName));
298     else
299     {
300       RINOK(t.OpenCallbackSpec->LoadFileInfo2(dirPrefix, fileName));
301     }
302 
303     t.InStream = inStream;
304     t.Path = fs2us(path);
305     t.ArcFormat = arcFormat;
306 
307     const UString progressTitle = LangString(IDS_OPENNING);
308     {
309       CProgressDialog &pd = t.OpenCallbackSpec->ProgressDialog;
310       pd.MainWindow = parentWindow;
311       pd.MainTitle = "7-Zip"; // LangString(IDS_APP_TITLE);
312       pd.MainAddTitle = progressTitle + L' ';
313       pd.WaitMode = true;
314     }
315 
316     {
317       NWindows::CThread thread;
318       RINOK(thread.Create(CThreadArchiveOpen::MyThreadFunction, &t));
319       t.OpenCallbackSpec->StartProgressDialog(progressTitle, thread);
320     }
321 
322     if (t.Result != S_FALSE && t.Result != S_OK)
323       return t.Result;
324 
325     if (t.Folder)
326     {
327       UString open_Errors, nonOpen_Errors;
328       GetFolderError(t.Folder, open_Errors, nonOpen_Errors);
329       if (!nonOpen_Errors.IsEmpty())
330       {
331         ErrorMessage = nonOpen_Errors;
332         // if (t.Result != S_OK) return t.Result;
333         /* if there are good open leves, and non0open level,
334            we could force error as critical error and return error here
335            but it's better to allow to open such rachives */
336         // return S_FALSE;
337       }
338     }
339 
340     // if (openCallbackSpec->PasswordWasAsked)
341     {
342       Encrypted = t.OpenCallbackSpec->PasswordIsDefined;
343       Password = t.OpenCallbackSpec->Password;
344     }
345 
346     if (t.Result == S_OK)
347     {
348       Library.Attach(library.Detach());
349       // Folder.Attach(t.Folder.Detach());
350       Folder = t.Folder;
351     }
352 
353     return t.Result;
354   }
355 
356   return S_FALSE;
357 }
358