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.

310 lines
11KB

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