The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

353 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../Application/jucer_Headers.h"
  20. #include "../Application/jucer_Application.h"
  21. #include "../ProjectSaving/jucer_ProjectExporter.h"
  22. #include "../Project/UI/jucer_HeaderComponent.h"
  23. #include "jucer_LicenseController.h"
  24. #if ! JUCER_ENABLE_GPL_MODE
  25. #include "jucer_LicenseWebview.h"
  26. #include "jucer_LicenseThread.h"
  27. #endif
  28. //==============================================================================
  29. const char* LicenseState::licenseTypeToString (LicenseState::Type type)
  30. {
  31. switch (type)
  32. {
  33. case Type::notLoggedIn: return "<notLoggedIn>";
  34. case Type::noLicenseChosenYet: return "<noLicenseChosenYet>";
  35. case Type::GPL: return "JUCE GPL";
  36. case Type::personal: return "JUCE Personal";
  37. case Type::edu: return "JUCE Education";
  38. case Type::indie: return "JUCE Indie";
  39. case Type::pro: return "JUCE Pro";
  40. default: return "<unknown>";
  41. }
  42. }
  43. static const char* getLicenseStateValue (LicenseState::Type type)
  44. {
  45. switch (type)
  46. {
  47. case LicenseState::Type::GPL: return "GPL";
  48. case LicenseState::Type::personal: return "personal";
  49. case LicenseState::Type::edu: return "edu";
  50. case LicenseState::Type::indie: return "indie";
  51. case LicenseState::Type::pro: return "pro";
  52. case LicenseState::Type::notLoggedIn:
  53. case LicenseState::Type::noLicenseChosenYet:
  54. default: return nullptr;
  55. }
  56. }
  57. static LicenseState::Type getLicenseTypeFromValue (const String& d)
  58. {
  59. if (d == getLicenseStateValue (LicenseState::Type::GPL)) return LicenseState::Type::GPL;
  60. if (d == getLicenseStateValue (LicenseState::Type::personal)) return LicenseState::Type::personal;
  61. if (d == getLicenseStateValue (LicenseState::Type::edu)) return LicenseState::Type::edu;
  62. if (d == getLicenseStateValue (LicenseState::Type::indie)) return LicenseState::Type::indie;
  63. if (d == getLicenseStateValue (LicenseState::Type::pro)) return LicenseState::Type::pro;
  64. return LicenseState::Type::noLicenseChosenYet;
  65. }
  66. static const char* getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData type)
  67. {
  68. switch (type)
  69. {
  70. case LicenseState::ApplicationUsageData::enabled: return "enabled";
  71. case LicenseState::ApplicationUsageData::disabled: return "disabled";
  72. case LicenseState::ApplicationUsageData::notChosenYet:
  73. default: return "notChosen";
  74. }
  75. }
  76. static LicenseState::ApplicationUsageData getApplicationUsageDataTypeFromValue (const String& value)
  77. {
  78. if (value == getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData::enabled)) return LicenseState::ApplicationUsageData::enabled;
  79. if (value == getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData::disabled)) return LicenseState::ApplicationUsageData::disabled;
  80. return LicenseState::ApplicationUsageData::notChosenYet;
  81. }
  82. #if ! JUCER_ENABLE_GPL_MODE
  83. struct LicenseController::ModalCompletionCallback : ModalComponentManager::Callback
  84. {
  85. ModalCompletionCallback (LicenseController& controller) : owner (controller) {}
  86. void modalStateFinished (int returnValue) override { owner.modalStateFinished (returnValue); }
  87. LicenseController& owner;
  88. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModalCompletionCallback)
  89. };
  90. #endif
  91. //==============================================================================
  92. LicenseController::LicenseController()
  93. : state (licenseStateFromSettings (ProjucerApplication::getApp().settings->getGlobalProperties()))
  94. {
  95. #if JUCER_ENABLE_GPL_MODE
  96. state.type = LicenseState::Type::GPL;
  97. state.username = "GPL mode";
  98. #endif
  99. }
  100. LicenseController::~LicenseController()
  101. {
  102. #if ! JUCER_ENABLE_GPL_MODE
  103. thread.reset();
  104. closeWebview (-1);
  105. #endif
  106. }
  107. LicenseState LicenseController::getState() const noexcept
  108. {
  109. LicenseState projucerState = state;
  110. // if the user has never logged in before and the user is running from command line
  111. // then we have no way to ask the user to log in, so fallback to GPL mode
  112. if (guiNotInitialisedYet
  113. && (state.type == LicenseState::Type::notLoggedIn
  114. || state.type == LicenseState::Type::noLicenseChosenYet))
  115. {
  116. projucerState.type = LicenseState::Type::GPL;
  117. projucerState.username = "GPL mode";
  118. }
  119. return projucerState;
  120. }
  121. void LicenseController::startWebviewIfNeeded()
  122. {
  123. if (guiNotInitialisedYet)
  124. {
  125. guiNotInitialisedYet = false;
  126. auto stateParam = getState();
  127. listeners.call ([&] (StateChangedCallback& l) { l.licenseStateChanged (stateParam); });
  128. }
  129. #if ! JUCER_ENABLE_GPL_MODE
  130. if (thread == nullptr)
  131. thread.reset (new LicenseThread (*this, false));
  132. #endif
  133. }
  134. void LicenseController::logout()
  135. {
  136. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  137. #if ! JUCER_ENABLE_GPL_MODE
  138. thread.reset();
  139. updateState ({});
  140. #if ! JUCE_LINUX
  141. WebBrowserComponent::clearCookies();
  142. #endif
  143. thread.reset (new LicenseThread (*this, false));
  144. #endif
  145. }
  146. void LicenseController::chooseNewLicense()
  147. {
  148. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  149. #if ! JUCER_ENABLE_GPL_MODE
  150. thread.reset();
  151. thread.reset (new LicenseThread (*this, true));
  152. #endif
  153. }
  154. void LicenseController::setApplicationUsageDataState (LicenseState::ApplicationUsageData newState)
  155. {
  156. if (state.applicationUsageDataState != newState)
  157. {
  158. state.applicationUsageDataState = newState;
  159. ProjucerApplication::getApp().setAnalyticsEnabled (newState == LicenseState::ApplicationUsageData::enabled);
  160. updateState (state);
  161. }
  162. }
  163. //==============================================================================
  164. #if ! JUCER_ENABLE_GPL_MODE
  165. void LicenseController::closeWebview (int result)
  166. {
  167. if (licenseWebview != nullptr)
  168. licenseWebview->exitModalState (result);
  169. }
  170. void LicenseController::modalStateFinished (int result)
  171. {
  172. licenseWebview = nullptr;
  173. if (result == -1 && (state.type == LicenseState::Type::notLoggedIn
  174. || state.type == LicenseState::Type::noLicenseChosenYet))
  175. JUCEApplication::getInstance()->systemRequestedQuit();
  176. }
  177. void LicenseController::ensureLicenseWebviewIsOpenWithPage (const String& param)
  178. {
  179. if (licenseWebview != nullptr)
  180. {
  181. licenseWebview->goToURL (param);
  182. licenseWebview->toFront (true);
  183. }
  184. else
  185. {
  186. #if ! JUCE_LINUX
  187. WebBrowserComponent::clearCookies();
  188. #endif
  189. licenseWebview = new LicenseWebview (new ModalCompletionCallback (*this), param);
  190. }
  191. }
  192. void LicenseController::queryWebview (const String& startURL, const String& valueToQuery,
  193. HashMap<String, String>& result)
  194. {
  195. ensureLicenseWebviewIsOpenWithPage (startURL);
  196. licenseWebview->setPageCallback ([this,valueToQuery,&result] (const String& cmd, const HashMap<String, String>& params)
  197. {
  198. if (valueToQuery.isEmpty() || cmd == valueToQuery)
  199. {
  200. result.clear();
  201. for (HashMap<String, String>::Iterator it = params.begin(); it != params.end(); ++it)
  202. result.set (it.getKey(), it.getValue());
  203. if (thread != nullptr && ! thread->threadShouldExit())
  204. thread->finished.signal();
  205. }
  206. });
  207. licenseWebview->setNewWindowCallback ([this, &result] (const String& url)
  208. {
  209. if (url.endsWith ("get-juce/indie") || url.endsWith ("get-juce/pro"))
  210. {
  211. result.clear();
  212. result.set ("page-redirect", url);
  213. if (thread != nullptr && ! thread->threadShouldExit())
  214. thread->finished.signal();
  215. }
  216. });
  217. }
  218. #endif
  219. void LicenseController::updateState (const LicenseState& newState)
  220. {
  221. auto& props = ProjucerApplication::getApp().settings->getGlobalProperties();
  222. auto oldLicenseType = state.type;
  223. state = newState;
  224. licenseStateToSettings (state, props);
  225. auto stateParam = getState();
  226. listeners.call ([&] (StateChangedCallback& l) { l.licenseStateChanged (stateParam); });
  227. if (oldLicenseType != state.type)
  228. {
  229. StringPairArray data;
  230. data.set ("label", state.licenseTypeToString (state.type));
  231. Analytics::getInstance()->logEvent ("License Type", data, ProjucerAnalyticsEvent::userEvent);
  232. }
  233. }
  234. LicenseState LicenseController::licenseStateFromOldSettings (XmlElement* licenseXml)
  235. {
  236. LicenseState result;
  237. result.type = getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {}));
  238. result.applicationUsageDataState = getApplicationUsageDataTypeFromValue (licenseXml->getChildElementAllSubText ("applicationUsageData", {}));
  239. result.username = licenseXml->getChildElementAllSubText ("username", {});
  240. result.email = licenseXml->getChildElementAllSubText ("email", {});
  241. result.authToken = licenseXml->getChildElementAllSubText ("authToken", {});
  242. MemoryOutputStream imageData;
  243. Base64::convertFromBase64 (imageData, licenseXml->getChildElementAllSubText ("avatar", {}));
  244. result.avatar = ImageFileFormat::loadFrom (imageData.getData(), imageData.getDataSize());
  245. return result;
  246. }
  247. LicenseState LicenseController::licenseStateFromSettings (PropertiesFile& props)
  248. {
  249. if (auto licenseXml = props.getXmlValue ("license"))
  250. {
  251. // this is here for backwards compatibility with old-style settings files using XML text elements
  252. if (licenseXml->getChildElementAllSubText ("type", {}) != String())
  253. {
  254. auto stateFromOldSettings = licenseStateFromOldSettings (licenseXml.get());
  255. licenseStateToSettings (stateFromOldSettings, props);
  256. return stateFromOldSettings;
  257. }
  258. LicenseState result;
  259. result.type = getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {}));
  260. result.applicationUsageDataState = getApplicationUsageDataTypeFromValue (licenseXml->getStringAttribute ("applicationUsageData", {}));
  261. result.username = licenseXml->getStringAttribute ("username", {});
  262. result.email = licenseXml->getStringAttribute ("email", {});
  263. result.authToken = licenseXml->getStringAttribute ("authToken", {});
  264. MemoryOutputStream imageData;
  265. Base64::convertFromBase64 (imageData, licenseXml->getStringAttribute ("avatar", {}));
  266. result.avatar = ImageFileFormat::loadFrom (imageData.getData(), imageData.getDataSize());
  267. return result;
  268. }
  269. return {};
  270. }
  271. void LicenseController::licenseStateToSettings (const LicenseState& state, PropertiesFile& props)
  272. {
  273. props.removeValue ("license");
  274. if (state.type != LicenseState::Type::notLoggedIn && state.username.isNotEmpty())
  275. {
  276. XmlElement licenseXml ("license");
  277. if (auto* typeString = getLicenseStateValue (state.type))
  278. licenseXml.setAttribute ("type", typeString);
  279. licenseXml.setAttribute ("applicationUsageData", getApplicationUsageDataStateValue (state.applicationUsageDataState));
  280. licenseXml.setAttribute ("username", state.username);
  281. licenseXml.setAttribute ("email", state.email);
  282. licenseXml.setAttribute ("authToken", state.authToken);
  283. MemoryOutputStream imageData;
  284. if (state.avatar.isValid() && PNGImageFormat().writeImageToStream (state.avatar, imageData))
  285. licenseXml.setAttribute ("avatar", Base64::toBase64 (imageData.getData(), imageData.getDataSize()));
  286. props.setValue ("license", &licenseXml);
  287. }
  288. props.saveIfNeeded();
  289. }