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.

422 lines
8.7KB

  1. #include <stdio.h>
  2. #include <assert.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <sys/param.h> // for MAXPATHLEN
  8. #include <fcntl.h>
  9. #include <zip.h>
  10. #include <jansson.h>
  11. #if ARCH_WIN
  12. #include <windows.h>
  13. #include <direct.h>
  14. #define mkdir(_dir, _perms) _mkdir(_dir)
  15. #else
  16. #include <dlfcn.h>
  17. #endif
  18. #include <dirent.h>
  19. #include "plugin.hpp"
  20. #include "app.hpp"
  21. #include "asset.hpp"
  22. #include "util/request.hpp"
  23. namespace rack {
  24. std::list<Plugin*> gPlugins;
  25. std::string gToken;
  26. std::string gTagNames[NUM_TAGS] = {
  27. "Amplifier/VCA",
  28. "Attenuator",
  29. "Blank",
  30. "Clock",
  31. "Controller",
  32. "Delay",
  33. "Digital",
  34. "Distortion",
  35. "Drum",
  36. "Dual/Stereo",
  37. "Dynamics",
  38. "Effect",
  39. "Envelope Follower",
  40. "Envelope Generator",
  41. "Equalizer",
  42. "External",
  43. "Filter/VCF",
  44. "Function Generator",
  45. "Granular",
  46. "LFO",
  47. "Logic",
  48. "Low Pass Gate",
  49. "MIDI",
  50. "Mixer",
  51. "Multiple",
  52. "Noise",
  53. "Oscillator/VCO",
  54. "Panning",
  55. "Quad",
  56. "Quantizer",
  57. "Random",
  58. "Reverb",
  59. "Ring Modulator",
  60. "Sample and Hold",
  61. "Sampler",
  62. "Sequencer",
  63. "Slew Limiter",
  64. "Switch",
  65. "Synth Voice",
  66. "Tuner",
  67. "Utility",
  68. "Visual",
  69. "Waveshaper",
  70. };
  71. static bool isDownloading = false;
  72. static float downloadProgress = 0.0;
  73. static std::string downloadName;
  74. static std::string loginStatus;
  75. Plugin::~Plugin() {
  76. for (Model *model : models) {
  77. delete model;
  78. }
  79. }
  80. void Plugin::addModel(Model *model) {
  81. assert(!model->plugin);
  82. model->plugin = this;
  83. models.push_back(model);
  84. }
  85. static int loadPlugin(std::string path) {
  86. std::string libraryFilename;
  87. #if ARCH_LIN
  88. libraryFilename = path + "/" + "plugin.so";
  89. #elif ARCH_WIN
  90. libraryFilename = path + "/" + "plugin.dll";
  91. #elif ARCH_MAC
  92. libraryFilename = path + "/" + "plugin.dylib";
  93. #endif
  94. // Load dynamic/shared library
  95. #if ARCH_WIN
  96. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  97. HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
  98. SetErrorMode(0);
  99. if (!handle) {
  100. int error = GetLastError();
  101. warn("Failed to load library %s: %d", libraryFilename.c_str(), error);
  102. return -1;
  103. }
  104. #elif ARCH_LIN || ARCH_MAC
  105. void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
  106. if (!handle) {
  107. warn("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
  108. return -1;
  109. }
  110. #endif
  111. // Call plugin's init() function
  112. typedef void (*InitCallback)(Plugin *);
  113. InitCallback initCallback;
  114. #if ARCH_WIN
  115. initCallback = (InitCallback) GetProcAddress(handle, "init");
  116. #elif ARCH_LIN || ARCH_MAC
  117. initCallback = (InitCallback) dlsym(handle, "init");
  118. #endif
  119. if (!initCallback) {
  120. warn("Failed to read init() symbol in %s", libraryFilename.c_str());
  121. return -2;
  122. }
  123. // Construct and initialize Plugin instance
  124. Plugin *plugin = new Plugin();
  125. plugin->path = path;
  126. plugin->handle = handle;
  127. initCallback(plugin);
  128. // Reject plugin if slug already exists
  129. for (Plugin *p : gPlugins) {
  130. if (plugin->slug == p->slug) {
  131. warn("Plugin \"%s\" is already loaded, not attempting to load it again");
  132. // TODO
  133. // Fix memory leak with `plugin` here
  134. return -1;
  135. }
  136. }
  137. // Add plugin to list
  138. gPlugins.push_back(plugin);
  139. info("Loaded plugin %s", libraryFilename.c_str());
  140. return 0;
  141. }
  142. static void loadPlugins(std::string path) {
  143. DIR *dir = opendir(path.c_str());
  144. if (dir) {
  145. struct dirent *d;
  146. while ((d = readdir(dir))) {
  147. if (d->d_name[0] == '.')
  148. continue;
  149. loadPlugin(path + "/" + d->d_name);
  150. }
  151. closedir(dir);
  152. }
  153. }
  154. ////////////////////
  155. // plugin helpers
  156. ////////////////////
  157. static int extractZipHandle(zip_t *za, const char *dir) {
  158. int err = 0;
  159. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  160. zip_stat_t zs;
  161. err = zip_stat_index(za, i, 0, &zs);
  162. if (err)
  163. return err;
  164. int nameLen = strlen(zs.name);
  165. char path[MAXPATHLEN];
  166. snprintf(path, sizeof(path), "%s/%s", dir, zs.name);
  167. if (zs.name[nameLen - 1] == '/') {
  168. err = mkdir(path, 0755);
  169. if (err && errno != EEXIST)
  170. return err;
  171. }
  172. else {
  173. zip_file_t *zf = zip_fopen_index(za, i, 0);
  174. if (!zf)
  175. return 1;
  176. FILE *outFile = fopen(path, "wb");
  177. if (!outFile)
  178. continue;
  179. while (1) {
  180. char buffer[4096];
  181. int len = zip_fread(zf, buffer, sizeof(buffer));
  182. if (len <= 0)
  183. break;
  184. fwrite(buffer, 1, len, outFile);
  185. }
  186. err = zip_fclose(zf);
  187. if (err)
  188. return err;
  189. fclose(outFile);
  190. }
  191. }
  192. return 0;
  193. }
  194. static int extractZip(const char *filename, const char *dir) {
  195. int err = 0;
  196. zip_t *za = zip_open(filename, 0, &err);
  197. if (!za)
  198. return 1;
  199. if (!err) {
  200. err = extractZipHandle(za, dir);
  201. }
  202. zip_close(za);
  203. return err;
  204. }
  205. static void refreshPurchase(json_t *pluginJ) {
  206. json_t *slugJ = json_object_get(pluginJ, "slug");
  207. if (!slugJ) return;
  208. std::string slug = json_string_value(slugJ);
  209. json_t *nameJ = json_object_get(pluginJ, "name");
  210. if (!nameJ) return;
  211. std::string name = json_string_value(nameJ);
  212. json_t *versionJ = json_object_get(pluginJ, "version");
  213. if (!versionJ) return;
  214. std::string version = json_string_value(versionJ);
  215. // Check whether the plugin is already loaded
  216. for (Plugin *plugin : gPlugins) {
  217. if (plugin->slug == slug && plugin->version == version) {
  218. return;
  219. }
  220. }
  221. // Append token and version to download URL
  222. std::string url = gApiHost;
  223. url += "/download";
  224. url += "?product=";
  225. url += slug;
  226. url += "&version=";
  227. url += requestEscape(gApplicationVersion);
  228. url += "&token=";
  229. url += requestEscape(gToken);
  230. // If plugin is not loaded, download the zip file to /plugins
  231. downloadName = name;
  232. downloadProgress = 0.0;
  233. // Download zip
  234. std::string pluginsDir = assetLocal("plugins");
  235. std::string pluginPath = pluginsDir + "/" + slug;
  236. std::string zipPath = pluginPath + ".zip";
  237. bool success = requestDownload(url, zipPath, &downloadProgress);
  238. if (success) {
  239. // Unzip file
  240. int err = extractZip(zipPath.c_str(), pluginsDir.c_str());
  241. if (!err) {
  242. // Delete zip
  243. remove(zipPath.c_str());
  244. // Load plugin
  245. loadPlugin(pluginPath);
  246. }
  247. }
  248. downloadName = "";
  249. }
  250. ////////////////////
  251. // plugin API
  252. ////////////////////
  253. void pluginInit() {
  254. // Load core
  255. // This function is defined in core.cpp
  256. Plugin *coreManufacturer = new Plugin();
  257. init(coreManufacturer);
  258. gPlugins.push_back(coreManufacturer);
  259. // Load plugins from global directory
  260. std::string globalPlugins = assetGlobal("plugins");
  261. info("Loading plugins from %s", globalPlugins.c_str());
  262. loadPlugins(globalPlugins);
  263. // Load plugins from local directory
  264. std::string localPlugins = assetLocal("plugins");
  265. if (globalPlugins != localPlugins) {
  266. mkdir(localPlugins.c_str(), 0755);
  267. info("Loading plugins from %s", localPlugins.c_str());
  268. loadPlugins(localPlugins);
  269. }
  270. }
  271. void pluginDestroy() {
  272. for (Plugin *plugin : gPlugins) {
  273. // Free library handle
  274. #if ARCH_WIN
  275. if (plugin->handle)
  276. FreeLibrary((HINSTANCE)plugin->handle);
  277. #elif ARCH_LIN || ARCH_MAC
  278. if (plugin->handle)
  279. dlclose(plugin->handle);
  280. #endif
  281. // For some reason this segfaults.
  282. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  283. // delete plugin;
  284. }
  285. gPlugins.clear();
  286. }
  287. void pluginLogIn(std::string email, std::string password) {
  288. json_t *reqJ = json_object();
  289. json_object_set(reqJ, "email", json_string(email.c_str()));
  290. json_object_set(reqJ, "password", json_string(password.c_str()));
  291. json_t *resJ = requestJson(METHOD_POST, gApiHost + "/token", reqJ);
  292. json_decref(reqJ);
  293. if (resJ) {
  294. json_t *errorJ = json_object_get(resJ, "error");
  295. if (errorJ) {
  296. const char *errorStr = json_string_value(errorJ);
  297. loginStatus = errorStr;
  298. }
  299. else {
  300. json_t *tokenJ = json_object_get(resJ, "token");
  301. if (tokenJ) {
  302. const char *tokenStr = json_string_value(tokenJ);
  303. gToken = tokenStr;
  304. loginStatus = "";
  305. }
  306. }
  307. json_decref(resJ);
  308. }
  309. }
  310. void pluginLogOut() {
  311. gToken = "";
  312. }
  313. void pluginRefresh() {
  314. if (gToken.empty())
  315. return;
  316. isDownloading = true;
  317. downloadProgress = 0.0;
  318. downloadName = "";
  319. json_t *reqJ = json_object();
  320. json_object_set(reqJ, "token", json_string(gToken.c_str()));
  321. json_t *resJ = requestJson(METHOD_GET, gApiHost + "/purchases", reqJ);
  322. json_decref(reqJ);
  323. if (resJ) {
  324. json_t *errorJ = json_object_get(resJ, "error");
  325. if (errorJ) {
  326. const char *errorStr = json_string_value(errorJ);
  327. warn("Plugin refresh error: %s", errorStr);
  328. }
  329. else {
  330. json_t *purchasesJ = json_object_get(resJ, "purchases");
  331. size_t index;
  332. json_t *purchaseJ;
  333. json_array_foreach(purchasesJ, index, purchaseJ) {
  334. refreshPurchase(purchaseJ);
  335. }
  336. }
  337. json_decref(resJ);
  338. }
  339. isDownloading = false;
  340. }
  341. void pluginCancelDownload() {
  342. // TODO
  343. }
  344. bool pluginIsLoggedIn() {
  345. return gToken != "";
  346. }
  347. bool pluginIsDownloading() {
  348. return isDownloading;
  349. }
  350. float pluginGetDownloadProgress() {
  351. return downloadProgress;
  352. }
  353. std::string pluginGetDownloadName() {
  354. return downloadName;
  355. }
  356. std::string pluginGetLoginStatus() {
  357. return loginStatus;
  358. }
  359. } // namespace rack