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.

603 lines
14KB

  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 <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <sys/param.h> // for MAXPATHLEN
  13. #include <fcntl.h>
  14. #include <thread>
  15. #include <stdexcept>
  16. #define ZIP_STATIC
  17. #include <zip.h>
  18. #include <jansson.h>
  19. #if defined ARCH_WIN
  20. #include <windows.h>
  21. #include <direct.h>
  22. #define mkdir(_dir, _perms) _mkdir(_dir)
  23. #else
  24. #include <dlfcn.h>
  25. #endif
  26. #include <dirent.h>
  27. #include <osdialog.h>
  28. namespace rack {
  29. namespace plugin {
  30. ////////////////////
  31. // private API
  32. ////////////////////
  33. static bool loadPlugin(std::string path) {
  34. // Load plugin.json
  35. std::string metadataFilename = path + "/plugin.json";
  36. FILE *file = fopen(metadataFilename.c_str(), "r");
  37. if (!file) {
  38. WARN("Plugin metadata file %s does not exist", metadataFilename.c_str());
  39. return false;
  40. }
  41. DEFER({
  42. fclose(file);
  43. });
  44. json_error_t error;
  45. json_t *rootJ = json_loadf(file, 0, &error);
  46. if (!rootJ) {
  47. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  48. return false;
  49. }
  50. DEFER({
  51. json_decref(rootJ);
  52. });
  53. // Load plugin library
  54. std::string libraryFilename;
  55. #if defined ARCH_LIN
  56. libraryFilename = path + "/" + "plugin.so";
  57. #elif defined ARCH_WIN
  58. libraryFilename = path + "/" + "plugin.dll";
  59. #elif ARCH_MAC
  60. libraryFilename = path + "/" + "plugin.dylib";
  61. #endif
  62. // Check file existence
  63. if (!system::isFile(libraryFilename)) {
  64. WARN("Plugin file %s does not exist", libraryFilename.c_str());
  65. return false;
  66. }
  67. // Load dynamic/shared library
  68. #if defined ARCH_WIN
  69. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  70. HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
  71. SetErrorMode(0);
  72. if (!handle) {
  73. int error = GetLastError();
  74. WARN("Failed to load library %s: code %d", libraryFilename.c_str(), error);
  75. return false;
  76. }
  77. #else
  78. void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
  79. if (!handle) {
  80. WARN("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
  81. return false;
  82. }
  83. #endif
  84. // Call plugin's init() function
  85. typedef void (*InitCallback)(Plugin *);
  86. InitCallback initCallback;
  87. #if defined ARCH_WIN
  88. initCallback = (InitCallback) GetProcAddress(handle, "init");
  89. #else
  90. initCallback = (InitCallback) dlsym(handle, "init");
  91. #endif
  92. if (!initCallback) {
  93. WARN("Failed to read init() symbol in %s", libraryFilename.c_str());
  94. return false;
  95. }
  96. // Construct and initialize Plugin instance
  97. Plugin *plugin = new Plugin;
  98. plugin->path = path;
  99. plugin->handle = handle;
  100. initCallback(plugin);
  101. plugin->fromJson(rootJ);
  102. // Check slug
  103. if (!isSlugValid(plugin->slug)) {
  104. WARN("Plugin slug \"%s\" is invalid", plugin->slug.c_str());
  105. // TODO Fix memory leak with `plugin`
  106. return false;
  107. }
  108. // Reject plugin if slug already exists
  109. Plugin *oldPlugin = getPlugin(plugin->slug);
  110. if (oldPlugin) {
  111. WARN("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str());
  112. // TODO Fix memory leak with `plugin`
  113. return false;
  114. }
  115. // Add plugin to list
  116. plugins.push_back(plugin);
  117. INFO("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str());
  118. return true;
  119. }
  120. static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
  121. // Check that "status" is "available"
  122. json_t *statusJ = json_object_get(manifestJ, "status");
  123. if (!statusJ) {
  124. return false;
  125. }
  126. std::string status = json_string_value(statusJ);
  127. if (status != "available") {
  128. return false;
  129. }
  130. // Get latest version
  131. json_t *latestVersionJ = json_object_get(manifestJ, "latestVersion");
  132. if (!latestVersionJ) {
  133. WARN("Could not get latest version of plugin %s", slug.c_str());
  134. return false;
  135. }
  136. std::string latestVersion = json_string_value(latestVersionJ);
  137. // Check whether we already have a plugin with the same slug and version
  138. Plugin *plugin = getPlugin(slug);
  139. if (plugin && plugin->version == latestVersion) {
  140. return false;
  141. }
  142. json_t *nameJ = json_object_get(manifestJ, "name");
  143. std::string name;
  144. if (nameJ) {
  145. name = json_string_value(nameJ);
  146. }
  147. else {
  148. name = slug;
  149. }
  150. #if defined ARCH_WIN
  151. std::string arch = "win";
  152. #elif ARCH_MAC
  153. std::string arch = "mac";
  154. #elif defined ARCH_LIN
  155. std::string arch = "lin";
  156. #endif
  157. std::string downloadUrl;
  158. downloadUrl = app::API_URL;
  159. downloadUrl += "/download";
  160. if (dryRun) {
  161. downloadUrl += "/available";
  162. }
  163. downloadUrl += "?token=" + network::encodeUrl(token);
  164. downloadUrl += "&slug=" + network::encodeUrl(slug);
  165. downloadUrl += "&version=" + network::encodeUrl(latestVersion);
  166. downloadUrl += "&arch=" + network::encodeUrl(arch);
  167. if (dryRun) {
  168. // Check if available
  169. json_t *availableResJ = network::requestJson(network::METHOD_GET, downloadUrl, NULL);
  170. if (!availableResJ) {
  171. WARN("Could not check whether download is available");
  172. return false;
  173. }
  174. DEFER({
  175. json_decref(availableResJ);
  176. });
  177. json_t *successJ = json_object_get(availableResJ, "success");
  178. return json_boolean_value(successJ);
  179. }
  180. else {
  181. downloadName = name;
  182. downloadProgress = 0.0;
  183. INFO("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str());
  184. // Download zip
  185. std::string pluginDest = asset::user("plugins/" + slug + ".zip");
  186. if (!network::requestDownload(downloadUrl, pluginDest, &downloadProgress)) {
  187. WARN("Plugin %s download was unsuccessful", slug.c_str());
  188. return false;
  189. }
  190. downloadName = "";
  191. return true;
  192. }
  193. }
  194. static void loadPlugins(std::string path) {
  195. std::string message;
  196. for (std::string pluginPath : system::listEntries(path)) {
  197. if (!system::isDirectory(pluginPath))
  198. continue;
  199. if (!loadPlugin(pluginPath)) {
  200. message += string::f("Could not load plugin %s\n", pluginPath.c_str());
  201. }
  202. }
  203. if (!message.empty()) {
  204. message += "See log for details.";
  205. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  206. }
  207. }
  208. /** Returns 0 if successful */
  209. static int extractZipHandle(zip_t *za, const char *dir) {
  210. int err;
  211. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  212. zip_stat_t zs;
  213. err = zip_stat_index(za, i, 0, &zs);
  214. if (err) {
  215. WARN("zip_stat_index() failed: error %d", err);
  216. return err;
  217. }
  218. int nameLen = strlen(zs.name);
  219. char path[MAXPATHLEN];
  220. snprintf(path, sizeof(path), "%s/%s", dir, zs.name);
  221. if (zs.name[nameLen - 1] == '/') {
  222. if (mkdir(path, 0755)) {
  223. if (errno != EEXIST) {
  224. WARN("mkdir(%s) failed: error %d", path, errno);
  225. return errno;
  226. }
  227. }
  228. }
  229. else {
  230. zip_file_t *zf = zip_fopen_index(za, i, 0);
  231. if (!zf) {
  232. WARN("zip_fopen_index() failed");
  233. return -1;
  234. }
  235. FILE *outFile = fopen(path, "wb");
  236. if (!outFile)
  237. continue;
  238. while (1) {
  239. char buffer[1<<15];
  240. int len = zip_fread(zf, buffer, sizeof(buffer));
  241. if (len <= 0)
  242. break;
  243. fwrite(buffer, 1, len, outFile);
  244. }
  245. err = zip_fclose(zf);
  246. if (err) {
  247. WARN("zip_fclose() failed: error %d", err);
  248. return err;
  249. }
  250. fclose(outFile);
  251. }
  252. }
  253. return 0;
  254. }
  255. /** Returns 0 if successful */
  256. static int extractZip(const char *filename, const char *path) {
  257. int err;
  258. zip_t *za = zip_open(filename, 0, &err);
  259. if (!za) {
  260. WARN("Could not open zip %s: error %d", filename, err);
  261. return err;
  262. }
  263. DEFER({
  264. zip_close(za);
  265. });
  266. err = extractZipHandle(za, path);
  267. return err;
  268. }
  269. static void extractPackages(std::string path) {
  270. std::string message;
  271. for (std::string packagePath : system::listEntries(path)) {
  272. if (string::extension(packagePath) != "zip")
  273. continue;
  274. INFO("Extracting package %s", packagePath.c_str());
  275. // Extract package
  276. if (extractZip(packagePath.c_str(), path.c_str())) {
  277. WARN("Package %s failed to extract", packagePath.c_str());
  278. message += string::f("Could not extract package %s\n", packagePath.c_str());
  279. continue;
  280. }
  281. // Remove package
  282. if (remove(packagePath.c_str())) {
  283. WARN("Could not delete file %s: error %d", packagePath.c_str(), errno);
  284. }
  285. }
  286. if (!message.empty()) {
  287. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  288. }
  289. }
  290. ////////////////////
  291. // public API
  292. ////////////////////
  293. void init(bool devMode) {
  294. // Load core
  295. // This function is defined in core.cpp
  296. Plugin *corePlugin = new Plugin;
  297. ::init(corePlugin);
  298. plugins.push_back(corePlugin);
  299. // Get user plugins directory
  300. std::string userPlugins = asset::user("plugins");
  301. mkdir(userPlugins.c_str(), 0755);
  302. if (!devMode) {
  303. // Copy Fundamental package to plugins directory if folder does not exist
  304. std::string fundamentalSrc = asset::system("Fundamental.zip");
  305. std::string fundamentalDest = asset::user("plugins/Fundamental.zip");
  306. std::string fundamentalDir = asset::user("plugins/Fundamental");
  307. if (system::isFile(fundamentalSrc) && !system::isFile(fundamentalDest) && !system::isDirectory(fundamentalDir)) {
  308. system::copyFile(fundamentalSrc, fundamentalDest);
  309. }
  310. }
  311. // Extract packages and load plugins
  312. extractPackages(userPlugins);
  313. loadPlugins(userPlugins);
  314. }
  315. void destroy() {
  316. for (Plugin *plugin : plugins) {
  317. // Free library handle
  318. #if defined ARCH_WIN
  319. if (plugin->handle)
  320. FreeLibrary((HINSTANCE) plugin->handle);
  321. #else
  322. if (plugin->handle)
  323. dlclose(plugin->handle);
  324. #endif
  325. // For some reason this segfaults.
  326. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  327. // delete plugin;
  328. }
  329. plugins.clear();
  330. }
  331. void logIn(std::string email, std::string password) {
  332. json_t *reqJ = json_object();
  333. json_object_set(reqJ, "email", json_string(email.c_str()));
  334. json_object_set(reqJ, "password", json_string(password.c_str()));
  335. std::string tokenUrl = app::API_URL;
  336. tokenUrl += "/token";
  337. json_t *resJ = network::requestJson(network::METHOD_POST, tokenUrl, reqJ);
  338. json_decref(reqJ);
  339. if (resJ) {
  340. json_t *errorJ = json_object_get(resJ, "error");
  341. if (errorJ) {
  342. const char *errorStr = json_string_value(errorJ);
  343. loginStatus = errorStr;
  344. }
  345. else {
  346. json_t *tokenJ = json_object_get(resJ, "token");
  347. if (tokenJ) {
  348. const char *tokenStr = json_string_value(tokenJ);
  349. token = tokenStr;
  350. loginStatus = "";
  351. }
  352. }
  353. json_decref(resJ);
  354. }
  355. }
  356. void logOut() {
  357. token = "";
  358. }
  359. bool sync(bool dryRun) {
  360. if (token.empty())
  361. return false;
  362. bool available = false;
  363. if (!dryRun) {
  364. isDownloading = true;
  365. downloadProgress = 0.0;
  366. downloadName = "Updating plugins...";
  367. }
  368. DEFER({
  369. isDownloading = false;
  370. });
  371. // Get user's plugins list
  372. json_t *pluginsReqJ = json_object();
  373. json_object_set(pluginsReqJ, "token", json_string(token.c_str()));
  374. std::string pluginsUrl = app::API_URL;
  375. pluginsUrl += "/plugins";
  376. json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ);
  377. json_decref(pluginsReqJ);
  378. if (!pluginsResJ) {
  379. WARN("Request for user's plugins failed");
  380. return false;
  381. }
  382. DEFER({
  383. json_decref(pluginsResJ);
  384. });
  385. json_t *errorJ = json_object_get(pluginsResJ, "error");
  386. if (errorJ) {
  387. WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
  388. return false;
  389. }
  390. // Get community manifests
  391. std::string manifestsUrl = app::API_URL;
  392. manifestsUrl += "/community/manifests";
  393. json_t *manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, NULL);
  394. if (!manifestsResJ) {
  395. WARN("Request for community manifests failed");
  396. return false;
  397. }
  398. DEFER({
  399. json_decref(manifestsResJ);
  400. });
  401. // Check each plugin in list of plugin slugs
  402. json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
  403. if (!pluginsJ) {
  404. WARN("No plugins array");
  405. return false;
  406. }
  407. json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
  408. if (!manifestsJ) {
  409. WARN("No manifests object");
  410. return false;
  411. }
  412. size_t slugIndex;
  413. json_t *slugJ;
  414. json_array_foreach(pluginsJ, slugIndex, slugJ) {
  415. std::string slug = json_string_value(slugJ);
  416. // Search for slug in manifests
  417. const char *manifestSlug;
  418. json_t *manifestJ = NULL;
  419. json_object_foreach(manifestsJ, manifestSlug, manifestJ) {
  420. if (slug == std::string(manifestSlug))
  421. break;
  422. }
  423. if (!manifestJ)
  424. continue;
  425. if (syncPlugin(slug, manifestJ, dryRun)) {
  426. available = true;
  427. }
  428. }
  429. return available;
  430. }
  431. void cancelDownload() {
  432. // TODO
  433. }
  434. bool isLoggedIn() {
  435. return token != "";
  436. }
  437. Plugin *getPlugin(std::string pluginSlug) {
  438. for (Plugin *plugin : plugins) {
  439. if (plugin->slug == pluginSlug) {
  440. return plugin;
  441. }
  442. }
  443. return NULL;
  444. }
  445. Model *getModel(std::string pluginSlug, std::string modelSlug) {
  446. Plugin *plugin = getPlugin(pluginSlug);
  447. if (!plugin)
  448. return NULL;
  449. Model *model = plugin->getModel(modelSlug);
  450. if (!model)
  451. return NULL;
  452. return model;
  453. }
  454. std::string getAllowedTag(std::string tag) {
  455. tag = string::lowercase(tag);
  456. for (std::string allowedTag : allowedTags) {
  457. if (tag == string::lowercase(allowedTag))
  458. return allowedTag;
  459. }
  460. return "";
  461. }
  462. bool isSlugValid(std::string slug) {
  463. for (char c : slug) {
  464. if (!(std::isalnum(c) || c == '-' || c == '_'))
  465. return false;
  466. }
  467. return true;
  468. }
  469. std::list<Plugin*> plugins;
  470. std::string token;
  471. bool isDownloading = false;
  472. float downloadProgress = 0.f;
  473. std::string downloadName;
  474. std::string loginStatus;
  475. const std::vector<std::string> allowedTags = {
  476. "VCA",
  477. "Arpeggiator",
  478. "Attenuator",
  479. "Blank",
  480. "Chorus",
  481. "Clock Modulator", // Clock dividers, multipliers, etc.
  482. "Clock",
  483. "Compressor",
  484. "Controller", // Use only if the artist "performs" with this module. Knobs are not sufficient. Examples: on-screen keyboard, XY pad.
  485. "Delay",
  486. "Digital",
  487. "Distortion",
  488. "Drum",
  489. "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.
  490. "Dynamics",
  491. "Effect",
  492. "Envelope Follower",
  493. "Envelope Generator",
  494. "Equalizer",
  495. "External",
  496. "Filter",
  497. "Flanger",
  498. "Function Generator",
  499. "Granular",
  500. "LFO",
  501. "Limiter",
  502. "Logic",
  503. "Low Pass Gate",
  504. "MIDI",
  505. "Mixer",
  506. "Multiple",
  507. "Noise",
  508. "VCO",
  509. "Panning",
  510. "Phaser",
  511. "Physical Modeling",
  512. "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.
  513. "Quantizer",
  514. "Random",
  515. "Recording",
  516. "Reverb",
  517. "Ring Modulator",
  518. "Sample and Hold",
  519. "Sampler",
  520. "Sequencer",
  521. "Slew Limiter",
  522. "Switch",
  523. "Synth Voice", // A synth voice must have an envelope built-in.
  524. "Tuner",
  525. "Utility", // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc.
  526. "Visual",
  527. "Vocoder",
  528. "Waveshaper",
  529. };
  530. } // namespace plugin
  531. } // namespace rack