1 /*
2   Software License :
3 
4   Copyright (c) 2007-2009, The Open Effects Association Ltd. All rights reserved.
5 
6   Redistribution and use in source and binary forms, with or without
7   modification, are permitted provided that the following conditions are met:
8 
9   * Redistributions of source code must retain the above copyright notice,
10   this list of conditions and the following disclaimer.
11   * Redistributions in binary form must reproduce the above copyright notice,
12   this list of conditions and the following disclaimer in the documentation
13   and/or other materials provided with the distribution.
14   * Neither the name The Open Effects Association Ltd, nor the names of its
15   contributors may be used to endorse or promote products derived from this
16   software without specific prior written permission.
17 
18   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include <assert.h>
31 
32 #include <map>
33 #include <string>
34 #include <iostream>
35 #include <fstream>
36 #include <sstream> // stringstream
37 #include <stdexcept>
38 
39 #include <string.h>
40 #include <stdlib.h>
41 
42 #include "expat.h"
43 
44 // ofx
45 #include "ofxCore.h"
46 #include "ofxImageEffect.h"
47 
48 // ofx host
49 #include "ofxhBinary.h"
50 #include "ofxhPropertySuite.h"
51 #include "ofxhMemory.h"
52 #include "ofxhPluginAPICache.h"
53 #include "ofxhPluginCache.h"
54 #include "ofxhHost.h"
55 #include "ofxhXml.h"
56 #include "ofxhUtilities.h"
57 
58 #if defined (__linux__) || defined (__DragonFly__)
59 
60 #define DIRLIST_SEP_CHARS ":;"
61 #define DIRSEP "/"
62 #include <dirent.h>
63 
getArchStr()64 static const char *getArchStr()
65 {
66   if(sizeof(void *) == 4) {
67 #if defined(__linux__)
68     return  "Linux-x86";
69 #else
70     return  "FreeBSD-x86";
71 #endif
72   }
73   else {
74 #if defined(__linux__)
75     return  "Linux-x86-64";
76 #else
77     return  "FreeBSD-x86-64";
78 #endif
79   }
80 }
81 
82 #define ARCHSTR getArchStr()
83 
84 #elif defined (__APPLE__)
85 
86 #define DIRLIST_SEP_CHARS ";:"
87 #if defined(__x86_64) || defined(__x86_64__)
88 #define ARCHSTR "MacOS-x86-64"
89 #else
90 #define ARCHSTR "MacOS"
91 #endif
92 #define DIRSEP "/"
93 #include <dirent.h>
94 
95 #elif defined (WINDOWS)
96 #define DIRLIST_SEP_CHARS ";"
97 #ifdef _WIN64
98 #define ARCHSTR "win64"
99 #else
100 #define ARCHSTR "win32"
101 #endif
102 #define DIRSEP "\\"
103 
104 #include "shlobj.h"
105 #endif
106 
107 
108 bool OFX::Host::PluginCache::_useStdOFXPluginsLocation = true;
109 OFX::Host::PluginCache* OFX::Host::PluginCache::gPluginCachePtr = 0;
110 
111 // Define this to enable ofx plugin cache debug messages.
112 //#define CACHE_DEBUG
113 
114 using namespace OFX::Host;
115 
116 
117 /// try to open the plugin bundle object and query it for plugins
loadPluginInfo(PluginCache * cache)118 void PluginBinary::loadPluginInfo(PluginCache *cache) {
119   if (isInvalid()) {
120     return;
121   }
122 
123   if (_binary) {
124     _fileModificationTime = _binary->getTime();
125     _fileSize = _binary->getSize();
126 
127     // Take a reference to load the binary only once per session. It will
128     // eventually be unloaded in the destructor (see below).
129     // This avoid lots of useless calls to dlopen()/dlclose().
130     if (!_binary->isLoaded()) {
131       _binary->ref();
132     }
133   }
134 
135 
136   if (_binary) {
137     _getNumberOfPlugins = (OfxGetNumberOfPluginsFunc) _binary->findSymbol("OfxGetNumberOfPlugins");
138     _getPluginFunc = (OfxGetPluginFunc) _binary->findSymbol("OfxGetPlugin");
139   }
140 
141 
142   if (_getNumberOfPlugins == 0 || _getPluginFunc == 0) {
143     if (_binary) {
144       _binary->setInvalid(true);
145     }
146 
147   } else {
148     int pluginCount = (*_getNumberOfPlugins)();
149     _plugins.clear();
150     _plugins.reserve(pluginCount);
151 
152     for (int i=0;i<pluginCount;i++) {
153       OfxPlugin *plug = (*_getPluginFunc)(i);
154 
155       APICache::PluginAPICacheI *api = cache->findApiHandler(plug->pluginApi, plug->apiVersion);
156       assert(api);
157 
158       _plugins.push_back(api->newPlugin(this, i, plug));
159     }
160   }
161 }
162 
~PluginBinary()163 PluginBinary::~PluginBinary() {
164   std::vector<Plugin*>::iterator i = _plugins.begin();
165   while (i != _plugins.end()) {
166     const APICache::PluginAPICacheI &api = (*i)->getApiHandler();
167     api.unloadPlugin(*i);
168     delete *i;
169     i++;
170   }
171   // release the last reference to the binary, which should unload it
172   // if this reference was taken by loadPluginInfo().
173   if (_binary) {
174     if (_binary->isLoaded()) {
175       _binary->unref();
176     }
177     assert(!_binary->isLoaded());
178   }
179 }
180 
PluginHandle(Plugin * p,OFX::Host::Host * host)181 PluginHandle::PluginHandle(Plugin *p, OFX::Host::Host *host)
182 {
183   _b = p->getBinary();
184   if (_b->_binary) {
185     _b->_binary->ref();
186   }
187   _op = 0;
188   OfxPlugin* (*getPlug)(int) = 0;
189   if (_b->_getPluginFunc) {
190     getPlug = _b->_getPluginFunc;
191   }
192   if (!getPlug && _b->_binary) {
193     getPlug = (OfxPlugin*(*)(int)) _b->_binary->findSymbol("OfxGetPlugin");
194   }
195   if (getPlug) {
196     _op = getPlug(p->getIndex());
197     if (_op) {
198       _op->setHost(host->getHandle());
199     }
200   }
201 }
202 
~PluginHandle()203 PluginHandle::~PluginHandle() {
204   if (_b->_binary) {
205     _b->_binary->unref();
206   }
207 }
208 
209 
210 #if defined (WINDOWS)
211 const std::wstring&
getStdOFXPluginPath(const std::string & hostId)212 PluginCache::getStdOFXPluginPath(const std::string &hostId)
213 {
214   static std::wstring ret;
215   static int gotIt = 0;
216   if(!gotIt) {
217 	gotIt = 1;
218     wchar_t buffer[MAX_PATH];
219     SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, SHGFP_TYPE_CURRENT, buffer);
220 	std::wstring str;
221 	str.append(L"\\OFX\\");
222 	str.append(OFX::utf8_to_utf16(hostId));
223 	wcscat_s(buffer, MAX_PATH, str.c_str());
224 	ret = std::wstring(buffer);
225   }
226   return ret;
227 }
228 #endif
229 
230 static
OFXGetEnv(const char * e)231 std::string OFXGetEnv(const char* e)
232 {
233 #if defined(WINDOWS) && !defined(__MINGW32__)
234   size_t requiredSize;
235   getenv_s(&requiredSize, 0, 0, e);
236   std::vector<char> buffer(requiredSize);
237   if(requiredSize >0)
238     {
239       getenv_s(&requiredSize, &buffer.front(), requiredSize, e);
240       return &buffer.front();
241     }
242   return "";
243 #else
244   if(getenv(e))
245     return getenv(e);
246 #endif
247   return "";
248 }
249 
getPluginCache()250 PluginCache* PluginCache::getPluginCache()
251 {
252   if(!gPluginCachePtr)
253     gPluginCachePtr = new PluginCache();
254   return gPluginCachePtr;
255 }
256 
clearPluginCache()257 void PluginCache::clearPluginCache()
258 {
259   delete gPluginCachePtr;
260   gPluginCachePtr = 0;
261 }
262 
~PluginCache()263 PluginCache::~PluginCache()
264 {
265   for(std::list<PluginBinary *>::iterator it=_binaries.begin(); it != _binaries.end(); ++it) {
266     delete (*it);
267   }
268   _binaries.clear();
269 }
270 
PluginCache()271 PluginCache::PluginCache()
272 : _hostSpec(NULL)
273 #ifdef OFX_USE_STATIC_PLUGINS
274 , _staticBinary(NULL)
275 #endif
276 , _xmlCurrentBinary(NULL)
277 , _xmlCurrentPlugin(NULL)
278 {
279   _cacheVersion = "";
280   _ignoreCache = false;
281   _dirty = false;
282   _enablePluginSeek = true;
283 
284   std::string s = OFXGetEnv("OFX_PLUGIN_PATH");
285 
286 
287   while (s.length()) {
288 
289     int spos = int(s.find_first_of(DIRLIST_SEP_CHARS));
290 
291     std::string path;
292 
293     if (spos != -1) {
294       path = s.substr(0, spos);
295       s = s.substr(spos+1);
296     }
297     else {
298       path = s;
299       s = "";
300     }
301 
302     _pluginPath.push_back(path);
303   }
304 
305   if (!_useStdOFXPluginsLocation)
306     return;
307 
308 #if defined(WINDOWS)
309 
310   std::wstring wpath = getStdOFXPluginPath();
311   std::string path = OFX::utf16_to_utf8(wpath);
312 
313   _pluginPath.push_back(path);
314   _pluginPath.push_back("C:\\Program Files\\Common Files\\OFX\\Plugins");
315 #endif
316 #if defined(__linux__) || defined(__DragonFly__)
317   _pluginPath.push_back("/usr/local/OFX/Plugins");
318 #endif
319 #if defined(__APPLE__)
320   _pluginPath.push_back("/Library/OFX/Plugins");
321 #endif
322 }
323 
setPluginHostPath(const std::string & hostId)324 void PluginCache::setPluginHostPath(const std::string &hostId) {
325 #if defined(WINDOWS)
326   std::wstring wpath = getStdOFXPluginPath(hostId);
327   std::string path = OFX::utf16_to_utf8(wpath);
328 
329   _pluginPath.push_back(path);
330   _pluginPath.push_back("C:\\Program Files\\Common Files\\OFX\\" + hostId);
331 #endif
332 #if defined(__linux__) || defined(__DragonFly__)
333   _pluginPath.push_back("/usr/OFX/" + hostId);
334 #endif
335 #if defined(__APPLE__)
336   _pluginPath.push_back("/Library/OFX/" + hostId);
337 #endif
338 }
339 
scanDirectory(std::set<std::string> & foundBinFiles,const std::string & dir,bool recurse)340 void PluginCache::scanDirectory(std::set<std::string> &foundBinFiles, const std::string &dir, bool recurse)
341 {
342 #ifdef CACHE_DEBUG
343   printf("looking in %s for plugins\n", dir.c_str());
344 #endif
345 
346 #if defined (WINDOWS)
347   WIN32_FIND_DATAW findData;
348   HANDLE findHandle;
349 #else
350   DIR *d = opendir(dir.c_str());
351   if (!d) {
352     return;
353   }
354 #endif
355 
356   _pluginDirs.push_back(dir.c_str());
357 
358 #if defined (UNIX)
359   while (dirent *de = readdir(d))
360 #elif defined (WINDOWS)
361     {
362 
363       std::wstring ws = OFX::utf8_to_utf16((dir + "\\*"));
364       findHandle = FindFirstFileW(ws.c_str(), &findData);
365     }
366   if (findHandle == INVALID_HANDLE_VALUE)
367     {
368       return;
369     }
370 
371   while (1)
372 #endif
373     {
374 #if defined (UNIX)
375       std::string name = de->d_name;
376       bool isdir = true;
377 #else
378       std::wstring wname = findData.cFileName;
379       std::string name = OFX::utf16_to_utf8(wname);
380 
381       bool isdir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
382 #endif
383       if (name.find(".ofx.bundle") != std::string::npos) {
384         std::string barename = name.substr(0, name.length() - strlen(".bundle"));
385         std::string bundlename = dir + DIRSEP + name;
386         std::string binpath = dir + DIRSEP + name + DIRSEP "Contents" DIRSEP + ARCHSTR + DIRSEP + barename;
387 
388         // don't insert binpath yet, do it later because of Mac OS X Universal stuff
389         //foundBinFiles.insert(binpath);
390 
391 #if defined(__APPLE__) && (defined(__x86_64) || defined(__x86_64__))
392         /* From the OpenFX specification:
393 
394            MacOS-x86-64 - for Apple Macintosh OS X, specifically on
395            intel x86 CPUs running AMD's 64 bit extensions. 64 bit host
396            applications should check this first, and if it doesn't
397            exist or is empty, fall back to "MacOS" looking for a
398            universal binary.
399         */
400 
401         std::string binpath_universal = dir + DIRSEP + name + DIRSEP "Contents" DIRSEP + "MacOS" + DIRSEP + barename;
402         if (_knownBinFiles.find(binpath_universal) != _knownBinFiles.end()) {
403           binpath = binpath_universal;
404         }
405 #endif
406 
407         if (_knownBinFiles.find(binpath) == _knownBinFiles.end()) {
408 #ifdef CACHE_DEBUG
409           printf("found non-cached binary %s\n", binpath.c_str());
410 #endif
411           _dirty = true;
412 
413           // the binary was not in the cache
414 
415           PluginBinary *pb = 0;
416 #if defined(__x86_64) || defined(__x86_64__)
417           pb = new PluginBinary(binpath, bundlename, this);
418 #  if defined(__APPLE__)
419           if (pb->isInvalid()) {
420             // fallback to "MacOS"
421             delete pb;
422             binpath = binpath_universal;
423             pb = new PluginBinary(binpath, bundlename, this);
424           }
425 #  endif
426 #else
427           pb = new PluginBinary(binpath, bundlename, this);
428 #endif
429           _binaries.push_back(pb);
430           _knownBinFiles.insert(binpath);
431           foundBinFiles.insert(binpath);
432 
433           for (int j=0;j<pb->getNPlugins();j++) {
434             Plugin *plug = &pb->getPlugin(j);
435             const APICache::PluginAPICacheI &api = plug->getApiHandler();
436             api.loadFromPlugin(plug);
437           }
438         } else {
439 #ifdef CACHE_DEBUG
440           printf("found cached binary %s\n", binpath.c_str());
441 #endif
442         }
443         // insert final path (universal or not) in the list of found files
444         foundBinFiles.insert(binpath);
445       } else {
446         if (isdir && (recurse && !name.empty() && name[0] != '@' && name[name.size() - 1] != '.')) {
447           scanDirectory(foundBinFiles, dir + DIRSEP + name, recurse);
448         }
449       }
450 #if defined(WINDOWS)
451       int rval = FindNextFileW(findHandle, &findData);
452 
453       if (rval == 0) {
454         break;
455       }
456 #endif
457     }
458 
459 #if defined(UNIX)
460   closedir(d);
461 #else
462   FindClose(findHandle);
463 #endif
464 }
465 
seekPluginFile(const std::string & baseName) const466 std::string PluginCache::seekPluginFile(const std::string &baseName) const {
467   // Exit early if disabled
468   if (!_enablePluginSeek)
469     return "";
470 
471   for (std::list<std::string>::const_iterator paths= _pluginDirs.begin();
472        paths != _pluginDirs.end();
473        paths++) {
474     std::string candidate = *paths + DIRSEP + baseName;
475     FILE *f = fopen(candidate.c_str(), "r");
476     if (f) {
477       fclose(f);
478       return candidate;
479     }
480   }
481   return "";
482 }
483 
scanPluginFiles()484 void PluginCache::scanPluginFiles()
485 {
486   std::set<std::string> foundBinFiles;
487 
488   for (std::list<std::string>::iterator paths= _pluginPath.begin();
489        paths != _pluginPath.end();
490        paths++) {
491     scanDirectory(foundBinFiles, *paths, _nonrecursePath.find(*paths) == _nonrecursePath.end());
492   }
493 
494 #ifdef OFX_USE_STATIC_PLUGINS
495   // Now add all static plug-ins
496 
497   // If already loaded from the cache, the static binary must point to the same host binary file
498   assert(!_staticBinary || _staticBinary->getFilePath() == _hostAppBinFilePath);
499   if (!_staticBinary) {
500     _staticBinary = new PluginBinary(_hostAppBinFilePath, &OfxGetNumberOfPlugins,&OfxGetPlugin,this, 0, 0);
501     if (_staticBinary->isInvalid()) {
502       std::cerr << "WARNING: ignoring statically linked plug-ins because host application binary file path (" << _hostAppBinFilePath << ") is wrongly set" << std::endl;
503       delete _staticBinary;
504       _staticBinary = 0;
505     } else {
506       _dirty = true;
507       for (int j=0;j<_staticBinary->getNPlugins();j++) {
508          Plugin *plug = &_staticBinary->getPlugin(j);
509          const APICache::PluginAPICacheI &api = plug->getApiHandler();
510          api.loadFromPlugin(plug);
511       }
512       _binaries.push_back(_staticBinary);
513     }
514   }
515 #endif
516 
517   std::list<PluginBinary *>::iterator i=_binaries.begin();
518   while (i!=_binaries.end()) {
519     PluginBinary *pb = *i;
520 
521     if (!pb->isStaticallyLinkedPlugin() && foundBinFiles.find(pb->getFilePath()) == foundBinFiles.end()) {
522 
523       // the binary was in the cache, but was not on the path
524 
525       _dirty = true;
526       i = _binaries.erase(i);
527       delete pb;
528 
529     } else {
530 
531       bool binChanged = pb->hasBinaryChanged();
532 
533       // the binary was in the cache, but the binary has changed and thus we need to reload
534       if (binChanged) {
535         // The static PluginBinary already had loadPluginInfo called anyway at this point
536         if (!pb->isStaticallyLinkedPlugin()) {
537           pb->loadPluginInfo(this);
538         }
539         _dirty = true;
540       }
541 
542       for (int j=0;j<pb->getNPlugins();j++) {
543         Plugin *plug = &pb->getPlugin(j);
544         APICache::PluginAPICacheI &api = plug->getApiHandler();
545 
546         if (binChanged) {
547           api.loadFromPlugin(plug);
548         }
549 
550         std::string reason;
551 
552         if (api.pluginSupported(plug, reason)) {
553           _plugins.push_back(plug);
554           api.confirmPlugin(plug, _pluginPath);
555         } else {
556           std::cerr << "ignoring plugin " << plug->getIdentifier() <<
557             " as unsupported (" << reason << ")" << std::endl;
558         }
559       }
560 
561       i++;
562     }
563   }
564 }
565 
566 
567 /// callback for XML parser
elementBeginHandler(void * userData,const XML_Char * name,const XML_Char ** atts)568 static void elementBeginHandler(void *userData, const XML_Char *name, const XML_Char **atts) {
569   PluginCache::getPluginCache()->elementBeginCallback(userData, name, atts);
570 }
571 
572 /// callback for XML parser
elementCharHandler(void * userData,const XML_Char * data,int len)573 static void elementCharHandler(void *userData, const XML_Char *data, int len) {
574   PluginCache::getPluginCache()->elementCharCallback(userData, data, len);
575 }
576 
577 /// callback for XML parser
elementEndHandler(void * userData,const XML_Char * name)578 static void elementEndHandler(void *userData, const XML_Char *name) {
579   PluginCache::getPluginCache()->elementEndCallback(userData, name);
580 }
581 
mapHasAll(const std::map<std::string,std::string> & attmap,const char ** atts)582 static bool mapHasAll(const std::map<std::string, std::string> &attmap, const char **atts) {
583   while (*atts) {
584     if (attmap.find(*atts) == attmap.end()) {
585       return false;
586     }
587     atts++;
588   }
589   return true;
590 }
591 
elementBeginCallback(void *,const XML_Char * name,const XML_Char ** atts)592 void PluginCache::elementBeginCallback(void */*userData*/, const XML_Char *name, const XML_Char **atts) {
593   if (_ignoreCache) {
594     return;
595   }
596 
597   std::string ename = name;
598   std::map<std::string, std::string> attmap;
599 
600   while (*atts) {
601     attmap[atts[0]] = atts[1];
602     atts += 2;
603   }
604 
605   /// XXX: validate in general
606 
607   if (ename == "cache") {
608     std::string cacheversion = attmap["version"];
609     if (cacheversion != _cacheVersion) {
610 #ifdef CACHE_DEBUG
611       printf("mismatched version, ignoring cache (got '%s', wanted '%s')\n",
612              cacheversion.c_str(),
613              _cacheVersion.c_str());
614 #endif
615       _ignoreCache = true;
616     }
617   }
618 
619   if (ename == "binary") {
620     const char *binAtts[] = {"static_bin", "path", "bundle_path", "mtime", "size", NULL};
621 
622     if (!mapHasAll(attmap, binAtts)) {
623       // no path: bad XML
624     }
625 
626 #ifdef OFX_USE_STATIC_PLUGINS
627     bool isStaticLinkedCachedBinary = OFX::Host::Property::stringToInt(attmap["static_bin"]);
628 #endif
629 
630     std::string fname = attmap["path"];
631     std::string bname = attmap["bundle_path"];
632     time_t mtime = OFX::Host::Property::stringToInt(attmap["mtime"]);
633     off_t size = OFX::Host::Property::stringToInt(attmap["size"]);
634 
635     PluginBinary* pb;
636 #ifdef OFX_USE_STATIC_PLUGINS
637     if (isStaticLinkedCachedBinary) {
638       // only 1 static binary allowed!
639       if (_staticBinary) {
640         _ignoreCache = true;
641         return;
642       }
643       // We need to provide the 2 function pointers for the static binary
644       pb = new PluginBinary(_hostAppBinFilePath, &OfxGetNumberOfPlugins,&OfxGetPlugin, this, &fname, &mtime, &size);
645       _staticBinary = pb;
646     } else
647 #endif
648     {
649       pb = new PluginBinary(fname, bname, mtime, size);
650     }
651     _xmlCurrentBinary = pb;
652     _binaries.push_back(_xmlCurrentBinary);
653     _knownBinFiles.insert(fname);
654 
655     return;
656   }
657 
658   if (ename == "plugin" && _xmlCurrentBinary && !_xmlCurrentBinary->hasBinaryChanged()) {
659     const char *plugAtts[] = {"api", "name", "index", "api_version", "major_version", "minor_version", NULL};
660 
661     if (!mapHasAll(attmap, plugAtts)) {
662       // no path: bad XML
663     }
664 
665     std::string api = attmap["api"];
666     std::string rawIdentifier = attmap["name"];
667 
668     std::string identifier = rawIdentifier;
669 
670     // Who says the pluginIdentifier is case-insensitive? OFX 1.3 spec doesn't mention this.
671     // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id472588
672     //for (size_t i=0;i<identifier.size();i++) {
673     //  identifier[i] = tolower(identifier[i]);
674     //}
675 
676     int idx = OFX::Host::Property::stringToInt(attmap["index"]);
677     int api_version = OFX::Host::Property::stringToInt(attmap["api_version"]);
678     int major_version = OFX::Host::Property::stringToInt(attmap["major_version"]);
679     int minor_version = OFX::Host::Property::stringToInt(attmap["minor_version"]);
680 
681     APICache::PluginAPICacheI *apiCache = findApiHandler(api, api_version);
682     if (apiCache) {
683 
684       Plugin *pe = apiCache->newPlugin(_xmlCurrentBinary, idx, api, api_version, identifier, rawIdentifier, major_version, minor_version);
685       _xmlCurrentBinary->addPlugin(pe);
686       _xmlCurrentPlugin = pe;
687       apiCache->beginXmlParsing(pe);
688     }
689 
690     return;
691   }
692 
693   if (_xmlCurrentPlugin) {
694     APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
695     api.xmlElementBegin(name, attmap);
696   }
697 
698 }
699 
elementCharCallback(void *,const XML_Char * data,int size)700 void PluginCache::elementCharCallback(void */*userData*/, const XML_Char *data, int size)
701 {
702   if (_ignoreCache) {
703     return;
704   }
705 
706   std::string s(data, size);
707   if (_xmlCurrentPlugin) {
708     APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
709     api.xmlCharacterHandler(s);
710   } else {
711     /// XXX: we only want whitespace
712   }
713 }
714 
elementEndCallback(void *,const XML_Char * name)715 void PluginCache::elementEndCallback(void */*userData*/, const XML_Char *name) {
716   if (_ignoreCache) {
717     return;
718   }
719 
720   std::string ename = name;
721 
722   /// XXX: validation?
723 
724   if (ename == "plugin") {
725     if (_xmlCurrentPlugin) {
726       APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
727       api.endXmlParsing();
728     }
729     _xmlCurrentPlugin = 0;
730     return;
731   }
732 
733   if (ename == "bundle") {
734     _xmlCurrentBinary = 0;
735     return;
736   }
737 
738   if (_xmlCurrentPlugin) {
739     APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
740     api.xmlElementEnd(name);
741   }
742 }
743 
readCache(std::istream & ifs)744 void PluginCache::readCache(std::istream &ifs) {
745   XML_Parser xP = XML_ParserCreate(NULL);
746   if (!xP) {
747     throw std::runtime_error("Error creating XML parser");
748 
749   }
750   XML_SetElementHandler(xP, elementBeginHandler, elementEndHandler);
751   XML_SetCharacterDataHandler(xP, elementCharHandler);
752   int done = false;
753   char buf[4096];
754   while (!done && !ifs.eof()) {
755     if (!ifs.good()) {
756       throw std::runtime_error("Error reading cache stream");
757     }
758     ifs.read(buf, sizeof(buf));
759     int n = (int)ifs.gcount();
760     done = (n != sizeof(buf));
761 
762     if (XML_Parse(xP, buf, n, done) == XML_STATUS_ERROR) {
763       XML_Error errorCode = XML_GetErrorCode(xP);
764       XML_Size errorLine = XML_GetCurrentLineNumber(xP);
765       XML_Size errorCol = XML_GetCurrentColumnNumber(xP);
766       const XML_LChar *errorString = XML_ErrorString(errorCode);
767       std::stringstream errorDesc;
768       errorDesc << "XML parsing error at line " << errorLine << ":" << errorCol;
769       errorDesc << ": " << errorString;
770       XML_ParserFree(xP);
771       throw std::runtime_error(errorDesc.str());
772     }
773   }
774   XML_ParserFree(xP);
775 }
776 
writePluginCache(std::ostream & os) const777 void PluginCache::writePluginCache(std::ostream &os) const {
778 #ifdef CACHE_DEBUG
779   printf("writing pluginCache with version = %s\n", _cacheVersion.c_str());
780 #endif
781 
782   os << "<cache version=\"" << _cacheVersion << "\">\n";
783   for (std::list<PluginBinary *>::const_iterator i=_binaries.begin();i!=_binaries.end();i++) {
784     PluginBinary *b = *i;
785 
786     std::vector<Plugin*> plugins(b->getNPlugins());
787     for (std::size_t j = 0; j < plugins.size(); ++j) {
788       plugins[j] = &b->getPlugin((int)j);
789     }
790 
791     os << "<bundle>\n";
792     os << "  <binary "
793        << XML::attribute("static_bin", int(b->isStaticallyLinkedPlugin()))
794        << XML::attribute("bundle_path", b->getBundlePath())
795        << XML::attribute("path", b->getFilePath())
796        << XML::attribute("mtime", int(b->getFileModificationTime()))
797        << XML::attribute("size", int(b->getFileSize())) << "/>\n";
798 
799     for (int j=0;j<(int)plugins.size();j++) {
800       Plugin *p = plugins[j];
801 
802 
803       os << "  <plugin "
804          << XML::attribute("name", p->getRawIdentifier())
805          << XML::attribute("index", p->getIndex())
806          << XML::attribute("api", p->getPluginApi())
807          << XML::attribute("api_version", p->getApiVersion())
808          << XML::attribute("major_version", p->getVersionMajor())
809          << XML::attribute("minor_version", p->getVersionMinor())
810          << ">\n";
811 
812       const APICache::PluginAPICacheI &api = p->getApiHandler();
813       os << "    <apiproperties>\n";
814       api.saveXML(p, os);
815       os << "    </apiproperties>\n";
816 
817       os << "  </plugin>\n";
818     }
819     os << "</bundle>\n";
820   }
821   os << "</cache>\n";
822 }
823 
824 
findApiHandler(const std::string & api,int version)825 APICache::PluginAPICacheI *PluginCache::findApiHandler(const std::string &api, int version) {
826   std::list<PluginCacheSupportedApi>::iterator i = _apiHandlers.begin();
827   while (i != _apiHandlers.end()) {
828     if (i->matches(api, version)) {
829       return i->handler;
830     }
831     i++;
832   }
833   return 0;
834 }
835