You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

666 lines
17KB

  1. #include <plugin.hpp>
  2. #include <system.hpp>
  3. #include <network.hpp>
  4. #include <asset.hpp>
  5. #include <string.hpp>
  6. #include <app.hpp>
  7. #include <app/common.hpp>
  8. #include <plugin/callbacks.hpp>
  9. #include <settings.hpp>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <unistd.h>
  13. #include <sys/param.h> // for MAXPATHLEN
  14. #include <fcntl.h>
  15. #include <thread>
  16. #include <map>
  17. #include <stdexcept>
  18. #define ZIP_STATIC
  19. #include <zip.h>
  20. #include <jansson.h>
  21. #if defined ARCH_WIN
  22. #include <windows.h>
  23. #include <direct.h>
  24. #define mkdir(_dir, _perms) _mkdir(_dir)
  25. #else
  26. #include <dlfcn.h>
  27. #endif
  28. #include <dirent.h>
  29. #include <osdialog.h>
  30. namespace rack {
  31. namespace plugin {
  32. ////////////////////
  33. // private API
  34. ////////////////////
  35. typedef void (*InitCallback)(Plugin*);
  36. static InitCallback loadLibrary(Plugin *plugin) {
  37. // Load plugin library
  38. std::string libraryFilename;
  39. #if defined ARCH_LIN
  40. libraryFilename = plugin->path + "/" + "plugin.so";
  41. #elif defined ARCH_WIN
  42. libraryFilename = plugin->path + "/" + "plugin.dll";
  43. #elif ARCH_MAC
  44. libraryFilename = plugin->path + "/" + "plugin.dylib";
  45. #endif
  46. // Check file existence
  47. if (!system::isFile(libraryFilename)) {
  48. throw UserException(string::f("Library %s does not exist", libraryFilename.c_str()));
  49. }
  50. // Load dynamic/shared library
  51. #if defined ARCH_WIN
  52. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  53. HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
  54. SetErrorMode(0);
  55. if (!handle) {
  56. int error = GetLastError();
  57. throw UserException(string::f("Failed to load library %s: code %d", libraryFilename.c_str(), error));
  58. }
  59. #else
  60. void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
  61. if (!handle) {
  62. throw UserException(string::f("Failed to load library %s: %s", libraryFilename.c_str(), dlerror()));
  63. }
  64. #endif
  65. plugin->handle = handle;
  66. // Get plugin's init() function
  67. InitCallback initCallback;
  68. #if defined ARCH_WIN
  69. initCallback = (InitCallback) GetProcAddress(handle, "init");
  70. #else
  71. initCallback = (InitCallback) dlsym(handle, "init");
  72. #endif
  73. if (!initCallback) {
  74. throw UserException(string::f("Failed to read init() symbol in %s", libraryFilename.c_str()));
  75. }
  76. return initCallback;
  77. }
  78. /** If path is blank, loads Core */
  79. static Plugin *loadPlugin(std::string path) {
  80. Plugin *plugin = new Plugin;
  81. try {
  82. plugin->path = path;
  83. // Get modified timestamp
  84. if (path != "") {
  85. struct stat statbuf;
  86. if (!stat(path.c_str(), &statbuf)) {
  87. #if defined ARCH_MAC
  88. plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9;
  89. #elif defined ARCH_WIN
  90. plugin->modifiedTimestamp = (double) statbuf.st_mtime;
  91. #elif defined ARCH_LIN
  92. plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9;
  93. #endif
  94. }
  95. }
  96. // DEBUG("%lf", plugin->modifiedTimestamp);
  97. // Load plugin.json
  98. std::string metadataFilename = (path == "") ? asset::system("Core.json") : (path + "/plugin.json");
  99. FILE *file = fopen(metadataFilename.c_str(), "r");
  100. if (!file) {
  101. throw UserException(string::f("Metadata file %s does not exist", metadataFilename.c_str()));
  102. }
  103. DEFER({
  104. fclose(file);
  105. });
  106. json_error_t error;
  107. json_t *rootJ = json_loadf(file, 0, &error);
  108. if (!rootJ) {
  109. throw UserException(string::f("JSON parsing error at %s %d:%d %s", metadataFilename.c_str(), error.line, error.column, error.text));
  110. }
  111. DEFER({
  112. json_decref(rootJ);
  113. });
  114. // Call init callback
  115. InitCallback initCallback;
  116. if (path == "") {
  117. initCallback = ::init;
  118. }
  119. else {
  120. initCallback = loadLibrary(plugin);
  121. }
  122. initCallback(plugin);
  123. // Load manifest
  124. plugin->fromJson(rootJ);
  125. // Reject plugin if slug already exists
  126. Plugin *oldPlugin = getPlugin(plugin->slug);
  127. if (oldPlugin) {
  128. throw UserException(string::f("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()));
  129. }
  130. INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), path.c_str());
  131. plugins.push_back(plugin);
  132. }
  133. catch (UserException &e) {
  134. WARN("Could not load plugin %s: %s", path.c_str(), e.what());
  135. delete plugin;
  136. plugin = NULL;
  137. }
  138. return plugin;
  139. }
  140. static void loadPlugins(std::string path) {
  141. for (std::string pluginPath : system::getEntries(path)) {
  142. if (!system::isDirectory(pluginPath))
  143. continue;
  144. if (!loadPlugin(pluginPath)) {
  145. // Ignore bad plugins. They are reported in the log.
  146. }
  147. }
  148. }
  149. /** Returns 0 if successful */
  150. static int extractZipHandle(zip_t *za, std::string dir) {
  151. int err;
  152. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  153. zip_stat_t zs;
  154. err = zip_stat_index(za, i, 0, &zs);
  155. if (err) {
  156. WARN("zip_stat_index() failed: error %d", err);
  157. return err;
  158. }
  159. std::string path = dir + "/" + zs.name;
  160. if (path[path.size() - 1] == '/') {
  161. system::createDirectory(path);
  162. // HACK
  163. // Create and delete file to update the directory's mtime.
  164. std::string tmpPath = path + "/.tmp";
  165. FILE *tmpFile = fopen(tmpPath.c_str(), "w");
  166. fclose(tmpFile);
  167. std::remove(tmpPath.c_str());
  168. }
  169. else {
  170. zip_file_t *zf = zip_fopen_index(za, i, 0);
  171. if (!zf) {
  172. WARN("zip_fopen_index() failed");
  173. return -1;
  174. }
  175. FILE *outFile = fopen(path.c_str(), "wb");
  176. if (!outFile)
  177. continue;
  178. while (1) {
  179. char buffer[1 << 15];
  180. int len = zip_fread(zf, buffer, sizeof(buffer));
  181. if (len <= 0)
  182. break;
  183. fwrite(buffer, 1, len, outFile);
  184. }
  185. err = zip_fclose(zf);
  186. if (err) {
  187. WARN("zip_fclose() failed: error %d", err);
  188. return err;
  189. }
  190. fclose(outFile);
  191. }
  192. }
  193. return 0;
  194. }
  195. /** Returns 0 if successful */
  196. static int extractZip(std::string filename, std::string path) {
  197. int err;
  198. zip_t *za = zip_open(filename.c_str(), 0, &err);
  199. if (!za) {
  200. WARN("Could not open zip %s: error %d", filename.c_str(), err);
  201. return err;
  202. }
  203. DEFER({
  204. zip_close(za);
  205. });
  206. err = extractZipHandle(za, path);
  207. return err;
  208. }
  209. static void extractPackages(std::string path) {
  210. std::string message;
  211. for (std::string packagePath : system::getEntries(path)) {
  212. if (string::filenameExtension(packagePath) != "zip")
  213. continue;
  214. INFO("Extracting package %s", packagePath.c_str());
  215. // Extract package
  216. if (extractZip(packagePath, path)) {
  217. WARN("Package %s failed to extract", packagePath.c_str());
  218. message += string::f("Could not extract package %s\n", packagePath.c_str());
  219. continue;
  220. }
  221. // Remove package
  222. if (remove(packagePath.c_str())) {
  223. WARN("Could not delete file %s: error %d", packagePath.c_str(), errno);
  224. }
  225. }
  226. if (!message.empty()) {
  227. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  228. }
  229. }
  230. ////////////////////
  231. // public API
  232. ////////////////////
  233. void init() {
  234. // Load Core
  235. loadPlugin("");
  236. // Get user plugins directory
  237. mkdir(asset::pluginsPath.c_str(), 0755);
  238. // Extract packages and load plugins
  239. extractPackages(asset::pluginsPath);
  240. loadPlugins(asset::pluginsPath);
  241. // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it
  242. std::string fundamentalSrc = asset::system("Fundamental.zip");
  243. std::string fundamentalDir = asset::pluginsPath + "/Fundamental";
  244. if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) {
  245. INFO("Extracting bundled Fundamental package");
  246. extractZip(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
  247. loadPlugin(fundamentalDir);
  248. }
  249. // Sync in a detached thread
  250. if (!settings::devMode) {
  251. std::thread t([] {
  252. queryUpdates();
  253. });
  254. t.detach();
  255. }
  256. }
  257. void destroy() {
  258. for (Plugin *plugin : plugins) {
  259. // Free library handle
  260. #if defined ARCH_WIN
  261. if (plugin->handle)
  262. FreeLibrary((HINSTANCE) plugin->handle);
  263. #else
  264. if (plugin->handle)
  265. dlclose(plugin->handle);
  266. #endif
  267. // For some reason this segfaults.
  268. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  269. // delete plugin;
  270. }
  271. plugins.clear();
  272. }
  273. void logIn(const std::string &email, const std::string &password) {
  274. loginStatus = "Logging in...";
  275. json_t *reqJ = json_object();
  276. json_object_set(reqJ, "email", json_string(email.c_str()));
  277. json_object_set(reqJ, "password", json_string(password.c_str()));
  278. std::string url = app::API_URL + "/token";
  279. json_t *resJ = network::requestJson(network::METHOD_POST, url, reqJ);
  280. json_decref(reqJ);
  281. if (!resJ) {
  282. loginStatus = "No response from server";
  283. return;
  284. }
  285. DEFER({
  286. json_decref(resJ);
  287. });
  288. json_t *errorJ = json_object_get(resJ, "error");
  289. if (errorJ) {
  290. const char *errorStr = json_string_value(errorJ);
  291. loginStatus = errorStr;
  292. return;
  293. }
  294. json_t *tokenJ = json_object_get(resJ, "token");
  295. if (!tokenJ) {
  296. loginStatus = "No token in response";
  297. return;
  298. }
  299. const char *tokenStr = json_string_value(tokenJ);
  300. settings::token = tokenStr;
  301. loginStatus = "";
  302. queryUpdates();
  303. }
  304. void logOut() {
  305. settings::token = "";
  306. updates.clear();
  307. }
  308. bool isLoggedIn() {
  309. return settings::token != "";
  310. }
  311. void queryUpdates() {
  312. if (settings::token.empty())
  313. return;
  314. updates.clear();
  315. updateStatus = "Querying for updates...";
  316. // Get user's plugins list
  317. std::string pluginsUrl = app::API_URL + "/plugins";
  318. json_t *pluginsReqJ = json_object();
  319. json_object_set(pluginsReqJ, "token", json_string(settings::token.c_str()));
  320. json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ);
  321. json_decref(pluginsReqJ);
  322. if (!pluginsResJ) {
  323. WARN("Request for user's plugins failed");
  324. updateStatus = "Could not query updates";
  325. return;
  326. }
  327. DEFER({
  328. json_decref(pluginsResJ);
  329. });
  330. json_t *errorJ = json_object_get(pluginsResJ, "error");
  331. if (errorJ) {
  332. WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
  333. updateStatus = "Could not query updates";
  334. return;
  335. }
  336. // Get library manifests
  337. std::string manifestsUrl = app::API_URL + "/library/manifests";
  338. json_t *manifestsReq = json_object();
  339. json_object_set(manifestsReq, "version", json_string(app::API_VERSION.c_str()));
  340. json_t *manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, manifestsReq);
  341. json_decref(manifestsReq);
  342. if (!manifestsResJ) {
  343. WARN("Request for library manifests failed");
  344. updateStatus = "Could not query updates";
  345. return;
  346. }
  347. DEFER({
  348. json_decref(manifestsResJ);
  349. });
  350. json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
  351. json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
  352. size_t pluginIndex;
  353. json_t *pluginJ;
  354. json_array_foreach(pluginsJ, pluginIndex, pluginJ) {
  355. Update update;
  356. // Get plugin manifest
  357. update.pluginSlug = json_string_value(pluginJ);
  358. json_t *manifestJ = json_object_get(manifestsJ, update.pluginSlug.c_str());
  359. if (!manifestJ) {
  360. WARN("VCV account has plugin %s but no manifest was found", update.pluginSlug.c_str());
  361. continue;
  362. }
  363. // Get plugin name
  364. json_t *nameJ = json_object_get(manifestJ, "name");
  365. if (nameJ)
  366. update.pluginName = json_string_value(nameJ);
  367. // Get version
  368. json_t *versionJ = json_object_get(manifestJ, "version");
  369. if (!versionJ) {
  370. WARN("Plugin %s has no version in manifest", update.pluginSlug.c_str());
  371. continue;
  372. }
  373. update.version = json_string_value(versionJ);
  374. // Check if update is needed
  375. Plugin *p = getPlugin(update.pluginSlug);
  376. if (p && p->version == update.version)
  377. continue;
  378. // Check status
  379. json_t *statusJ = json_object_get(manifestJ, "status");
  380. if (!statusJ)
  381. continue;
  382. std::string status = json_string_value(statusJ);
  383. if (status != "available")
  384. continue;
  385. // Get changelog URL
  386. json_t *changelogUrlJ = json_object_get(manifestJ, "changelogUrl");
  387. if (changelogUrlJ) {
  388. update.changelogUrl = json_string_value(changelogUrlJ);
  389. }
  390. updates.push_back(update);
  391. }
  392. updateStatus = "";
  393. }
  394. bool hasUpdates() {
  395. for (Update &update : updates) {
  396. if (update.progress < 1.f)
  397. return true;
  398. }
  399. return false;
  400. }
  401. static bool isSyncingUpdate = false;
  402. static bool isSyncingUpdates = false;
  403. void syncUpdate(Update *update) {
  404. isSyncingUpdate = true;
  405. DEFER({
  406. isSyncingUpdate = false;
  407. });
  408. #if defined ARCH_WIN
  409. std::string arch = "win";
  410. #elif ARCH_MAC
  411. std::string arch = "mac";
  412. #elif defined ARCH_LIN
  413. std::string arch = "lin";
  414. #endif
  415. std::string downloadUrl = app::API_URL + "/download";
  416. downloadUrl += "?token=" + network::encodeUrl(settings::token);
  417. downloadUrl += "&slug=" + network::encodeUrl(update->pluginSlug);
  418. downloadUrl += "&version=" + network::encodeUrl(update->version);
  419. downloadUrl += "&arch=" + network::encodeUrl(arch);
  420. INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), arch.c_str());
  421. // Download zip
  422. std::string pluginDest = asset::pluginsPath + "/" + update->pluginSlug + ".zip";
  423. if (!network::requestDownload(downloadUrl, pluginDest, &update->progress)) {
  424. WARN("Plugin %s download was unsuccessful", update->pluginSlug.c_str());
  425. return;
  426. }
  427. }
  428. void syncUpdates() {
  429. isSyncingUpdates = true;
  430. DEFER({
  431. isSyncingUpdates = false;
  432. });
  433. if (settings::token.empty())
  434. return;
  435. for (Update &update : updates) {
  436. if (update.progress < 1.f)
  437. syncUpdate(&update);
  438. }
  439. restartRequested = true;
  440. }
  441. bool isSyncing() {
  442. return isSyncingUpdate || isSyncingUpdates;
  443. }
  444. Plugin *getPlugin(const std::string &pluginSlug) {
  445. std::string slug = normalizeSlug(pluginSlug);
  446. for (Plugin *plugin : plugins) {
  447. if (plugin->slug == slug) {
  448. return plugin;
  449. }
  450. }
  451. return NULL;
  452. }
  453. Model *getModel(const std::string &pluginSlug, const std::string &modelSlug) {
  454. Plugin *plugin = getPlugin(pluginSlug);
  455. if (!plugin)
  456. return NULL;
  457. Model *model = plugin->getModel(modelSlug);
  458. if (!model)
  459. return NULL;
  460. return model;
  461. }
  462. /** List of allowed tags in human display form, alphabetized.
  463. All tags here should be in sentence caps for display consistency.
  464. However, tags are case-insensitive in plugin metadata.
  465. */
  466. const std::set<std::string> allowedTags = {
  467. "Arpeggiator",
  468. "Attenuator", // With a level knob and not much else.
  469. "Blank", // No parameters or ports. Serves no purpose except visual.
  470. "Chorus",
  471. "Clock generator",
  472. "Clock modulator", // Clock dividers, multipliers, etc.
  473. "Compressor", // With threshold, ratio, knee, etc parameters.
  474. "Controller", // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad.
  475. "Delay",
  476. "Digital",
  477. "Distortion",
  478. "Drum",
  479. "Dual", // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module.
  480. "Dynamics",
  481. "Effect",
  482. "Envelope follower",
  483. "Envelope generator",
  484. "Equalizer",
  485. "Expander", // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module.
  486. "External",
  487. "Flanger",
  488. "Function generator",
  489. "Granular",
  490. "LFO",
  491. "Limiter",
  492. "Logic",
  493. "Low pass gate",
  494. "MIDI",
  495. "Mixer",
  496. "Multiple",
  497. "Noise",
  498. "Panning",
  499. "Phaser",
  500. "Physical modeling",
  501. "Polyphonic",
  502. "Quad", // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module.
  503. "Quantizer",
  504. "Random",
  505. "Recording",
  506. "Reverb",
  507. "Ring modulator",
  508. "Sample and hold",
  509. "Sampler",
  510. "Sequencer",
  511. "Slew limiter",
  512. "Switch",
  513. "Synth voice", // A synth voice must have, at the minimum, a built-in oscillator and envelope.
  514. "Tuner",
  515. "Utility", // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc.
  516. "VCA",
  517. "VCF",
  518. "VCO",
  519. "Visual",
  520. "Vocoder",
  521. "Waveshaper",
  522. };
  523. /** List of common synonyms for allowed tags.
  524. Aliases and tags must be lowercase.
  525. */
  526. const std::map<std::string, std::string> tagAliases = {
  527. {"amplifier", "vca"},
  528. {"clock", "clock generator"},
  529. {"drums", "drum"},
  530. {"eq", "equalizer"},
  531. {"filter", "vcf"},
  532. {"low frequency oscillator", "lfo"},
  533. {"lowpass gate", "low pass gate"},
  534. {"oscillator", "vco"},
  535. {"percussion", "drum"},
  536. {"poly", "polyphonic"},
  537. {"s&h", "sample and hold"},
  538. {"voltage controlled amplifier", "vca"},
  539. {"voltage controlled filter", "vcf"},
  540. {"voltage controlled oscillator", "vco"},
  541. };
  542. std::string normalizeTag(const std::string &tag) {
  543. std::string lowercaseTag = string::lowercase(tag);
  544. // Transform aliases
  545. auto it = tagAliases.find(lowercaseTag);
  546. if (it != tagAliases.end())
  547. lowercaseTag = it->second;
  548. // Find allowed tag
  549. for (const std::string &allowedTag : allowedTags) {
  550. if (lowercaseTag == string::lowercase(allowedTag))
  551. return allowedTag;
  552. }
  553. return "";
  554. }
  555. bool isSlugValid(const std::string &slug) {
  556. for (char c : slug) {
  557. if (!(std::isalnum(c) || c == '-' || c == '_'))
  558. return false;
  559. }
  560. return true;
  561. }
  562. std::string normalizeSlug(const std::string &slug) {
  563. std::string s;
  564. for (char c : slug) {
  565. if (!(std::isalnum(c) || c == '-' || c == '_'))
  566. continue;
  567. s += c;
  568. }
  569. return s;
  570. }
  571. std::vector<Plugin*> plugins;
  572. std::string loginStatus;
  573. std::vector<Update> updates;
  574. std::string updateStatus;
  575. bool restartRequested = false;
  576. } // namespace plugin
  577. } // namespace rack