export_plugin.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*************************************************************************/
  2. /* export_plugin.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "export_plugin.h"
  31. Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
  32. if (p_preset->get("codesign/enable")) {
  33. return _code_sign(p_preset, p_path);
  34. } else {
  35. return OK;
  36. }
  37. }
  38. Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
  39. Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
  40. if (err != OK) {
  41. return err;
  42. }
  43. _rcedit_add_data(p_preset, p_path);
  44. if (p_preset->get("codesign/enable") && err == OK) {
  45. err = _code_sign(p_preset, p_path);
  46. }
  47. return err;
  48. }
  49. void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
  50. EditorExportPlatformPC::get_export_options(r_options);
  51. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
  52. #ifdef WINDOWS_ENABLED
  53. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0));
  54. #endif
  55. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
  56. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), ""));
  57. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
  58. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), ""));
  59. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1));
  60. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), ""));
  61. r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
  62. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
  63. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
  64. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
  65. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
  66. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
  67. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
  68. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
  69. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
  70. }
  71. void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
  72. String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
  73. if (rcedit_path == String()) {
  74. return;
  75. }
  76. if (!FileAccess::exists(rcedit_path)) {
  77. ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included.");
  78. return;
  79. }
  80. #ifndef WINDOWS_ENABLED
  81. // On non-Windows we need WINE to run rcedit
  82. String wine_path = EditorSettings::get_singleton()->get("export/windows/wine");
  83. if (wine_path != String() && !FileAccess::exists(wine_path)) {
  84. ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included.");
  85. return;
  86. }
  87. if (wine_path == String()) {
  88. wine_path = "wine"; // try to run wine from PATH
  89. }
  90. #endif
  91. String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
  92. String file_verion = p_preset->get("application/file_version");
  93. String product_version = p_preset->get("application/product_version");
  94. String company_name = p_preset->get("application/company_name");
  95. String product_name = p_preset->get("application/product_name");
  96. String file_description = p_preset->get("application/file_description");
  97. String copyright = p_preset->get("application/copyright");
  98. String trademarks = p_preset->get("application/trademarks");
  99. String comments = p_preset->get("application/comments");
  100. List<String> args;
  101. args.push_back(p_path);
  102. if (icon_path != String()) {
  103. args.push_back("--set-icon");
  104. args.push_back(icon_path);
  105. }
  106. if (file_verion != String()) {
  107. args.push_back("--set-file-version");
  108. args.push_back(file_verion);
  109. }
  110. if (product_version != String()) {
  111. args.push_back("--set-product-version");
  112. args.push_back(product_version);
  113. }
  114. if (company_name != String()) {
  115. args.push_back("--set-version-string");
  116. args.push_back("CompanyName");
  117. args.push_back(company_name);
  118. }
  119. if (product_name != String()) {
  120. args.push_back("--set-version-string");
  121. args.push_back("ProductName");
  122. args.push_back(product_name);
  123. }
  124. if (file_description != String()) {
  125. args.push_back("--set-version-string");
  126. args.push_back("FileDescription");
  127. args.push_back(file_description);
  128. }
  129. if (copyright != String()) {
  130. args.push_back("--set-version-string");
  131. args.push_back("LegalCopyright");
  132. args.push_back(copyright);
  133. }
  134. if (trademarks != String()) {
  135. args.push_back("--set-version-string");
  136. args.push_back("LegalTrademarks");
  137. args.push_back(trademarks);
  138. }
  139. #ifdef WINDOWS_ENABLED
  140. OS::get_singleton()->execute(rcedit_path, args);
  141. #else
  142. // On non-Windows we need WINE to run rcedit
  143. args.push_front(rcedit_path);
  144. OS::get_singleton()->execute(wine_path, args);
  145. #endif
  146. }
  147. Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
  148. List<String> args;
  149. #ifdef WINDOWS_ENABLED
  150. String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool");
  151. if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
  152. ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting.");
  153. return ERR_FILE_NOT_FOUND;
  154. }
  155. if (signtool_path == String()) {
  156. signtool_path = "signtool"; // try to run signtool from PATH
  157. }
  158. #else
  159. String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode");
  160. if (signtool_path != String() && !FileAccess::exists(signtool_path)) {
  161. ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting.");
  162. return ERR_FILE_NOT_FOUND;
  163. }
  164. if (signtool_path == String()) {
  165. signtool_path = "osslsigncode"; // try to run signtool from PATH
  166. }
  167. #endif
  168. args.push_back("sign");
  169. //identity
  170. #ifdef WINDOWS_ENABLED
  171. int id_type = p_preset->get("codesign/identity_type");
  172. if (id_type == 0) { //auto select
  173. args.push_back("/a");
  174. } else if (id_type == 1) { //pkcs12
  175. if (p_preset->get("codesign/identity") != "") {
  176. args.push_back("/f");
  177. args.push_back(p_preset->get("codesign/identity"));
  178. } else {
  179. EditorNode::add_io_error("codesign: no identity found");
  180. return FAILED;
  181. }
  182. } else if (id_type == 2) { //Windows certificate store
  183. if (p_preset->get("codesign/identity") != "") {
  184. args.push_back("/sha1");
  185. args.push_back(p_preset->get("codesign/identity"));
  186. } else {
  187. EditorNode::add_io_error("codesign: no identity found");
  188. return FAILED;
  189. }
  190. } else {
  191. EditorNode::add_io_error("codesign: invalid identity type");
  192. return FAILED;
  193. }
  194. #else
  195. if (p_preset->get("codesign/identity") != "") {
  196. args.push_back("-pkcs12");
  197. args.push_back(p_preset->get("codesign/identity"));
  198. } else {
  199. EditorNode::add_io_error("codesign: no identity found");
  200. return FAILED;
  201. }
  202. #endif
  203. //password
  204. if (p_preset->get("codesign/password") != "") {
  205. #ifdef WINDOWS_ENABLED
  206. args.push_back("/p");
  207. #else
  208. args.push_back("-pass");
  209. #endif
  210. args.push_back(p_preset->get("codesign/password"));
  211. }
  212. //timestamp
  213. if (p_preset->get("codesign/timestamp")) {
  214. if (p_preset->get("codesign/timestamp_server") != "") {
  215. #ifdef WINDOWS_ENABLED
  216. args.push_back("/tr");
  217. args.push_back(p_preset->get("codesign/timestamp_server_url"));
  218. args.push_back("/td");
  219. if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
  220. args.push_back("sha1");
  221. } else {
  222. args.push_back("sha256");
  223. }
  224. #else
  225. args.push_back("-ts");
  226. args.push_back(p_preset->get("codesign/timestamp_server_url"));
  227. #endif
  228. } else {
  229. EditorNode::add_io_error("codesign: invalid timestamp server");
  230. return FAILED;
  231. }
  232. }
  233. //digest
  234. #ifdef WINDOWS_ENABLED
  235. args.push_back("/fd");
  236. #else
  237. args.push_back("-h");
  238. #endif
  239. if ((int)p_preset->get("codesign/digest_algorithm") == 0) {
  240. args.push_back("sha1");
  241. } else {
  242. args.push_back("sha256");
  243. }
  244. //description
  245. if (p_preset->get("codesign/description") != "") {
  246. #ifdef WINDOWS_ENABLED
  247. args.push_back("/d");
  248. #else
  249. args.push_back("-n");
  250. #endif
  251. args.push_back(p_preset->get("codesign/description"));
  252. }
  253. //user options
  254. PackedStringArray user_args = p_preset->get("codesign/custom_options");
  255. for (int i = 0; i < user_args.size(); i++) {
  256. String user_arg = user_args[i].strip_edges();
  257. if (!user_arg.is_empty()) {
  258. args.push_back(user_arg);
  259. }
  260. }
  261. #ifndef WINDOWS_ENABLED
  262. args.push_back("-in");
  263. #endif
  264. args.push_back(p_path);
  265. #ifndef WINDOWS_ENABLED
  266. args.push_back("-out");
  267. args.push_back(p_path + "_signed");
  268. #endif
  269. String str;
  270. Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true);
  271. ERR_FAIL_COND_V(err != OK, err);
  272. print_line("codesign (" + p_path + "): " + str);
  273. #ifndef WINDOWS_ENABLED
  274. if (str.find("SignTool Error") != -1) {
  275. #else
  276. if (str.find("Failed") != -1) {
  277. #endif
  278. return FAILED;
  279. }
  280. #ifndef WINDOWS_ENABLED
  281. DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
  282. err = tmp_dir->remove(p_path);
  283. ERR_FAIL_COND_V(err != OK, err);
  284. err = tmp_dir->rename(p_path + "_signed", p_path);
  285. ERR_FAIL_COND_V(err != OK, err);
  286. #endif
  287. return OK;
  288. }