1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   PluginManager.cpp
6 
7   Leland Lucius
8 
9 *******************************************************************//*!
10 
11 \file PluginManager.cpp
12 \brief
13 
14 ************************************************************************//**
15 \class PluginManager
16 \brief PluginManager maintains a list of all plug ins.  That covers modules,
17 effects, generators, analysis-effects, commands.  It also has functions
18 for shared and private configs - which need to move out.
19 *****************************************************************************/
20 
21 
22 #include "PluginManager.h"
23 
24 
25 
26 #include <algorithm>
27 
28 #include <wx/log.h>
29 #include <wx/tokenzr.h>
30 
31 #include "ModuleInterface.h"
32 
33 #include "AudacityFileConfig.h"
34 #include "Internat.h" // for macro XO
35 #include "FileNames.h"
36 #include "MemoryX.h"
37 #include "ModuleManager.h"
38 #include "PlatformCompatibility.h"
39 #include "widgets/AudacityMessageBox.h"
40 
41 ///////////////////////////////////////////////////////////////////////////////
42 //
43 // Plugindescriptor
44 //
45 ///////////////////////////////////////////////////////////////////////////////
46 
PluginDescriptor()47 PluginDescriptor::PluginDescriptor()
48 {
49    mPluginType = PluginTypeNone;
50    mEnabled = false;
51    mValid = false;
52    mInstance = nullptr;
53 
54    mEffectType = EffectTypeNone;
55    mEffectInteractive = false;
56    mEffectDefault = false;
57    mEffectLegacy = false;
58    mEffectRealtime = false;
59    mEffectAutomatable = false;
60 }
61 
~PluginDescriptor()62 PluginDescriptor::~PluginDescriptor()
63 {
64 }
65 
66 PluginDescriptor &PluginDescriptor::operator =(PluginDescriptor &&) = default;
67 
IsInstantiated() const68 bool PluginDescriptor::IsInstantiated() const
69 {
70    return mInstance != nullptr;
71 }
72 
GetInstance()73 ComponentInterface *PluginDescriptor::GetInstance()
74 {
75    if (!mInstance)
76    {
77       if (GetPluginType() == PluginTypeModule)
78          mInstance = ModuleManager::Get().CreateProviderInstance(GetID(), GetPath());
79       else
80       {
81          muInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath());
82          mInstance = muInstance.get();
83       }
84    }
85 
86    return mInstance;
87 }
88 
SetInstance(std::unique_ptr<ComponentInterface> instance)89 void PluginDescriptor::SetInstance(std::unique_ptr<ComponentInterface> instance)
90 {
91    muInstance = std::move(instance);
92    mInstance = muInstance.get();
93 }
94 
GetPluginType() const95 PluginType PluginDescriptor::GetPluginType() const
96 {
97    return mPluginType;
98 }
99 
GetID() const100 const PluginID & PluginDescriptor::GetID() const
101 {
102    return mID;
103 }
104 
GetProviderID() const105 const PluginID & PluginDescriptor::GetProviderID() const
106 {
107    return mProviderID;
108 }
109 
GetPath() const110 const PluginPath & PluginDescriptor::GetPath() const
111 {
112    return mPath;
113 }
114 
GetSymbol() const115 const ComponentInterfaceSymbol & PluginDescriptor::GetSymbol() const
116 {
117    return mSymbol;
118 }
119 
GetUntranslatedVersion() const120 wxString PluginDescriptor::GetUntranslatedVersion() const
121 {
122    return mVersion;
123 }
124 
GetVendor() const125 wxString PluginDescriptor::GetVendor() const
126 {
127    return mVendor;
128 }
129 
IsEnabled() const130 bool PluginDescriptor::IsEnabled() const
131 {
132    return mEnabled;
133 }
134 
IsValid() const135 bool PluginDescriptor::IsValid() const
136 {
137    return mValid;
138 }
139 
SetPluginType(PluginType type)140 void PluginDescriptor::SetPluginType(PluginType type)
141 {
142    mPluginType = type;
143 }
144 
SetID(const PluginID & ID)145 void PluginDescriptor::SetID(const PluginID & ID)
146 {
147    mID = ID;
148 }
149 
SetProviderID(const PluginID & providerID)150 void PluginDescriptor::SetProviderID(const PluginID & providerID)
151 {
152    mProviderID = providerID;
153 }
154 
SetPath(const PluginPath & path)155 void PluginDescriptor::SetPath(const PluginPath & path)
156 {
157    mPath = path;
158 }
159 
SetSymbol(const ComponentInterfaceSymbol & symbol)160 void PluginDescriptor::SetSymbol(const ComponentInterfaceSymbol & symbol)
161 {
162    mSymbol = symbol;
163 }
164 
SetVersion(const wxString & version)165 void PluginDescriptor::SetVersion(const wxString & version)
166 {
167    mVersion = version;
168 }
169 
SetVendor(const wxString & vendor)170 void PluginDescriptor::SetVendor(const wxString & vendor)
171 {
172    mVendor = vendor;
173 }
174 
SetEnabled(bool enable)175 void PluginDescriptor::SetEnabled(bool enable)
176 {
177    mEnabled = enable;
178 }
179 
SetValid(bool valid)180 void PluginDescriptor::SetValid(bool valid)
181 {
182    mValid = valid;
183 }
184 
185 // Effects
186 
GetEffectFamily() const187 wxString PluginDescriptor::GetEffectFamily() const
188 {
189    return mEffectFamily;
190 }
191 
GetEffectType() const192 EffectType PluginDescriptor::GetEffectType() const
193 {
194    return mEffectType;
195 }
196 
IsEffectInteractive() const197 bool PluginDescriptor::IsEffectInteractive() const
198 {
199    return mEffectInteractive;
200 }
201 
IsEffectDefault() const202 bool PluginDescriptor::IsEffectDefault() const
203 {
204    return mEffectDefault;
205 }
206 
IsEffectLegacy() const207 bool PluginDescriptor::IsEffectLegacy() const
208 {
209    return mEffectLegacy;
210 }
211 
IsEffectRealtime() const212 bool PluginDescriptor::IsEffectRealtime() const
213 {
214    return mEffectRealtime;
215 }
216 
IsEffectAutomatable() const217 bool PluginDescriptor::IsEffectAutomatable() const
218 {
219    return mEffectAutomatable;
220 }
221 
SetEffectFamily(const wxString & family)222 void PluginDescriptor::SetEffectFamily(const wxString & family)
223 {
224    mEffectFamily = family;
225 }
226 
SetEffectType(EffectType type)227 void PluginDescriptor::SetEffectType(EffectType type)
228 {
229    mEffectType = type;
230 }
231 
SetEffectInteractive(bool interactive)232 void PluginDescriptor::SetEffectInteractive(bool interactive)
233 {
234    mEffectInteractive = interactive;
235 }
236 
SetEffectDefault(bool dflt)237 void PluginDescriptor::SetEffectDefault(bool dflt)
238 {
239    mEffectDefault = dflt;
240 }
241 
SetEffectLegacy(bool legacy)242 void PluginDescriptor::SetEffectLegacy(bool legacy)
243 {
244    mEffectLegacy = legacy;
245 }
246 
SetEffectRealtime(bool realtime)247 void PluginDescriptor::SetEffectRealtime(bool realtime)
248 {
249    mEffectRealtime = realtime;
250 }
251 
SetEffectAutomatable(bool automatable)252 void PluginDescriptor::SetEffectAutomatable(bool automatable)
253 {
254    mEffectAutomatable = automatable;
255 }
256 
257 // Importer
258 
GetImporterIdentifier() const259 const wxString & PluginDescriptor::GetImporterIdentifier() const
260 {
261    return mImporterIdentifier;
262 }
263 
SetImporterIdentifier(const wxString & identifier)264 void PluginDescriptor::SetImporterIdentifier(const wxString & identifier)
265 {
266    mImporterIdentifier = identifier;
267 }
268 
GetImporterExtensions() const269 const FileExtensions & PluginDescriptor::GetImporterExtensions()
270    const
271 {
272    return mImporterExtensions;
273 }
274 
SetImporterExtensions(FileExtensions extensions)275 void PluginDescriptor::SetImporterExtensions( FileExtensions extensions )
276 {
277    mImporterExtensions = std::move( extensions );
278 }
279 
280 ///////////////////////////////////////////////////////////////////////////////
281 //
282 // PluginManager
283 //
284 ///////////////////////////////////////////////////////////////////////////////
285 
286 // Registry has the list of plug ins
287 #define REGVERKEY wxString(wxT("/pluginregistryversion"))
288 #define REGVERCUR wxString(wxT("1.1"))
289 #define REGROOT wxString(wxT("/pluginregistry/"))
290 
291 // Settings has the values of the plug in settings.
292 #define SETVERKEY wxString(wxT("/pluginsettingsversion"))
293 #define SETVERCUR wxString(wxT("1.0"))
294 #define SETROOT wxString(wxT("/pluginsettings/"))
295 
296 #define KEY_ID                         wxT("ID")
297 #define KEY_PATH                       wxT("Path")
298 #define KEY_SYMBOL                     wxT("Symbol")
299 #define KEY_NAME                       wxT("Name")
300 #define KEY_VENDOR                     wxT("Vendor")
301 #define KEY_VERSION                    wxT("Version")
302 #define KEY_DESCRIPTION                wxT("Description")
303 #define KEY_LASTUPDATED                wxT("LastUpdated")
304 #define KEY_ENABLED                    wxT("Enabled")
305 #define KEY_VALID                      wxT("Valid")
306 #define KEY_PROVIDERID                 wxT("ProviderID")
307 #define KEY_EFFECTTYPE                 wxT("EffectType")
308 #define KEY_EFFECTFAMILY               wxT("EffectFamily")
309 #define KEY_EFFECTDEFAULT              wxT("EffectDefault")
310 #define KEY_EFFECTINTERACTIVE          wxT("EffectInteractive")
311 #define KEY_EFFECTREALTIME             wxT("EffectRealtime")
312 #define KEY_EFFECTAUTOMATABLE          wxT("EffectAutomatable")
313 #define KEY_EFFECTTYPE_NONE            wxT("None")
314 #define KEY_EFFECTTYPE_ANALYZE         wxT("Analyze")
315 #define KEY_EFFECTTYPE_GENERATE        wxT("Generate")
316 #define KEY_EFFECTTYPE_PROCESS         wxT("Process")
317 #define KEY_EFFECTTYPE_TOOL            wxT("Tool")
318 #define KEY_EFFECTTYPE_HIDDEN          wxT("Hidden")
319 #define KEY_IMPORTERIDENT              wxT("ImporterIdent")
320 //#define KEY_IMPORTERFILTER             wxT("ImporterFilter")
321 #define KEY_IMPORTEREXTENSIONS         wxT("ImporterExtensions")
322 
323 // ============================================================================
324 //
325 // PluginManagerInterface implementation
326 //
327 // ============================================================================
328 
DefaultRegistrationCallback(ModuleInterface * provider,ComponentInterface * pInterface)329 const PluginID &PluginManagerInterface::DefaultRegistrationCallback(
330    ModuleInterface *provider, ComponentInterface *pInterface )
331 {
332    EffectDefinitionInterface * pEInterface = dynamic_cast<EffectDefinitionInterface*>(pInterface);
333    if( pEInterface )
334       return PluginManager::Get().RegisterPlugin(provider, pEInterface, PluginTypeEffect);
335    ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
336    if( pCInterface )
337       return PluginManager::Get().RegisterPlugin(provider, pCInterface);
338    static wxString empty;
339    return empty;
340 }
341 
AudacityCommandRegistrationCallback(ModuleInterface * provider,ComponentInterface * pInterface)342 const PluginID &PluginManagerInterface::AudacityCommandRegistrationCallback(
343    ModuleInterface *provider, ComponentInterface *pInterface )
344 {
345    ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
346    if( pCInterface )
347       return PluginManager::Get().RegisterPlugin(provider, pCInterface);
348    static wxString empty;
349    return empty;
350 }
351 
GetPluginEnabledSetting(const PluginID & ID) const352 RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) const
353 {
354    auto pPlugin = GetPlugin( ID );
355    if ( pPlugin )
356       return GetPluginEnabledSetting( *pPlugin );
357    return {};
358 }
359 
GetPluginEnabledSetting(const PluginDescriptor & desc) const360 RegistryPath PluginManager::GetPluginEnabledSetting(
361    const PluginDescriptor &desc ) const
362 {
363    switch ( desc.GetPluginType() ) {
364       case PluginTypeModule: {
365          // Retrieve optional family symbol that was recorded in
366          // RegisterPlugin() for the module
367          auto family = desc.GetEffectFamily();
368          if ( family.empty() ) // as for built-in effect and command modules
369             return {};
370          else
371             return wxT('/') + family + wxT("/Enable");
372       }
373       case PluginTypeEffect:
374          // do NOT use GetEffectFamily() for this descriptor, but instead,
375          // delegate to the plugin descriptor of the provider, which may
376          // be different (may be empty)
377          return GetPluginEnabledSetting( desc.GetProviderID() );
378       default:
379          return {};
380    }
381 }
382 
IsPluginRegistered(const PluginPath & path,const TranslatableString * pName)383 bool PluginManager::IsPluginRegistered(
384    const PluginPath &path, const TranslatableString *pName)
385 {
386    for (auto &pair : mPlugins) {
387       if (auto &descriptor = pair.second; descriptor.GetPath() == path) {
388          if (pName)
389             descriptor.SetSymbol(
390                { descriptor.GetSymbol().Internal(), *pName });
391          return true;
392       }
393    }
394    return false;
395 }
396 
RegisterPlugin(ModuleInterface * module)397 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *module)
398 {
399    PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule);
400    plug.SetEffectFamily(module->GetOptionalFamilySymbol().Internal());
401 
402    plug.SetEnabled(true);
403    plug.SetValid(true);
404 
405    return plug.GetID();
406 }
407 
RegisterPlugin(ModuleInterface * provider,ComponentInterface * command)408 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, ComponentInterface *command)
409 {
410    PluginDescriptor & plug = CreatePlugin(GetID(command), command, (PluginType)PluginTypeAudacityCommand);
411 
412    plug.SetProviderID(PluginManager::GetID(provider));
413 
414    plug.SetEnabled(true);
415    plug.SetValid(true);
416 
417    return plug.GetID();
418 }
419 
RegisterPlugin(ModuleInterface * provider,EffectDefinitionInterface * effect,int type)420 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, EffectDefinitionInterface *effect, int type)
421 {
422    PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type);
423 
424    plug.SetProviderID(PluginManager::GetID(provider));
425 
426    plug.SetEffectType(effect->GetClassification());
427    plug.SetEffectFamily(effect->GetFamily().Internal());
428    plug.SetEffectInteractive(effect->IsInteractive());
429    plug.SetEffectDefault(effect->IsDefault());
430    plug.SetEffectRealtime(effect->SupportsRealtime());
431    plug.SetEffectAutomatable(effect->SupportsAutomation());
432 
433    plug.SetEnabled(true);
434    plug.SetValid(true);
435 
436    return plug.GetID();
437 }
438 
FindFilesInPathList(const wxString & pattern,const FilePaths & pathList,FilePaths & files,bool directories)439 void PluginManager::FindFilesInPathList(const wxString & pattern,
440                                         const FilePaths & pathList,
441                                         FilePaths & files,
442                                         bool directories)
443 {
444 
445    wxLogNull nolog;
446 
447    // Why bother...
448    if (pattern.empty())
449    {
450       return;
451    }
452 
453    // TODO:  We REALLY need to figure out the "Audacity" plug-in path(s)
454 
455    FilePaths paths;
456 
457    // Add the "per-user" plug-ins directory
458    {
459       const wxFileName &ff = FileNames::PlugInDir();
460       paths.push_back(ff.GetFullPath());
461    }
462 
463    // Add the "Audacity" plug-ins directory
464    wxFileName ff = PlatformCompatibility::GetExecutablePath();
465 #if defined(__WXMAC__)
466    // Path ends for example in "Audacity.app/Contents/MacOSX"
467    //ff.RemoveLastDir();
468    //ff.RemoveLastDir();
469    // just remove the MacOSX part.
470    ff.RemoveLastDir();
471 #endif
472    ff.AppendDir(wxT("plug-ins"));
473    paths.push_back(ff.GetPath());
474 
475    // Weed out duplicates
476    for (const auto &filePath : pathList)
477    {
478       ff = filePath;
479       const wxString path{ ff.GetFullPath() };
480       if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND)
481       {
482          paths.push_back(path);
483       }
484    }
485 
486    // Find all matching files in each path
487    for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
488    {
489       ff = paths[i] + wxFILE_SEP_PATH + pattern;
490       wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES);
491    }
492 
493    return;
494 }
495 
HasSharedConfigGroup(const PluginID & ID,const RegistryPath & group)496 bool PluginManager::HasSharedConfigGroup(const PluginID & ID, const RegistryPath & group)
497 {
498    return HasGroup(SharedGroup(ID, group));
499 }
500 
GetSharedConfigSubgroups(const PluginID & ID,const RegistryPath & group,RegistryPaths & subgroups)501 bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups)
502 {
503    return GetSubgroups(SharedGroup(ID, group), subgroups);
504 }
505 
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,wxString & value,const wxString & defval)506 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval)
507 {
508    return GetConfig(SharedKey(ID, group, key), value, defval);
509 }
510 
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,int & value,int defval)511 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval)
512 {
513    return GetConfig(SharedKey(ID, group, key), value, defval);
514 }
515 
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,bool & value,bool defval)516 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval)
517 {
518    return GetConfig(SharedKey(ID, group, key), value, defval);
519 }
520 
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,float & value,float defval)521 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval)
522 {
523    return GetConfig(SharedKey(ID, group, key), value, defval);
524 }
525 
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,double & value,double defval)526 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval)
527 {
528    return GetConfig(SharedKey(ID, group, key), value, defval);
529 }
530 
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const wxString & value)531 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value)
532 {
533    return SetConfig(SharedKey(ID, group, key), value);
534 }
535 
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const int & value)536 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value)
537 {
538    return SetConfig(SharedKey(ID, group, key), value);
539 }
540 
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const bool & value)541 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value)
542 {
543    return SetConfig(SharedKey(ID, group, key), value);
544 }
545 
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const float & value)546 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value)
547 {
548    return SetConfig(SharedKey(ID, group, key), value);
549 }
550 
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const double & value)551 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value)
552 {
553    return SetConfig(SharedKey(ID, group, key), value);
554 }
555 
RemoveSharedConfigSubgroup(const PluginID & ID,const RegistryPath & group)556 bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const RegistryPath & group)
557 {
558    bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group));
559    if (result)
560    {
561       GetSettings()->Flush();
562    }
563 
564    return result;
565 }
566 
RemoveSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)567 bool PluginManager::RemoveSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
568 {
569    bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key));
570    if (result)
571    {
572       GetSettings()->Flush();
573    }
574 
575    return result;
576 }
577 
HasPrivateConfigGroup(const PluginID & ID,const RegistryPath & group)578 bool PluginManager::HasPrivateConfigGroup(const PluginID & ID, const RegistryPath & group)
579 {
580    return HasGroup(PrivateGroup(ID, group));
581 }
582 
GetPrivateConfigSubgroups(const PluginID & ID,const RegistryPath & group,RegistryPaths & subgroups)583 bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups)
584 {
585    return GetSubgroups(PrivateGroup(ID, group), subgroups);
586 }
587 
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,wxString & value,const wxString & defval)588 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval)
589 {
590    return GetConfig(PrivateKey(ID, group, key), value, defval);
591 }
592 
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,int & value,int defval)593 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval)
594 {
595    return GetConfig(PrivateKey(ID, group, key), value, defval);
596 }
597 
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,bool & value,bool defval)598 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval)
599 {
600    return GetConfig(PrivateKey(ID, group, key), value, defval);
601 }
602 
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,float & value,float defval)603 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval)
604 {
605    return GetConfig(PrivateKey(ID, group, key), value, defval);
606 }
607 
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,double & value,double defval)608 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval)
609 {
610    return GetConfig(PrivateKey(ID, group, key), value, defval);
611 }
612 
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const wxString & value)613 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value)
614 {
615    return SetConfig(PrivateKey(ID, group, key), value);
616 }
617 
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const int & value)618 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value)
619 {
620    return SetConfig(PrivateKey(ID, group, key), value);
621 }
622 
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const bool & value)623 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value)
624 {
625    return SetConfig(PrivateKey(ID, group, key), value);
626 }
627 
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const float & value)628 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value)
629 {
630    return SetConfig(PrivateKey(ID, group, key), value);
631 }
632 
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const double & value)633 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value)
634 {
635    return SetConfig(PrivateKey(ID, group, key), value);
636 }
637 
RemovePrivateConfigSubgroup(const PluginID & ID,const RegistryPath & group)638 bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const RegistryPath & group)
639 {
640    bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group));
641    if (result)
642    {
643       GetSettings()->Flush();
644    }
645 
646    return result;
647 }
648 
RemovePrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)649 bool PluginManager::RemovePrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
650 {
651    bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key));
652    if (result)
653    {
654       GetSettings()->Flush();
655    }
656 
657    return result;
658 }
659 
660 // ============================================================================
661 //
662 // PluginManager
663 //
664 // ============================================================================
665 
666 // The one and only PluginManager
667 std::unique_ptr<PluginManager> PluginManager::mInstance{};
668 
669 // ----------------------------------------------------------------------------
670 // Creation/Destruction
671 // ----------------------------------------------------------------------------
672 
PluginManager()673 PluginManager::PluginManager()
674 {
675    mSettings = NULL;
676 }
677 
~PluginManager()678 PluginManager::~PluginManager()
679 {
680    // Ensure termination (harmless if already done)
681    Terminate();
682 }
683 
684 // ----------------------------------------------------------------------------
685 // PluginManager implementation
686 // ----------------------------------------------------------------------------
687 
688 // ============================================================================
689 //
690 // Return reference to singleton
691 //
692 // (Thread-safe...no active threading during construction or after destruction)
693 // ============================================================================
694 
Get()695 PluginManager & PluginManager::Get()
696 {
697    if (!mInstance)
698    {
699       mInstance.reset(safenew PluginManager);
700    }
701 
702    return *mInstance;
703 }
704 
Initialize()705 void PluginManager::Initialize()
706 {
707    // Always load the registry first
708    Load();
709 
710    // And force load of setting to verify it's accessible
711    GetSettings();
712 
713    auto &mm = ModuleManager::Get();
714    mm.DiscoverProviders();
715    for (const auto &[id, module] : mm.Providers()) {
716       RegisterPlugin(module.get());
717       // Allow the module to auto-register children
718       module->AutoRegisterPlugins(*this);
719    }
720 
721    // And finally check for updates
722 #ifndef EXPERIMENTAL_EFFECT_MANAGEMENT
723    CheckForUpdates();
724 #else
725    const bool kFast = true;
726    CheckForUpdates( kFast );
727 #endif
728 }
729 
Terminate()730 void PluginManager::Terminate()
731 {
732    // Get rid of all non-module plugins first
733    PluginMap::iterator iter = mPlugins.begin();
734    while (iter != mPlugins.end())
735    {
736       PluginDescriptor & plug = iter->second;
737       if (plug.GetPluginType() == PluginTypeEffect)
738       {
739          mPlugins.erase(iter++);
740          continue;
741       }
742 
743       ++iter;
744    }
745 
746    // Now get rid of the modules
747    iter = mPlugins.begin();
748    while (iter != mPlugins.end())
749    {
750       mPlugins.erase(iter++);
751    }
752 }
753 
DropFile(const wxString & fileName)754 bool PluginManager::DropFile(const wxString &fileName)
755 {
756    auto &mm = ModuleManager::Get();
757    const wxFileName src{ fileName };
758 
759    for (auto &plug : PluginsOfType(PluginTypeModule)) {
760       auto module = static_cast<ModuleInterface *>
761          (mm.CreateProviderInstance(plug.GetID(), plug.GetPath()));
762       if (! module)
763          continue;
764 
765       const auto &ff = module->InstallPath();
766       const auto &extensions = module->GetFileExtensions();
767       if ( !ff.empty() &&
768           extensions.Index(src.GetExt(), false) != wxNOT_FOUND ) {
769          TranslatableString errMsg;
770          // Do dry-run test of the file format
771          unsigned nPlugIns =
772             module->DiscoverPluginsAtPath(fileName, errMsg, {});
773          if (nPlugIns) {
774             // File contents are good for this module, so check no others.
775             // All branches of this block return true, even in case of
776             // failure for other reasons, to signal that other drag-and-drop
777             // actions should not be tried.
778 
779             // Find path to copy it
780             wxFileName dst;
781             dst.AssignDir( ff );
782             dst.SetFullName( src.GetFullName() );
783             if ( dst.Exists() ) {
784                // Query whether to overwrite
785                bool overwrite = (wxYES == ::AudacityMessageBox(
786                   XO("Overwrite the plug-in file %s?")
787                      .Format( dst.GetFullPath() ),
788                   XO("Plug-in already exists"),
789                   wxYES_NO ) );
790                if ( !overwrite )
791                   return true;
792             }
793 
794             // Move the file or subtree
795             bool copied = false;
796             auto dstPath = dst.GetFullPath();
797             if ( src.FileExists() )
798                // A simple one-file plug-in
799                copied = FileNames::DoCopyFile(
800                   src.GetFullPath(), dstPath, true );
801             else {
802                // A sub-folder
803                // such as for some VST packages
804                // Recursive copy needed -- to do
805                return true;
806             }
807 
808             if (!copied) {
809                ::AudacityMessageBox(
810                   XO("Plug-in file is in use. Failed to overwrite") );
811                return true;
812             }
813 
814             // Register for real
815             std::vector<PluginID> ids;
816             std::vector<wxString> names;
817             nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg,
818                [&](ModuleInterface *provider, ComponentInterface *ident)
819                                                      -> const PluginID& {
820                   // Register as by default, but also collecting the PluginIDs
821                   // and names
822                   auto &id = PluginManagerInterface::DefaultRegistrationCallback(
823                         provider, ident);
824                   ids.push_back(id);
825                   names.push_back( ident->GetSymbol().Translation() );
826                   return id;
827                });
828             if ( ! nPlugIns ) {
829                // Unlikely after the dry run succeeded
830                ::AudacityMessageBox(
831                   XO("Failed to register:\n%s").Format( errMsg ) );
832                return true;
833             }
834 
835             // Ask whether to enable the plug-ins
836             if (auto nIds = ids.size()) {
837                auto message = XPC(
838                /* i18n-hint A plug-in is an optional added program for a sound
839                 effect, or generator, or analyzer */
840                   "Enable this plug-in?\n",
841                   "Enable these plug-ins?\n",
842                   0,
843                   "plug-ins"
844                )( nIds );
845                for (const auto &name : names)
846                   message.Join( Verbatim( name ), wxT("\n") );
847                bool enable = (wxYES == ::AudacityMessageBox(
848                   message,
849                   XO("Enable new plug-ins"),
850                   wxYES_NO ) );
851                for (const auto &id : ids)
852                   mPlugins[id].SetEnabled(enable);
853                // Make changes to enabled status persist:
854                this->Save();
855             }
856 
857             return true;
858          }
859       }
860    }
861 
862    return false;
863 }
864 
Load()865 void PluginManager::Load()
866 {
867    // Create/Open the registry
868    auto pRegistry = AudacityFileConfig::Create(
869       {}, {}, FileNames::PluginRegistry());
870    auto &registry = *pRegistry;
871 
872    // If this group doesn't exist then we have something that's not a registry.
873    // We should probably warn the user, but it's pretty unlikely that this will happen.
874    if (!registry.HasGroup(REGROOT))
875    {
876       // Must start over
877       // This DeleteAll affects pluginregistry.cfg only, not audacity.cfg
878       // That is, the memory of on/off states of effect (and generator,
879       // analyzer, and tool) plug-ins
880       registry.DeleteAll();
881       registry.Flush();
882       return;
883    }
884 
885    // Check for a registry version that we can understand
886    // TODO: Should also check for a registry file that is newer than
887    // what we can understand.
888    wxString regver = registry.Read(REGVERKEY);
889    if (regver < REGVERCUR )
890    {
891       // Conversion code here, for when registry version changes.
892 
893       // We iterate through the effects, possibly updating their info.
894       wxString groupName;
895       long groupIndex;
896       wxString group = GetPluginTypeString(PluginTypeEffect);
897       wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
898       wxArrayString groupsToDelete;
899 
900       registry.SetPath(cfgPath);
901       for (bool cont = registry.GetFirstGroup(groupName, groupIndex);
902          cont;
903          registry.SetPath(cfgPath),
904          cont = registry.GetNextGroup(groupName, groupIndex))
905       {
906          registry.SetPath(groupName);
907          wxString effectSymbol = registry.Read(KEY_SYMBOL, "");
908          wxString effectVersion = registry.Read(KEY_VERSION, "");
909 
910 
911          // For 2.3.0 the plugins we distribute have moved around.
912          // So we upped the registry version number to 1.1.
913          // These particular config edits were originally written to fix Bug 1914.
914          if (regver <= "1.0") {
915             // Nyquist prompt is a built-in that has moved to the tools menu.
916             if (effectSymbol == NYQUIST_PROMPT_ID) {
917                registry.Write(KEY_EFFECTTYPE, "Tool");
918             // Old version of SDE was in Analyze menu.  Now it is in Tools.
919             // We don't want both the old and the new.
920             } else if ((effectSymbol == "Sample Data Export") && (effectVersion == "n/a")) {
921                groupsToDelete.push_back(cfgPath + groupName);
922             // Old version of SDI was in Generate menu.  Now it is in Tools.
923             } else if ((effectSymbol == "Sample Data Import") && (effectVersion == "n/a")) {
924                groupsToDelete.push_back(cfgPath + groupName);
925             }
926          }
927 
928       }
929       // Doing the deletion within the search loop risked skipping some items,
930       // hence the delayed delete.
931       for (unsigned int i = 0; i < groupsToDelete.size(); i++) {
932          registry.DeleteGroup(groupsToDelete[i]);
933       }
934       registry.SetPath("");
935       registry.Write(REGVERKEY, REGVERCUR);
936       // Updates done.  Make sure we read the updated data later.
937       registry.Flush();
938    }
939 
940    // Load all provider plugins first
941    LoadGroup(&registry, PluginTypeModule);
942 
943    // Now the rest
944    LoadGroup(&registry, PluginTypeEffect);
945    LoadGroup(&registry, PluginTypeAudacityCommand );
946    LoadGroup(&registry, PluginTypeExporter);
947    LoadGroup(&registry, PluginTypeImporter);
948 
949    LoadGroup(&registry, PluginTypeStub);
950    return;
951 }
952 
LoadGroup(FileConfig * pRegistry,PluginType type)953 void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type)
954 {
955 #ifdef __WXMAC__
956    // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins
957    // bundled with other versions of Audacity, assuming both versions
958    // were properly installed in /Applications (or whatever it is called in
959    // your locale)
960 
961    const auto fullExePath = PlatformCompatibility::GetExecutablePath();
962 
963    // Strip rightmost path components up to *.app
964    wxFileName exeFn{ fullExePath };
965    exeFn.SetEmptyExt();
966    exeFn.SetName(wxString{});
967    while(exeFn.GetDirCount() && !exeFn.GetDirs().back().EndsWith(".app"))
968       exeFn.RemoveLastDir();
969 
970    const auto goodPath = exeFn.GetPath();
971 
972    if(exeFn.GetDirCount())
973       exeFn.RemoveLastDir();
974    const auto possiblyBadPath = exeFn.GetPath();
975 
976    auto AcceptPath = [&](const wxString &path) {
977       if (!path.StartsWith(possiblyBadPath))
978          // Assume it's not under /Applications
979          return true;
980       if (path.StartsWith(goodPath))
981          // It's bundled with this executable
982          return true;
983       return false;
984    };
985 #else
986    auto AcceptPath = [](const wxString&){ return true; };
987 #endif
988 
989    wxString strVal;
990    bool boolVal;
991    wxString groupName;
992    long groupIndex;
993    wxString group = GetPluginTypeString(type);
994    wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
995 
996    pRegistry->SetPath(cfgPath);
997    for (bool cont = pRegistry->GetFirstGroup(groupName, groupIndex);
998         cont;
999         pRegistry->SetPath(cfgPath),
1000         cont = pRegistry->GetNextGroup(groupName, groupIndex))
1001    {
1002       PluginDescriptor plug;
1003 
1004       pRegistry->SetPath(groupName);
1005 
1006       groupName = ConvertID(groupName);
1007 
1008       // Bypass group if the ID is already in use
1009       if (mPlugins.count(groupName))
1010          continue;
1011 
1012       // Set the ID and type
1013       plug.SetID(groupName);
1014       plug.SetPluginType(type);
1015 
1016       // Get the provider ID and bypass group if not found
1017       if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString))
1018       {
1019          // Bypass group if the provider isn't valid
1020          if (!strVal.empty() && !mPlugins.count(strVal))
1021             continue;
1022       }
1023       plug.SetProviderID(PluginID(strVal));
1024 
1025       // Get the path (optional)
1026       pRegistry->Read(KEY_PATH, &strVal, wxEmptyString);
1027       if (!AcceptPath(strVal))
1028          // Ignore the obsolete path in the config file, during session,
1029          // but don't remove it from the file.  Maybe you really want to
1030          // switch back to the other version of Audacity and lose nothing.
1031          continue;
1032       plug.SetPath(strVal);
1033 
1034       /*
1035        // PRL: Ignore names  written in configs before 2.3.0!
1036        // use Internal string only!  Let the present version of Audacity map
1037        // that to a user-visible string.
1038       // Get the name and bypass group if not found
1039       if (!pRegistry->Read(KEY_NAME, &strVal))
1040       {
1041          continue;
1042       }
1043       plug.SetName(strVal);
1044        */
1045 
1046       // Get the symbol...Audacity 2.3.0 or later requires it
1047       // bypass group if not found
1048       // Note, KEY_SYMBOL started getting written to config files in 2.1.0.
1049       // KEY_NAME (now ignored) was written before that, but only for VST
1050       // effects.
1051       if (!pRegistry->Read(KEY_SYMBOL, &strVal))
1052          continue;
1053 
1054       // Related to Bug2778: config file only remembered an internal name,
1055       // so this symbol may not contain the correct TranslatableString.
1056       // See calls to IsPluginRegistered which can correct that.
1057       plug.SetSymbol(strVal);
1058 
1059       // Get the version and bypass group if not found
1060       if (!pRegistry->Read(KEY_VERSION, &strVal))
1061       {
1062          continue;
1063       }
1064       plug.SetVersion(strVal);
1065 
1066       // Get the vendor and bypass group if not found
1067       if (!pRegistry->Read(KEY_VENDOR, &strVal))
1068       {
1069          continue;
1070       }
1071       plug.SetVendor( strVal );
1072 
1073 #if 0
1074       // This was done before version 2.2.2, but the value was not really used
1075       // But absence of a value will cause early versions to skip the group
1076       // Therefore we still write a blank to keep pluginregistry.cfg
1077       // backwards-compatible
1078 
1079       // Get the description and bypass group if not found
1080       if (!pRegistry->Read(KEY_DESCRIPTION, &strVal))
1081       {
1082          continue;
1083       }
1084 #endif
1085 
1086       // Is it enabled...default to no if not found
1087       pRegistry->Read(KEY_ENABLED, &boolVal, false);
1088       plug.SetEnabled(boolVal);
1089 
1090       // Is it valid...default to no if not found
1091       pRegistry->Read(KEY_VALID, &boolVal, false);
1092       plug.SetValid(boolVal);
1093 
1094       switch (type)
1095       {
1096          case PluginTypeModule:
1097          {
1098             // Nothing to do here yet
1099          }
1100          break;
1101 
1102          case PluginTypeEffect:
1103          {
1104             // Get the effect type and bypass group if not found
1105             if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal))
1106                continue;
1107 
1108             if (strVal == KEY_EFFECTTYPE_NONE)
1109                plug.SetEffectType(EffectTypeNone);
1110             else if (strVal == KEY_EFFECTTYPE_ANALYZE)
1111                plug.SetEffectType(EffectTypeAnalyze);
1112             else if (strVal == KEY_EFFECTTYPE_GENERATE)
1113                plug.SetEffectType(EffectTypeGenerate);
1114             else if (strVal == KEY_EFFECTTYPE_PROCESS)
1115                plug.SetEffectType(EffectTypeProcess);
1116             else if (strVal == KEY_EFFECTTYPE_TOOL)
1117                plug.SetEffectType(EffectTypeTool);
1118             else if (strVal == KEY_EFFECTTYPE_HIDDEN)
1119                plug.SetEffectType(EffectTypeHidden);
1120             else
1121                continue;
1122 
1123             // Get the effect family and bypass group if not found
1124             if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal))
1125             {
1126                continue;
1127             }
1128             plug.SetEffectFamily(strVal);
1129 
1130             // Is it a default (above the line) effect and bypass group if not found
1131             if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal))
1132             {
1133                continue;
1134             }
1135             plug.SetEffectDefault(boolVal);
1136 
1137             // Is it an interactive effect and bypass group if not found
1138             if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal))
1139             {
1140                continue;
1141             }
1142             plug.SetEffectInteractive(boolVal);
1143 
1144             // Is it a realtime capable effect and bypass group if not found
1145             if (!pRegistry->Read(KEY_EFFECTREALTIME, &boolVal))
1146             {
1147                continue;
1148             }
1149             plug.SetEffectRealtime(boolVal);
1150 
1151             // Does the effect support automation...bypass group if not found
1152             if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
1153             {
1154                continue;
1155             }
1156             plug.SetEffectAutomatable(boolVal);
1157          }
1158          break;
1159 
1160          case PluginTypeImporter:
1161          {
1162             // Get the importer identifier and bypass group if not found
1163             if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal))
1164             {
1165                continue;
1166             }
1167             plug.SetImporterIdentifier(strVal);
1168 
1169             // Get the importer extensions and bypass group if not found
1170             if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal))
1171             {
1172                continue;
1173             }
1174             FileExtensions extensions;
1175             wxStringTokenizer tkr(strVal, wxT(":"));
1176             while (tkr.HasMoreTokens())
1177             {
1178                extensions.push_back(tkr.GetNextToken());
1179             }
1180             plug.SetImporterExtensions(extensions);
1181          }
1182          break;
1183 
1184          case PluginTypeStub:
1185          {
1186             // Nothing additional for stubs
1187          }
1188          break;
1189 
1190          // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0.
1191          case PluginTypeNone:
1192          {
1193             // Used for stub groups
1194          }
1195          break;
1196 
1197          default:
1198          {
1199             continue;
1200          }
1201       }
1202 
1203       // Everything checked out...accept the plugin
1204       mPlugins[groupName] = std::move(plug);
1205    }
1206 
1207    return;
1208 }
1209 
Save()1210 void PluginManager::Save()
1211 {
1212    // Create/Open the registry
1213    auto pRegistry = AudacityFileConfig::Create(
1214       {}, {}, FileNames::PluginRegistry());
1215    auto &registry = *pRegistry;
1216 
1217    // Clear pluginregistry.cfg (not audacity.cfg)
1218    registry.DeleteAll();
1219 
1220    // Write the version string
1221    registry.Write(REGVERKEY, REGVERCUR);
1222 
1223    // Save the individual groups
1224    SaveGroup(&registry, PluginTypeEffect);
1225    SaveGroup(&registry, PluginTypeExporter);
1226    SaveGroup(&registry, PluginTypeAudacityCommand);
1227    SaveGroup(&registry, PluginTypeImporter);
1228    SaveGroup(&registry, PluginTypeStub);
1229 
1230    // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0
1231    // and 2.1.1+.  This should be removed after a few releases past 2.1.0.
1232    //SaveGroup(&registry, PluginTypeNone);
1233 
1234    // And now the providers
1235    SaveGroup(&registry, PluginTypeModule);
1236 
1237    // Just to be safe
1238    registry.Flush();
1239 }
1240 
SaveGroup(FileConfig * pRegistry,PluginType type)1241 void PluginManager::SaveGroup(FileConfig *pRegistry, PluginType type)
1242 {
1243    wxString group = GetPluginTypeString(type);
1244    for (auto &pair : mPlugins) {
1245       auto & plug = pair.second;
1246 
1247       if (plug.GetPluginType() != type)
1248       {
1249          continue;
1250       }
1251 
1252       pRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
1253 
1254       pRegistry->Write(KEY_PATH, plug.GetPath());
1255 
1256       // See comments with the corresponding load-time call to SetSymbol().
1257       pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal());
1258 
1259       // PRL:  Writing KEY_NAME which is no longer read, but older Audacity
1260       // versions expect to find it.
1261       pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid().MSGID());
1262 
1263       pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion());
1264       pRegistry->Write(KEY_VENDOR, plug.GetVendor());
1265       // Write a blank -- see comments in LoadGroup:
1266       pRegistry->Write(KEY_DESCRIPTION, wxString{});
1267       pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID());
1268       pRegistry->Write(KEY_ENABLED, plug.IsEnabled());
1269       pRegistry->Write(KEY_VALID, plug.IsValid());
1270 
1271       switch (type)
1272       {
1273          case PluginTypeModule:
1274          break;
1275 
1276          case PluginTypeEffect:
1277          {
1278             EffectType etype = plug.GetEffectType();
1279             wxString stype;
1280             if (etype == EffectTypeNone)
1281                stype = KEY_EFFECTTYPE_NONE;
1282             else if (etype == EffectTypeAnalyze)
1283                stype = KEY_EFFECTTYPE_ANALYZE;
1284             else if (etype == EffectTypeGenerate)
1285                stype = KEY_EFFECTTYPE_GENERATE;
1286             else if (etype == EffectTypeProcess)
1287                stype = KEY_EFFECTTYPE_PROCESS;
1288             else if (etype == EffectTypeTool)
1289                stype = KEY_EFFECTTYPE_TOOL;
1290             else if (etype == EffectTypeHidden)
1291                stype = KEY_EFFECTTYPE_HIDDEN;
1292 
1293             pRegistry->Write(KEY_EFFECTTYPE, stype);
1294             pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily());
1295             pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
1296             pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
1297             pRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime());
1298             pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
1299          }
1300          break;
1301 
1302          case PluginTypeImporter:
1303          {
1304             pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
1305             const auto & extensions = plug.GetImporterExtensions();
1306             wxString strExt;
1307             for (size_t i = 0, cnt = extensions.size(); i < cnt; i++)
1308             {
1309                strExt += extensions[i] + wxT(":");
1310             }
1311             strExt.RemoveLast(1);
1312             pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt);
1313          }
1314          break;
1315 
1316          default:
1317          break;
1318       }
1319    }
1320 
1321    return;
1322 }
1323 
1324 // If bFast is true, do not do a full check.  Just check the ones
1325 // that are quick to check.  Currently (Feb 2017) just Nyquist
1326 // and built-ins.
CheckForUpdates(bool bFast)1327 void PluginManager::CheckForUpdates(bool bFast)
1328 {
1329    ModuleManager & mm = ModuleManager::Get();
1330    wxArrayString pathIndex;
1331    for (auto &pair : mPlugins) {
1332       auto &plug = pair.second;
1333 
1334       // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
1335       if (plug.GetPluginType() != PluginTypeNone)
1336          pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';')));
1337    }
1338 
1339    // Check all known plugins to ensure they are still valid and scan for NEW ones.
1340    //
1341    // All NEW plugins get a stub entry created that will remain in place until the
1342    // user enables or disables the plugin.
1343    //
1344    // Because we use the plugins "path" as returned by the providers, we can actually
1345    // have multiple providers report the same path since, at this point, they only
1346    // know that the path might possibly be one supported by the provider.
1347    //
1348    // When the user enables the plugin, each provider that reported it will be asked
1349    // to register the plugin.
1350    for (auto &pair : mPlugins) {
1351       auto &plug = pair.second;
1352       const PluginID & plugID = plug.GetID();
1353       const wxString & plugPath = plug.GetPath();
1354       PluginType plugType = plug.GetPluginType();
1355 
1356       // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
1357       if (plugType == PluginTypeNone)
1358       {
1359          continue;
1360       }
1361 
1362       if ( plugType == PluginTypeModule  )
1363       {
1364          if( bFast )
1365          {
1366             // Skip modules, when doing a fast refresh/check.
1367          }
1368          else if (!mm.IsProviderValid(plugID, plugPath))
1369          {
1370             plug.SetEnabled(false);
1371             plug.SetValid(false);
1372          }
1373          else
1374          {
1375             // Collect plugin paths
1376             PluginPaths paths;
1377             if (auto provider = mm.CreateProviderInstance( plugID, plugPath ) )
1378                paths = provider->FindPluginPaths( *this );
1379             for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
1380             {
1381                wxString path = paths[i].BeforeFirst(wxT(';'));;
1382                if ( ! make_iterator_range( pathIndex ).contains( path ) )
1383                {
1384                   PluginID ID = plugID + wxT("_") + path;
1385                   PluginDescriptor & plug2 = mPlugins[ID];  // This will create a NEW descriptor
1386                   plug2.SetPluginType(PluginTypeStub);
1387                   plug2.SetID(ID);
1388                   plug2.SetProviderID(plugID);
1389                   plug2.SetPath(path);
1390                   plug2.SetEnabled(false);
1391                   plug2.SetValid(false);
1392                }
1393             }
1394          }
1395       }
1396       else if (plugType != PluginTypeNone && plugType != PluginTypeStub)
1397       {
1398          plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath, bFast));
1399          if (!plug.IsValid())
1400          {
1401             plug.SetEnabled(false);
1402          }
1403       }
1404    }
1405 
1406    Save();
1407 
1408    return;
1409 }
1410 
1411 // Here solely for the purpose of Nyquist Workbench until
1412 // a better solution is devised.
RegisterPlugin(std::unique_ptr<EffectDefinitionInterface> effect,PluginType type)1413 const PluginID & PluginManager::RegisterPlugin(
1414    std::unique_ptr<EffectDefinitionInterface> effect, PluginType type)
1415 {
1416    PluginDescriptor & plug =
1417       CreatePlugin(GetID(effect.get()), effect.get(), type);
1418 
1419    plug.SetEffectType(effect->GetType());
1420    plug.SetEffectFamily(effect->GetFamily().Internal());
1421    plug.SetEffectInteractive(effect->IsInteractive());
1422    plug.SetEffectDefault(effect->IsDefault());
1423    plug.SetEffectRealtime(effect->SupportsRealtime());
1424    plug.SetEffectAutomatable(effect->SupportsAutomation());
1425 
1426    plug.SetInstance(std::move(effect));
1427    plug.SetEffectLegacy(true);
1428    plug.SetEnabled(true);
1429    plug.SetValid(true);
1430 
1431    return plug.GetID();
1432 }
1433 
UnregisterPlugin(const PluginID & ID)1434 void PluginManager::UnregisterPlugin(const PluginID & ID)
1435 {
1436    mPlugins.erase(ID);
1437 }
1438 
GetPluginCount(PluginType type)1439 int PluginManager::GetPluginCount(PluginType type)
1440 {
1441    return count_if(mPlugins.begin(), mPlugins.end(), [type](auto &pair){
1442       return pair.second.GetPluginType() == type; });
1443 }
1444 
GetPlugin(const PluginID & ID) const1445 const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) const
1446 {
1447    if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1448       return nullptr;
1449    else
1450       return &iter->second;
1451 }
1452 
Advance(bool incrementing)1453 void PluginManager::Iterator::Advance(bool incrementing)
1454 {
1455    const auto end = mPm.mPlugins.end();
1456    if (incrementing && mIterator != end)
1457       ++mIterator;
1458    bool all = mPluginType == PluginTypeNone && mEffectType == EffectTypeNone;
1459    for (; mIterator != end; ++mIterator) {
1460       auto &plug = mIterator->second;
1461       if (!all && !(plug.IsValid() && plug.IsEnabled()))
1462          continue;
1463       auto plugType = plug.GetPluginType();
1464       if ((mPluginType == PluginTypeNone || (plugType & mPluginType)) &&
1465          (mEffectType == EffectTypeNone || plug.GetEffectType() == mEffectType)) {
1466          if (!all && (plugType & PluginTypeEffect)) {
1467             // This preference may be written by EffectsPrefs
1468             auto setting = mPm.GetPluginEnabledSetting( plug );
1469             if (!(setting.empty() || gPrefs->Read( setting, true )))
1470                continue;
1471          }
1472          // Pause iteration at this match
1473          break;
1474       }
1475    }
1476 }
1477 
Iterator(PluginManager & manager)1478 PluginManager::Iterator::Iterator(PluginManager &manager)
1479 : mPm{ manager }
1480 , mIterator{ manager.mPlugins.begin() }
1481 {
1482 }
1483 
Iterator(PluginManager & manager,int type)1484 PluginManager::Iterator::Iterator(PluginManager &manager, int type)
1485 : mPm{ manager }
1486 , mIterator{ manager.mPlugins.begin() }
1487 , mPluginType{ type }
1488 {
1489    Advance(false);
1490 }
1491 
Iterator(PluginManager & manager,EffectType type)1492 PluginManager::Iterator::Iterator(PluginManager &manager, EffectType type)
1493 : mPm{ manager }
1494 , mIterator{ manager.mPlugins.begin() }
1495 , mEffectType{ type }
1496 {
1497    Advance(false);
1498 }
1499 
operator ++()1500 auto PluginManager::Iterator::operator ++() -> Iterator &
1501 {
1502    Advance(true);
1503    return *this;
1504 }
1505 
IsPluginEnabled(const PluginID & ID)1506 bool PluginManager::IsPluginEnabled(const PluginID & ID)
1507 {
1508    if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1509       return false;
1510    else
1511       return iter->second.IsEnabled();
1512 }
1513 
EnablePlugin(const PluginID & ID,bool enable)1514 void PluginManager::EnablePlugin(const PluginID & ID, bool enable)
1515 {
1516    if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1517       return;
1518    else
1519       iter->second.SetEnabled(enable);
1520 }
1521 
GetSymbol(const PluginID & ID)1522 const ComponentInterfaceSymbol & PluginManager::GetSymbol(const PluginID & ID)
1523 {
1524    if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) {
1525       static ComponentInterfaceSymbol empty;
1526       return empty;
1527    }
1528    else
1529       return iter->second.GetSymbol();
1530 }
1531 
GetInstance(const PluginID & ID)1532 ComponentInterface *PluginManager::GetInstance(const PluginID & ID)
1533 {
1534    if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1535       return nullptr;
1536    else {
1537       auto &plug = iter->second;
1538 
1539       // If not dealing with legacy effects, make sure the provider is loaded
1540       if (!plug.IsEffectLegacy())
1541       {
1542          const PluginID & prov = plug.GetProviderID();
1543          if (auto iter2 = mPlugins.find(prov); iter2 == mPlugins.end())
1544             return nullptr;
1545          else
1546             iter2->second.GetInstance();
1547       }
1548 
1549       return plug.GetInstance();
1550    }
1551 }
1552 
GetID(ModuleInterface * module)1553 PluginID PluginManager::GetID(ModuleInterface *module)
1554 {
1555    return ModuleManager::GetID(module);
1556 }
1557 
GetID(ComponentInterface * command)1558 PluginID PluginManager::GetID(ComponentInterface *command)
1559 {
1560    return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1561                            GetPluginTypeString(PluginTypeAudacityCommand),
1562                            wxEmptyString,
1563                            command->GetVendor().Internal(),
1564                            command->GetSymbol().Internal(),
1565                            command->GetPath());
1566 }
1567 
GetID(EffectDefinitionInterface * effect)1568 PluginID PluginManager::GetID(EffectDefinitionInterface *effect)
1569 {
1570    return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1571                            GetPluginTypeString(PluginTypeEffect),
1572                            effect->GetFamily().Internal(),
1573                            effect->GetVendor().Internal(),
1574                            effect->GetSymbol().Internal(),
1575                            effect->GetPath());
1576 }
1577 
1578 // This string persists in configuration files
1579 // So config compatibility will break if it is changed across Audacity versions
GetPluginTypeString(PluginType type)1580 wxString PluginManager::GetPluginTypeString(PluginType type)
1581 {
1582    wxString str;
1583 
1584    switch (type)
1585    {
1586    default:
1587    case PluginTypeNone:
1588       str = wxT("Placeholder");
1589       break;
1590    case PluginTypeStub:
1591       str = wxT("Stub");
1592       break;
1593    case PluginTypeEffect:
1594       str = wxT("Effect");
1595       break;
1596    case PluginTypeAudacityCommand:
1597       str = wxT("Generic");
1598       break;
1599    case PluginTypeExporter:
1600       str = wxT("Exporter");
1601       break;
1602    case PluginTypeImporter:
1603       str = wxT("Importer");
1604       break;
1605    case PluginTypeModule:
1606       str = ModuleManager::GetPluginTypeString();
1607       break;
1608    }
1609 
1610    return str;
1611 }
1612 
CreatePlugin(const PluginID & id,ComponentInterface * ident,PluginType type)1613 PluginDescriptor & PluginManager::CreatePlugin(const PluginID & id,
1614                                                ComponentInterface *ident,
1615                                                PluginType type)
1616 {
1617    // This will either create a NEW entry or replace an existing entry
1618    PluginDescriptor & plug = mPlugins[id];
1619 
1620    plug.SetPluginType(type);
1621 
1622    plug.SetID(id);
1623    plug.SetPath(ident->GetPath());
1624    plug.SetSymbol(ident->GetSymbol());
1625    plug.SetVendor(ident->GetVendor().Internal());
1626    plug.SetVersion(ident->GetVersion());
1627 
1628    return plug;
1629 }
1630 
GetSettings()1631 FileConfig *PluginManager::GetSettings()
1632 {
1633    if (!mSettings)
1634    {
1635       mSettings =
1636          AudacityFileConfig::Create({}, {}, FileNames::PluginSettings());
1637 
1638       // Check for a settings version that we can understand
1639       if (mSettings->HasEntry(SETVERKEY))
1640       {
1641          wxString setver = mSettings->Read(SETVERKEY, SETVERKEY);
1642          if (setver < SETVERCUR )
1643          {
1644             // This is where we'd put in conversion code when the
1645             // settings version changes.
1646             //
1647             // Should also check for a settings file that is newer than
1648             // what we can understand.
1649          }
1650       }
1651       else
1652       {
1653          // Make sure is has a version string
1654          mSettings->Write(SETVERKEY, SETVERCUR);
1655          mSettings->Flush();
1656       }
1657    }
1658 
1659    return mSettings.get();
1660 }
1661 
HasGroup(const RegistryPath & group)1662 bool PluginManager::HasGroup(const RegistryPath & group)
1663 {
1664    auto settings = GetSettings();
1665 
1666    bool res = settings->HasGroup(group);
1667    if (res)
1668    {
1669       // The group exists, but empty groups aren't considered valid
1670       wxString oldPath = settings->GetPath();
1671       settings->SetPath(group);
1672       res = settings->GetNumberOfEntries() || settings->GetNumberOfGroups();
1673       settings->SetPath(oldPath);
1674    }
1675 
1676    return res;
1677 }
1678 
GetSubgroups(const RegistryPath & group,RegistryPaths & subgroups)1679 bool PluginManager::GetSubgroups(const RegistryPath & group, RegistryPaths & subgroups)
1680 {
1681    if (group.empty() || !HasGroup(group))
1682    {
1683       return false;
1684    }
1685 
1686    wxString path = GetSettings()->GetPath();
1687    GetSettings()->SetPath(group);
1688 
1689    wxString name;
1690    long index = 0;
1691    if (GetSettings()->GetFirstGroup(name, index))
1692    {
1693       do
1694       {
1695          subgroups.push_back(name);
1696       } while (GetSettings()->GetNextGroup(name, index));
1697    }
1698 
1699    GetSettings()->SetPath(path);
1700 
1701    return true;
1702 }
1703 
GetConfig(const RegistryPath & key,int & value,int defval)1704 bool PluginManager::GetConfig(const RegistryPath & key, int & value, int defval)
1705 {
1706    bool result = false;
1707 
1708    if (!key.empty())
1709    {
1710       result = GetSettings()->Read(key, &value, defval);
1711    }
1712 
1713    return result;
1714 }
1715 
GetConfig(const RegistryPath & key,wxString & value,const wxString & defval)1716 bool PluginManager::GetConfig(const RegistryPath & key, wxString & value, const wxString & defval)
1717 {
1718    bool result = false;
1719 
1720    if (!key.empty())
1721    {
1722       wxString wxval;
1723 
1724       result = GetSettings()->Read(key, &wxval, defval);
1725 
1726       value = wxval;
1727    }
1728 
1729    return result;
1730 }
1731 
GetConfig(const RegistryPath & key,bool & value,bool defval)1732 bool PluginManager::GetConfig(const RegistryPath & key, bool & value, bool defval)
1733 {
1734    bool result = false;
1735 
1736    if (!key.empty())
1737    {
1738       result = GetSettings()->Read(key, &value, defval);
1739    }
1740 
1741    return result;
1742 }
1743 
GetConfig(const RegistryPath & key,float & value,float defval)1744 bool PluginManager::GetConfig(const RegistryPath & key, float & value, float defval)
1745 {
1746    bool result = false;
1747 
1748    if (!key.empty())
1749    {
1750       double dval = 0.0;
1751 
1752       result = GetSettings()->Read(key, &dval, (double) defval);
1753 
1754       value = (float) dval;
1755    }
1756 
1757    return result;
1758 }
1759 
GetConfig(const RegistryPath & key,double & value,double defval)1760 bool PluginManager::GetConfig(const RegistryPath & key, double & value, double defval)
1761 {
1762    bool result = false;
1763 
1764    if (!key.empty())
1765    {
1766       result = GetSettings()->Read(key, &value, defval);
1767    }
1768 
1769    return result;
1770 }
1771 
SetConfig(const RegistryPath & key,const wxString & value)1772 bool PluginManager::SetConfig(const RegistryPath & key, const wxString & value)
1773 {
1774    bool result = false;
1775 
1776    if (!key.empty())
1777    {
1778       wxString wxval = value;
1779       result = GetSettings()->Write(key, wxval);
1780       if (result)
1781       {
1782          result = GetSettings()->Flush();
1783       }
1784    }
1785 
1786    return result;
1787 }
1788 
SetConfig(const RegistryPath & key,const int & value)1789 bool PluginManager::SetConfig(const RegistryPath & key, const int & value)
1790 {
1791    bool result = false;
1792 
1793    if (!key.empty())
1794    {
1795       result = GetSettings()->Write(key, value);
1796       if (result)
1797       {
1798          result = GetSettings()->Flush();
1799       }
1800    }
1801 
1802    return result;
1803 }
1804 
SetConfig(const RegistryPath & key,const bool & value)1805 bool PluginManager::SetConfig(const RegistryPath & key, const bool & value)
1806 {
1807    bool result = false;
1808 
1809    if (!key.empty())
1810    {
1811       result = GetSettings()->Write(key, value);
1812       if (result)
1813       {
1814          result = GetSettings()->Flush();
1815       }
1816    }
1817 
1818    return result;
1819 }
1820 
SetConfig(const RegistryPath & key,const float & value)1821 bool PluginManager::SetConfig(const RegistryPath & key, const float & value)
1822 {
1823    bool result = false;
1824 
1825    if (!key.empty())
1826    {
1827       result = GetSettings()->Write(key, value);
1828       if (result)
1829       {
1830          result = GetSettings()->Flush();
1831       }
1832    }
1833 
1834    return result;
1835 }
1836 
SetConfig(const RegistryPath & key,const double & value)1837 bool PluginManager::SetConfig(const RegistryPath & key, const double & value)
1838 {
1839    bool result = false;
1840 
1841    if (!key.empty())
1842    {
1843       result = GetSettings()->Write(key, value);
1844       if (result)
1845       {
1846          result = GetSettings()->Flush();
1847       }
1848    }
1849 
1850    return result;
1851 }
1852 
1853 /* Return value is a key for lookup in a config file */
SettingsPath(const PluginID & ID,bool shared)1854 RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared)
1855 {
1856    // All the strings reported by PluginDescriptor and used in this function
1857    // persist in the plugin settings configuration file, so they should not
1858    // be changed across Audacity versions, or else compatibility of the
1859    // configuration files will break.
1860 
1861    if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1862       return {};
1863    else {
1864       const PluginDescriptor & plug = iter->second;
1865 
1866       wxString id = GetPluginTypeString(plug.GetPluginType()) +
1867                     wxT("_") +
1868                     plug.GetEffectFamily() + // is empty for non-Effects
1869                     wxT("_") +
1870                     plug.GetVendor() +
1871                     wxT("_") +
1872                     (shared ? wxString{} : plug.GetSymbol().Internal());
1873 
1874       return SETROOT +
1875              ConvertID(id) +
1876              wxCONFIG_PATH_SEPARATOR +
1877              (shared ? wxT("shared") : wxT("private")) +
1878              wxCONFIG_PATH_SEPARATOR;
1879    }
1880 }
1881 
1882 /* Return value is a key for lookup in a config file */
SharedGroup(const PluginID & ID,const RegistryPath & group)1883 RegistryPath PluginManager::SharedGroup(const PluginID & ID, const RegistryPath & group)
1884 {
1885    wxString path = SettingsPath(ID, true);
1886 
1887    wxFileName ff(group);
1888    if (!ff.GetName().empty())
1889    {
1890       path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
1891    }
1892 
1893    return path;
1894 }
1895 
1896 /* Return value is a key for lookup in a config file */
SharedKey(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)1897 RegistryPath PluginManager::SharedKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
1898 {
1899    auto path = SharedGroup(ID, group);
1900    if (path.empty())
1901    {
1902       return path;
1903    }
1904 
1905    return path + key;
1906 }
1907 
1908 /* Return value is a key for lookup in a config file */
PrivateGroup(const PluginID & ID,const RegistryPath & group)1909 RegistryPath PluginManager::PrivateGroup(const PluginID & ID, const RegistryPath & group)
1910 {
1911    auto path = SettingsPath(ID, false);
1912 
1913    wxFileName ff(group);
1914    if (!ff.GetName().empty())
1915    {
1916       path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
1917    }
1918 
1919    return path;
1920 }
1921 
1922 /* Return value is a key for lookup in a config file */
PrivateKey(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)1923 RegistryPath PluginManager::PrivateKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
1924 {
1925    auto path = PrivateGroup(ID, group);
1926    if (path.empty())
1927    {
1928       return path;
1929    }
1930 
1931    return path + key;
1932 }
1933 
1934 // Sanitize the ID...not the best solution, but will suffice until this
1935 // is converted to XML.  We use base64 encoding to preserve case.
ConvertID(const PluginID & ID)1936 wxString PluginManager::ConvertID(const PluginID & ID)
1937 {
1938    if (ID.StartsWith(wxT("base64:")))
1939    {
1940       wxString id = ID.Mid(7);
1941       ArrayOf<char> buf{ id.length() / 4 * 3 };
1942       id =  wxString::FromUTF8(buf.get(), b64decode(id, buf.get()));
1943       return id;
1944    }
1945 
1946    const wxCharBuffer & buf = ID.ToUTF8();
1947    return wxT("base64:") + b64encode(buf, strlen(buf));
1948 }
1949 
1950 ////////////////////////////////////////////////////////////////////////////////
1951 // Base64 en/decoding
1952 //
1953 // Original routines marked as public domain and found at:
1954 //
1955 // http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64
1956 //
1957 ////////////////////////////////////////////////////////////////////////////////
1958 
1959 // Lookup table for encoding
1960 const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
1961 const static char padc = wxT('=');
1962 
b64encode(const void * in,int len)1963 wxString PluginManager::b64encode(const void *in, int len)
1964 {
1965    unsigned char *p = (unsigned char *) in;
1966    wxString out;
1967 
1968    unsigned long temp;
1969    for (int i = 0; i < len / 3; i++)
1970    {
1971       temp  = (*p++) << 16; //Convert to big endian
1972       temp += (*p++) << 8;
1973       temp += (*p++);
1974       out += cset[(temp & 0x00FC0000) >> 18];
1975       out += cset[(temp & 0x0003F000) >> 12];
1976       out += cset[(temp & 0x00000FC0) >> 6];
1977       out += cset[(temp & 0x0000003F)];
1978    }
1979 
1980    switch (len % 3)
1981    {
1982       case 1:
1983          temp  = (*p++) << 16; //Convert to big endian
1984          out += cset[(temp & 0x00FC0000) >> 18];
1985          out += cset[(temp & 0x0003F000) >> 12];
1986          out += padc;
1987          out += padc;
1988       break;
1989 
1990       case 2:
1991          temp  = (*p++) << 16; //Convert to big endian
1992          temp += (*p++) << 8;
1993          out += cset[(temp & 0x00FC0000) >> 18];
1994          out += cset[(temp & 0x0003F000) >> 12];
1995          out += cset[(temp & 0x00000FC0) >> 6];
1996          out += padc;
1997       break;
1998    }
1999 
2000    return out;
2001 }
2002 
b64decode(const wxString & in,void * out)2003 int PluginManager::b64decode(const wxString &in, void *out)
2004 {
2005    int len = in.length();
2006    unsigned char *p = (unsigned char *) out;
2007 
2008    if (len % 4)  //Sanity check
2009    {
2010       return 0;
2011    }
2012 
2013    int padding = 0;
2014    if (len)
2015    {
2016       if (in[len - 1] == padc)
2017       {
2018          padding++;
2019       }
2020 
2021       if (in[len - 2] == padc)
2022       {
2023          padding++;
2024       }
2025    }
2026 
2027    //const char *a = in.mb_str();
2028    //Setup a vector to hold the result
2029    unsigned long temp = 0; //Holds decoded quanta
2030    int i = 0;
2031    while (i < len)
2032    {
2033       for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++)
2034       {
2035          unsigned char c = in[i];
2036          temp <<= 6;
2037 
2038          if (c >= 0x41 && c <= 0x5A)
2039          {
2040             temp |= c - 0x41;
2041          }
2042          else if (c >= 0x61 && c <= 0x7A)
2043          {
2044             temp |= c - 0x47;
2045          }
2046          else if (c >= 0x30 && c <= 0x39)
2047          {
2048             temp |= c + 0x04;
2049          }
2050          else if (c == 0x2B)
2051          {
2052             temp |= 0x3E;
2053          }
2054          else if (c == 0x2F)
2055          {
2056             temp |= 0x3F;
2057          }
2058          else if (c == padc)
2059          {
2060             switch (len - i)
2061             {
2062                case 1: //One pad character
2063                   *p++ = (temp >> 16) & 0x000000FF;
2064                   *p++ = (temp >> 8) & 0x000000FF;
2065                   return p - (unsigned char *) out;
2066                case 2: //Two pad characters
2067                   *p++ = (temp >> 10) & 0x000000FF;
2068                   return p - (unsigned char *) out;
2069             }
2070          }
2071          i++;
2072       }
2073       *p++ = (temp >> 16) & 0x000000FF;
2074       *p++ = (temp >> 8) & 0x000000FF;
2075       *p++ = temp & 0x000000FF;
2076    }
2077 
2078    return p - (unsigned char *) out;
2079 }
2080 
2081 // This is defined out-of-line here, to keep ComponentInterface free of other
2082 // #include directives.
GetName()2083 TranslatableString ComponentInterface::GetName()
2084 {
2085    return GetSymbol().Msgid();
2086 }
2087