diff options
741 files changed, 43078 insertions, 3427 deletions
diff --git a/.travis.yml b/.travis.yml index 9df2388b8e..552a4c44b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ env: global: - SCONS_CACHE=$HOME/.scons_cache - SCONS_CACHE_LIMIT=1024 + - OPTIONS="verbose=yes progress=no openmp=no gdnative_wrapper=yes" cache: directories: @@ -18,10 +19,10 @@ matrix: - env: STATIC_CHECKS=yes os: linux compiler: clang - - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools + - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools OPTIONS="${OPTIONS} builtin_openssl=yes" os: linux compiler: gcc - - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang OPTIONS="${OPTIONS} builtin_openssl=yes" os: linux compiler: clang #- env: GODOT_TARGET=windows TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools @@ -90,5 +91,5 @@ script: - if [ "$STATIC_CHECKS" = "yes" ]; then sh ./misc/travis/clang-format.sh; else - scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS verbose=yes progress=no openmp=no gdnative_wrapper=yes; + scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $OPTIONS; fi diff --git a/AUTHORS.md b/AUTHORS.md index 9a373896f8..752300cb5b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -60,11 +60,12 @@ name is available. Hiroshi Ogawa (hi-ogawa) Hubert Jarosz (Marqin) Hugo Locurcio (Calinou) - Ian (ianb96) + Ian Bishop (ianb96) Ignacio Etcheverry (neikeq) Indah Sylvia (ISylvox) J08nY Jakub Grzesik (kubecz3k) + Jérôme GULLY (Nutriz) Johan Manuel (29jm) Joshua Grams (JoshuaGrams) Juan Linietsky (reduz) @@ -100,6 +101,7 @@ name is available. Timo (toger5) V. Vamsi Krishna (vkbsb) Vinzenz Feenstra (vinzenz) + Wilhem Barbier (nounoursheureux) Will Nations (willnationsdev) Wilson E. Alvarez (Rubonnek) Yuri Roubinski (Chaosus) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index b6a00ea5be..bcfaf61664 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -122,13 +122,18 @@ License: Expat and Zlib Files: ./thirdparty/b2d_convexdecomp/ Comment: Box2D (ConvexDecomp) Copyright: 2007, Eric Jordan -Copyright: 2006-2009, Erin Catto + 2006-2009, Erin Catto +License: Zlib + +Files: ./thirdparty/bullet/ +Comment: Bullet Continuous Collision Detection and Physics Library +Copyright: 2003-2008, Erwin Coumans License: Zlib Files: ./thirdparty/certs/ca-certificates.crt -Comment: FIXME -Copyright: FIXME -License: FIXME +Comment: CA certificates +Copyright: Mozilla Contributors +License: MPL-2.0 Files: ./thirdparty/enet/ Comment: ENet @@ -1017,6 +1022,381 @@ License: ISC ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +License: MPL-2.0 + Mozilla Public License Version 2.0 + ================================== + . + 1. Definitions + -------------- + . + 1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + . + 1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + . + 1.3. "Contribution" + means Covered Software of a particular Contributor. + . + 1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + . + 1.5. "Incompatible With Secondary Licenses" + means + . + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + . + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + . + 1.6. "Executable Form" + means any form of the work other than Source Code Form. + . + 1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + . + 1.8. "License" + means this document. + . + 1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + . + 1.10. "Modifications" + means any of the following: + . + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + . + (b) any new file in Source Code Form that contains any Covered + Software. + . + 1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + . + 1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + . + 1.13. "Source Code Form" + means the form of the work preferred for making modifications. + . + 1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + . + 2. License Grants and Conditions + -------------------------------- + . + 2.1. Grants + . + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + . + (a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + . + (b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + . + 2.2. Effective Date + . + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + . + 2.3. Limitations on Grant Scope + . + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + . + (a) for any code that a Contributor has removed from Covered Software; + or + . + (b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + . + (c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + . + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + . + 2.4. Subsequent Licenses + . + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + . + 2.5. Representation + . + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights + to grant the rights to its Contributions conveyed by this License. + . + 2.6. Fair Use + . + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + . + 2.7. Conditions + . + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted + in Section 2.1. + . + 3. Responsibilities + ------------------- + . + 3.1. Distribution of Source Form + . + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + . + 3.2. Distribution of Executable Form + . + If You distribute Covered Software in Executable Form then: + . + (a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + . + (b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + . + 3.3. Distribution of a Larger Work + . + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + . + 3.4. Notices + . + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, + or limitations of liability) contained within the Source Code Form of + the Covered Software, except that You may alter any license notices to + the extent required to remedy known factual inaccuracies. + . + 3.5. Application of Additional Terms + . + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + . + 4. Inability to Comply Due to Statute or Regulation + --------------------------------------------------- + . + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Software due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description must + be placed in a text file included with all distributions of the Covered + Software under this License. Except to the extent prohibited by statute + or regulation, such description must be sufficiently detailed for a + recipient of ordinary skill to be able to understand it. + . + 5. Termination + -------------- + . + 5.1. The rights granted under this License will terminate automatically + if You fail to comply with any of its terms. However, if You become + compliant, then the rights granted under this License from a particular + Contributor are reinstated (a) provisionally, unless and until such + Contributor explicitly and finally terminates Your grants, and (b) on an + ongoing basis, if such Contributor fails to notify You of the + non-compliance by some reasonable means prior to 60 days after You have + come back into compliance. Moreover, Your grants from a particular + Contributor are reinstated on an ongoing basis if such Contributor + notifies You of the non-compliance by some reasonable means, this is the + first time You have received notice of non-compliance with this License + from such Contributor, and You become compliant prior to 30 days after + Your receipt of the notice. + . + 5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + . + 5.3. In the event of termination under Sections 5.1 or 5.2 above, all + end user license agreements (excluding distributors and resellers) which + have been validly granted by You or Your distributors under this License + prior to termination shall survive termination. + . + ************************************************************************ + * * + * 6. Disclaimer of Warranty * + * ------------------------- * + * * + * Covered Software is provided under this License on an "as is" * + * basis, without warranty of any kind, either expressed, implied, or * + * statutory, including, without limitation, warranties that the * + * Covered Software is free of defects, merchantable, fit for a * + * particular purpose or non-infringing. The entire risk as to the * + * quality and performance of the Covered Software is with You. * + * Should any Covered Software prove defective in any respect, You * + * (not any Contributor) assume the cost of any necessary servicing, * + * repair, or correction. This disclaimer of warranty constitutes an * + * essential part of this License. No use of any Covered Software is * + * authorized under this License except under this disclaimer. * + * * + ************************************************************************ + . + ************************************************************************ + * * + * 7. Limitation of Liability * + * -------------------------- * + * * + * Under no circumstances and under no legal theory, whether tort * + * (including negligence), contract, or otherwise, shall any * + * Contributor, or anyone who distributes Covered Software as * + * permitted above, be liable to You for any direct, indirect, * + * special, incidental, or consequential damages of any character * + * including, without limitation, damages for lost profits, loss of * + * goodwill, work stoppage, computer failure or malfunction, or any * + * and all other commercial damages or losses, even if such party * + * shall have been informed of the possibility of such damages. This * + * limitation of liability shall not apply to liability for death or * + * personal injury resulting from such party's negligence to the * + * extent applicable law prohibits such limitation. Some * + * jurisdictions do not allow the exclusion or limitation of * + * incidental or consequential damages, so this exclusion and * + * limitation may not apply to You. * + * * + ************************************************************************ + . + 8. Litigation + ------------- + . + Any litigation relating to this License may be brought only in the + courts of a jurisdiction where the defendant maintains its principal + place of business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. + Nothing in this Section shall prevent a party's ability to bring + cross-claims or counter-claims. + . + 9. Miscellaneous + ---------------- + . + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides + that the language of a contract shall be construed against the drafter + shall not be used to construe this License against a Contributor. + . + 10. Versions of the License + --------------------------- + . + 10.1. New Versions + . + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + . + 10.2. Effect of New Versions + . + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + . + 10.3. Modified Versions + . + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + . + 10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses + . + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + . + Exhibit A - Source Code Form License Notice + ------------------------------------------- + . + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + . + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to look + for such a notice. + . + You may add additional accurate notices of copyright ownership. + . + Exhibit B - "Incompatible With Secondary Licenses" Notice + --------------------------------------------------------- + . + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + License: OFL-1.1 PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide @@ -24,10 +24,12 @@ generous deed immortalized in the next stable release of Godot Engine. Andreas Brandon Lamb Christian Uldall Pedersen + Christopher Igoe Christoph Woinke E Hewert Hein-Pieter van Braam Jamal Alyafei + Jay Sistar Jordan M Lucas Matthieu Huvé Nathan Warden @@ -36,6 +38,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ruslan Mustakov Slobodan Milnovic Stephan Lanfermann + Stoney Meyerhoeffer Thomas Mathews ## Gold donors @@ -50,33 +53,35 @@ generous deed immortalized in the next stable release of Godot Engine. Rémi Verschelde Zaven Muradyan + Allen Schade Andreas Schüle Austen McRae Benjamin Botwin Bernhard Liebl - Cody Brocious - Gerald E Butler - Jahn Johansen Johannes Wuensch + Josep G. Camarasa Kris Michael Libre-Dépanne Ranoller Svenne Krap + Timothy Hagberg BanjoNode2D + Brandon Chris Serino Conrad Curry Craig Smith David Churchill Dean Harmon + Dexter Miguel + Garrett Dockins Guilherme Felipe de C. G. da Silva - Henrique Alves + Harman Bains + Karsten Bock Laurence Bannister - PrzemysÅ‚aw GoÅ‚Ä…b (n-pigeon) Robert Willes Robin Arys ScottMakesGames - summerblind Testus Maximus Thomas Bjarnelöf Xavier Tan @@ -86,25 +91,31 @@ generous deed immortalized in the next stable release of Godot Engine. Andreas Haas Arnaud Verstuyf Chris Brown + Chris Wilson Cody Parker D Daniel Eliasinski - Daniel Langegger Eric Monson Ezra Theunissen flesk François Cantin - Giovanni Solimeno + GGGames.org + Heath Hayes Jeppe Zapp + Jeremi Biernacki joe513 + Juraj Móza Justin Arnold Justo Delgado Baudà Leandro Voltolino - Lucien Boudy + Lisandro Lorea Markus Wiesner + Nick Nikitin Pablo Cholaky + Paolo Munoz Patrick Schnorbus Pete Goodwin + Rami Ryan Estes Ted Travis Womack @@ -113,24 +124,31 @@ generous deed immortalized in the next stable release of Godot Engine. ## Silver donors 1D_Inc + Adam Carr Alder Stefano Alessandro Senese - Alex Barsukov Ãlvaro DomÃnguez López - Andres Cuevas + Andrea Badii + Andrew Thomas Anthony Bongiovanni + Arda Erol + Arthur S. Muszynski Avencherus Bastian Böhm + Benedikt + Benjamin Beshara Ben Vercammen Blair Allen Bryanna M Bryan Stevenson + Carwyn Edwards Casey Foote Christian Baune Christian Winter Collin Shooltz Daniel Egger Daniel Kaplan + Daniel Langegger Daniel Mircea David Cravens David May @@ -146,52 +164,57 @@ generous deed immortalized in the next stable release of Godot Engine. Gerrit Großkopf Gilberto K. Otubo Guldoman - HardRound HeartBeast Heribert Hirth Hunter Jones Jaime Ruiz-Borau Vizárraga + Jed Rose Jeff Hungerford - Jerry Chen - Jesse Liles + Jonas Yamazaki + Jonathan Nieto Jonathon Josh 'Cheeseness' Bush Juan Negrier + Judd JuDelCo Julian Murgia - Juraj Móza - Karonis KC Chan Kevin Boyer Kevin Kamper Meejach Petersen Klavdij Voncina + Lars pfeffer Linus Lind Lundgren - Lisandro Lorea magodev + Martin Eigel Martin Novák Matthew Fitzpatrick Matthias Hölzl + Maurice Meysel Max R.R. Collada memoryruins mhilbrunner + Michael Dürwald Michael Gringauz - Mikael Olsson MoM monokrome Moritz Laass nee Neil Blakey-Milner + Niclas Eriksen Nik Lee Niko Leopold + Noi Sek Oleg Tyshchenko Pablo Seibelt Pan Ip Pat LaBine Patrick Nafarrete + Patric Vormstein Paul Mason PaweÅ‚ Kowal Pierre-Igor Berthet Pietro Vertechi + Piotr Kaczmarski Richman Stewart Rodolfo Baeza Roger Burgess @@ -202,11 +225,12 @@ generous deed immortalized in the next stable release of Godot Engine. Sootstone Stephen Traskal Theo Cranmore - Thomas Norman + Thomas Bell + Thomas Herzog & Xananax Tom Larrow UltyX Wout Standaert - Xananax & karroffel + Yu He ## Bronze donors diff --git a/core/dictionary.cpp b/core/dictionary.cpp index ae1af86b77..e3f4aa5f28 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -34,15 +34,10 @@ #include "safe_refcount.h" #include "variant.h" -struct _DictionaryVariantHash { - - static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); } -}; - struct DictionaryPrivate { SafeRefCount refcount; - OrderedHashMap<Variant, Variant, _DictionaryVariantHash> variant_map; + OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map; }; void Dictionary::get_key_list(List<Variant> *p_keys) const { @@ -50,7 +45,7 @@ void Dictionary::get_key_list(List<Variant> *p_keys) const { if (_p->variant_map.empty()) return; - for (OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::Element E = _p->variant_map.front(); E; E = E.next()) { + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { p_keys->push_back(E.key()); } } @@ -66,7 +61,7 @@ const Variant &Dictionary::operator[](const Variant &p_key) const { } const Variant *Dictionary::getptr(const Variant &p_key) const { - OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::ConstElement E = ((const OrderedHashMap<Variant, Variant, _DictionaryVariantHash> *)&_p->variant_map)->find(p_key); + OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key); if (!E) return NULL; @@ -75,7 +70,7 @@ const Variant *Dictionary::getptr(const Variant &p_key) const { Variant *Dictionary::getptr(const Variant &p_key) { - OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::Element E = _p->variant_map.find(p_key); + OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(p_key); if (!E) return NULL; @@ -84,7 +79,7 @@ Variant *Dictionary::getptr(const Variant &p_key) { Variant Dictionary::get_valid(const Variant &p_key) const { - OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::ConstElement E = ((const OrderedHashMap<Variant, Variant, _DictionaryVariantHash> *)&_p->variant_map)->find(p_key); + OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::ConstElement E = ((const OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator> *)&_p->variant_map)->find(p_key); if (!E) return Variant(); @@ -177,7 +172,7 @@ Array Dictionary::keys() const { return varr; int i = 0; - for (OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::Element E = _p->variant_map.front(); E; E = E.next()) { + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { varr[i] = E.key(); i++; } @@ -193,7 +188,7 @@ Array Dictionary::values() const { return varr; int i = 0; - for (OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::Element E = _p->variant_map.front(); E; E = E.next()) { + for (OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.front(); E; E = E.next()) { varr[i] = E.get(); i++; } @@ -209,7 +204,7 @@ const Variant *Dictionary::next(const Variant *p_key) const { return &_p->variant_map.front().key(); return NULL; } - OrderedHashMap<Variant, Variant, _DictionaryVariantHash>::Element E = _p->variant_map.find(*p_key); + OrderedHashMap<Variant, Variant, VariantHasher, VariantComparator>::Element E = _p->variant_map.find(*p_key); if (E && E.next()) return &E.next().key(); diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 7b2bccdfec..ef886cdb3c 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -83,7 +83,7 @@ int64_t FileAccessNetworkClient::get_64() { void FileAccessNetworkClient::_thread_func() { - client->set_nodelay(true); + client->set_no_delay(true); while (!quit) { DEBUG_PRINT("SEM WAIT - " + itos(sem->get())); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 9cfa810034..5d008904ff 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -55,6 +55,7 @@ void StreamPeerTCP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port); ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host); + ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay); BIND_ENUM_CONSTANT(STATUS_NONE); BIND_ENUM_CONSTANT(STATUS_CONNECTING); diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index fc84525c5f..8a16d820f2 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -65,7 +65,7 @@ public: virtual void disconnect_from_host() = 0; virtual IP_Address get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; - virtual void set_nodelay(bool p_enabled) = 0; + virtual void set_no_delay(bool p_enabled) = 0; static Ref<StreamPeerTCP> create_ref(); static StreamPeerTCP *create(); diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 957e16f92c..5bae74ac7e 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -74,7 +74,7 @@ bool Triangulate::is_inside_triangle(real_t Ax, real_t Ay, cCROSSap = cx * apy - cy * apx; bCROSScp = bx * cpy - by * cpx; - return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); + return ((aCROSSbp > 0.0) && (bCROSScp > 0.0) && (cCROSSap > 0.0)); }; bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V) { diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 368b4ad09d..033b4b12b9 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -479,6 +479,9 @@ void FileAccess::store_double(double p_dest) { uint64_t FileAccess::get_modified_time(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) + return 0; + FileAccess *fa = create_for_path(p_file); ERR_FAIL_COND_V(!fa, 0); diff --git a/core/os/input_event.h b/core/os/input_event.h index 72057659d2..ad754d0d1f 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -110,8 +110,8 @@ enum JoystickList { JOY_WII_C = JOY_BUTTON_5, JOY_WII_Z = JOY_BUTTON_6, - JOY_WII_MINUS = JOY_BUTTON_9, - JOY_WII_PLUS = JOY_BUTTON_10, + JOY_WII_MINUS = JOY_BUTTON_10, + JOY_WII_PLUS = JOY_BUTTON_11, // end of history diff --git a/core/resource.cpp b/core/resource.cpp index 38f938932c..2eeed50d9d 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -74,7 +74,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { bool exists = ResourceCache::resources.has(p_path); ResourceCache::lock->read_unlock(); - ERR_EXPLAIN("Another resource is loaded from path: " + p_path); + ERR_EXPLAIN("Another resource is loaded from path: " + p_path + " (possible cyclic resource inclusion)"); ERR_FAIL_COND(exists); } } diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index ff2f17103c..3b203f6977 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -119,8 +119,8 @@ _ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, // The actual advertised functions; they'll call the right implementation -uint32_t atomic_conditional_increment(register uint32_t *counter) { - return _atomic_conditional_increment_impl(counter); +uint32_t atomic_conditional_increment(register uint32_t *pw) { + return _atomic_conditional_increment_impl(pw); } uint32_t atomic_decrement(register uint32_t *pw) { @@ -143,8 +143,8 @@ uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val return _atomic_exchange_if_greater_impl(pw, val); } -uint64_t atomic_conditional_increment(register uint64_t *counter) { - return _atomic_conditional_increment_impl(counter); +uint64_t atomic_conditional_increment(register uint64_t *pw) { + return _atomic_conditional_increment_impl(pw); } uint64_t atomic_decrement(register uint64_t *pw) { diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 0da377453e..c0e115e300 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -291,7 +291,8 @@ void ScriptDebuggerLocal::profiling_end() { void ScriptDebuggerLocal::send_message(const String &p_message, const Array &p_args) { - print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args))); + // This needs to be cleaned up entirely. + // print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args))); } void ScriptDebuggerLocal::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index a297bb738f..41071ace5d 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -598,7 +598,13 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { Array send_props; for (int i = 0; i < properties.size(); i++) { const PropertyInfo &pi = properties[i].first; - const Variant &var = properties[i].second; + Variant &var = properties[i].second; + + WeakRef *ref = Object::cast_to<WeakRef>(var); + if (ref) { + var = ref->get_ref(); + } + RES res = var; Array prop; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index e9f7af3f63..5607751a27 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -1539,7 +1539,7 @@ void register_variant_methods() { ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, normalized, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, inverse, varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, REAL, "by", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray()); @@ -1748,10 +1748,10 @@ void register_variant_methods() { ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, rotated, REAL, "phi", varray()); ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, scaled, VECTOR2, "scale", varray()); ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, translated, VECTOR2, "offset", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, xform, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, xform_inv, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, basis_xform, NIL, "v", varray()); - ADDFUNC1R(TRANSFORM2D, TRANSFORM2D, Transform2D, basis_xform_inv, NIL, "v", varray()); + ADDFUNC1R(TRANSFORM2D, NIL, Transform2D, xform, NIL, "v", varray()); + ADDFUNC1R(TRANSFORM2D, NIL, Transform2D, xform_inv, NIL, "v", varray()); + ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform, VECTOR2, "v", varray()); + ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform_inv, VECTOR2, "v", varray()); ADDFUNC2R(TRANSFORM2D, TRANSFORM2D, Transform2D, interpolate_with, TRANSFORM2D, "transform", REAL, "weight", varray()); ADDFUNC0R(BASIS, BASIS, Basis, inverse, varray()); diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index b9694d0e6d..d4bd937f49 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@GDScript" category="Core" version="3.0-rc1"> +<class name="@GDScript" category="Core" version="3.0-stable"> <brief_description> Built-in GDScript functions. </brief_description> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 8b3e6345ec..1f6256f8c9 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@GlobalScope" category="Core" version="3.0-rc1"> +<class name="@GlobalScope" category="Core" version="3.0-stable"> <brief_description> Global scope constants and variables. </brief_description> diff --git a/doc/classes/@NativeScript.xml b/doc/classes/@NativeScript.xml index b583fa8361..8fde9e3dc3 100644 --- a/doc/classes/@NativeScript.xml +++ b/doc/classes/@NativeScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@NativeScript" category="Core" version="3.0-rc1"> +<class name="@NativeScript" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/@VisualScript.xml b/doc/classes/@VisualScript.xml index cc31953eb0..4867a5a22b 100644 --- a/doc/classes/@VisualScript.xml +++ b/doc/classes/@VisualScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@VisualScript" category="Core" version="3.0-rc1"> +<class name="@VisualScript" category="Core" version="3.0-stable"> <brief_description> Built-in visual script functions. </brief_description> diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index c288e2a0c3..f253d49dc0 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AABB" category="Built-In Types" version="3.0-rc1"> +<class name="AABB" category="Built-In Types" version="3.0-stable"> <brief_description> Axis-Aligned Bounding Box. </brief_description> @@ -7,7 +7,7 @@ AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml index 7aec464336..cc9370e8fe 100644 --- a/doc/classes/ARVRAnchor.xml +++ b/doc/classes/ARVRAnchor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRAnchor" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="ARVRAnchor" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Anchor point in AR Space </brief_description> diff --git a/doc/classes/ARVRCamera.xml b/doc/classes/ARVRCamera.xml index 9d904c4965..b603a69337 100644 --- a/doc/classes/ARVRCamera.xml +++ b/doc/classes/ARVRCamera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRCamera" inherits="Camera" category="Core" version="3.0-rc1"> +<class name="ARVRCamera" inherits="Camera" category="Core" version="3.0-stable"> <brief_description> A camera node with a few overrules for AR/VR applied such as location tracking. </brief_description> diff --git a/doc/classes/ARVRController.xml b/doc/classes/ARVRController.xml index 2706cc825c..07692f5ab2 100644 --- a/doc/classes/ARVRController.xml +++ b/doc/classes/ARVRController.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRController" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="ARVRController" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> A spatial node representing a spatially tracked controller. </brief_description> diff --git a/doc/classes/ARVRInterface.xml b/doc/classes/ARVRInterface.xml index 50f49ae66c..bf9dde7706 100644 --- a/doc/classes/ARVRInterface.xml +++ b/doc/classes/ARVRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRInterface" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="ARVRInterface" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Base class for ARVR interface implementation. </brief_description> diff --git a/doc/classes/ARVROrigin.xml b/doc/classes/ARVROrigin.xml index 1ee17938d3..ec9b79bf84 100644 --- a/doc/classes/ARVROrigin.xml +++ b/doc/classes/ARVROrigin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVROrigin" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="ARVROrigin" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Our origin point in AR/VR. </brief_description> diff --git a/doc/classes/ARVRPositionalTracker.xml b/doc/classes/ARVRPositionalTracker.xml index c9bb742b35..ea2139406b 100644 --- a/doc/classes/ARVRPositionalTracker.xml +++ b/doc/classes/ARVRPositionalTracker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRPositionalTracker" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ARVRPositionalTracker" inherits="Object" category="Core" version="3.0-stable"> <brief_description> A tracked object </brief_description> diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml index 1eec98a40d..29d217d1d3 100644 --- a/doc/classes/ARVRServer.xml +++ b/doc/classes/ARVRServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ARVRServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> This is our AR/VR Server. </brief_description> diff --git a/doc/classes/AStar.xml b/doc/classes/AStar.xml index 37ee6362a4..0573e4edf0 100644 --- a/doc/classes/AStar.xml +++ b/doc/classes/AStar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AStar" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="AStar" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> AStar class representation that uses vectors as edges. </brief_description> diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index 76ee439d9d..a6cc40238b 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AcceptDialog" inherits="WindowDialog" category="Core" version="3.0-rc1"> +<class name="AcceptDialog" inherits="WindowDialog" category="Core" version="3.0-stable"> <brief_description> Base dialog for user notification. </brief_description> diff --git a/doc/classes/AnimatedSprite.xml b/doc/classes/AnimatedSprite.xml index f13a4e5fe8..3e67fd6840 100644 --- a/doc/classes/AnimatedSprite.xml +++ b/doc/classes/AnimatedSprite.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AnimatedSprite" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="AnimatedSprite" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Sprite node that can use multiple textures for animation. </brief_description> diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml index 4f37c78884..f72211776e 100644 --- a/doc/classes/AnimatedSprite3D.xml +++ b/doc/classes/AnimatedSprite3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AnimatedSprite3D" inherits="SpriteBase3D" category="Core" version="3.0-rc1"> +<class name="AnimatedSprite3D" inherits="SpriteBase3D" category="Core" version="3.0-stable"> <brief_description> 2D sprite node in 3D world, that can use multiple 2D textures for animation. </brief_description> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 104cdb114c..f8c94dd12d 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Animation" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Animation" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Contains data used to animate everything in the engine. </brief_description> @@ -8,7 +8,7 @@ Animations are just data containers, and must be added to odes such as an [AnimationPlayer] or [AnimationTreePlayer] to be played back. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/animation/index.html + http://docs.godotengine.org/en/3.0/tutorials/animation/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 4baef0a7f0..b15f0ec732 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AnimationPlayer" inherits="Node" category="Core" version="3.0-rc1"> +<class name="AnimationPlayer" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Container and player of [Animation] resources. </brief_description> @@ -7,8 +7,8 @@ An animation player is used for general purpose playback of [Animation] resources. It contains a dictionary of animations (referenced by name) and custom blend times between their transitions. Additionally, animations can be played and blended in different channels. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/animations.html - http://docs.godotengine.org/en/3.0/learning/features/animation/index.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html + http://docs.godotengine.org/en/3.0/tutorials/animation/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/AnimationTreePlayer.xml b/doc/classes/AnimationTreePlayer.xml index ed463bb23a..c09121857e 100644 --- a/doc/classes/AnimationTreePlayer.xml +++ b/doc/classes/AnimationTreePlayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AnimationTreePlayer" inherits="Node" category="Core" version="3.0-rc1"> +<class name="AnimationTreePlayer" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Animation Player that uses a node graph for blending Animations. </brief_description> diff --git a/doc/classes/Area.xml b/doc/classes/Area.xml index d727dea27f..2853159c0e 100644 --- a/doc/classes/Area.xml +++ b/doc/classes/Area.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Area" inherits="CollisionObject" category="Core" version="3.0-rc1"> +<class name="Area" inherits="CollisionObject" category="Core" version="3.0-stable"> <brief_description> General purpose area node for detection and 3D physics influence. </brief_description> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 23b49adbed..8e8382d5de 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Area2D" inherits="CollisionObject2D" category="Core" version="3.0-rc1"> +<class name="Area2D" inherits="CollisionObject2D" category="Core" version="3.0-stable"> <brief_description> 2D area for detection and 2D physics influence. </brief_description> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 373c387658..4d282761fe 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Array" category="Built-In Types" version="3.0-rc1"> +<class name="Array" category="Built-In Types" version="3.0-stable"> <brief_description> Generic array datatype. </brief_description> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index b6374ee459..7b74b678bd 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ArrayMesh" inherits="Mesh" category="Core" version="3.0-rc1"> +<class name="ArrayMesh" inherits="Mesh" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/AtlasTexture.xml b/doc/classes/AtlasTexture.xml index 20acca5403..87cd072d99 100644 --- a/doc/classes/AtlasTexture.xml +++ b/doc/classes/AtlasTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AtlasTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="AtlasTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> Packs multiple small textures in a single, bigger one. Helps to optimize video memory costs and render calls. </brief_description> diff --git a/doc/classes/AudioBusLayout.xml b/doc/classes/AudioBusLayout.xml index 4fa13d02e3..d960093fc6 100644 --- a/doc/classes/AudioBusLayout.xml +++ b/doc/classes/AudioBusLayout.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioBusLayout" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="AudioBusLayout" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Stores information about the audiobusses. </brief_description> diff --git a/doc/classes/AudioEffect.xml b/doc/classes/AudioEffect.xml index 04d26b2bd5..81c72e34b2 100644 --- a/doc/classes/AudioEffect.xml +++ b/doc/classes/AudioEffect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffect" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="AudioEffect" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Audio Effect For Audio. </brief_description> diff --git a/doc/classes/AudioEffectAmplify.xml b/doc/classes/AudioEffectAmplify.xml index 5286789f87..7a031f7f47 100644 --- a/doc/classes/AudioEffectAmplify.xml +++ b/doc/classes/AudioEffectAmplify.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectAmplify" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectAmplify" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Amplify audio effect to an Audio bus. Increases or decreases the volume of the selected audio bus. diff --git a/doc/classes/AudioEffectBandLimitFilter.xml b/doc/classes/AudioEffectBandLimitFilter.xml index 43e8732c13..592735e098 100644 --- a/doc/classes/AudioEffectBandLimitFilter.xml +++ b/doc/classes/AudioEffectBandLimitFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectBandLimitFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectBandLimitFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> Adds a band limit filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectBandPassFilter.xml b/doc/classes/AudioEffectBandPassFilter.xml index e0b98859cc..3705590c83 100644 --- a/doc/classes/AudioEffectBandPassFilter.xml +++ b/doc/classes/AudioEffectBandPassFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectBandPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectBandPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> Adds a band pass filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectChorus.xml b/doc/classes/AudioEffectChorus.xml index b5992a279a..4bcd0e2552 100644 --- a/doc/classes/AudioEffectChorus.xml +++ b/doc/classes/AudioEffectChorus.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectChorus" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectChorus" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a chorus audio effect. </brief_description> diff --git a/doc/classes/AudioEffectCompressor.xml b/doc/classes/AudioEffectCompressor.xml index c1b9a84f3c..4fd28326a2 100644 --- a/doc/classes/AudioEffectCompressor.xml +++ b/doc/classes/AudioEffectCompressor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectCompressor" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectCompressor" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Compressor audio effect to an Audio bus. Reduces sounds that exceed a certain threshold level, smooths out the dynamics and increases the overall volume. diff --git a/doc/classes/AudioEffectDelay.xml b/doc/classes/AudioEffectDelay.xml index dca01bc2bd..d253ffab96 100644 --- a/doc/classes/AudioEffectDelay.xml +++ b/doc/classes/AudioEffectDelay.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectDelay" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectDelay" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Delay audio effect to an Audio bus. Plays input signal back after a period of time. Two tap delay and feedback options. diff --git a/doc/classes/AudioEffectDistortion.xml b/doc/classes/AudioEffectDistortion.xml index 7559c2d055..91a1961b30 100644 --- a/doc/classes/AudioEffectDistortion.xml +++ b/doc/classes/AudioEffectDistortion.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectDistortion" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectDistortion" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Distortion audio effect to an Audio bus. Modify the sound to make it dirty. diff --git a/doc/classes/AudioEffectEQ.xml b/doc/classes/AudioEffectEQ.xml index 4a4d72bc56..667982d529 100644 --- a/doc/classes/AudioEffectEQ.xml +++ b/doc/classes/AudioEffectEQ.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectEQ" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectEQ" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Base class for audio equalizers. Gives you control over frequencies. Use it to create a custom equalizer if [AudioEffectEQ6], [AudioEffectEQ10] or [AudioEffectEQ21] don't fit your needs. diff --git a/doc/classes/AudioEffectEQ10.xml b/doc/classes/AudioEffectEQ10.xml index 5535c8b1f8..9e5303a266 100644 --- a/doc/classes/AudioEffectEQ10.xml +++ b/doc/classes/AudioEffectEQ10.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectEQ10" inherits="AudioEffectEQ" category="Core" version="3.0-rc1"> +<class name="AudioEffectEQ10" inherits="AudioEffectEQ" category="Core" version="3.0-stable"> <brief_description> Adds a 10-band equalizer audio effect to an Audio bus. Gives you control over frequencies from 31 Hz to 16000 Hz. Each frequency can be modulated between -60/+24 dB. diff --git a/doc/classes/AudioEffectEQ21.xml b/doc/classes/AudioEffectEQ21.xml index a69530a257..ec0db8bbbe 100644 --- a/doc/classes/AudioEffectEQ21.xml +++ b/doc/classes/AudioEffectEQ21.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectEQ21" inherits="AudioEffectEQ" category="Core" version="3.0-rc1"> +<class name="AudioEffectEQ21" inherits="AudioEffectEQ" category="Core" version="3.0-stable"> <brief_description> Adds a 21-band equalizer audio effect to an Audio bus. Gives you control over frequencies from 22 Hz to 22000 Hz. Each frequency can be modulated between -60/+24 dB. diff --git a/doc/classes/AudioEffectEQ6.xml b/doc/classes/AudioEffectEQ6.xml index d73d19042b..f5748f0a36 100644 --- a/doc/classes/AudioEffectEQ6.xml +++ b/doc/classes/AudioEffectEQ6.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectEQ6" inherits="AudioEffectEQ" category="Core" version="3.0-rc1"> +<class name="AudioEffectEQ6" inherits="AudioEffectEQ" category="Core" version="3.0-stable"> <brief_description> Adds a 6-band equalizer audio effect to an Audio bus. Gives you control over frequencies from 32 Hz to 10000 Hz. Each frequency can be modulated between -60/+24 dB. diff --git a/doc/classes/AudioEffectFilter.xml b/doc/classes/AudioEffectFilter.xml index f79df18ae4..9c0eb38612 100644 --- a/doc/classes/AudioEffectFilter.xml +++ b/doc/classes/AudioEffectFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectFilter" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectFilter" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectHighPassFilter.xml b/doc/classes/AudioEffectHighPassFilter.xml index b0b2dca1bb..451c65a228 100644 --- a/doc/classes/AudioEffectHighPassFilter.xml +++ b/doc/classes/AudioEffectHighPassFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectHighPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectHighPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> Adds a high pass filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectHighShelfFilter.xml b/doc/classes/AudioEffectHighShelfFilter.xml index 4419143f6f..a26d4a4e6b 100644 --- a/doc/classes/AudioEffectHighShelfFilter.xml +++ b/doc/classes/AudioEffectHighShelfFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectHighShelfFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectHighShelfFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/AudioEffectLimiter.xml b/doc/classes/AudioEffectLimiter.xml index 1f7d68e704..b6fe7b74e5 100644 --- a/doc/classes/AudioEffectLimiter.xml +++ b/doc/classes/AudioEffectLimiter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectLimiter" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectLimiter" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a soft clip Limiter audio effect to an Audio bus. </brief_description> diff --git a/doc/classes/AudioEffectLowPassFilter.xml b/doc/classes/AudioEffectLowPassFilter.xml index bc1965a8cb..25c80e2ea0 100644 --- a/doc/classes/AudioEffectLowPassFilter.xml +++ b/doc/classes/AudioEffectLowPassFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectLowPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectLowPassFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> Adds a low pass filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectLowShelfFilter.xml b/doc/classes/AudioEffectLowShelfFilter.xml index 5578badf88..f60429ca15 100644 --- a/doc/classes/AudioEffectLowShelfFilter.xml +++ b/doc/classes/AudioEffectLowShelfFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectLowShelfFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectLowShelfFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/AudioEffectNotchFilter.xml b/doc/classes/AudioEffectNotchFilter.xml index 2bee6a4380..ac78d9c3e5 100644 --- a/doc/classes/AudioEffectNotchFilter.xml +++ b/doc/classes/AudioEffectNotchFilter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectNotchFilter" inherits="AudioEffectFilter" category="Core" version="3.0-rc1"> +<class name="AudioEffectNotchFilter" inherits="AudioEffectFilter" category="Core" version="3.0-stable"> <brief_description> Adds a notch filter to the Audio Bus. </brief_description> diff --git a/doc/classes/AudioEffectPanner.xml b/doc/classes/AudioEffectPanner.xml index 6916a68dae..493e83393a 100644 --- a/doc/classes/AudioEffectPanner.xml +++ b/doc/classes/AudioEffectPanner.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectPanner" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectPanner" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Panner audio effect to an Audio bus. Pans sound left or right. </brief_description> diff --git a/doc/classes/AudioEffectPhaser.xml b/doc/classes/AudioEffectPhaser.xml index a7d75ab594..3cc89e73f8 100644 --- a/doc/classes/AudioEffectPhaser.xml +++ b/doc/classes/AudioEffectPhaser.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectPhaser" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectPhaser" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Phaser audio effect to an Audio bus. Combines the original signal with a copy that is slightly out of phase with the original. diff --git a/doc/classes/AudioEffectPitchShift.xml b/doc/classes/AudioEffectPitchShift.xml index 313e0e249a..b0e7dbc049 100644 --- a/doc/classes/AudioEffectPitchShift.xml +++ b/doc/classes/AudioEffectPitchShift.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectPitchShift" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectPitchShift" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Pitch shift audio effect to an Audio bus. Raises or lowers the pitch of original sound. diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml index a880870dce..46d1866ff0 100644 --- a/doc/classes/AudioEffectReverb.xml +++ b/doc/classes/AudioEffectReverb.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectReverb" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectReverb" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> Adds a Reverb audio effect to an Audio bus. Simulates the sound of acoustic environments such as rooms, concert halls, caverns, or an open spaces. diff --git a/doc/classes/AudioEffectStereoEnhance.xml b/doc/classes/AudioEffectStereoEnhance.xml index 8148dd6659..6e6e9af5c6 100644 --- a/doc/classes/AudioEffectStereoEnhance.xml +++ b/doc/classes/AudioEffectStereoEnhance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioEffectStereoEnhance" inherits="AudioEffect" category="Core" version="3.0-rc1"> +<class name="AudioEffectStereoEnhance" inherits="AudioEffect" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index c078359c11..a6352bca1e 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="AudioServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Server interface for low level audio access. </brief_description> @@ -7,7 +7,7 @@ AudioServer is a low level server interface for audio access. It is in charge of creating sample data (playable audio) as well as its playback via a voice interface. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/audio/audio_buses.html + http://docs.godotengine.org/en/3.0/tutorials/audio/audio_buses.html </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 7de0cb91f1..f06ca0f901 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStream" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="AudioStream" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Base class for audio streams. </brief_description> @@ -7,7 +7,7 @@ Base class for audio streams. Audio streams are used for music playback, or other types of streamed sounds that don't fit or require more flexibility than a [Sample]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/audio/audio_streams.html + http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml index 13a020dbf1..3babff52d1 100644 --- a/doc/classes/AudioStreamPlayback.xml +++ b/doc/classes/AudioStreamPlayback.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamPlayback" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="AudioStreamPlayback" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Meta class for playing back audio. </brief_description> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index 219afb2116..e2cb442c36 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamPlayer" inherits="Node" category="Core" version="3.0-rc1"> +<class name="AudioStreamPlayer" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Plays back audio. </brief_description> @@ -8,7 +8,7 @@ </description> <tutorials> http://docs.godotengine.org/en/latest/learning/features/audio/index.html - http://docs.godotengine.org/en/3.0/learning/features/audio/audio_streams.html + http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml index fc4f4b34cf..2abb86472b 100644 --- a/doc/classes/AudioStreamPlayer2D.xml +++ b/doc/classes/AudioStreamPlayer2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamPlayer2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="AudioStreamPlayer2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Plays audio in 2D. </brief_description> @@ -8,7 +8,7 @@ </description> <tutorials> http://docs.godotengine.org/en/latest/learning/features/audio/index.html - http://docs.godotengine.org/en/3.0/learning/features/audio/audio_streams.html + http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index 49d5723871..a40a6b4f96 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamPlayer3D" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="AudioStreamPlayer3D" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Plays 3D sound in 3D space </brief_description> @@ -8,7 +8,7 @@ </description> <tutorials> http://docs.godotengine.org/en/latest/learning/features/audio/index.html - http://docs.godotengine.org/en/3.0/learning/features/audio/audio_streams.html + http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamRandomPitch.xml b/doc/classes/AudioStreamRandomPitch.xml index d6c6834971..a78c412f26 100644 --- a/doc/classes/AudioStreamRandomPitch.xml +++ b/doc/classes/AudioStreamRandomPitch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamRandomPitch" inherits="AudioStream" category="Core" version="3.0-rc1"> +<class name="AudioStreamRandomPitch" inherits="AudioStream" category="Core" version="3.0-stable"> <brief_description> Plays audio with random pitch tweaking. </brief_description> diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml index a5185ee9e6..8e11a870ca 100644 --- a/doc/classes/AudioStreamSample.xml +++ b/doc/classes/AudioStreamSample.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamSample" inherits="AudioStream" category="Core" version="3.0-rc1"> +<class name="AudioStreamSample" inherits="AudioStream" category="Core" version="3.0-stable"> <brief_description> Plays audio. </brief_description> diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml index f22c44705a..e11b55a6a0 100644 --- a/doc/classes/BackBufferCopy.xml +++ b/doc/classes/BackBufferCopy.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BackBufferCopy" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="BackBufferCopy" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Copies a region of the screen (or the whole screen) to a buffer so it can be accessed with the texscreen() shader instruction. </brief_description> diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml index e193b8d89f..a9033906dc 100644 --- a/doc/classes/BakedLightmap.xml +++ b/doc/classes/BakedLightmap.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BakedLightmap" inherits="VisualInstance" category="Core" version="3.0-rc1"> +<class name="BakedLightmap" inherits="VisualInstance" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/baked_lightmaps.html + http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html </tutorials> <demos> </demos> diff --git a/doc/classes/BakedLightmapData.xml b/doc/classes/BakedLightmapData.xml index aa9c5b2ff7..0fcd984c5e 100644 --- a/doc/classes/BakedLightmapData.xml +++ b/doc/classes/BakedLightmapData.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BakedLightmapData" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="BakedLightmapData" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index 4e6da3a10f..6d7ee544e4 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BaseButton" inherits="Control" category="Core" version="3.0-rc1"> +<class name="BaseButton" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Base class for different kinds of buttons. </brief_description> diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 46468b8200..c90dfeea27 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Basis" category="Built-In Types" version="3.0-rc1"> +<class name="Basis" category="Built-In Types" version="3.0-stable"> <brief_description> 3x3 matrix datatype. </brief_description> diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml index b31bbdef9b..f85c538456 100644 --- a/doc/classes/BitMap.xml +++ b/doc/classes/BitMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BitMap" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="BitMap" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Boolean matrix. </brief_description> diff --git a/doc/classes/BitmapFont.xml b/doc/classes/BitmapFont.xml index 27287a0b49..77c1e0949c 100644 --- a/doc/classes/BitmapFont.xml +++ b/doc/classes/BitmapFont.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BitmapFont" inherits="Font" category="Core" version="3.0-rc1"> +<class name="BitmapFont" inherits="Font" category="Core" version="3.0-stable"> <brief_description> Renders text using [code]*.fnt[/code] fonts. </brief_description> diff --git a/doc/classes/BoneAttachment.xml b/doc/classes/BoneAttachment.xml index b65b5d5715..77c8bd603e 100644 --- a/doc/classes/BoneAttachment.xml +++ b/doc/classes/BoneAttachment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BoneAttachment" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="BoneAttachment" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> A node that will attach to a bone. </brief_description> diff --git a/doc/classes/BoxContainer.xml b/doc/classes/BoxContainer.xml index c2297f5645..b509e83985 100644 --- a/doc/classes/BoxContainer.xml +++ b/doc/classes/BoxContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BoxContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="BoxContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Base class for box containers. </brief_description> diff --git a/doc/classes/BoxShape.xml b/doc/classes/BoxShape.xml index c7e3bef2ba..00b4b6bdbb 100644 --- a/doc/classes/BoxShape.xml +++ b/doc/classes/BoxShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BoxShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="BoxShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Box shape resource. </brief_description> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 48c2ccaa82..bc1608baf8 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Button" inherits="BaseButton" category="Core" version="3.0-rc1"> +<class name="Button" inherits="BaseButton" category="Core" version="3.0-stable"> <brief_description> Standard themed Button. </brief_description> diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml index 2fa40ad338..19a75843e1 100644 --- a/doc/classes/ButtonGroup.xml +++ b/doc/classes/ButtonGroup.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ButtonGroup" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="ButtonGroup" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Group of Buttons. </brief_description> diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml index fa7f0fea94..e481799c5c 100644 --- a/doc/classes/Camera.xml +++ b/doc/classes/Camera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Camera" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Camera" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Camera node, displays from a point of view. </brief_description> diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml index 28da2a9dd1..f3825019a2 100644 --- a/doc/classes/Camera2D.xml +++ b/doc/classes/Camera2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Camera2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Camera2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Camera node for 2D scenes. </brief_description> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index ed53bc85be..bb3d4938b8 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CanvasItem" inherits="Node" category="Core" version="3.0-rc1"> +<class name="CanvasItem" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Base class of anything 2D. </brief_description> @@ -11,8 +11,8 @@ Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/2d/2d_transforms.html - http://docs.godotengine.org/en/3.0/learning/features/2d/custom_drawing_in_2d.html + http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html + http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html </tutorials> <demos> </demos> @@ -415,7 +415,7 @@ <return type="bool"> </return> <description> - Returns [code]true[/code] if the node is in the [SceneTree] and is visible on-screen. + Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and its inherited visibility is also [code]true[/code]. </description> </method> <method name="make_canvas_position_local" qualifiers="const"> diff --git a/doc/classes/CanvasItemMaterial.xml b/doc/classes/CanvasItemMaterial.xml index 5c50fd53fd..5ec32bc63d 100644 --- a/doc/classes/CanvasItemMaterial.xml +++ b/doc/classes/CanvasItemMaterial.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CanvasItemMaterial" inherits="Material" category="Core" version="3.0-rc1"> +<class name="CanvasItemMaterial" inherits="Material" category="Core" version="3.0-stable"> <brief_description> A material for [CanvasItem]s. </brief_description> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 625fa217ca..3b7ba5b1ad 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CanvasLayer" inherits="Node" category="Core" version="3.0-rc1"> +<class name="CanvasLayer" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Canvas drawing layer. </brief_description> @@ -7,8 +7,8 @@ Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [code]CanvasLayer[/code] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [code]CanvasLayer[/code] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/2d/2d_transforms.html - http://docs.godotengine.org/en/3.0/learning/features/2d/canvas_layers.html + http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html + http://docs.godotengine.org/en/3.0/tutorials/2d/canvas_layers.html </tutorials> <demos> </demos> diff --git a/doc/classes/CanvasModulate.xml b/doc/classes/CanvasModulate.xml index 0a65066ee9..827502d6c0 100644 --- a/doc/classes/CanvasModulate.xml +++ b/doc/classes/CanvasModulate.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CanvasModulate" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="CanvasModulate" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Tint the entire canvas. </brief_description> diff --git a/doc/classes/CapsuleMesh.xml b/doc/classes/CapsuleMesh.xml index 56d72ae8f6..3a291128c7 100644 --- a/doc/classes/CapsuleMesh.xml +++ b/doc/classes/CapsuleMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CapsuleMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="CapsuleMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a capsule-shaped [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/CapsuleShape.xml b/doc/classes/CapsuleShape.xml index 20afb0f751..1ff4f5aa00 100644 --- a/doc/classes/CapsuleShape.xml +++ b/doc/classes/CapsuleShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CapsuleShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="CapsuleShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Capsule shape for collisions. </brief_description> diff --git a/doc/classes/CapsuleShape2D.xml b/doc/classes/CapsuleShape2D.xml index c86e9199fe..0b306643b9 100644 --- a/doc/classes/CapsuleShape2D.xml +++ b/doc/classes/CapsuleShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CapsuleShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="CapsuleShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Capsule shape for 2D collisions. </brief_description> diff --git a/doc/classes/CenterContainer.xml b/doc/classes/CenterContainer.xml index 4ddba40fd5..a4e17bf0b6 100644 --- a/doc/classes/CenterContainer.xml +++ b/doc/classes/CenterContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CenterContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="CenterContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Keeps children controls centered. </brief_description> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index b46d1036ec..25340ed284 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CheckBox" inherits="Button" category="Core" version="3.0-rc1"> +<class name="CheckBox" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Binary choice user interface widget </brief_description> diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index f48ea652a6..ec214fc498 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CheckButton" inherits="Button" category="Core" version="3.0-rc1"> +<class name="CheckButton" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Checkable button. </brief_description> diff --git a/doc/classes/CircleShape2D.xml b/doc/classes/CircleShape2D.xml index 36852796e8..0c8b2088b3 100644 --- a/doc/classes/CircleShape2D.xml +++ b/doc/classes/CircleShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CircleShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="CircleShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Circular shape for 2D collisions. </brief_description> diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 3161895b2f..e90a84f07c 100644 --- a/doc/classes/ClassDB.xml +++ b/doc/classes/ClassDB.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ClassDB" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ClassDB" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Class information repository. </brief_description> diff --git a/doc/classes/CollisionObject.xml b/doc/classes/CollisionObject.xml index 7f00c2531c..8b397f3227 100644 --- a/doc/classes/CollisionObject.xml +++ b/doc/classes/CollisionObject.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionObject" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="CollisionObject" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Base node for collision objects. </brief_description> diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 980c36de9d..5b4ab46fd9 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionObject2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="CollisionObject2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Base node for 2D collision objects. </brief_description> diff --git a/doc/classes/CollisionPolygon.xml b/doc/classes/CollisionPolygon.xml index 29b2ab0246..79c41e632e 100644 --- a/doc/classes/CollisionPolygon.xml +++ b/doc/classes/CollisionPolygon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionPolygon" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="CollisionPolygon" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Editor-only class for defining a collision polygon in 3D space. </brief_description> diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml index 2d7839d75a..0b17e6d26e 100644 --- a/doc/classes/CollisionPolygon2D.xml +++ b/doc/classes/CollisionPolygon2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionPolygon2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="CollisionPolygon2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Defines a 2D collision polygon. </brief_description> diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml index f7215ea09f..36a015ce80 100644 --- a/doc/classes/CollisionShape.xml +++ b/doc/classes/CollisionShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionShape" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="CollisionShape" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Node that represents collision shape data in 3D space. </brief_description> @@ -7,7 +7,7 @@ Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml index 3bfa29968b..ed5a094996 100644 --- a/doc/classes/CollisionShape2D.xml +++ b/doc/classes/CollisionShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CollisionShape2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="CollisionShape2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Node that represents collision shape data in 2D space. </brief_description> @@ -7,7 +7,7 @@ Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index c7a8ac863b..11c59531a8 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Color" category="Built-In Types" version="3.0-rc1"> +<class name="Color" category="Built-In Types" version="3.0-stable"> <brief_description> Color in RGBA format with some support for ARGB format. </brief_description> @@ -204,8 +204,7 @@ var c = Color(1, .5, .2) print(str(c.to_32())) # prints 4294934323 [/codeblock] - - [i]This is same as [method to_ARGB32] but may be changed later to support RGBA format instead[/i]. + [i]This is same as [method to_argb32] but may be changed later to support RGBA format instead[/i]. </description> </method> </methods> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index e57480c4fe..c69fc360a8 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ColorPicker" inherits="BoxContainer" category="Core" version="3.0-rc1"> +<class name="ColorPicker" inherits="BoxContainer" category="Core" version="3.0-stable"> <brief_description> Color picker control. </brief_description> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index e83d2cc8e9..47fc198638 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ColorPickerButton" inherits="Button" category="Core" version="3.0-rc1"> +<class name="ColorPickerButton" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Button that pops out a [ColorPicker] </brief_description> diff --git a/doc/classes/ColorRect.xml b/doc/classes/ColorRect.xml index 8ac19c30c5..332d0f573f 100644 --- a/doc/classes/ColorRect.xml +++ b/doc/classes/ColorRect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ColorRect" inherits="Control" category="Core" version="3.0-rc1"> +<class name="ColorRect" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Colored rect for canvas. </brief_description> diff --git a/doc/classes/ConcavePolygonShape.xml b/doc/classes/ConcavePolygonShape.xml index 4abe765814..4700491921 100644 --- a/doc/classes/ConcavePolygonShape.xml +++ b/doc/classes/ConcavePolygonShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConcavePolygonShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="ConcavePolygonShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Concave polygon shape. </brief_description> diff --git a/doc/classes/ConcavePolygonShape2D.xml b/doc/classes/ConcavePolygonShape2D.xml index 84705f7680..c339ee65a0 100644 --- a/doc/classes/ConcavePolygonShape2D.xml +++ b/doc/classes/ConcavePolygonShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConcavePolygonShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="ConcavePolygonShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Concave polygon 2D shape resource for physics. </brief_description> diff --git a/doc/classes/ConeTwistJoint.xml b/doc/classes/ConeTwistJoint.xml index 6e45d6264b..b7a4894ea7 100644 --- a/doc/classes/ConeTwistJoint.xml +++ b/doc/classes/ConeTwistJoint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConeTwistJoint" inherits="Joint" category="Core" version="3.0-rc1"> +<class name="ConeTwistJoint" inherits="Joint" category="Core" version="3.0-stable"> <brief_description> A twist joint between two 3D bodies </brief_description> diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index d43ef064aa..17243b2100 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConfigFile" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="ConfigFile" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Helper class to handle INI-style files. </brief_description> diff --git a/doc/classes/ConfirmationDialog.xml b/doc/classes/ConfirmationDialog.xml index c5d2c991ee..2759c1b883 100644 --- a/doc/classes/ConfirmationDialog.xml +++ b/doc/classes/ConfirmationDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConfirmationDialog" inherits="AcceptDialog" category="Core" version="3.0-rc1"> +<class name="ConfirmationDialog" inherits="AcceptDialog" category="Core" version="3.0-stable"> <brief_description> Dialog for confirmation of actions. </brief_description> diff --git a/doc/classes/Container.xml b/doc/classes/Container.xml index 5696f2819f..0447c80887 100644 --- a/doc/classes/Container.xml +++ b/doc/classes/Container.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Container" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Container" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Base node for containers. </brief_description> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 9f3263528d..9db93bb6a9 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Control" inherits="CanvasItem" category="Core" version="3.0-rc1"> +<class name="Control" inherits="CanvasItem" category="Core" version="3.0-stable"> <brief_description> All User Interface nodes inherit from Control. Features anchors and margins to adapt its position and size to its parent. </brief_description> @@ -8,13 +8,13 @@ For more information on Godot's UI system, anchors, margins, and containers, see the related tutorials in the manual. To build flexible UIs, you'll need a mix of UI elements that inherit from [code]Control[/code] and [Container] nodes. [b]User Interface nodes and input[/b] Godot sends input events to the scene's root node first, by calling [method Node._input]. [method Node._input] forwards the event down the node tree to the nodes under the mouse cursor, or on keyboard focus. To do so, it calls [method MainLoop._input_event]. Call [method accept_event] so no other node receives the event. Once you accepted an input, it becomes handled so [method Node._unhandled_input] will not process it. - Only one [code]Control[/code] node can be in keyboard focus. Only the node in focus will receive keyboard events. To get the focus, call [method set_focus_mode]. [code]Control[/code] nodes lose focus when another node grabs it, or if you hide the node in focus. - Call [method set_ignore_mouse] to tell a [code]Control[/code] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button. + Only one [code]Control[/code] node can be in keyboard focus. Only the node in focus will receive keyboard events. To get the focus, call [method grab_focus]. [code]Control[/code] nodes lose focus when another node grabs it, or if you hide the node in focus. + Set [member mouse_filter] to MOUSE_FILTER_IGNORE to tell a [code]Control[/code] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button. [Theme] resources change the Control's appearance. If you change the [Theme] on a [code]Control[/code] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/gui/index.html - http://docs.godotengine.org/en/3.0/learning/features/2d/custom_drawing_in_2d.html + http://docs.godotengine.org/en/3.0/tutorials/gui/index.html + http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html </tutorials> <demos> </demos> diff --git a/doc/classes/ConvexPolygonShape.xml b/doc/classes/ConvexPolygonShape.xml index 115fbafd4b..2b809ca80a 100644 --- a/doc/classes/ConvexPolygonShape.xml +++ b/doc/classes/ConvexPolygonShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConvexPolygonShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="ConvexPolygonShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Convex polygon shape for 3D physics. </brief_description> diff --git a/doc/classes/ConvexPolygonShape2D.xml b/doc/classes/ConvexPolygonShape2D.xml index 920c267d2a..b5ce052b4b 100644 --- a/doc/classes/ConvexPolygonShape2D.xml +++ b/doc/classes/ConvexPolygonShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ConvexPolygonShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="ConvexPolygonShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Convex Polygon Shape for 2D physics. </brief_description> diff --git a/doc/classes/CubeMap.xml b/doc/classes/CubeMap.xml index 5b481b9cb6..26fda49803 100644 --- a/doc/classes/CubeMap.xml +++ b/doc/classes/CubeMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CubeMap" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="CubeMap" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A CubeMap is a 6 sided 3D texture. </brief_description> diff --git a/doc/classes/CubeMesh.xml b/doc/classes/CubeMesh.xml index 4ccb53abcb..c7ec4d9dfe 100644 --- a/doc/classes/CubeMesh.xml +++ b/doc/classes/CubeMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CubeMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="CubeMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Generate an axis-aligned cuboid [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 9ab12fd7f2..388d8ab54f 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Curve" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Curve" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A mathematic curve. </brief_description> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index 3ab13fae6c..2dbfcbc6ec 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Curve2D" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Curve2D" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Describes a Bezier curve in 2D space. </brief_description> diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index b52b180fe2..acb6b0d72f 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Curve3D" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Curve3D" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Describes a Bezier curve in 3D space. </brief_description> diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml index 8c3fde5e7b..bc090d623a 100644 --- a/doc/classes/CurveTexture.xml +++ b/doc/classes/CurveTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CurveTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="CurveTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> A texture that shows a curve. </brief_description> diff --git a/doc/classes/CylinderMesh.xml b/doc/classes/CylinderMesh.xml index 1f5feae3c4..e57ffd2841 100644 --- a/doc/classes/CylinderMesh.xml +++ b/doc/classes/CylinderMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CylinderMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="CylinderMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a cylindrical [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/DampedSpringJoint2D.xml b/doc/classes/DampedSpringJoint2D.xml index 0f12b738cc..950817dc04 100644 --- a/doc/classes/DampedSpringJoint2D.xml +++ b/doc/classes/DampedSpringJoint2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="DampedSpringJoint2D" inherits="Joint2D" category="Core" version="3.0-rc1"> +<class name="DampedSpringJoint2D" inherits="Joint2D" category="Core" version="3.0-stable"> <brief_description> Damped spring constraint for 2D physics. </brief_description> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 8f20f079b8..37845dbae4 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Dictionary" category="Built-In Types" version="3.0-rc1"> +<class name="Dictionary" category="Built-In Types" version="3.0-stable"> <brief_description> Dictionary type. </brief_description> diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml index d8db695176..bbb8936a12 100644 --- a/doc/classes/DirectionalLight.xml +++ b/doc/classes/DirectionalLight.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="DirectionalLight" inherits="Light" category="Core" version="3.0-rc1"> +<class name="DirectionalLight" inherits="Light" category="Core" version="3.0-stable"> <brief_description> Directional Light, such as the Sun or the Moon. </brief_description> @@ -7,7 +7,7 @@ A DirectionalLight is a type of [Light] node that emits light constantly in one direction (the negative z axis of the node). It is used lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored, only the basis is used do determine light direction. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/lights_and_shadows.html + http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html </tutorials> <demos> </demos> diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index 776db64fe6..bfd1e2872d 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Directory" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Directory" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Type used to handle the filesystem. </brief_description> @@ -23,7 +23,7 @@ [/codeblock] </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/filesystem.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html </tutorials> <demos> </demos> diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml index deaa594809..06291c1e61 100644 --- a/doc/classes/DynamicFont.xml +++ b/doc/classes/DynamicFont.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="DynamicFont" inherits="Font" category="Core" version="3.0-rc1"> +<class name="DynamicFont" inherits="Font" category="Core" version="3.0-stable"> <brief_description> DynamicFont renders vector font files at runtime. </brief_description> diff --git a/doc/classes/DynamicFontData.xml b/doc/classes/DynamicFontData.xml index 7be4a18720..5f4e598f36 100644 --- a/doc/classes/DynamicFontData.xml +++ b/doc/classes/DynamicFontData.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="DynamicFontData" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="DynamicFontData" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Used with [DynamicFont] to describe the location of a font file. </brief_description> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index b220c07de4..ede892e7ef 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorExportPlugin" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorExportPlugin" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 71dabfbcd5..b3a12dbede 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorFileDialog" inherits="ConfirmationDialog" category="Core" version="3.0-rc1"> +<class name="EditorFileDialog" inherits="ConfirmationDialog" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index e58861a062..1eda817d33 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorFileSystem" inherits="Node" category="Core" version="3.0-rc1"> +<class name="EditorFileSystem" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Resource filesystem, as the editor sees it. </brief_description> diff --git a/doc/classes/EditorFileSystemDirectory.xml b/doc/classes/EditorFileSystemDirectory.xml index 211e9e6f3d..005bebaca8 100644 --- a/doc/classes/EditorFileSystemDirectory.xml +++ b/doc/classes/EditorFileSystemDirectory.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorFileSystemDirectory" inherits="Object" category="Core" version="3.0-rc1"> +<class name="EditorFileSystemDirectory" inherits="Object" category="Core" version="3.0-stable"> <brief_description> A diretory for the resource filesystem. </brief_description> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index c90afa1c56..07d6f92274 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorImportPlugin" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorImportPlugin" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Registers a custom resource importer in the editor. Use the class to parse any file and import it as a new resource type. </brief_description> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 467c8ed836..61c93becde 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorInterface" inherits="Node" category="Core" version="3.0-rc1"> +<class name="EditorInterface" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Editor interface and main components. </brief_description> @@ -100,6 +100,15 @@ Shows the given property on the given [code]object[/code] in the Editor's Inspector dock. </description> </method> + <method name="is_plugin_enabled" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="plugin" type="String"> + </argument> + <description> + Returns the enabled status of a plugin. The plugin name is the same as its directory name. + </description> + </method> <method name="make_mesh_previews"> <return type="Array"> </return> @@ -155,6 +164,17 @@ <description> </description> </method> + <method name="set_plugin_enabled"> + <return type="void"> + </return> + <argument index="0" name="plugin" type="String"> + </argument> + <argument index="1" name="enabled" type="bool"> + </argument> + <description> + Sets the enabled status of a plugin. The plugin name is the same as its directory name. + </description> + </method> </methods> <constants> </constants> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 6ff6053b77..8f4784334d 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorPlugin" inherits="Node" category="Core" version="3.0-rc1"> +<class name="EditorPlugin" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Used by the editor to extend its functionality. </brief_description> diff --git a/doc/classes/EditorResourceConversionPlugin.xml b/doc/classes/EditorResourceConversionPlugin.xml index 3175c81e59..9facba18b0 100644 --- a/doc/classes/EditorResourceConversionPlugin.xml +++ b/doc/classes/EditorResourceConversionPlugin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorResourceConversionPlugin" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorResourceConversionPlugin" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index 68c171fbaa..9b14771f0f 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorResourcePreview" inherits="Node" category="Core" version="3.0-rc1"> +<class name="EditorResourcePreview" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Helper to generate previews of resources or files. </brief_description> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index 7c0ecef0c2..d116e56cf7 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorResourcePreviewGenerator" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorResourcePreviewGenerator" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Custom generator of previews. </brief_description> diff --git a/doc/classes/EditorSceneImporter.xml b/doc/classes/EditorSceneImporter.xml index 30ec16b3b1..31846ed401 100644 --- a/doc/classes/EditorSceneImporter.xml +++ b/doc/classes/EditorSceneImporter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneImporter" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorSceneImporter" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index 2d4f7b9dbe..4c0709249e 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorScenePostImport" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorScenePostImport" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml index 162711569a..a3cd12ab50 100644 --- a/doc/classes/EditorScript.xml +++ b/doc/classes/EditorScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorScript" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EditorScript" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Base script that can be used to add extension functions to the editor. </brief_description> diff --git a/doc/classes/EditorSelection.xml b/doc/classes/EditorSelection.xml index 29f353c10d..0424ab5e43 100644 --- a/doc/classes/EditorSelection.xml +++ b/doc/classes/EditorSelection.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSelection" inherits="Object" category="Core" version="3.0-rc1"> +<class name="EditorSelection" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Manages the SceneTree selection in the editor. </brief_description> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index fe840a62e4..a37ea0fec7 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSettings" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="EditorSettings" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Object that holds the project-independent editor settings. </brief_description> diff --git a/doc/classes/EditorSpatialGizmo.xml b/doc/classes/EditorSpatialGizmo.xml index 9fab1d4f8b..ba026166bb 100644 --- a/doc/classes/EditorSpatialGizmo.xml +++ b/doc/classes/EditorSpatialGizmo.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSpatialGizmo" inherits="SpatialGizmo" category="Core" version="3.0-rc1"> +<class name="EditorSpatialGizmo" inherits="SpatialGizmo" category="Core" version="3.0-stable"> <brief_description> Custom gizmo for editing Spatial objects. </brief_description> diff --git a/doc/classes/EncodedObjectAsID.xml b/doc/classes/EncodedObjectAsID.xml index 47e18694b4..604ce42516 100644 --- a/doc/classes/EncodedObjectAsID.xml +++ b/doc/classes/EncodedObjectAsID.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EncodedObjectAsID" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="EncodedObjectAsID" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index b8ebf9749e..ad5e75bad5 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Engine" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Engine" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Access to basic engine properties. </brief_description> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index fcd7b17b18..ea35ef89f3 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Environment" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Environment" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Resource for environment nodes (like [WorldEnvironment]) that define multiple rendering options. </brief_description> @@ -12,8 +12,8 @@ - Adjustments </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/environment_and_post_processing.html - http://docs.godotengine.org/en/3.0/learning/features/3d/high_dynamic_range.html + http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html + http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html </tutorials> <demos> </demos> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index f1106f61b7..3ac2a7e643 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="File" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="File" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Type to handle file reading and writing operations. </brief_description> @@ -22,7 +22,7 @@ [/codeblock] </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/filesystem.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html </tutorials> <demos> </demos> diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 1897298890..cee64b7ade 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="FileDialog" inherits="ConfirmationDialog" category="Core" version="3.0-rc1"> +<class name="FileDialog" inherits="ConfirmationDialog" category="Core" version="3.0-stable"> <brief_description> Dialog for selecting files or directories in the filesystem. </brief_description> diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 1b009916fb..7190067b09 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Font" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Font" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Internationalized font and text drawing support. </brief_description> diff --git a/doc/classes/FuncRef.xml b/doc/classes/FuncRef.xml index ef3ff96a4b..8ad6da9adc 100644 --- a/doc/classes/FuncRef.xml +++ b/doc/classes/FuncRef.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="FuncRef" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="FuncRef" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Reference to a function in an object. </brief_description> diff --git a/doc/classes/GIProbe.xml b/doc/classes/GIProbe.xml index 487f0113f6..e9f1dfecaf 100644 --- a/doc/classes/GIProbe.xml +++ b/doc/classes/GIProbe.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GIProbe" inherits="VisualInstance" category="Core" version="3.0-rc1"> +<class name="GIProbe" inherits="VisualInstance" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/gi_probes.html + http://docs.godotengine.org/en/3.0/tutorials/3d/gi_probes.html </tutorials> <demos> </demos> diff --git a/doc/classes/GIProbeData.xml b/doc/classes/GIProbeData.xml index af9de8615c..32a891e277 100644 --- a/doc/classes/GIProbeData.xml +++ b/doc/classes/GIProbeData.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GIProbeData" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="GIProbeData" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml index 01009edf9a..4be44b3819 100644 --- a/doc/classes/Generic6DOFJoint.xml +++ b/doc/classes/Generic6DOFJoint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Generic6DOFJoint" inherits="Joint" category="Core" version="3.0-rc1"> +<class name="Generic6DOFJoint" inherits="Joint" category="Core" version="3.0-stable"> <brief_description> The generic 6 degrees of freedom joint can implement a variety of joint-types by locking certain axes' rotation or translation. </brief_description> diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml index a40094d840..b3f63daf9c 100644 --- a/doc/classes/Geometry.xml +++ b/doc/classes/Geometry.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Geometry" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Geometry" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index 8f8e391288..8dd3c23ee2 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GeometryInstance" inherits="VisualInstance" category="Core" version="3.0-rc1"> +<class name="GeometryInstance" inherits="VisualInstance" category="Core" version="3.0-stable"> <brief_description> Base node for geometry based visual instances. </brief_description> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index 19012412ff..8b4bf4d448 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Gradient" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Gradient" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Color interpolator node. </brief_description> diff --git a/doc/classes/GradientTexture.xml b/doc/classes/GradientTexture.xml index ce8cc1f382..127a6d8204 100644 --- a/doc/classes/GradientTexture.xml +++ b/doc/classes/GradientTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GradientTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="GradientTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> Gradient filled texture. </brief_description> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 457a40eb7b..2090155e85 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GraphEdit" inherits="Control" category="Core" version="3.0-rc1"> +<class name="GraphEdit" inherits="Control" category="Core" version="3.0-stable"> <brief_description> GraphEdit is an area capable of showing various GraphNodes. It manages connection events between them. </brief_description> @@ -12,6 +12,42 @@ <demos> </demos> <methods> + <method name="add_valid_connection_type"> + <return type="void"> + </return> + <argument index="0" name="from_type" type="int"> + </argument> + <argument index="1" name="to_type" type="int"> + </argument> + <description> + Makes possible the connection between two different slot types. The type is defined with the [method GraphNode.set_slot] method. + </description> + </method> + <method name="add_valid_left_disconnect_type"> + <return type="void"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <description> + Makes possible to disconnect nodes when dragging from the slot at the left if it has the specified type. + </description> + </method> + <method name="add_valid_right_disconnect_type"> + <return type="void"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <description> + Makes possible to disconnect nodes when dragging from the slot at the right if it has the specified type. + </description> + </method> + <method name="clear_connections"> + <return type="void"> + </return> + <description> + Remove all connections between nodes. + </description> + </method> <method name="connect_node"> <return type="int" enum="Error"> </return> @@ -64,12 +100,53 @@ Return true if the 'from_port' slot of 'from' GraphNode is connected to the 'to_port' slot of 'to' GraphNode. </description> </method> + <method name="is_valid_connection_type" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="from_type" type="int"> + </argument> + <argument index="1" name="to_type" type="int"> + </argument> + <description> + Returns whether it's possible to connect slots of the specified types. + </description> + </method> + <method name="remove_valid_connection_type"> + <return type="void"> + </return> + <argument index="0" name="from_type" type="int"> + </argument> + <argument index="1" name="to_type" type="int"> + </argument> + <description> + Makes it not possible to connect between two different slot types. The type is defined with the [method GraphNode.set_slot] method. + </description> + </method> + <method name="remove_valid_left_disconnect_type"> + <return type="void"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <description> + Removes the possibility to disconnect nodes when dragging from the slot at the left if it has the specified type. + </description> + </method> + <method name="remove_valid_right_disconnect_type"> + <return type="void"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <description> + Removes the possibility to disconnect nodes when dragging from the slot at the right if it has the specified type. + </description> + </method> <method name="set_selected"> <return type="void"> </return> <argument index="0" name="node" type="Node"> </argument> <description> + Sets the specified [code]node[/code] as the one selected. </description> </method> </methods> diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index 396d86a1c9..ce8cb053ce 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GraphNode" inherits="Container" category="Core" version="3.0-rc1"> +<class name="GraphNode" inherits="Container" category="Core" version="3.0-stable"> <brief_description> A GraphNode is a container with several input and output slots allowing connections between GraphNodes. Slots can have different, incompatible types. </brief_description> diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index 6ce7f7fad0..8463acc1d4 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="GridContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Grid container used to arrange elements in a grid like layout </brief_description> diff --git a/doc/classes/GrooveJoint2D.xml b/doc/classes/GrooveJoint2D.xml index d5bfbe3007..c1aa10026c 100644 --- a/doc/classes/GrooveJoint2D.xml +++ b/doc/classes/GrooveJoint2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GrooveJoint2D" inherits="Joint2D" category="Core" version="3.0-rc1"> +<class name="GrooveJoint2D" inherits="Joint2D" category="Core" version="3.0-stable"> <brief_description> Groove constraint for 2D physics. </brief_description> diff --git a/doc/classes/HBoxContainer.xml b/doc/classes/HBoxContainer.xml index 853d082b8d..3597b4d2d0 100644 --- a/doc/classes/HBoxContainer.xml +++ b/doc/classes/HBoxContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HBoxContainer" inherits="BoxContainer" category="Core" version="3.0-rc1"> +<class name="HBoxContainer" inherits="BoxContainer" category="Core" version="3.0-stable"> <brief_description> Horizontal box container. </brief_description> diff --git a/doc/classes/HScrollBar.xml b/doc/classes/HScrollBar.xml index 460d3d498e..ed0a5ae3ad 100644 --- a/doc/classes/HScrollBar.xml +++ b/doc/classes/HScrollBar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HScrollBar" inherits="ScrollBar" category="Core" version="3.0-rc1"> +<class name="HScrollBar" inherits="ScrollBar" category="Core" version="3.0-stable"> <brief_description> Horizontal scroll bar. </brief_description> diff --git a/doc/classes/HSeparator.xml b/doc/classes/HSeparator.xml index 8434fc4fff..ac02f76146 100644 --- a/doc/classes/HSeparator.xml +++ b/doc/classes/HSeparator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HSeparator" inherits="Separator" category="Core" version="3.0-rc1"> +<class name="HSeparator" inherits="Separator" category="Core" version="3.0-stable"> <brief_description> Horizontal separator. </brief_description> diff --git a/doc/classes/HSlider.xml b/doc/classes/HSlider.xml index b7ab92def2..791bc5159b 100644 --- a/doc/classes/HSlider.xml +++ b/doc/classes/HSlider.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HSlider" inherits="Slider" category="Core" version="3.0-rc1"> +<class name="HSlider" inherits="Slider" category="Core" version="3.0-stable"> <brief_description> Horizontal slider. </brief_description> diff --git a/doc/classes/HSplitContainer.xml b/doc/classes/HSplitContainer.xml index 3f9d3d71af..68a794eb9f 100644 --- a/doc/classes/HSplitContainer.xml +++ b/doc/classes/HSplitContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HSplitContainer" inherits="SplitContainer" category="Core" version="3.0-rc1"> +<class name="HSplitContainer" inherits="SplitContainer" category="Core" version="3.0-stable"> <brief_description> Horizontal split container. </brief_description> diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 1c07bcf88f..3fa780e892 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HTTPClient" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="HTTPClient" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Hyper-text transfer protocol client. </brief_description> @@ -10,8 +10,8 @@ For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616). </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/networking/http_client_class.html - http://docs.godotengine.org/en/3.0/learning/features/networking/ssl_certificates.html + http://docs.godotengine.org/en/3.0/tutorials/networking/http_client_class.html + http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html </tutorials> <demos> </demos> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index be65600d81..8b8cc6b744 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HTTPRequest" inherits="Node" category="Core" version="3.0-rc1"> +<class name="HTTPRequest" inherits="Node" category="Core" version="3.0-stable"> <brief_description> A node with the ability to send HTTP requests. </brief_description> @@ -8,7 +8,7 @@ Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/networking/ssl_certificates.html + http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html </tutorials> <demos> </demos> diff --git a/doc/classes/HingeJoint.xml b/doc/classes/HingeJoint.xml index b6e2f6f9a2..367867dd00 100644 --- a/doc/classes/HingeJoint.xml +++ b/doc/classes/HingeJoint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="HingeJoint" inherits="Joint" category="Core" version="3.0-rc1"> +<class name="HingeJoint" inherits="Joint" category="Core" version="3.0-stable"> <brief_description> A hinge between two 3D bodies. </brief_description> diff --git a/doc/classes/IP.xml b/doc/classes/IP.xml index 9502944105..d01d88590e 100644 --- a/doc/classes/IP.xml +++ b/doc/classes/IP.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="IP" inherits="Object" category="Core" version="3.0-rc1"> +<class name="IP" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Internet protocol (IP) support functions like DNS resolution. </brief_description> diff --git a/doc/classes/IP_Unix.xml b/doc/classes/IP_Unix.xml index 70f83acc24..ba33793d72 100644 --- a/doc/classes/IP_Unix.xml +++ b/doc/classes/IP_Unix.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="IP_Unix" inherits="IP" category="Core" version="3.0-rc1"> +<class name="IP_Unix" inherits="IP" category="Core" version="3.0-stable"> <brief_description> Unix IP support. See [IP]. </brief_description> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 37fb67c07e..a38bfeb35e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Image" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Image" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Image datatype. </brief_description> @@ -217,7 +217,7 @@ <return type="int" enum="Image.Format"> </return> <description> - Returns the image's raw data. + Returns the image’s format. See [code]FORMAT_*[/code] constants. </description> </method> <method name="get_height" qualifiers="const"> diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml index 7fa8b5eea4..96261c8e12 100644 --- a/doc/classes/ImageTexture.xml +++ b/doc/classes/ImageTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ImageTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="ImageTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> A [Texture] based on an [Image]. </brief_description> diff --git a/doc/classes/ImmediateGeometry.xml b/doc/classes/ImmediateGeometry.xml index 41d1429c02..0376854bb6 100644 --- a/doc/classes/ImmediateGeometry.xml +++ b/doc/classes/ImmediateGeometry.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ImmediateGeometry" inherits="GeometryInstance" category="Core" version="3.0-rc1"> +<class name="ImmediateGeometry" inherits="GeometryInstance" category="Core" version="3.0-stable"> <brief_description> Draws simple geometry from code. </brief_description> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 5b0b63abec..946b7b9bac 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Input" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Input" inherits="Object" category="Core" version="3.0-stable"> <brief_description> A Singleton that deals with inputs. </brief_description> diff --git a/doc/classes/InputDefault.xml b/doc/classes/InputDefault.xml index 7ddc32d668..b804c29cd0 100644 --- a/doc/classes/InputDefault.xml +++ b/doc/classes/InputDefault.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputDefault" inherits="Input" category="Core" version="3.0-rc1"> +<class name="InputDefault" inherits="Input" category="Core" version="3.0-stable"> <brief_description> Default implementation of the [Input] class. </brief_description> diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index 322c85b3ab..07372d382d 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEvent" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="InputEvent" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Generic input event </brief_description> @@ -7,8 +7,8 @@ Base class of all sort of input event. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html - http://docs.godotengine.org/en/3.0/learning/features/2d/2d_transforms.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml index 246f2dc12d..5ef1711e75 100644 --- a/doc/classes/InputEventAction.xml +++ b/doc/classes/InputEventAction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventAction" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventAction" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Input event type for actions. </brief_description> @@ -7,7 +7,7 @@ Contains a generic action which can be targeted from several type of inputs. Actions can be created from the project settings menu [code]Project > Project Settings > Input Map[/code]. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html#actions + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#actions </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventGesture.xml b/doc/classes/InputEventGesture.xml index 4e85906629..4d82363b21 100644 --- a/doc/classes/InputEventGesture.xml +++ b/doc/classes/InputEventGesture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventGesture" inherits="InputEventWithModifiers" category="Core" version="3.0-rc1"> +<class name="InputEventGesture" inherits="InputEventWithModifiers" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/InputEventJoypadButton.xml b/doc/classes/InputEventJoypadButton.xml index ed83422981..47ba2f08ca 100644 --- a/doc/classes/InputEventJoypadButton.xml +++ b/doc/classes/InputEventJoypadButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventJoypadButton" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventJoypadButton" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Input event for gamepad buttons. </brief_description> @@ -7,7 +7,7 @@ Input event type for gamepad buttons. For joysticks see [InputEventJoypadMotion]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventJoypadMotion.xml b/doc/classes/InputEventJoypadMotion.xml index c29d05d922..7d40fcd4b3 100644 --- a/doc/classes/InputEventJoypadMotion.xml +++ b/doc/classes/InputEventJoypadMotion.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventJoypadMotion" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventJoypadMotion" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Input event type for gamepad joysticks and other motions. For buttons see [code]InputEventJoypadMotion[/code]. </brief_description> @@ -7,7 +7,7 @@ Stores information about joystick motions. One [code]InputEventJoypadMotion[/code] represents one axis at a time. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml index f41f6923a5..a930491beb 100644 --- a/doc/classes/InputEventKey.xml +++ b/doc/classes/InputEventKey.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventKey" inherits="InputEventWithModifiers" category="Core" version="3.0-rc1"> +<class name="InputEventKey" inherits="InputEventWithModifiers" category="Core" version="3.0-stable"> <brief_description> Input event type for keyboard events. </brief_description> @@ -7,7 +7,7 @@ Stores key presses on the keyboard. Supports key presses, key releases and [member echo] events. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventMagnifyGesture.xml b/doc/classes/InputEventMagnifyGesture.xml index 2757ac21c4..880da473a6 100644 --- a/doc/classes/InputEventMagnifyGesture.xml +++ b/doc/classes/InputEventMagnifyGesture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventMagnifyGesture" inherits="InputEventGesture" category="Core" version="3.0-rc1"> +<class name="InputEventMagnifyGesture" inherits="InputEventGesture" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml index 5d9cda8605..ca29c23634 100644 --- a/doc/classes/InputEventMouse.xml +++ b/doc/classes/InputEventMouse.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventMouse" inherits="InputEventWithModifiers" category="Core" version="3.0-rc1"> +<class name="InputEventMouse" inherits="InputEventWithModifiers" category="Core" version="3.0-stable"> <brief_description> Base input event type for mouse events. </brief_description> @@ -7,7 +7,7 @@ Stores general mouse events information. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml index 1f7aa19386..a8dc087eca 100644 --- a/doc/classes/InputEventMouseButton.xml +++ b/doc/classes/InputEventMouseButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventMouseButton" inherits="InputEventMouse" category="Core" version="3.0-rc1"> +<class name="InputEventMouseButton" inherits="InputEventMouse" category="Core" version="3.0-stable"> <brief_description> Input event type for mouse button events. </brief_description> @@ -7,7 +7,7 @@ Contains mouse click information. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/mouse_and_input_coordinates.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml index 1183d1c16c..2bd175348b 100644 --- a/doc/classes/InputEventMouseMotion.xml +++ b/doc/classes/InputEventMouseMotion.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventMouseMotion" inherits="InputEventMouse" category="Core" version="3.0-rc1"> +<class name="InputEventMouseMotion" inherits="InputEventMouse" category="Core" version="3.0-stable"> <brief_description> Input event type for mouse motion events. </brief_description> @@ -7,7 +7,7 @@ Contains mouse motion information. Supports relative, absolute positions and speed. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/mouse_and_input_coordinates.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventPanGesture.xml b/doc/classes/InputEventPanGesture.xml index 17fd52c067..1a16c0f385 100644 --- a/doc/classes/InputEventPanGesture.xml +++ b/doc/classes/InputEventPanGesture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventPanGesture" inherits="InputEventGesture" category="Core" version="3.0-rc1"> +<class name="InputEventPanGesture" inherits="InputEventGesture" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml index 950f79018e..a9780e280a 100644 --- a/doc/classes/InputEventScreenDrag.xml +++ b/doc/classes/InputEventScreenDrag.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventScreenDrag" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventScreenDrag" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Input event type for screen drag events. (only available on mobile devices) @@ -8,7 +8,7 @@ Contains screen drag information. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml index bd94770da2..783c36099b 100644 --- a/doc/classes/InputEventScreenTouch.xml +++ b/doc/classes/InputEventScreenTouch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventScreenTouch" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventScreenTouch" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Input event type for screen touch events. (only available on mobile devices) @@ -8,7 +8,7 @@ Stores multi-touch press/release information. Supports touch press, touch release and [member index] for multi-touch count and order. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml index 3dc102aa61..a1b1b6523f 100644 --- a/doc/classes/InputEventWithModifiers.xml +++ b/doc/classes/InputEventWithModifiers.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputEventWithModifiers" inherits="InputEvent" category="Core" version="3.0-rc1"> +<class name="InputEventWithModifiers" inherits="InputEvent" category="Core" version="3.0-stable"> <brief_description> Base class for keys events with modifiers. </brief_description> @@ -7,7 +7,7 @@ Contains keys events information with modifiers support like [code]SHIFT[/code] or [code]ALT[/code]. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html </tutorials> <demos> </demos> diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index dde302c095..4cedd94094 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InputMap" inherits="Object" category="Core" version="3.0-rc1"> +<class name="InputMap" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Singleton that manages [InputEventAction]. </brief_description> @@ -7,7 +7,7 @@ Manages all [InputEventAction] which can be created/modified from the project settings menu [code]Project > Project Settings > Input Map[/code] or in code with [method add_action] and [method action_add_event]. See [method Node._input]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/inputs/inputevent.html#inputmap + http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#inputmap </tutorials> <demos> </demos> diff --git a/doc/classes/InstancePlaceholder.xml b/doc/classes/InstancePlaceholder.xml index 0ada92d44e..f698551d41 100644 --- a/doc/classes/InstancePlaceholder.xml +++ b/doc/classes/InstancePlaceholder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InstancePlaceholder" inherits="Node" category="Core" version="3.0-rc1"> +<class name="InstancePlaceholder" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Placeholder for the root [Node] of a [PackedScene]. </brief_description> diff --git a/doc/classes/InterpolatedCamera.xml b/doc/classes/InterpolatedCamera.xml index 5f26f9a755..de84a7df6e 100644 --- a/doc/classes/InterpolatedCamera.xml +++ b/doc/classes/InterpolatedCamera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="InterpolatedCamera" inherits="Camera" category="Core" version="3.0-rc1"> +<class name="InterpolatedCamera" inherits="Camera" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 7b38dba14a..bc95472ea8 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ItemList" inherits="Control" category="Core" version="3.0-rc1"> +<class name="ItemList" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Control that provides a list of selectable items (and/or icons) in a single column, or optionally in multiple columns. </brief_description> diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml index b171317d42..e69c05c3df 100644 --- a/doc/classes/JSON.xml +++ b/doc/classes/JSON.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="JSON" inherits="Object" category="Core" version="3.0-rc1"> +<class name="JSON" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Helper class for parsing JSON data. </brief_description> diff --git a/doc/classes/JSONParseResult.xml b/doc/classes/JSONParseResult.xml index 47899987cb..424720a871 100644 --- a/doc/classes/JSONParseResult.xml +++ b/doc/classes/JSONParseResult.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="JSONParseResult" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="JSONParseResult" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Data class wrapper for decoded JSON. </brief_description> diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml index f6ba522f1a..8183b48ece 100644 --- a/doc/classes/JavaScript.xml +++ b/doc/classes/JavaScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="JavaScript" inherits="Object" category="Core" version="3.0-rc1"> +<class name="JavaScript" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Singleton that connects the engine with the browser's JavaScript context in HTML5 export. </brief_description> @@ -7,7 +7,7 @@ The JavaScript singleton is implemented only in HTML5 export. It's used to access the browser's JavaScript context. This allows interaction with embedding pages or calling third-party JavaScript APIs. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/workflow/export/exporting_for_web.html#calling-javascript-from-script + http://docs.godotengine.org/en/3.0/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script </tutorials> <demos> </demos> diff --git a/doc/classes/Joint.xml b/doc/classes/Joint.xml index 7b1497e99a..8ead75d556 100644 --- a/doc/classes/Joint.xml +++ b/doc/classes/Joint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Joint" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Joint" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Base class for all 3D joints </brief_description> diff --git a/doc/classes/Joint2D.xml b/doc/classes/Joint2D.xml index 62d25895b2..22f267f26f 100644 --- a/doc/classes/Joint2D.xml +++ b/doc/classes/Joint2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Joint2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Joint2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Base node for all joint constraints in 2D physics. </brief_description> diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index a782a55504..1735501a47 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="KinematicBody" inherits="PhysicsBody" category="Core" version="3.0-rc1"> +<class name="KinematicBody" inherits="PhysicsBody" category="Core" version="3.0-stable"> <brief_description> Kinematic body 3D node. </brief_description> @@ -9,7 +9,7 @@ Kinematic Characters: KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/kinematic_character_2d.html + http://docs.godotengine.org/en/3.0/tutorials/physics/kinematic_character_2d.html </tutorials> <demos> </demos> @@ -85,7 +85,7 @@ [code]linear_velocity[/code] is a value in pixels per second. Unlike in for example [method move_and_collide], you should [i]not[/i] multiply it with [code]delta[/code] — this is done by the method. [code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games. If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes. - If the body collides, it will change direction a maximum of [code]max_bounces[/code] times before it stops. + If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops. [code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees. Returns the movement that remained when the body stopped. To get more detailed information about collisions that occurred, use [method get_slide_collision]. </description> diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index d00a887e42..f7303c19c4 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="KinematicBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-rc1"> +<class name="KinematicBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-stable"> <brief_description> Kinematic body 2D node. </brief_description> diff --git a/doc/classes/KinematicCollision.xml b/doc/classes/KinematicCollision.xml index cf1f13b514..613b135578 100644 --- a/doc/classes/KinematicCollision.xml +++ b/doc/classes/KinematicCollision.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="KinematicCollision" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="KinematicCollision" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Collision data for KinematicBody collisions. </brief_description> diff --git a/doc/classes/KinematicCollision2D.xml b/doc/classes/KinematicCollision2D.xml index 300861f3a9..bb949877ad 100644 --- a/doc/classes/KinematicCollision2D.xml +++ b/doc/classes/KinematicCollision2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="KinematicCollision2D" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="KinematicCollision2D" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Collision data for KinematicBody2D collisions. </brief_description> diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index 3863139652..bbf83308de 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Label" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Label" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Displays plain text in a line or wrapped inside a rectangle. For formatted text, use [RichTextLabel]. </brief_description> diff --git a/doc/classes/LargeTexture.xml b/doc/classes/LargeTexture.xml index c371c96990..219943f021 100644 --- a/doc/classes/LargeTexture.xml +++ b/doc/classes/LargeTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="LargeTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="LargeTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> A Texture capable of storing many smaller Textures with offsets. </brief_description> diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml index e6bf39848c..30722919d5 100644 --- a/doc/classes/Light.xml +++ b/doc/classes/Light.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Light" inherits="VisualInstance" category="Core" version="3.0-rc1"> +<class name="Light" inherits="VisualInstance" category="Core" version="3.0-stable"> <brief_description> Provides a base class for different kinds of light nodes. </brief_description> @@ -7,7 +7,7 @@ Light is the abstract base class for light nodes, so it shouldn't be used directly (It can't be instanced). Other types of light nodes inherit from it. Light contains the common variables and parameters used for lighting. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/lights_and_shadows.html + http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html </tutorials> <demos> </demos> diff --git a/doc/classes/Light2D.xml b/doc/classes/Light2D.xml index 97e82f49bf..019bf48082 100644 --- a/doc/classes/Light2D.xml +++ b/doc/classes/Light2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Light2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Light2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Casts light in a 2D environment. </brief_description> diff --git a/doc/classes/LightOccluder2D.xml b/doc/classes/LightOccluder2D.xml index b33e6f39c6..4ea82918e1 100644 --- a/doc/classes/LightOccluder2D.xml +++ b/doc/classes/LightOccluder2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="LightOccluder2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="LightOccluder2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Occludes light cast by a Light2D, casting shadows. </brief_description> diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 2898a84491..a5eadae715 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Line2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Line2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> A 2D line. </brief_description> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index c8798b8ee4..d82fafd4ec 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="LineEdit" inherits="Control" category="Core" version="3.0-rc1"> +<class name="LineEdit" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Control that provides single line string editing. </brief_description> diff --git a/doc/classes/LineShape2D.xml b/doc/classes/LineShape2D.xml index 90ff87ab73..a6a1e7eb36 100644 --- a/doc/classes/LineShape2D.xml +++ b/doc/classes/LineShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="LineShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="LineShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Line shape for 2D collisions. </brief_description> diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml index f68b6f757e..37bfb005ab 100644 --- a/doc/classes/LinkButton.xml +++ b/doc/classes/LinkButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="LinkButton" inherits="BaseButton" category="Core" version="3.0-rc1"> +<class name="LinkButton" inherits="BaseButton" category="Core" version="3.0-stable"> <brief_description> Simple button used to represent a link to some resource </brief_description> diff --git a/doc/classes/Listener.xml b/doc/classes/Listener.xml index be01a5a927..5765e071a4 100644 --- a/doc/classes/Listener.xml +++ b/doc/classes/Listener.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Listener" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Listener" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index fb0df1e0d2..5a49641531 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MainLoop" inherits="Object" category="Core" version="3.0-rc1"> +<class name="MainLoop" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Main loop is the abstract main loop base class. </brief_description> diff --git a/doc/classes/MarginContainer.xml b/doc/classes/MarginContainer.xml index 8a64394540..25ac7c5668 100644 --- a/doc/classes/MarginContainer.xml +++ b/doc/classes/MarginContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MarginContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="MarginContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Simple margin container. </brief_description> diff --git a/doc/classes/Marshalls.xml b/doc/classes/Marshalls.xml index a0a6bdf0d4..554c32c893 100644 --- a/doc/classes/Marshalls.xml +++ b/doc/classes/Marshalls.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Marshalls" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Marshalls" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Data transformation (marshalling) and encoding helpers. </brief_description> diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml index f5defdc4ac..5c31efc5e0 100644 --- a/doc/classes/Material.xml +++ b/doc/classes/Material.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Material" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Material" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Abstract base [Resource] for coloring and shading geometry. </brief_description> diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml index 09f9361f30..5b15bba22a 100644 --- a/doc/classes/MenuButton.xml +++ b/doc/classes/MenuButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MenuButton" inherits="Button" category="Core" version="3.0-rc1"> +<class name="MenuButton" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Special button that brings up a [PopupMenu] when clicked. </brief_description> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 4f606227a6..7fbc7768f7 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Mesh" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Mesh" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A [Resource] that contains vertex-array based geometry. </brief_description> diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index c9e69c8336..720ab6f5d2 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MeshDataTool" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="MeshDataTool" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index afd9c06b6c..3b9ed70e77 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MeshInstance" inherits="GeometryInstance" category="Core" version="3.0-rc1"> +<class name="MeshInstance" inherits="GeometryInstance" category="Core" version="3.0-stable"> <brief_description> Node that instances meshes into a scenario. </brief_description> diff --git a/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index 438ad97adc..774deab0ef 100644 --- a/doc/classes/MeshLibrary.xml +++ b/doc/classes/MeshLibrary.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MeshLibrary" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="MeshLibrary" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Library of meshes. </brief_description> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index e54fd446a0..270da019eb 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiMesh" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="MultiMesh" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Provides high performance mesh instancing. </brief_description> diff --git a/doc/classes/MultiMeshInstance.xml b/doc/classes/MultiMeshInstance.xml index 2896f514aa..aef372f810 100644 --- a/doc/classes/MultiMeshInstance.xml +++ b/doc/classes/MultiMeshInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiMeshInstance" inherits="GeometryInstance" category="Core" version="3.0-rc1"> +<class name="MultiMeshInstance" inherits="GeometryInstance" category="Core" version="3.0-stable"> <brief_description> Node that instances a [MultiMesh]. </brief_description> diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index aa87f839be..2ed02cad48 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Mutex" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Mutex" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> A synchronization Mutex. </brief_description> diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml index d0d9a961e7..86302694a0 100644 --- a/doc/classes/Navigation.xml +++ b/doc/classes/Navigation.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Navigation" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Navigation" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> A collection of [code]NavigationMesh[/code] resources and methods used for pathfinding. </brief_description> diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml index 1aecb49973..80d53c00a5 100644 --- a/doc/classes/Navigation2D.xml +++ b/doc/classes/Navigation2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Navigation2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Navigation2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 6ea5a5432a..5cde8942d1 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationMesh" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="NavigationMesh" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NavigationMeshInstance.xml b/doc/classes/NavigationMeshInstance.xml index 62f705148f..f329bad0eb 100644 --- a/doc/classes/NavigationMeshInstance.xml +++ b/doc/classes/NavigationMeshInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationMeshInstance" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="NavigationMeshInstance" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index ef3e9df0c6..6e8154c4e5 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPolygon" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="NavigationPolygon" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NavigationPolygonInstance.xml b/doc/classes/NavigationPolygonInstance.xml index d30fdad99b..023f13de89 100644 --- a/doc/classes/NavigationPolygonInstance.xml +++ b/doc/classes/NavigationPolygonInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationPolygonInstance" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="NavigationPolygonInstance" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml index 5a600319b1..8f746594bc 100644 --- a/doc/classes/NetworkedMultiplayerPeer.xml +++ b/doc/classes/NetworkedMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerPeer" inherits="PacketPeer" category="Core" version="3.0-rc1"> +<class name="NetworkedMultiplayerPeer" inherits="PacketPeer" category="Core" version="3.0-stable"> <brief_description> A high-level network interface to simplify multiplayer interactions. </brief_description> @@ -7,7 +7,7 @@ Manages the connection to network peers. Assigns unique IDs to each client connected to the server. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/networking/high_level_multiplayer.html + http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html </tutorials> <demos> </demos> diff --git a/doc/classes/Nil.xml b/doc/classes/Nil.xml index 2eb0f66a1f..2e845d511a 100644 --- a/doc/classes/Nil.xml +++ b/doc/classes/Nil.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Nil" category="Built-In Types" version="3.0-rc1"> +<class name="Nil" category="Built-In Types" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/NinePatchRect.xml b/doc/classes/NinePatchRect.xml index e1f87f2bd7..e67f0ea4a4 100644 --- a/doc/classes/NinePatchRect.xml +++ b/doc/classes/NinePatchRect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NinePatchRect" inherits="Control" category="Core" version="3.0-rc1"> +<class name="NinePatchRect" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Scalable texture-based frame that tiles the texture's centers and sides, but keeps the corners' original size. Perfect for panels and dialog boxes. </brief_description> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 6f64c865cd..46c8bf01a5 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Node" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Node" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Base class for all [i]scene[/i] objects. </brief_description> @@ -16,7 +16,7 @@ [b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections), with behaviour varying depending on the network mode ([method set_network_mode]) of the receiving peer. To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/scenes_and_nodes.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html </tutorials> <demos> </demos> diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index 17fcd09384..f51c5abe18 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Node2D" inherits="CanvasItem" category="Core" version="3.0-rc1"> +<class name="Node2D" inherits="CanvasItem" category="Core" version="3.0-stable"> <brief_description> A 2D game object, parent of all 2D related nodes. Has a position, rotation, scale and Z-index. </brief_description> @@ -7,7 +7,7 @@ A 2D game object, with a position, rotation and scale. All 2D physics nodes and sprites inherit from Node2D. Use Node2D as a parent node to move, scale and rotate children in a 2D project. Also gives control on the node's render order. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/2d/custom_drawing_in_2d.html + http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html </tutorials> <demos> </demos> diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index d583f1a290..6027f110da 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NodePath" category="Built-In Types" version="3.0-rc1"> +<class name="NodePath" category="Built-In Types" version="3.0-stable"> <brief_description> Pre-parsed scene tree path. </brief_description> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 522a00385a..80febff4af 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OS" inherits="Object" category="Core" version="3.0-rc1"> +<class name="OS" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Operating System functions. </brief_description> @@ -87,7 +87,18 @@ <argument index="3" name="output" type="Array" default="[ ]"> </argument> <description> - Execute the binary file in given path, optionally blocking until it returns. A process ID is returned. + Execute the file at the given path, optionally blocking until it returns. + Platform path resolution will take place. The resolved file must exist and be executable. + Returns a process id. + For example: + [codeblock] + var output = [] + var pid = OS.execute('ls', [], true, output) + [/codeblock] + If you wish to access a shell built-in or perform a composite command, a platform specific shell can be invoked. For example: + [codeblock] + var pid = OS.execute('CMD.exe', ['/C', 'cd %TEMP% && dir'], true, output) + [/codeblock] </description> </method> <method name="find_scancode_from_string" qualifiers="const"> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index f7bec317ef..8a7006ea97 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Object" category="Core" version="3.0-rc1"> +<class name="Object" category="Core" version="3.0-stable"> <brief_description> Base class for all non built-in types. </brief_description> diff --git a/doc/classes/OccluderPolygon2D.xml b/doc/classes/OccluderPolygon2D.xml index 2c1c9a96c5..f16aca487e 100644 --- a/doc/classes/OccluderPolygon2D.xml +++ b/doc/classes/OccluderPolygon2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OccluderPolygon2D" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="OccluderPolygon2D" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Defines a 2D polygon for LightOccluder2D. </brief_description> diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml index b3c3ce31e1..d4d00e2a9b 100644 --- a/doc/classes/OmniLight.xml +++ b/doc/classes/OmniLight.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OmniLight" inherits="Light" category="Core" version="3.0-rc1"> +<class name="OmniLight" inherits="Light" category="Core" version="3.0-stable"> <brief_description> OmniDirectional Light, such as a light bulb or a candle. </brief_description> @@ -7,7 +7,7 @@ An OmniDirectional light is a type of [Light] node that emits lights in all directions. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/lights_and_shadows.html + http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html </tutorials> <demos> </demos> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index d3a7b60b0c..56f4aa2082 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OptionButton" inherits="Button" category="Core" version="3.0-rc1"> +<class name="OptionButton" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Button control that provides selectable options when pressed. </brief_description> diff --git a/doc/classes/PCKPacker.xml b/doc/classes/PCKPacker.xml index bed763c554..a04f9ef6fb 100644 --- a/doc/classes/PCKPacker.xml +++ b/doc/classes/PCKPacker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PCKPacker" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="PCKPacker" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PHashTranslation.xml b/doc/classes/PHashTranslation.xml index 4b31b453eb..94fddad706 100644 --- a/doc/classes/PHashTranslation.xml +++ b/doc/classes/PHashTranslation.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PHashTranslation" inherits="Translation" category="Core" version="3.0-rc1"> +<class name="PHashTranslation" inherits="Translation" category="Core" version="3.0-stable"> <brief_description> Optimized translation. </brief_description> diff --git a/doc/classes/PackedDataContainer.xml b/doc/classes/PackedDataContainer.xml index 6d162ef04b..42d00e067b 100644 --- a/doc/classes/PackedDataContainer.xml +++ b/doc/classes/PackedDataContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PackedDataContainer" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="PackedDataContainer" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PackedDataContainerRef.xml b/doc/classes/PackedDataContainerRef.xml index 6a3d9a5f22..1adbbeb5a1 100644 --- a/doc/classes/PackedDataContainerRef.xml +++ b/doc/classes/PackedDataContainerRef.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PackedDataContainerRef" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="PackedDataContainerRef" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 852b1d836a..a5877a66d2 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PackedScene" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="PackedScene" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> An abstraction of a serialized scene. </brief_description> diff --git a/doc/classes/PacketPeer.xml b/doc/classes/PacketPeer.xml index bbd2f9630b..354ef1f3f3 100644 --- a/doc/classes/PacketPeer.xml +++ b/doc/classes/PacketPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PacketPeer" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="PacketPeer" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Abstraction and base class for packet-based protocols. </brief_description> diff --git a/doc/classes/PacketPeerStream.xml b/doc/classes/PacketPeerStream.xml index 6ca376a74f..b475c9d99e 100644 --- a/doc/classes/PacketPeerStream.xml +++ b/doc/classes/PacketPeerStream.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PacketPeerStream" inherits="PacketPeer" category="Core" version="3.0-rc1"> +<class name="PacketPeerStream" inherits="PacketPeer" category="Core" version="3.0-stable"> <brief_description> Wrapper to use a PacketPeer over a StreamPeer. </brief_description> diff --git a/doc/classes/PacketPeerUDP.xml b/doc/classes/PacketPeerUDP.xml index 93a6b285cf..d7b38297ba 100644 --- a/doc/classes/PacketPeerUDP.xml +++ b/doc/classes/PacketPeerUDP.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PacketPeerUDP" inherits="PacketPeer" category="Core" version="3.0-rc1"> +<class name="PacketPeerUDP" inherits="PacketPeer" category="Core" version="3.0-stable"> <brief_description> UDP packet peer. </brief_description> diff --git a/doc/classes/Panel.xml b/doc/classes/Panel.xml index 99b7728b79..38c605f8a4 100644 --- a/doc/classes/Panel.xml +++ b/doc/classes/Panel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Panel" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Panel" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Provides an opaque background for [Control] children. </brief_description> diff --git a/doc/classes/PanelContainer.xml b/doc/classes/PanelContainer.xml index 7fa43b6f22..4c0875dea5 100644 --- a/doc/classes/PanelContainer.xml +++ b/doc/classes/PanelContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PanelContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="PanelContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Panel container type. </brief_description> diff --git a/doc/classes/PanoramaSky.xml b/doc/classes/PanoramaSky.xml index 822975742e..b99f29e112 100644 --- a/doc/classes/PanoramaSky.xml +++ b/doc/classes/PanoramaSky.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PanoramaSky" inherits="Sky" category="Core" version="3.0-rc1"> +<class name="PanoramaSky" inherits="Sky" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ParallaxBackground.xml b/doc/classes/ParallaxBackground.xml index 77041115c5..0b9e3c9609 100644 --- a/doc/classes/ParallaxBackground.xml +++ b/doc/classes/ParallaxBackground.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ParallaxBackground" inherits="CanvasLayer" category="Core" version="3.0-rc1"> +<class name="ParallaxBackground" inherits="CanvasLayer" category="Core" version="3.0-stable"> <brief_description> A node used to create a parallax scrolling background. </brief_description> diff --git a/doc/classes/ParallaxLayer.xml b/doc/classes/ParallaxLayer.xml index ce02d7940f..1feb3054f3 100644 --- a/doc/classes/ParallaxLayer.xml +++ b/doc/classes/ParallaxLayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ParallaxLayer" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="ParallaxLayer" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> A parallax scrolling layer to be used with [ParallaxBackground]. </brief_description> diff --git a/doc/classes/Particles.xml b/doc/classes/Particles.xml index c0ed69bdd7..bdf62f2b14 100644 --- a/doc/classes/Particles.xml +++ b/doc/classes/Particles.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Particles" inherits="GeometryInstance" category="Core" version="3.0-rc1"> +<class name="Particles" inherits="GeometryInstance" category="Core" version="3.0-stable"> <brief_description> 3D particle emitter. </brief_description> diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml index 60b9d40f4d..20b9632c30 100644 --- a/doc/classes/Particles2D.xml +++ b/doc/classes/Particles2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Particles2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Particles2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> 2D particle emitter. </brief_description> diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml index 5eab80c349..28ca512ec4 100644 --- a/doc/classes/ParticlesMaterial.xml +++ b/doc/classes/ParticlesMaterial.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ParticlesMaterial" inherits="Material" category="Core" version="3.0-rc1"> +<class name="ParticlesMaterial" inherits="Material" category="Core" version="3.0-stable"> <brief_description> Particle properties for [Particles] and [Particles2D] nodes. </brief_description> diff --git a/doc/classes/Path.xml b/doc/classes/Path.xml index c03fca3be7..0d931e12fd 100644 --- a/doc/classes/Path.xml +++ b/doc/classes/Path.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Path" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Path" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Container for a [Curve3D]. </brief_description> diff --git a/doc/classes/Path2D.xml b/doc/classes/Path2D.xml index 6c79046e4b..e889027283 100644 --- a/doc/classes/Path2D.xml +++ b/doc/classes/Path2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Path2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Path2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Contains a [Curve2D] path for [PathFollow2D] nodes to follow. </brief_description> diff --git a/doc/classes/PathFollow.xml b/doc/classes/PathFollow.xml index 5b34a37e1b..144afa21e7 100644 --- a/doc/classes/PathFollow.xml +++ b/doc/classes/PathFollow.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PathFollow" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="PathFollow" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Point sampler for a [Path]. </brief_description> diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml index ecc575df16..2d844c22bb 100644 --- a/doc/classes/PathFollow2D.xml +++ b/doc/classes/PathFollow2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PathFollow2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="PathFollow2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Point sampler for a [Path2D]. </brief_description> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index f5079e9b4b..18e45f7c3f 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Performance" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Performance" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Exposes performance related data. </brief_description> diff --git a/doc/classes/Physics2DDirectBodyState.xml b/doc/classes/Physics2DDirectBodyState.xml index 3dad6fe279..190325396d 100644 --- a/doc/classes/Physics2DDirectBodyState.xml +++ b/doc/classes/Physics2DDirectBodyState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DDirectBodyState" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Physics2DDirectBodyState" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Direct access object to a physics body in the [Physics2DServer]. </brief_description> diff --git a/doc/classes/Physics2DDirectBodyStateSW.xml b/doc/classes/Physics2DDirectBodyStateSW.xml index 0c4d76ffac..f6bcaef7be 100644 --- a/doc/classes/Physics2DDirectBodyStateSW.xml +++ b/doc/classes/Physics2DDirectBodyStateSW.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DDirectBodyStateSW" inherits="Physics2DDirectBodyState" category="Core" version="3.0-rc1"> +<class name="Physics2DDirectBodyStateSW" inherits="Physics2DDirectBodyState" category="Core" version="3.0-stable"> <brief_description> Software implementation of [Physics2DDirectBodyState]. </brief_description> diff --git a/doc/classes/Physics2DDirectSpaceState.xml b/doc/classes/Physics2DDirectSpaceState.xml index 08b10da717..5e0908f497 100644 --- a/doc/classes/Physics2DDirectSpaceState.xml +++ b/doc/classes/Physics2DDirectSpaceState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DDirectSpaceState" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Physics2DDirectSpaceState" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Direct access object to a space in the [Physics2DServer]. </brief_description> @@ -7,7 +7,7 @@ Direct access object to a space in the [Physics2DServer]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/ray-casting.html + http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html </tutorials> <demos> </demos> diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml index 40c00d65c7..c0316aa1bc 100644 --- a/doc/classes/Physics2DServer.xml +++ b/doc/classes/Physics2DServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Physics2DServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Physics 2D Server. </brief_description> diff --git a/doc/classes/Physics2DServerSW.xml b/doc/classes/Physics2DServerSW.xml index 3fd1cc6ac6..6942c5e3af 100644 --- a/doc/classes/Physics2DServerSW.xml +++ b/doc/classes/Physics2DServerSW.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DServerSW" inherits="Physics2DServer" category="Core" version="3.0-rc1"> +<class name="Physics2DServerSW" inherits="Physics2DServer" category="Core" version="3.0-stable"> <brief_description> Software implementation of [Physics2DServer]. </brief_description> diff --git a/doc/classes/Physics2DShapeQueryParameters.xml b/doc/classes/Physics2DShapeQueryParameters.xml index 2d1b863379..7cd35f4167 100644 --- a/doc/classes/Physics2DShapeQueryParameters.xml +++ b/doc/classes/Physics2DShapeQueryParameters.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DShapeQueryParameters" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Physics2DShapeQueryParameters" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Parameters to be sent to a 2D shape physics query. </brief_description> diff --git a/doc/classes/Physics2DShapeQueryResult.xml b/doc/classes/Physics2DShapeQueryResult.xml index d583ed6a18..b080da43a2 100644 --- a/doc/classes/Physics2DShapeQueryResult.xml +++ b/doc/classes/Physics2DShapeQueryResult.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DShapeQueryResult" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Physics2DShapeQueryResult" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Physics2DTestMotionResult.xml b/doc/classes/Physics2DTestMotionResult.xml index 4ad4b011fc..8b4260c484 100644 --- a/doc/classes/Physics2DTestMotionResult.xml +++ b/doc/classes/Physics2DTestMotionResult.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Physics2DTestMotionResult" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Physics2DTestMotionResult" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml index 078512ccc9..27db2c8f07 100644 --- a/doc/classes/PhysicsBody.xml +++ b/doc/classes/PhysicsBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsBody" inherits="CollisionObject" category="Core" version="3.0-rc1"> +<class name="PhysicsBody" inherits="CollisionObject" category="Core" version="3.0-stable"> <brief_description> Base class for all objects affected by physics in 3D space. </brief_description> @@ -7,7 +7,7 @@ PhysicsBody is an abstract base class for implementing a physics body. All *Body types inherit from it. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index 340422aea6..34e86fa63a 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsBody2D" inherits="CollisionObject2D" category="Core" version="3.0-rc1"> +<class name="PhysicsBody2D" inherits="CollisionObject2D" category="Core" version="3.0-stable"> <brief_description> Base class for all objects affected by physics in 2D space. </brief_description> @@ -7,7 +7,7 @@ PhysicsBody2D is an abstract base class for implementing a physics body. All *Body2D types inherit from it. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/PhysicsDirectBodyState.xml b/doc/classes/PhysicsDirectBodyState.xml index 54de771469..1f9c492571 100644 --- a/doc/classes/PhysicsDirectBodyState.xml +++ b/doc/classes/PhysicsDirectBodyState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsDirectBodyState" inherits="Object" category="Core" version="3.0-rc1"> +<class name="PhysicsDirectBodyState" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PhysicsDirectSpaceState.xml b/doc/classes/PhysicsDirectSpaceState.xml index 88a567831b..6c8d084f26 100644 --- a/doc/classes/PhysicsDirectSpaceState.xml +++ b/doc/classes/PhysicsDirectSpaceState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsDirectSpaceState" inherits="Object" category="Core" version="3.0-rc1"> +<class name="PhysicsDirectSpaceState" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Direct access object to a space in the [PhysicsServer]. </brief_description> @@ -7,7 +7,7 @@ Direct access object to a space in the [PhysicsServer]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/ray-casting.html + http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html </tutorials> <demos> </demos> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index f822867666..9920d5364b 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="PhysicsServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Server interface for low level physics access. </brief_description> diff --git a/doc/classes/PhysicsShapeQueryParameters.xml b/doc/classes/PhysicsShapeQueryParameters.xml index d45d64f635..3f68136285 100644 --- a/doc/classes/PhysicsShapeQueryParameters.xml +++ b/doc/classes/PhysicsShapeQueryParameters.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsShapeQueryParameters" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="PhysicsShapeQueryParameters" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PhysicsShapeQueryResult.xml b/doc/classes/PhysicsShapeQueryResult.xml index b011342340..109074c35c 100644 --- a/doc/classes/PhysicsShapeQueryResult.xml +++ b/doc/classes/PhysicsShapeQueryResult.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PhysicsShapeQueryResult" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="PhysicsShapeQueryResult" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Result of a shape query in Physics2DServer. </brief_description> diff --git a/doc/classes/PinJoint.xml b/doc/classes/PinJoint.xml index eeea1d5565..a8cc0ad9b3 100644 --- a/doc/classes/PinJoint.xml +++ b/doc/classes/PinJoint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PinJoint" inherits="Joint" category="Core" version="3.0-rc1"> +<class name="PinJoint" inherits="Joint" category="Core" version="3.0-stable"> <brief_description> Pin Joint for 3D Shapes. </brief_description> diff --git a/doc/classes/PinJoint2D.xml b/doc/classes/PinJoint2D.xml index b626571e64..08fb849523 100644 --- a/doc/classes/PinJoint2D.xml +++ b/doc/classes/PinJoint2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PinJoint2D" inherits="Joint2D" category="Core" version="3.0-rc1"> +<class name="PinJoint2D" inherits="Joint2D" category="Core" version="3.0-stable"> <brief_description> Pin Joint for 2D Shapes. </brief_description> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index 307049e055..b1984ec03f 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Plane" category="Built-In Types" version="3.0-rc1"> +<class name="Plane" category="Built-In Types" version="3.0-stable"> <brief_description> Plane in hessian form. </brief_description> @@ -7,7 +7,7 @@ Plane represents a normalized plane equation. Basically, "normal" is the normal of the plane (a,b,c normalized), and "d" is the distance from the origin to the plane (in the direction of "normal"). "Over" or "Above" the plane is considered the side of the plane towards where the normal is pointing. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/PlaneMesh.xml b/doc/classes/PlaneMesh.xml index 9206cba288..cd97d5c6eb 100644 --- a/doc/classes/PlaneMesh.xml +++ b/doc/classes/PlaneMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PlaneMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="PlaneMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a planar [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/PlaneShape.xml b/doc/classes/PlaneShape.xml index fd6ea21847..5912a2a91b 100644 --- a/doc/classes/PlaneShape.xml +++ b/doc/classes/PlaneShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PlaneShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="PlaneShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index dad7d5d202..8c68c06e65 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Polygon2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Polygon2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> A 2D polygon. </brief_description> diff --git a/doc/classes/PolygonPathFinder.xml b/doc/classes/PolygonPathFinder.xml index c3867a0e99..cea33fa5c8 100644 --- a/doc/classes/PolygonPathFinder.xml +++ b/doc/classes/PolygonPathFinder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PolygonPathFinder" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="PolygonPathFinder" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml index 48489a2319..5e80072d46 100644 --- a/doc/classes/PoolByteArray.xml +++ b/doc/classes/PoolByteArray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolByteArray" category="Built-In Types" version="3.0-rc1"> +<class name="PoolByteArray" category="Built-In Types" version="3.0-stable"> <brief_description> Raw byte array. </brief_description> diff --git a/doc/classes/PoolColorArray.xml b/doc/classes/PoolColorArray.xml index 1b274fede9..47fb38f440 100644 --- a/doc/classes/PoolColorArray.xml +++ b/doc/classes/PoolColorArray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolColorArray" category="Built-In Types" version="3.0-rc1"> +<class name="PoolColorArray" category="Built-In Types" version="3.0-stable"> <brief_description> Array of Colors </brief_description> diff --git a/doc/classes/PoolIntArray.xml b/doc/classes/PoolIntArray.xml index 63079ae931..c24d565db8 100644 --- a/doc/classes/PoolIntArray.xml +++ b/doc/classes/PoolIntArray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolIntArray" category="Built-In Types" version="3.0-rc1"> +<class name="PoolIntArray" category="Built-In Types" version="3.0-stable"> <brief_description> Integer Array. </brief_description> diff --git a/doc/classes/PoolRealArray.xml b/doc/classes/PoolRealArray.xml index 54a8769993..49f4ed85d2 100644 --- a/doc/classes/PoolRealArray.xml +++ b/doc/classes/PoolRealArray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolRealArray" category="Built-In Types" version="3.0-rc1"> +<class name="PoolRealArray" category="Built-In Types" version="3.0-stable"> <brief_description> Real Array. </brief_description> diff --git a/doc/classes/PoolStringArray.xml b/doc/classes/PoolStringArray.xml index 7150b7d485..bc273e2fb6 100644 --- a/doc/classes/PoolStringArray.xml +++ b/doc/classes/PoolStringArray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolStringArray" category="Built-In Types" version="3.0-rc1"> +<class name="PoolStringArray" category="Built-In Types" version="3.0-stable"> <brief_description> String Array. </brief_description> diff --git a/doc/classes/PoolVector2Array.xml b/doc/classes/PoolVector2Array.xml index b282b619d4..d08cfbba33 100644 --- a/doc/classes/PoolVector2Array.xml +++ b/doc/classes/PoolVector2Array.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolVector2Array" category="Built-In Types" version="3.0-rc1"> +<class name="PoolVector2Array" category="Built-In Types" version="3.0-stable"> <brief_description> An Array of Vector2. </brief_description> diff --git a/doc/classes/PoolVector3Array.xml b/doc/classes/PoolVector3Array.xml index 94364dc3b9..99d5929ded 100644 --- a/doc/classes/PoolVector3Array.xml +++ b/doc/classes/PoolVector3Array.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PoolVector3Array" category="Built-In Types" version="3.0-rc1"> +<class name="PoolVector3Array" category="Built-In Types" version="3.0-stable"> <brief_description> An Array of Vector3. </brief_description> diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml index 6a897001b7..0e6d6c844d 100644 --- a/doc/classes/Popup.xml +++ b/doc/classes/Popup.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Popup" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Popup" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Base container control for popups and dialogs. </brief_description> diff --git a/doc/classes/PopupDialog.xml b/doc/classes/PopupDialog.xml index 6510a647b5..b47674b588 100644 --- a/doc/classes/PopupDialog.xml +++ b/doc/classes/PopupDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PopupDialog" inherits="Popup" category="Core" version="3.0-rc1"> +<class name="PopupDialog" inherits="Popup" category="Core" version="3.0-stable"> <brief_description> Base class for Popup Dialogs. </brief_description> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index da73abbabb..2ffdc07e1f 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PopupMenu" inherits="Popup" category="Core" version="3.0-rc1"> +<class name="PopupMenu" inherits="Popup" category="Core" version="3.0-stable"> <brief_description> PopupMenu displays a list of options. </brief_description> diff --git a/doc/classes/PopupPanel.xml b/doc/classes/PopupPanel.xml index e634017fc0..0de5319f59 100644 --- a/doc/classes/PopupPanel.xml +++ b/doc/classes/PopupPanel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PopupPanel" inherits="Popup" category="Core" version="3.0-rc1"> +<class name="PopupPanel" inherits="Popup" category="Core" version="3.0-stable"> <brief_description> Class for displaying popups with a panel background. </brief_description> diff --git a/doc/classes/Position2D.xml b/doc/classes/Position2D.xml index 0fbf7479e7..d06d9220a7 100644 --- a/doc/classes/Position2D.xml +++ b/doc/classes/Position2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Position2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Position2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Generic 2D Position hint for editing. </brief_description> diff --git a/doc/classes/Position3D.xml b/doc/classes/Position3D.xml index 3136293577..5f3aa004dc 100644 --- a/doc/classes/Position3D.xml +++ b/doc/classes/Position3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Position3D" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Position3D" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Generic 3D Position hint for editing </brief_description> diff --git a/doc/classes/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml index c472179c7f..7f0ee9ce37 100644 --- a/doc/classes/PrimitiveMesh.xml +++ b/doc/classes/PrimitiveMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PrimitiveMesh" inherits="Mesh" category="Core" version="3.0-rc1"> +<class name="PrimitiveMesh" inherits="Mesh" category="Core" version="3.0-stable"> <brief_description> Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh. </brief_description> diff --git a/doc/classes/PrismMesh.xml b/doc/classes/PrismMesh.xml index 33a9547689..389acc9320 100644 --- a/doc/classes/PrismMesh.xml +++ b/doc/classes/PrismMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PrismMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="PrismMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a prism-shaped [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/ProceduralSky.xml b/doc/classes/ProceduralSky.xml index ce8c2178db..9d7e590fa3 100644 --- a/doc/classes/ProceduralSky.xml +++ b/doc/classes/ProceduralSky.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ProceduralSky" inherits="Sky" category="Core" version="3.0-rc1"> +<class name="ProceduralSky" inherits="Sky" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml index 1ead8e9624..321f27008d 100644 --- a/doc/classes/ProgressBar.xml +++ b/doc/classes/ProgressBar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ProgressBar" inherits="Range" category="Core" version="3.0-rc1"> +<class name="ProgressBar" inherits="Range" category="Core" version="3.0-stable"> <brief_description> General purpose progress bar. </brief_description> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 3374eccc66..7d0856127c 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ProjectSettings" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ProjectSettings" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Contains global variables accessible from everywhere. </brief_description> <description> - Contains global variables accessible from everywhere. Use the normal [Object] API, such as "ProjectSettings.get(variable)", "ProjectSettings.set(variable,value)" or "ProjectSettings.has(variable)" to access them. Variables stored in project.godot are also loaded into ProjectSettings, making this object very useful for reading custom game configuration options. + Contains global variables accessible from everywhere. Use "ProjectSettings.get_setting(variable)", "ProjectSettings.set_setting(variable,value)" or "ProjectSettings.has_setting(variable)" to access them. Variables stored in project.godot are also loaded into ProjectSettings, making this object very useful for reading custom game configuration options. </description> <tutorials> </tutorials> diff --git a/doc/classes/ProximityGroup.xml b/doc/classes/ProximityGroup.xml index af731a9c1f..b4a424dfdd 100644 --- a/doc/classes/ProximityGroup.xml +++ b/doc/classes/ProximityGroup.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ProximityGroup" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="ProximityGroup" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> General purpose proximity-detection node. </brief_description> diff --git a/doc/classes/ProxyTexture.xml b/doc/classes/ProxyTexture.xml index 58b3f87038..68f03ca4fa 100644 --- a/doc/classes/ProxyTexture.xml +++ b/doc/classes/ProxyTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ProxyTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="ProxyTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/QuadMesh.xml b/doc/classes/QuadMesh.xml index 64493c8941..f1b38d379a 100644 --- a/doc/classes/QuadMesh.xml +++ b/doc/classes/QuadMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="QuadMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="QuadMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a square mesh. </brief_description> diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml index 4ff6210488..ead73d0267 100644 --- a/doc/classes/Quat.xml +++ b/doc/classes/Quat.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Quat" category="Built-In Types" version="3.0-rc1"> +<class name="Quat" category="Built-In Types" version="3.0-stable"> <brief_description> Quaternion. </brief_description> diff --git a/doc/classes/RID.xml b/doc/classes/RID.xml index e539bb43e5..1510d75679 100644 --- a/doc/classes/RID.xml +++ b/doc/classes/RID.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RID" category="Built-In Types" version="3.0-rc1"> +<class name="RID" category="Built-In Types" version="3.0-stable"> <brief_description> Handle for a [Resource]'s unique ID. </brief_description> diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml index 5cff3edd92..8a0c831805 100644 --- a/doc/classes/Range.xml +++ b/doc/classes/Range.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Range" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Range" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Abstract base class for range-based controls. </brief_description> diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index 9bf9c68525..09e0f8b643 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RayCast" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="RayCast" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Query the closest object intersecting a ray. </brief_description> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index 2d53206663..b76c880bb7 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RayCast2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="RayCast2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Query the closest object intersecting a ray. </brief_description> diff --git a/doc/classes/RayShape.xml b/doc/classes/RayShape.xml index 756377980f..6d83ed2903 100644 --- a/doc/classes/RayShape.xml +++ b/doc/classes/RayShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RayShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="RayShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Ray shape for 3D collisions. </brief_description> diff --git a/doc/classes/RayShape2D.xml b/doc/classes/RayShape2D.xml index a2ba1b4365..ce004b582b 100644 --- a/doc/classes/RayShape2D.xml +++ b/doc/classes/RayShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RayShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="RayShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Ray shape for 2D collisions. </brief_description> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 4890c2bc2c..6d953d2615 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Rect2" category="Built-In Types" version="3.0-rc1"> +<class name="Rect2" category="Built-In Types" version="3.0-stable"> <brief_description> 2D Axis-aligned bounding box. </brief_description> @@ -7,7 +7,7 @@ Rect2 consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/RectangleShape2D.xml b/doc/classes/RectangleShape2D.xml index 9b0d6182ad..4ecd0aadf4 100644 --- a/doc/classes/RectangleShape2D.xml +++ b/doc/classes/RectangleShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RectangleShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="RectangleShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Rectangle shape for 2D collisions. </brief_description> diff --git a/doc/classes/Reference.xml b/doc/classes/Reference.xml index 6ac3010c42..ba769db5e7 100644 --- a/doc/classes/Reference.xml +++ b/doc/classes/Reference.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Reference" inherits="Object" category="Core" version="3.0-rc1"> +<class name="Reference" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Base class for anything that keeps a reference count. </brief_description> diff --git a/doc/classes/ReferenceRect.xml b/doc/classes/ReferenceRect.xml index 77d7eff195..2a13eb3312 100644 --- a/doc/classes/ReferenceRect.xml +++ b/doc/classes/ReferenceRect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ReferenceRect" inherits="Control" category="Core" version="3.0-rc1"> +<class name="ReferenceRect" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Reference frame for GUI. </brief_description> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 50eca9699a..5db9f32486 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ReflectionProbe" inherits="VisualInstance" category="Core" version="3.0-rc1"> +<class name="ReflectionProbe" inherits="VisualInstance" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/reflection_probes.html + http://docs.godotengine.org/en/3.0/tutorials/3d/reflection_probes.html </tutorials> <demos> </demos> diff --git a/doc/classes/RemoteTransform.xml b/doc/classes/RemoteTransform.xml index b8d636590f..e9b8d2b746 100644 --- a/doc/classes/RemoteTransform.xml +++ b/doc/classes/RemoteTransform.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RemoteTransform" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="RemoteTransform" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> RemoteTransform leads the [Transform] of another [Spatial] derived Node in the scene. </brief_description> diff --git a/doc/classes/RemoteTransform2D.xml b/doc/classes/RemoteTransform2D.xml index 15bafa7161..c6022fd1ff 100644 --- a/doc/classes/RemoteTransform2D.xml +++ b/doc/classes/RemoteTransform2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RemoteTransform2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="RemoteTransform2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> RemoteTransform2D leads the [Transform2D] of another [CanvasItem] derived Node in the scene. </brief_description> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index f3d8b1cc0f..8113d81fbb 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Resource" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Resource" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Base class for all resources. </brief_description> diff --git a/doc/classes/ResourceImporter.xml b/doc/classes/ResourceImporter.xml index 95f70ecb6d..c68bdd613c 100644 --- a/doc/classes/ResourceImporter.xml +++ b/doc/classes/ResourceImporter.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporter" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="ResourceImporter" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ResourceInteractiveLoader.xml b/doc/classes/ResourceInteractiveLoader.xml index 877e3ae23d..8a8023c8ec 100644 --- a/doc/classes/ResourceInteractiveLoader.xml +++ b/doc/classes/ResourceInteractiveLoader.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceInteractiveLoader" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="ResourceInteractiveLoader" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Interactive Resource Loader. </brief_description> diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index b82d3a86ac..d4f61b7929 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceLoader" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ResourceLoader" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Resource Loader. </brief_description> diff --git a/doc/classes/ResourcePreloader.xml b/doc/classes/ResourcePreloader.xml index 438bde1272..868ca62963 100644 --- a/doc/classes/ResourcePreloader.xml +++ b/doc/classes/ResourcePreloader.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourcePreloader" inherits="Node" category="Core" version="3.0-rc1"> +<class name="ResourcePreloader" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Resource Preloader Node. </brief_description> diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml index 1d82cf7079..f4abe9f770 100644 --- a/doc/classes/ResourceSaver.xml +++ b/doc/classes/ResourceSaver.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceSaver" inherits="Object" category="Core" version="3.0-rc1"> +<class name="ResourceSaver" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Resource Saving Interface. </brief_description> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index a50d4e0393..24ae94d6db 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RichTextLabel" inherits="Control" category="Core" version="3.0-rc1"> +<class name="RichTextLabel" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Label that displays rich text. </brief_description> @@ -8,7 +8,7 @@ Note that assignments to [member bbcode_text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member bbcode_text] will erase previous edits made from other manual sources such as [method append_bbcode] and the [code]push_*[/code] / [method pop] methods. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/gui/bbcode_in_richtextlabel.html + http://docs.godotengine.org/en/3.0/tutorials/gui/bbcode_in_richtextlabel.html </tutorials> <demos> </demos> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index ffc5c86074..ad5da50dd6 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RigidBody" inherits="PhysicsBody" category="Core" version="3.0-rc1"> +<class name="RigidBody" inherits="PhysicsBody" category="Core" version="3.0-stable"> <brief_description> Physics Body whose position is determined through physics simulation in 3D space. </brief_description> @@ -11,7 +11,7 @@ As a warning, don't change RigidBody's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop will yield strange behavior. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> @@ -33,7 +33,7 @@ <argument index="1" name="impulse" type="Vector3"> </argument> <description> - Apply a positioned impulse (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied once, and only once. Both the impulse and the offset from the body origin are in global coordinates. + Apply a positioned impulse (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied once, and only once. Both the impulse and the position are in global coordinates, and the position is relative to the object's origin. </description> </method> <method name="get_colliding_bodies" qualifiers="const"> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 35b5449c0a..67ae92c664 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RigidBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-rc1"> +<class name="RigidBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-stable"> <brief_description> A body that is controlled by the 2D physics engine. </brief_description> diff --git a/doc/classes/SceneState.xml b/doc/classes/SceneState.xml index 7e2f55948d..6fec02c392 100644 --- a/doc/classes/SceneState.xml +++ b/doc/classes/SceneState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SceneState" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="SceneState" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> A script interface to a scene file's data. </brief_description> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index f6b8d25488..0fe220ae48 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SceneTree" inherits="MainLoop" category="Core" version="3.0-rc1"> +<class name="SceneTree" inherits="MainLoop" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/scene_tree.html - http://docs.godotengine.org/en/3.0/learning/features/viewports/multiple_resolutions.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html + http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html </tutorials> <demos> </demos> diff --git a/doc/classes/SceneTreeTimer.xml b/doc/classes/SceneTreeTimer.xml index 35b35c0384..f6b262d225 100644 --- a/doc/classes/SceneTreeTimer.xml +++ b/doc/classes/SceneTreeTimer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SceneTreeTimer" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="SceneTreeTimer" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 14570ab611..787d2c24bf 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Script" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Script" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A class stored as a resource. </brief_description> @@ -8,7 +8,7 @@ The 'new' method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/step_by_step/scripting.html + http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scripting.html </tutorials> <demos> </demos> diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index ff44afb065..89a8200d66 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ScriptEditor" inherits="PanelContainer" category="Core" version="3.0-rc1"> +<class name="ScriptEditor" inherits="PanelContainer" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ScrollBar.xml b/doc/classes/ScrollBar.xml index 4204a35c2a..53b20e55d5 100644 --- a/doc/classes/ScrollBar.xml +++ b/doc/classes/ScrollBar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ScrollBar" inherits="Range" category="Core" version="3.0-rc1"> +<class name="ScrollBar" inherits="Range" category="Core" version="3.0-stable"> <brief_description> Base class for scroll bars. </brief_description> diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index c2e553b4ef..a1e6441d1c 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ScrollContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="ScrollContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> A helper node for displaying scrollable elements (e.g. lists). </brief_description> diff --git a/doc/classes/SegmentShape2D.xml b/doc/classes/SegmentShape2D.xml index 256c97544c..56d9fe6d31 100644 --- a/doc/classes/SegmentShape2D.xml +++ b/doc/classes/SegmentShape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SegmentShape2D" inherits="Shape2D" category="Core" version="3.0-rc1"> +<class name="SegmentShape2D" inherits="Shape2D" category="Core" version="3.0-stable"> <brief_description> Segment shape for 2D collisions. </brief_description> diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml index 0ecd361a72..3d6f894f52 100644 --- a/doc/classes/Semaphore.xml +++ b/doc/classes/Semaphore.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Semaphore" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Semaphore" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> A synchronization Semaphore. </brief_description> diff --git a/doc/classes/Separator.xml b/doc/classes/Separator.xml index 1120ed3dc1..20cf870d75 100644 --- a/doc/classes/Separator.xml +++ b/doc/classes/Separator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Separator" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Separator" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Base class for separators. </brief_description> diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml index d39014fa2d..28f06f0caa 100644 --- a/doc/classes/Shader.xml +++ b/doc/classes/Shader.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Shader" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Shader" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> To be changed, ignore. </brief_description> @@ -7,7 +7,7 @@ To be changed, ignore. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/shading/index.html + http://docs.godotengine.org/en/3.0/tutorials/shading/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml index 7b4f9332c6..893746ec31 100644 --- a/doc/classes/ShaderMaterial.xml +++ b/doc/classes/ShaderMaterial.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ShaderMaterial" inherits="Material" category="Core" version="3.0-rc1"> +<class name="ShaderMaterial" inherits="Material" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Shape.xml b/doc/classes/Shape.xml index 706c66d38e..986d567c12 100644 --- a/doc/classes/Shape.xml +++ b/doc/classes/Shape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Shape" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Shape" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Base class for all 3D shape resources. </brief_description> @@ -7,7 +7,7 @@ Base class for all 3D shape resources. All 3D shapes that inherit from this can be set into a [PhysicsBody] or [Area]. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index 97171fdd2c..d2366faa7f 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Shape2D" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Shape2D" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Base class for all 2D Shapes. </brief_description> @@ -7,7 +7,7 @@ Base class for all 2D Shapes. All 2D shape types inherit from this. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/physics_introduction.html + http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html </tutorials> <demos> </demos> diff --git a/doc/classes/ShortCut.xml b/doc/classes/ShortCut.xml index 8169662858..f0908cfa44 100644 --- a/doc/classes/ShortCut.xml +++ b/doc/classes/ShortCut.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ShortCut" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="ShortCut" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A shortcut for binding input. </brief_description> diff --git a/doc/classes/Skeleton.xml b/doc/classes/Skeleton.xml index b2bf402e96..33022cca14 100644 --- a/doc/classes/Skeleton.xml +++ b/doc/classes/Skeleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Skeleton" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="Skeleton" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Skeleton for characters and animated objects. </brief_description> diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml index 34a5fd2f92..3cd3957f40 100644 --- a/doc/classes/Sky.xml +++ b/doc/classes/Sky.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Sky" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Sky" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> The base class for [PanoramaSky] and [ProceduralSky]. </brief_description> diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index a2c8551245..84c51572c3 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Slider" inherits="Range" category="Core" version="3.0-rc1"> +<class name="Slider" inherits="Range" category="Core" version="3.0-stable"> <brief_description> Base class for GUI Sliders. </brief_description> diff --git a/doc/classes/SliderJoint.xml b/doc/classes/SliderJoint.xml index 81ab064b8a..4507668292 100644 --- a/doc/classes/SliderJoint.xml +++ b/doc/classes/SliderJoint.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SliderJoint" inherits="Joint" category="Core" version="3.0-rc1"> +<class name="SliderJoint" inherits="Joint" category="Core" version="3.0-stable"> <brief_description> Piston kind of slider between two bodies in 3D. </brief_description> diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 8529823467..26f6f4003b 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Spatial" inherits="Node" category="Core" version="3.0-rc1"> +<class name="Spatial" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Most basic 3D game object, parent of all 3D related nodes. </brief_description> @@ -9,7 +9,7 @@ Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the Spatial object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the Spatial's transform. The word local below refers to this coordinate system. The coordinate system that is attached to the Spatial object itself is referred to as object-local coordinate system. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/introduction_to_3d.html + http://docs.godotengine.org/en/3.0/tutorials/3d/introduction_to_3d.html </tutorials> <demos> </demos> diff --git a/doc/classes/SpatialGizmo.xml b/doc/classes/SpatialGizmo.xml index a29dd98a98..cf6f0efd46 100644 --- a/doc/classes/SpatialGizmo.xml +++ b/doc/classes/SpatialGizmo.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpatialGizmo" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="SpatialGizmo" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index 044e6fbd18..eef18c9892 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpatialMaterial" inherits="Material" category="Core" version="3.0-rc1"> +<class name="SpatialMaterial" inherits="Material" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/spatial_material.html + http://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html </tutorials> <demos> </demos> diff --git a/doc/classes/SpatialVelocityTracker.xml b/doc/classes/SpatialVelocityTracker.xml index 1d84d41d5d..276b82899b 100644 --- a/doc/classes/SpatialVelocityTracker.xml +++ b/doc/classes/SpatialVelocityTracker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpatialVelocityTracker" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="SpatialVelocityTracker" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/SphereMesh.xml b/doc/classes/SphereMesh.xml index 0684b5a922..9f2d613267 100644 --- a/doc/classes/SphereMesh.xml +++ b/doc/classes/SphereMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SphereMesh" inherits="PrimitiveMesh" category="Core" version="3.0-rc1"> +<class name="SphereMesh" inherits="PrimitiveMesh" category="Core" version="3.0-stable"> <brief_description> Class representing a spherical [PrimitiveMesh]. </brief_description> diff --git a/doc/classes/SphereShape.xml b/doc/classes/SphereShape.xml index 22d4505233..a8df924bde 100644 --- a/doc/classes/SphereShape.xml +++ b/doc/classes/SphereShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SphereShape" inherits="Shape" category="Core" version="3.0-rc1"> +<class name="SphereShape" inherits="Shape" category="Core" version="3.0-stable"> <brief_description> Sphere shape for 3D collisions. </brief_description> diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml index 60e0d20d79..1762a66302 100644 --- a/doc/classes/SpinBox.xml +++ b/doc/classes/SpinBox.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpinBox" inherits="Range" category="Core" version="3.0-rc1"> +<class name="SpinBox" inherits="Range" category="Core" version="3.0-stable"> <brief_description> Numerical input text field. </brief_description> diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml index 993b55f5ee..3a7f22693a 100644 --- a/doc/classes/SplitContainer.xml +++ b/doc/classes/SplitContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SplitContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="SplitContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> Container for splitting and adjusting. </brief_description> diff --git a/doc/classes/SpotLight.xml b/doc/classes/SpotLight.xml index 68b2eec3a1..00991e75bc 100644 --- a/doc/classes/SpotLight.xml +++ b/doc/classes/SpotLight.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpotLight" inherits="Light" category="Core" version="3.0-rc1"> +<class name="SpotLight" inherits="Light" category="Core" version="3.0-stable"> <brief_description> Spotlight [Light], such as a reflector spotlight or a lantern. </brief_description> @@ -7,7 +7,7 @@ A SpotLight light is a type of [Light] node that emits lights in a specific direction, in the shape of a cone. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. TODO: Image of a spotlight. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/lights_and_shadows.html + http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html </tutorials> <demos> </demos> diff --git a/doc/classes/Sprite.xml b/doc/classes/Sprite.xml index 853a9ad0a9..c1afb35e09 100644 --- a/doc/classes/Sprite.xml +++ b/doc/classes/Sprite.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Sprite" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="Sprite" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> General purpose Sprite node. </brief_description> diff --git a/doc/classes/Sprite3D.xml b/doc/classes/Sprite3D.xml index feefd39c2e..ca0bd212aa 100644 --- a/doc/classes/Sprite3D.xml +++ b/doc/classes/Sprite3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Sprite3D" inherits="SpriteBase3D" category="Core" version="3.0-rc1"> +<class name="Sprite3D" inherits="SpriteBase3D" category="Core" version="3.0-stable"> <brief_description> 2D Sprite node in 3D world. </brief_description> diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml index c7e0e8f4a4..d91314e236 100644 --- a/doc/classes/SpriteBase3D.xml +++ b/doc/classes/SpriteBase3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpriteBase3D" inherits="GeometryInstance" category="Core" version="3.0-rc1"> +<class name="SpriteBase3D" inherits="GeometryInstance" category="Core" version="3.0-stable"> <brief_description> 2D Sprite node in 3D environment. </brief_description> diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index 1f6a9d095e..f76212b0d2 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SpriteFrames" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="SpriteFrames" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Sprite frame library for AnimatedSprite. </brief_description> diff --git a/doc/classes/StaticBody.xml b/doc/classes/StaticBody.xml index 5d005e52f7..e5b9d1c0d9 100644 --- a/doc/classes/StaticBody.xml +++ b/doc/classes/StaticBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StaticBody" inherits="PhysicsBody" category="Core" version="3.0-rc1"> +<class name="StaticBody" inherits="PhysicsBody" category="Core" version="3.0-stable"> <brief_description> Static body for 3D Physics. </brief_description> diff --git a/doc/classes/StaticBody2D.xml b/doc/classes/StaticBody2D.xml index 98ba1a8a79..d9e750f4f6 100644 --- a/doc/classes/StaticBody2D.xml +++ b/doc/classes/StaticBody2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StaticBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-rc1"> +<class name="StaticBody2D" inherits="PhysicsBody2D" category="Core" version="3.0-stable"> <brief_description> Static body for 2D Physics. </brief_description> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 014f8b8657..49aa26a601 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeer" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="StreamPeer" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Abstraction and base class for stream-based protocols. </brief_description> diff --git a/doc/classes/StreamPeerBuffer.xml b/doc/classes/StreamPeerBuffer.xml index f7ef8749ab..2c95a8a3cf 100644 --- a/doc/classes/StreamPeerBuffer.xml +++ b/doc/classes/StreamPeerBuffer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeerBuffer" inherits="StreamPeer" category="Core" version="3.0-rc1"> +<class name="StreamPeerBuffer" inherits="StreamPeer" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml index 23b7f71595..0c60213e49 100644 --- a/doc/classes/StreamPeerSSL.xml +++ b/doc/classes/StreamPeerSSL.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeerSSL" inherits="StreamPeer" category="Core" version="3.0-rc1"> +<class name="StreamPeerSSL" inherits="StreamPeer" category="Core" version="3.0-stable"> <brief_description> SSL Stream peer. </brief_description> @@ -7,7 +7,7 @@ SSL Stream peer. This object can be used to connect to SSL servers. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/networking/ssl_certificates.html + http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html </tutorials> <demos> </demos> diff --git a/doc/classes/StreamPeerTCP.xml b/doc/classes/StreamPeerTCP.xml index c65a7affd2..73e9b97367 100644 --- a/doc/classes/StreamPeerTCP.xml +++ b/doc/classes/StreamPeerTCP.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeerTCP" inherits="StreamPeer" category="Core" version="3.0-rc1"> +<class name="StreamPeerTCP" inherits="StreamPeer" category="Core" version="3.0-stable"> <brief_description> TCP Stream peer. </brief_description> @@ -29,6 +29,16 @@ Disconnect from host. </description> </method> + <method name="set_no_delay"> + <return type="void"> + </return> + <argument index="0" name="enabled" type="bool"> + </argument> + <description> + Disable Nagle algorithm to improve latency for small packets. + Note that for applications that send large packets, or need to transfer a lot of data, this can reduce total bandwidth. + </description> + </method> <method name="get_connected_host" qualifiers="const"> <return type="String"> </return> diff --git a/doc/classes/StreamTexture.xml b/doc/classes/StreamTexture.xml index beefdc8a86..c37b5d6fe4 100644 --- a/doc/classes/StreamTexture.xml +++ b/doc/classes/StreamTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="StreamTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> A .stex texture. </brief_description> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index a0605ec78e..bad4ada144 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="String" category="Built-In Types" version="3.0-rc1"> +<class name="String" category="Built-In Types" version="3.0-stable"> <brief_description> Built-in string class. </brief_description> diff --git a/doc/classes/StyleBox.xml b/doc/classes/StyleBox.xml index 9645cc9c96..a816b6a030 100644 --- a/doc/classes/StyleBox.xml +++ b/doc/classes/StyleBox.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StyleBox" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="StyleBox" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Base class for drawing stylized boxes for the UI. </brief_description> diff --git a/doc/classes/StyleBoxEmpty.xml b/doc/classes/StyleBoxEmpty.xml index 2d6424cc11..cd0bd9bf9d 100644 --- a/doc/classes/StyleBoxEmpty.xml +++ b/doc/classes/StyleBoxEmpty.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StyleBoxEmpty" inherits="StyleBox" category="Core" version="3.0-rc1"> +<class name="StyleBoxEmpty" inherits="StyleBox" category="Core" version="3.0-stable"> <brief_description> Empty stylebox (does not display anything). </brief_description> diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml index dfc9ab4e0a..2f26032c45 100644 --- a/doc/classes/StyleBoxFlat.xml +++ b/doc/classes/StyleBoxFlat.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StyleBoxFlat" inherits="StyleBox" category="Core" version="3.0-rc1"> +<class name="StyleBoxFlat" inherits="StyleBox" category="Core" version="3.0-stable"> <brief_description> Customizable Stylebox with a given set of parameters. (no texture required) </brief_description> diff --git a/doc/classes/StyleBoxLine.xml b/doc/classes/StyleBoxLine.xml index 027317eca7..ddabe9aede 100644 --- a/doc/classes/StyleBoxLine.xml +++ b/doc/classes/StyleBoxLine.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StyleBoxLine" inherits="StyleBox" category="Core" version="3.0-rc1"> +<class name="StyleBoxLine" inherits="StyleBox" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/StyleBoxTexture.xml b/doc/classes/StyleBoxTexture.xml index 710bc819b3..8bac0fc77e 100644 --- a/doc/classes/StyleBoxTexture.xml +++ b/doc/classes/StyleBoxTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="StyleBoxTexture" inherits="StyleBox" category="Core" version="3.0-rc1"> +<class name="StyleBoxTexture" inherits="StyleBox" category="Core" version="3.0-stable"> <brief_description> Texture Based 3x3 scale style. </brief_description> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index c6433fab40..64978a8bef 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SurfaceTool" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="SurfaceTool" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Helper tool to create geometry. </brief_description> diff --git a/doc/classes/TCP_Server.xml b/doc/classes/TCP_Server.xml index 804f4dc13f..b3db65de59 100644 --- a/doc/classes/TCP_Server.xml +++ b/doc/classes/TCP_Server.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TCP_Server" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="TCP_Server" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> TCP Server. </brief_description> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index 445c65b96e..9806c03349 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TabContainer" inherits="Control" category="Core" version="3.0-rc1"> +<class name="TabContainer" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Tabbed Container. </brief_description> diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml index 44a33c76ca..e4df4d0a31 100644 --- a/doc/classes/Tabs.xml +++ b/doc/classes/Tabs.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Tabs" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Tabs" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Tabs Control. </brief_description> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 20ec600f91..f84ebede1c 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextEdit" inherits="Control" category="Core" version="3.0-rc1"> +<class name="TextEdit" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Multiline text editing control. </brief_description> diff --git a/doc/classes/Texture.xml b/doc/classes/Texture.xml index 16fe2b8a3b..f21b41f939 100644 --- a/doc/classes/Texture.xml +++ b/doc/classes/Texture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Texture" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Texture" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Texture for 2D and 3D. </brief_description> diff --git a/doc/classes/TextureButton.xml b/doc/classes/TextureButton.xml index 71abfa0fca..3bda04fe58 100644 --- a/doc/classes/TextureButton.xml +++ b/doc/classes/TextureButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextureButton" inherits="BaseButton" category="Core" version="3.0-rc1"> +<class name="TextureButton" inherits="BaseButton" category="Core" version="3.0-stable"> <brief_description> Texture-based button. Supports Pressed, Hover, Disabled and Focused states. </brief_description> diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml index 363a803dfa..2456653786 100644 --- a/doc/classes/TextureProgress.xml +++ b/doc/classes/TextureProgress.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextureProgress" inherits="Range" category="Core" version="3.0-rc1"> +<class name="TextureProgress" inherits="Range" category="Core" version="3.0-stable"> <brief_description> Texture-based progress bar. Useful for loading screens and life or stamina bars. </brief_description> diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml index 4b2f0a5ab4..8b0fc09a6d 100644 --- a/doc/classes/TextureRect.xml +++ b/doc/classes/TextureRect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextureRect" inherits="Control" category="Core" version="3.0-rc1"> +<class name="TextureRect" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Draws a sprite or a texture inside a User Interface. The texture can tile or not. </brief_description> diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index fc5afacded..037f8cbd48 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Theme" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Theme" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Theme for controls. </brief_description> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index 75ff16c907..c3d1d1b24f 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Thread" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="Thread" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> A unit of execution in a process. </brief_description> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index faecf5664c..058a92d99c 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TileMap" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="TileMap" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Node for 2D tile-based maps. </brief_description> @@ -7,7 +7,7 @@ Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles (textures plus optional collision, navigation, and/or occluder shapes) which are used to create grid-based maps. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/2d/using_tilemaps.html + http://docs.godotengine.org/en/3.0/tutorials/2d/using_tilemaps.html </tutorials> <demos> </demos> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 8ba3911c56..95a5e55176 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TileSet" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="TileSet" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Tile library for tilemaps. </brief_description> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index e5841e95e2..383d3199f5 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Timer" inherits="Node" category="Core" version="3.0-rc1"> +<class name="Timer" inherits="Node" category="Core" version="3.0-stable"> <brief_description> A countdown timer. </brief_description> diff --git a/doc/classes/ToolButton.xml b/doc/classes/ToolButton.xml index 355bc21c1a..6597dc96e0 100644 --- a/doc/classes/ToolButton.xml +++ b/doc/classes/ToolButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ToolButton" inherits="Button" category="Core" version="3.0-rc1"> +<class name="ToolButton" inherits="Button" category="Core" version="3.0-stable"> <brief_description> Flat button helper class. </brief_description> diff --git a/doc/classes/TouchScreenButton.xml b/doc/classes/TouchScreenButton.xml index 49cdf73c8d..c187274a9d 100644 --- a/doc/classes/TouchScreenButton.xml +++ b/doc/classes/TouchScreenButton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TouchScreenButton" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="TouchScreenButton" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Button for touch screen devices. </brief_description> diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index 1cb808532d..cf1c9ea032 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Transform" category="Built-In Types" version="3.0-rc1"> +<class name="Transform" category="Built-In Types" version="3.0-stable"> <brief_description> 3D Transformation. 3x4 matrix. </brief_description> @@ -7,7 +7,7 @@ Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a [Basis] "basis" and an [Vector3] "origin". It is similar to a 3x4 matrix. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> @@ -143,7 +143,7 @@ <argument index="0" name="v" type="var"> </argument> <description> - Transforms the given vector "v" by this transform. + Transforms the given [Vector3], [Plane], or [AABB] by this transform. </description> </method> <method name="xform_inv"> @@ -152,7 +152,7 @@ <argument index="0" name="v" type="var"> </argument> <description> - Inverse-transforms the given vector "v" by this transform. + Inverse-transforms the given [Vector3], [Plane], or [AABB] by this transform. </description> </method> </methods> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index dbdcb5b4ab..e20f48ba14 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Transform2D" category="Built-In Types" version="3.0-rc1"> +<class name="Transform2D" category="Built-In Types" version="3.0-stable"> <brief_description> 2D Transformation. 3x2 matrix. </brief_description> @@ -52,18 +52,18 @@ </description> </method> <method name="basis_xform"> - <return type="Transform2D"> + <return type="Vector2"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Vector2"> </argument> <description> Transforms the given vector by this transform's basis (no translation). </description> </method> <method name="basis_xform_inv"> - <return type="Transform2D"> + <return type="Vector2"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Vector2"> </argument> <description> Inverse-transforms the given vector by this transform's basis (no translation). @@ -143,21 +143,21 @@ </description> </method> <method name="xform"> - <return type="Transform2D"> + <return type="var"> </return> <argument index="0" name="v" type="var"> </argument> <description> - Transforms the given vector "v" by this transform. + Transforms the given [Vector2] or [Rect2] by this transform. </description> </method> <method name="xform_inv"> - <return type="Transform2D"> + <return type="var"> </return> <argument index="0" name="v" type="var"> </argument> <description> - Inverse-transforms the given vector "v" by this transform. + Inverse-transforms the given [Vector2] or [Rect2] by this transform. </description> </method> </methods> diff --git a/doc/classes/Translation.xml b/doc/classes/Translation.xml index 65c7ba0ff5..53106f9c08 100644 --- a/doc/classes/Translation.xml +++ b/doc/classes/Translation.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Translation" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="Translation" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Language Translation. </brief_description> diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml index 35d71ccd37..5b61f46f96 100644 --- a/doc/classes/TranslationServer.xml +++ b/doc/classes/TranslationServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TranslationServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="TranslationServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Server that manages all translations. Translations can be set to it and removed from it. </brief_description> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index f2fa1189d3..09af7ee49a 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Tree" inherits="Control" category="Core" version="3.0-rc1"> +<class name="Tree" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Control to show a tree of items. </brief_description> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 057f4e7d4d..c41cfac37c 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TreeItem" inherits="Object" category="Core" version="3.0-rc1"> +<class name="TreeItem" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Control for a single item inside a [Tree]. </brief_description> diff --git a/doc/classes/TriangleMesh.xml b/doc/classes/TriangleMesh.xml index 758431a9ef..0a4ea1fa8f 100644 --- a/doc/classes/TriangleMesh.xml +++ b/doc/classes/TriangleMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TriangleMesh" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="TriangleMesh" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index a4f2eb7af2..d843c8341f 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Tween" inherits="Node" category="Core" version="3.0-rc1"> +<class name="Tween" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Node useful for animations with unknown start and end points. </brief_description> @@ -350,7 +350,7 @@ <signal name="tween_completed"> <argument index="0" name="object" type="Object"> </argument> - <argument index="1" name="key" type="String"> + <argument index="1" name="key" type="NodePath"> </argument> <description> This signal is emitted when a tween ends. @@ -359,7 +359,7 @@ <signal name="tween_started"> <argument index="0" name="object" type="Object"> </argument> - <argument index="1" name="key" type="String"> + <argument index="1" name="key" type="NodePath"> </argument> <description> This signal is emitted when a tween starts. @@ -368,7 +368,7 @@ <signal name="tween_step"> <argument index="0" name="object" type="Object"> </argument> - <argument index="1" name="key" type="String"> + <argument index="1" name="key" type="NodePath"> </argument> <argument index="2" name="elapsed" type="float"> </argument> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index 4d262c96d4..c95723e083 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="UndoRedo" inherits="Object" category="Core" version="3.0-rc1"> +<class name="UndoRedo" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Helper to manage UndoRedo in the editor or custom tools. </brief_description> diff --git a/doc/classes/VBoxContainer.xml b/doc/classes/VBoxContainer.xml index 558e5304d0..f92335eef1 100644 --- a/doc/classes/VBoxContainer.xml +++ b/doc/classes/VBoxContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VBoxContainer" inherits="BoxContainer" category="Core" version="3.0-rc1"> +<class name="VBoxContainer" inherits="BoxContainer" category="Core" version="3.0-stable"> <brief_description> Vertical box container. </brief_description> diff --git a/doc/classes/VScrollBar.xml b/doc/classes/VScrollBar.xml index f81871b2ff..d533db98be 100644 --- a/doc/classes/VScrollBar.xml +++ b/doc/classes/VScrollBar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VScrollBar" inherits="ScrollBar" category="Core" version="3.0-rc1"> +<class name="VScrollBar" inherits="ScrollBar" category="Core" version="3.0-stable"> <brief_description> Vertical version of [ScrollBar], which goes from left (min) to right (max). </brief_description> diff --git a/doc/classes/VSeparator.xml b/doc/classes/VSeparator.xml index 2cbb9b2b39..c6082a905d 100644 --- a/doc/classes/VSeparator.xml +++ b/doc/classes/VSeparator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VSeparator" inherits="Separator" category="Core" version="3.0-rc1"> +<class name="VSeparator" inherits="Separator" category="Core" version="3.0-stable"> <brief_description> Vertical version of [Separator]. </brief_description> diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml index 19306c9444..b91c8a7c2a 100644 --- a/doc/classes/VSlider.xml +++ b/doc/classes/VSlider.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VSlider" inherits="Slider" category="Core" version="3.0-rc1"> +<class name="VSlider" inherits="Slider" category="Core" version="3.0-stable"> <brief_description> Vertical slider. </brief_description> diff --git a/doc/classes/VSplitContainer.xml b/doc/classes/VSplitContainer.xml index 1639319084..e6e6e189e8 100644 --- a/doc/classes/VSplitContainer.xml +++ b/doc/classes/VSplitContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VSplitContainer" inherits="SplitContainer" category="Core" version="3.0-rc1"> +<class name="VSplitContainer" inherits="SplitContainer" category="Core" version="3.0-stable"> <brief_description> Vertical split container. </brief_description> diff --git a/doc/classes/Variant.xml b/doc/classes/Variant.xml index 03933e7f28..f27f358015 100644 --- a/doc/classes/Variant.xml +++ b/doc/classes/Variant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Variant" category="Core" version="3.0-rc1"> +<class name="Variant" category="Core" version="3.0-stable"> <brief_description> The most important data type in Godot. </brief_description> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index ac54c2f09c..6a87bbf66c 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Vector2" category="Built-In Types" version="3.0-rc1"> +<class name="Vector2" category="Built-In Types" version="3.0-stable"> <brief_description> Vector used for 2D Math. </brief_description> @@ -7,7 +7,7 @@ 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index dafb215177..0dc808329f 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Vector3" category="Built-In Types" version="3.0-rc1"> +<class name="Vector3" category="Built-In Types" version="3.0-stable"> <brief_description> Vector class, which performs basic 3D vector math operations. </brief_description> @@ -7,7 +7,7 @@ Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/math/index.html + http://docs.godotengine.org/en/3.0/tutorials/math/index.html </tutorials> <demos> </demos> @@ -216,7 +216,7 @@ <method name="snapped"> <return type="Vector3"> </return> - <argument index="0" name="by" type="float"> + <argument index="0" name="by" type="Vector3"> </argument> <description> Returns a copy of the vector, snapped to the lowest neared multiple. diff --git a/doc/classes/VehicleBody.xml b/doc/classes/VehicleBody.xml index 01d1cfb40b..fe58806344 100644 --- a/doc/classes/VehicleBody.xml +++ b/doc/classes/VehicleBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VehicleBody" inherits="RigidBody" category="Core" version="3.0-rc1"> +<class name="VehicleBody" inherits="RigidBody" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index 5510d79826..e36a1abe9d 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VehicleWheel" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="VehicleWheel" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml index 1526dbfe11..55755ca5df 100644 --- a/doc/classes/VideoPlayer.xml +++ b/doc/classes/VideoPlayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoPlayer" inherits="Control" category="Core" version="3.0-rc1"> +<class name="VideoPlayer" inherits="Control" category="Core" version="3.0-stable"> <brief_description> Control to play video files. </brief_description> diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 3b048081d9..a4e4ee3452 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStream" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="VideoStream" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 8bc8913764..0698a61529 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Viewport" inherits="Node" category="Core" version="3.0-rc1"> +<class name="Viewport" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Creates a sub-view into the screen. </brief_description> @@ -12,8 +12,8 @@ Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/2d/2d_transforms.html - http://docs.godotengine.org/en/3.0/learning/features/viewports/index.html + http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html + http://docs.godotengine.org/en/3.0/tutorials/viewports/index.html </tutorials> <demos> </demos> @@ -59,6 +59,7 @@ <argument index="0" name="info" type="int" enum="Viewport.RenderInfo"> </argument> <description> + Get the specific information about the viewport from rendering pipeline. </description> </method> <method name="get_size_override" qualifiers="const"> @@ -182,58 +183,82 @@ </methods> <members> <member name="arvr" type="bool" setter="set_use_arvr" getter="use_arvr"> + If [code]true[/code] the viewport will be used in AR/VR process. Default value: [code]false[/code]. </member> <member name="audio_listener_enable_2d" type="bool" setter="set_as_audio_listener_2d" getter="is_audio_listener_2d"> + If [code]true[/code] the viewport will process 2D audio streams. Default value: [code]false[/code]. </member> <member name="audio_listener_enable_3d" type="bool" setter="set_as_audio_listener" getter="is_audio_listener"> + If [code]true[/code] the viewport will process 3D audio streams. Default value: [code]false[/code]. </member> <member name="canvas_transform" type="Transform2D" setter="set_canvas_transform" getter="get_canvas_transform"> The canvas transform of the viewport, useful for changing the on-screen positions of all child [CanvasItem]s. This is relative to the global canvas transform of the viewport. </member> <member name="debug_draw" type="int" setter="set_debug_draw" getter="get_debug_draw" enum="Viewport.DebugDraw"> + The overlay mode for test rendered geometry in debug purposes. Default value: [code]DEBUG_DRAW_DISABLED[/code]. </member> <member name="disable_3d" type="bool" setter="set_disable_3d" getter="is_3d_disabled"> + If [code]true[/code] the viewport will disable 3D rendering. For actual disabling use [code]usage[/code]. Default value: [code]false[/code]. </member> <member name="global_canvas_transform" type="Transform2D" setter="set_global_canvas_transform" getter="get_global_canvas_transform"> The global canvas transform of the viewport. The canvas transform is relative to this. </member> <member name="gui_disable_input" type="bool" setter="set_disable_input" getter="is_input_disabled"> + If [code]true[/code] the viewport will not receive input event. Default value: [code]false[/code]. </member> <member name="gui_snap_controls_to_pixels" type="bool" setter="set_snap_controls_to_pixels" getter="is_snap_controls_to_pixels_enabled"> + If [code]true[/code] the GUI controls on the viewport will lay pixel perfectly. Default value: [code]true[/code]. </member> <member name="hdr" type="bool" setter="set_hdr" getter="get_hdr"> + If [code]true[/code] the viewport rendering will receive benefits from High Dynamic Range algorithm. Default value: [code]true[/code]. </member> <member name="msaa" type="int" setter="set_msaa" getter="get_msaa" enum="Viewport.MSAA"> + The multisample anti-aliasing mode. Default value: [code]MSAA_DISABLED[/code]. </member> <member name="own_world" type="bool" setter="set_use_own_world" getter="is_using_own_world"> + If [code]true[/code] the viewport will use [World] defined in [code]world[/code] property. Default value: [code]false[/code]. </member> <member name="physics_object_picking" type="bool" setter="set_physics_object_picking" getter="get_physics_object_picking"> + If [code]true[/code] the objects rendered by viewport become subjects of mouse picking process. Default value: [code]false[/code]. </member> <member name="render_target_clear_mode" type="int" setter="set_clear_mode" getter="get_clear_mode" enum="Viewport.ClearMode"> + The clear mode when viewport used as a render target. Default value: [code]CLEAR_MODE_ALWAYS[/code]. </member> <member name="render_target_update_mode" type="int" setter="set_update_mode" getter="get_update_mode" enum="Viewport.UpdateMode"> + The update mode when viewport used as a render target. Default value: [code]UPDATE_WHEN_VISIBLE[/code]. </member> <member name="render_target_v_flip" type="bool" setter="set_vflip" getter="get_vflip"> + If [code]true[/code] the result of rendering will be flipped vertically. Default value: [code]false[/code]. </member> <member name="shadow_atlas_quad_0" type="int" setter="set_shadow_atlas_quadrant_subdiv" getter="get_shadow_atlas_quadrant_subdiv" enum="Viewport.ShadowAtlasQuadrantSubdiv"> + The subdivision amount of first quadrant on shadow atlas. Default value: [code]SHADOW_ATLAS_QUADRANT_SUBDIV_4[/code]. </member> <member name="shadow_atlas_quad_1" type="int" setter="set_shadow_atlas_quadrant_subdiv" getter="get_shadow_atlas_quadrant_subdiv" enum="Viewport.ShadowAtlasQuadrantSubdiv"> + The subdivision amount of second quadrant on shadow atlas. Default value: [code]SHADOW_ATLAS_QUADRANT_SUBDIV_4[/code]. </member> <member name="shadow_atlas_quad_2" type="int" setter="set_shadow_atlas_quadrant_subdiv" getter="get_shadow_atlas_quadrant_subdiv" enum="Viewport.ShadowAtlasQuadrantSubdiv"> + The subdivision amount of third quadrant on shadow atlas. Default value: [code]SHADOW_ATLAS_QUADRANT_SUBDIV_16[/code]. </member> <member name="shadow_atlas_quad_3" type="int" setter="set_shadow_atlas_quadrant_subdiv" getter="get_shadow_atlas_quadrant_subdiv" enum="Viewport.ShadowAtlasQuadrantSubdiv"> + The subdivision amount of fourth quadrant on shadow atlas. Default value: [code]SHADOW_ATLAS_QUADRANT_SUBDIV_64[/code]. </member> <member name="shadow_atlas_size" type="int" setter="set_shadow_atlas_size" getter="get_shadow_atlas_size"> + The resolution of shadow atlas. Both width and height is equal to one value. </member> <member name="size" type="Vector2" setter="set_size" getter="get_size"> + The width and height of viewport. </member> <member name="transparent_bg" type="bool" setter="set_transparent_background" getter="has_transparent_background"> + If [code]true[/code] the viewport should render its background as transparent. Default value: [code]false[/code]. </member> <member name="usage" type="int" setter="set_usage" getter="get_usage" enum="Viewport.Usage"> + The rendering mode of viewport. Default value: [code]USAGE_3D[/code]. </member> <member name="world" type="World" setter="set_world" getter="get_world"> + The custom [World] which can be used as 3D environment source. </member> <member name="world_2d" type="World2D" setter="set_world_2d" getter="get_world_2d"> + The custom [World2D] which can be used as 2D environment source. </member> </members> <signals> @@ -254,6 +279,7 @@ Update the render target only when it is visible. This is the default value. </constant> <constant name="UPDATE_ALWAYS" value="3" enum="UpdateMode"> + Always update the render target. </constant> <constant name="SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED" value="0" enum="ShadowAtlasQuadrantSubdiv"> </constant> @@ -270,30 +296,42 @@ <constant name="SHADOW_ATLAS_QUADRANT_SUBDIV_1024" value="6" enum="ShadowAtlasQuadrantSubdiv"> </constant> <constant name="SHADOW_ATLAS_QUADRANT_SUBDIV_MAX" value="7" enum="ShadowAtlasQuadrantSubdiv"> + Enum limiter. Do not use it directly. </constant> <constant name="RENDER_INFO_OBJECTS_IN_FRAME" value="0" enum="RenderInfo"> + Amount of objects in frame. </constant> <constant name="RENDER_INFO_VERTICES_IN_FRAME" value="1" enum="RenderInfo"> + Amount of vertices in frame. </constant> <constant name="RENDER_INFO_MATERIAL_CHANGES_IN_FRAME" value="2" enum="RenderInfo"> + Amount of material changes in frame. </constant> <constant name="RENDER_INFO_SHADER_CHANGES_IN_FRAME" value="3" enum="RenderInfo"> + Amount of shader changes in frame. </constant> <constant name="RENDER_INFO_SURFACE_CHANGES_IN_FRAME" value="4" enum="RenderInfo"> + Amount of surface changes in frame. </constant> <constant name="RENDER_INFO_DRAW_CALLS_IN_FRAME" value="5" enum="RenderInfo"> + Amount of draw calls in frame. </constant> <constant name="RENDER_INFO_MAX" value="6" enum="RenderInfo"> + Enum limiter. Do not use it directly. </constant> <constant name="DEBUG_DRAW_DISABLED" value="0" enum="DebugDraw"> + Objects are displayed normally. </constant> <constant name="DEBUG_DRAW_UNSHADED" value="1" enum="DebugDraw"> + Objects are displayed without light information. </constant> <constant name="DEBUG_DRAW_OVERDRAW" value="2" enum="DebugDraw"> </constant> <constant name="DEBUG_DRAW_WIREFRAME" value="3" enum="DebugDraw"> + Objects are displayed in wireframe style. </constant> <constant name="MSAA_DISABLED" value="0" enum="MSAA"> + Multisample anti-aliasing mode disabled. This is the default value. </constant> <constant name="MSAA_2X" value="1" enum="MSAA"> </constant> diff --git a/doc/classes/ViewportContainer.xml b/doc/classes/ViewportContainer.xml index 377d129f63..a2cd9a0217 100644 --- a/doc/classes/ViewportContainer.xml +++ b/doc/classes/ViewportContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ViewportContainer" inherits="Container" category="Core" version="3.0-rc1"> +<class name="ViewportContainer" inherits="Container" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index f83331e347..5943759c55 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ViewportTexture" inherits="Texture" category="Core" version="3.0-rc1"> +<class name="ViewportTexture" inherits="Texture" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/VisibilityEnabler.xml b/doc/classes/VisibilityEnabler.xml index a9b439ada4..faf0451bae 100644 --- a/doc/classes/VisibilityEnabler.xml +++ b/doc/classes/VisibilityEnabler.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisibilityEnabler" inherits="VisibilityNotifier" category="Core" version="3.0-rc1"> +<class name="VisibilityEnabler" inherits="VisibilityNotifier" category="Core" version="3.0-stable"> <brief_description> Enable certain nodes only when visible. </brief_description> diff --git a/doc/classes/VisibilityEnabler2D.xml b/doc/classes/VisibilityEnabler2D.xml index a4460ed8d0..acd9d86c2a 100644 --- a/doc/classes/VisibilityEnabler2D.xml +++ b/doc/classes/VisibilityEnabler2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisibilityEnabler2D" inherits="VisibilityNotifier2D" category="Core" version="3.0-rc1"> +<class name="VisibilityEnabler2D" inherits="VisibilityNotifier2D" category="Core" version="3.0-stable"> <brief_description> Enable certain nodes only when visible. </brief_description> diff --git a/doc/classes/VisibilityNotifier.xml b/doc/classes/VisibilityNotifier.xml index 89a7607055..94b08b78ed 100644 --- a/doc/classes/VisibilityNotifier.xml +++ b/doc/classes/VisibilityNotifier.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisibilityNotifier" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="VisibilityNotifier" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Detects when the node is visible on screen. </brief_description> diff --git a/doc/classes/VisibilityNotifier2D.xml b/doc/classes/VisibilityNotifier2D.xml index 8c5bde787a..bcd52cf9ca 100644 --- a/doc/classes/VisibilityNotifier2D.xml +++ b/doc/classes/VisibilityNotifier2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisibilityNotifier2D" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="VisibilityNotifier2D" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Detects when the node is visible on screen. </brief_description> diff --git a/doc/classes/VisualInstance.xml b/doc/classes/VisualInstance.xml index 7b927f906f..1c54cb53ac 100644 --- a/doc/classes/VisualInstance.xml +++ b/doc/classes/VisualInstance.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualInstance" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="VisualInstance" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index ed987f7047..a546b86c57 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualServer" inherits="Object" category="Core" version="3.0-rc1"> +<class name="VisualServer" inherits="Object" category="Core" version="3.0-stable"> <brief_description> Server for anything visible. </brief_description> @@ -913,7 +913,7 @@ Syncronizes threads. </description> </method> - <method name="free"> + <method name="free_rid"> <return type="void"> </return> <argument index="0" name="rid" type="RID"> diff --git a/doc/classes/WeakRef.xml b/doc/classes/WeakRef.xml index 58ea5f0cf0..0c4801b66e 100644 --- a/doc/classes/WeakRef.xml +++ b/doc/classes/WeakRef.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WeakRef" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="WeakRef" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Holds an [Object], but does not contribute to the reference count if the object is a reference. </brief_description> diff --git a/doc/classes/WindowDialog.xml b/doc/classes/WindowDialog.xml index a1116825c9..891e489be5 100644 --- a/doc/classes/WindowDialog.xml +++ b/doc/classes/WindowDialog.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WindowDialog" inherits="Popup" category="Core" version="3.0-rc1"> +<class name="WindowDialog" inherits="Popup" category="Core" version="3.0-stable"> <brief_description> Base class for window dialogs. </brief_description> diff --git a/doc/classes/World.xml b/doc/classes/World.xml index 97631b8e0d..cad286a1ac 100644 --- a/doc/classes/World.xml +++ b/doc/classes/World.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="World" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="World" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Class that has everything pertaining to a world. </brief_description> @@ -7,7 +7,7 @@ Class that has everything pertaining to a world. A physics space, a visual scenario and a sound space. Spatial nodes register their resources into the current world. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/ray-casting.html + http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html </tutorials> <demos> </demos> diff --git a/doc/classes/World2D.xml b/doc/classes/World2D.xml index 57f91b1fa9..1a2745e6cb 100644 --- a/doc/classes/World2D.xml +++ b/doc/classes/World2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="World2D" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="World2D" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> Class that has everything pertaining to a 2D world. </brief_description> @@ -7,7 +7,7 @@ Class that has everything pertaining to a 2D world. A physics space, a visual scenario and a sound space. 2D nodes register their resources into the current 2D world. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/physics/ray-casting.html + http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html </tutorials> <demos> </demos> diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index 3969a8c502..468e618e72 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WorldEnvironment" inherits="Node" category="Core" version="3.0-rc1"> +<class name="WorldEnvironment" inherits="Node" category="Core" version="3.0-stable"> <brief_description> Default environment properties for the entire scene (post-processing effects, lightning and background settings). </brief_description> @@ -9,7 +9,7 @@ The [code]WorldEnvironment[/code] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/environment_and_post_processing.html + http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html </tutorials> <demos> </demos> diff --git a/doc/classes/XMLParser.xml b/doc/classes/XMLParser.xml index a892c867a2..a404074401 100644 --- a/doc/classes/XMLParser.xml +++ b/doc/classes/XMLParser.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="XMLParser" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="XMLParser" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Low-level class for creating parsers for XML files. </brief_description> diff --git a/doc/classes/YSort.xml b/doc/classes/YSort.xml index b54b48691b..e4818df62b 100644 --- a/doc/classes/YSort.xml +++ b/doc/classes/YSort.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="YSort" inherits="Node2D" category="Core" version="3.0-rc1"> +<class name="YSort" inherits="Node2D" category="Core" version="3.0-stable"> <brief_description> Sort all child nodes based on their Y positions. </brief_description> diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml index 492363d243..cce5dd3b45 100644 --- a/doc/classes/bool.xml +++ b/doc/classes/bool.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="bool" category="Built-In Types" version="3.0-rc1"> +<class name="bool" category="Built-In Types" version="3.0-stable"> <brief_description> Boolean built-in type </brief_description> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 3be4c3398e..812bd9b3a9 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="float" category="Built-In Types" version="3.0-rc1"> +<class name="float" category="Built-In Types" version="3.0-stable"> <brief_description> Float built-in type </brief_description> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index 5329bd7e69..49b8b3949a 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="int" category="Built-In Types" version="3.0-rc1"> +<class name="int" category="Built-In Types" version="3.0-stable"> <brief_description> Integer built-in type. </brief_description> diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp index ba9481d36b..17112e5ab5 100644 --- a/drivers/unix/stream_peer_tcp_posix.cpp +++ b/drivers/unix/stream_peer_tcp_posix.cpp @@ -304,7 +304,7 @@ Error StreamPeerTCPPosix::read(uint8_t *p_buffer, int p_bytes, int &r_received, return OK; }; -void StreamPeerTCPPosix::set_nodelay(bool p_enabled) { +void StreamPeerTCPPosix::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; diff --git a/drivers/unix/stream_peer_tcp_posix.h b/drivers/unix/stream_peer_tcp_posix.h index 5770ae48f4..bcebe57771 100644 --- a/drivers/unix/stream_peer_tcp_posix.h +++ b/drivers/unix/stream_peer_tcp_posix.h @@ -77,7 +77,7 @@ public: virtual Status get_status() const; virtual void disconnect_from_host(); - virtual void set_nodelay(bool p_enabled); + virtual void set_no_delay(bool p_enabled); static void make_default(); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 832d75b17d..613dc8c406 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -148,6 +148,9 @@ void FileAccessWindows::close() { } save_path = ""; + if (rename_error) { + ERR_EXPLAIN("Safe save failed. This may be a permissions problem, but also may happen because you are running a paranoid antivirus. If this is the case, please switch to Windows Defender or disable the 'safe save' option in editor settings. This makes it work, but increases the risk of file corruption in a crash."); + } ERR_FAIL_COND(rename_error); } } diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp index d6a320fb5e..55775fc231 100644 --- a/drivers/windows/stream_peer_tcp_winsock.cpp +++ b/drivers/windows/stream_peer_tcp_winsock.cpp @@ -332,7 +332,7 @@ Error StreamPeerTCPWinsock::connect_to_host(const IP_Address &p_host, uint16_t p return OK; }; -void StreamPeerTCPWinsock::set_nodelay(bool p_enabled) { +void StreamPeerTCPWinsock::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); diff --git a/drivers/windows/stream_peer_tcp_winsock.h b/drivers/windows/stream_peer_tcp_winsock.h index 9be8414878..a0177d374e 100644 --- a/drivers/windows/stream_peer_tcp_winsock.h +++ b/drivers/windows/stream_peer_tcp_winsock.h @@ -81,7 +81,7 @@ public: static void make_default(); static void cleanup(); - virtual void set_nodelay(bool p_enabled); + virtual void set_no_delay(bool p_enabled); StreamPeerTCPWinsock(); ~StreamPeerTCPWinsock(); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index d26bdad899..48ceb82dc3 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -255,8 +255,9 @@ void CreateDialog::_update_search() { if (base_type == "Node" && type.begins_with("Editor")) continue; // do not show editor nodes - if (base_type == "Resource" && ClassDB::is_parent_class(type, "Script")) - continue; // do not show script nodes + if (base_type == "Resource" && ClassDB::is_parent_class(type, "PluginScript")) + // PluginScript must be initialized before use, which is not possible here + continue; if (!ClassDB::can_instance(type)) continue; // can't create what can't be instanced diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index f356c16827..22b33cc98f 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -458,6 +458,23 @@ void EditorFileDialog::_item_selected(int p_item) { get_ok()->set_disabled(_is_open_should_be_disabled()); } +void EditorFileDialog::_multi_selected(int p_item, bool p_selected) { + + int current = p_item; + if (current < 0 || current >= item_list->get_item_count()) + return; + + Dictionary d = item_list->get_item_metadata(current); + + if (!d["dir"] && p_selected) { + + file->set_text(d["name"]); + _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + } + + get_ok()->set_disabled(_is_open_should_be_disabled()); +} + void EditorFileDialog::_items_clear_selection() { item_list->unselect_all(); @@ -1290,6 +1307,7 @@ void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorFileDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_item_selected"), &EditorFileDialog::_item_selected); + ClassDB::bind_method(D_METHOD("_multi_selected"), &EditorFileDialog::_multi_selected); ClassDB::bind_method(D_METHOD("_items_clear_selection"), &EditorFileDialog::_items_clear_selection); ClassDB::bind_method(D_METHOD("_item_list_item_rmb_selected"), &EditorFileDialog::_item_list_item_rmb_selected); ClassDB::bind_method(D_METHOD("_item_list_rmb_clicked"), &EditorFileDialog::_item_list_rmb_clicked); @@ -1598,6 +1616,7 @@ EditorFileDialog::EditorFileDialog() { connect("confirmed", this, "_action_pressed"); item_list->connect("item_selected", this, "_item_selected", varray(), CONNECT_DEFERRED); + item_list->connect("multi_selected", this, "_multi_selected", varray(), CONNECT_DEFERRED); item_list->connect("item_activated", this, "_item_db_selected", varray()); item_list->connect("nothing_selected", this, "_items_clear_selection"); dir->connect("text_entered", this, "_dir_entered"); diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 05f66eadbf..b1f8f1108c 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -159,6 +159,7 @@ private: void _recent_selected(int p_idx); void _item_selected(int p_item); + void _multi_selected(int p_item, bool p_selected); void _items_clear_selection(); void _item_dc_selected(int p_item); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2f0c41c6e1..98991cd7c0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4815,6 +4815,10 @@ EditorNode::EditorNode() { Ref<EditorSceneImporterGLTF> import_gltf; import_gltf.instance(); import_scene->add_importer(import_gltf); + + Ref<EditorSceneImporterESCN> import_escn; + import_escn.instance(); + import_scene->add_importer(import_escn); } Ref<ResourceImporterBitMap> import_bitmap; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 9dd8a7232f..b1a0efaea6 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -235,6 +235,14 @@ Control *EditorInterface::get_base_control() { return EditorNode::get_singleton()->get_gui_base(); } +void EditorInterface::set_plugin_enabled(const String &p_plugin, bool p_enabled) { + EditorNode::get_singleton()->set_addon_plugin_enabled(p_plugin, p_enabled); +} + +bool EditorInterface::is_plugin_enabled(const String &p_plugin) const { + return EditorNode::get_singleton()->is_addon_plugin_enabled(p_plugin); +} + Error EditorInterface::save_scene() { if (!get_edited_scene_root()) return ERR_CANT_CREATE; @@ -271,6 +279,9 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("select_file", "p_file"), &EditorInterface::select_file); ClassDB::bind_method(D_METHOD("get_selected_path"), &EditorInterface::get_selected_path); + ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled); + ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled); + ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene); ClassDB::bind_method(D_METHOD("save_scene_as", "path", "with_preview"), &EditorInterface::save_scene_as, DEFVAL(true)); } diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 145dccc2d5..3d585120c0 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -90,6 +90,9 @@ public: Control *get_base_control(); + void set_plugin_enabled(const String &p_plugin, bool p_enabled); + bool is_plugin_enabled(const String &p_plugin) const; + Error save_scene(); void save_scene_as(const String &p_scene, bool p_with_preview = true); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 122034eaa3..433f501fc8 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -383,7 +383,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/files/restore_scripts_on_load", true); _initial_set("text_editor/completion/complete_file_paths", true); _initial_set("text_editor/files/maximum_recent_files", 20); - hints["text_editor/files/maximum_recent_files"] = PropertyInfo(Variant::INT, "text_editor/files/maximum_recent_files", PROPERTY_HINT_RANGE, "1, 200, 0"); + hints["text_editor/files/maximum_recent_files"] = PropertyInfo(Variant::INT, "text_editor/files/maximum_recent_files", PROPERTY_HINT_RANGE, "1, 200, 1"); _initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false); _initial_set("docks/scene_tree/draw_relationship_lines", false); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 914316ee61..8bcb599a67 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -145,7 +145,7 @@ public: bool has_setting(const String &p_setting) const; void erase(const String &p_setting); void raise_order(const String &p_setting); - void set_initial_value(const StringName &p_setting, const Variant &p_value, bool update_current = false); + void set_initial_value(const StringName &p_setting, const Variant &p_value, bool p_update_current = false); void set_manually(const StringName &p_setting, const Variant &p_value, bool p_emit_signal = false) { if (p_emit_signal) _set(p_setting, p_value); diff --git a/editor/editor_sub_scene.cpp b/editor/editor_sub_scene.cpp index 7527b198b8..056ee59860 100644 --- a/editor/editor_sub_scene.cpp +++ b/editor/editor_sub_scene.cpp @@ -121,10 +121,10 @@ void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_s } } -void EditorSubScene::_remove_selection_child(Node *n) { - if (n->get_child_count() > 0) { - for (int i = 0; i < n->get_child_count(); i++) { - Node *c = n->get_child(i); +void EditorSubScene::_remove_selection_child(Node *p_node) { + if (p_node->get_child_count() > 0) { + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *c = p_node->get_child(i); List<Node *>::Element *E = selection.find(c); if (E) { selection.move_to_back(E); diff --git a/editor/editor_sub_scene.h b/editor/editor_sub_scene.h index cf4d6fd5b9..202ba03cea 100644 --- a/editor/editor_sub_scene.h +++ b/editor/editor_sub_scene.h @@ -50,7 +50,7 @@ class EditorSubScene : public ConfirmationDialog { void _fill_tree(Node *p_node, TreeItem *p_parent); void _selected_changed(); void _item_multi_selected(Object *p_object, int p_cell, bool p_selected); - void _remove_selection_child(Node *c); + void _remove_selection_child(Node *p_node); void _reown(Node *p_node, List<Node *> *p_to_reown); void ok_pressed(); diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 6cbca3f733..c4ecf3c098 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -30,6 +30,8 @@ #include "export_template_manager.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "editor_node.h" #include "editor_scale.h" #include "io/json.h" @@ -422,6 +424,11 @@ void ExportTemplateManager::_http_download_templates_completed(int p_status, int void ExportTemplateManager::_begin_template_download(const String &p_url) { + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + OS::get_singleton()->shell_open(p_url); + return; + } + for (int i = 0; i < template_list->get_child_count(); i++) { BaseButton *b = Object::cast_to<BaseButton>(template_list->get_child(0)); if (b) { @@ -576,7 +583,7 @@ ExportTemplateManager::ExportTemplateManager() { template_downloader->add_child(vbc); ScrollContainer *sc = memnew(ScrollContainer); sc->set_custom_minimum_size(Size2(400, 200) * EDSCALE); - vbc->add_margin_child(TTR("Select mirror from list: "), sc); + vbc->add_margin_child(TTR("Select mirror from list: (Shift+Click: Open in Browser)"), sc); template_list = memnew(VBoxContainer); sc->add_child(template_list); sc->set_enable_v_scroll(true); diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp index 56fb7633e7..a218070933 100644 --- a/editor/fileserver/editor_file_server.cpp +++ b/editor/fileserver/editor_file_server.cpp @@ -55,7 +55,7 @@ void EditorFileServer::_subthread_start(void *s) { ClientData *cd = (ClientData *)s; - cd->connection->set_nodelay(true); + cd->connection->set_no_delay(true); uint8_t buf4[8]; Error err = cd->connection->get_data(buf4, 4); if (err != OK) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index cc9c9a11d7..8ead8d3982 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -77,7 +77,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory return true; } -void FileSystemDock::_update_tree(bool keep_collapse_state) { +void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_root) { Vector<String> uncollapsed_paths; if (keep_collapse_state) { @@ -129,6 +129,10 @@ void FileSystemDock::_update_tree(bool keep_collapse_state) { ti->set_metadata(0, fave); } + if (p_uncollapse_root) { + uncollapsed_paths.push_back("res://"); + } + _create_tree(root, EditorFileSystem::get_singleton()->get_filesystem(), uncollapsed_paths); tree->ensure_cursor_is_visible(); updating_tree = false; @@ -154,6 +158,7 @@ void FileSystemDock::_notification(int p_what) { } else { tree->set_v_size_flags(SIZE_FILL); + button_tree->hide(); if (!tree->is_visible()) { tree->show(); button_favorite->show(); @@ -163,7 +168,6 @@ void FileSystemDock::_notification(int p_what) { if (!file_list_vb->is_visible()) { file_list_vb->show(); - button_tree->hide(); _update_files(true); } } @@ -204,7 +208,7 @@ void FileSystemDock::_notification(int p_what) { if (EditorFileSystem::get_singleton()->is_scanning()) { _set_scanning_mode(); } else { - _update_tree(false); + _update_tree(false, true); } } break; diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index af80557465..2e86b83efc 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -157,7 +157,7 @@ private: bool import_dock_needs_update; bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths); - void _update_tree(bool keep_collapse_state); + void _update_tree(bool keep_collapse_state, bool p_uncollapse_root = false); void _update_files(bool p_keep_selection); void _update_file_display_toggle_button(); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 060953d36a..44948b8209 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -46,6 +46,7 @@ #include "scene/resources/box_shape.h" #include "scene/resources/plane_shape.h" #include "scene/resources/ray_shape.h" +#include "scene/resources/scene_format_text.h" #include "scene/resources/sphere_shape.h" uint32_t EditorSceneImporter::get_import_flags() const { @@ -1395,3 +1396,25 @@ ResourceImporterScene *ResourceImporterScene::singleton = NULL; ResourceImporterScene::ResourceImporterScene() { singleton = this; } +/////////////////////////////////////// + +uint32_t EditorSceneImporterESCN::get_import_flags() const { + return IMPORT_SCENE; +} +void EditorSceneImporterESCN::get_extensions(List<String> *r_extensions) const { + r_extensions->push_back("escn"); +} +Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { + + Error error; + Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error); + ERR_FAIL_COND_V(!ps.is_valid(), NULL); + + Node *scene = ps->instance(); + ERR_FAIL_COND_V(!scene, NULL); + + return scene; +} +Ref<Animation> EditorSceneImporterESCN::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) { + ERR_FAIL_V(Ref<Animation>()); +} diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index d5f9d53e91..9c3ec7a29b 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -155,4 +155,14 @@ public: ResourceImporterScene(); }; +class EditorSceneImporterESCN : public EditorSceneImporter { + GDCLASS(EditorSceneImporterESCN, EditorSceneImporter); + +public: + virtual uint32_t get_import_flags() const; + virtual void get_extensions(List<String> *r_extensions) const; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL); + virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps); +}; + #endif // RESOURCEIMPORTERSCENE_H diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 670f13b6c8..7d6025cb03 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1953,9 +1953,9 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { if (node) { real_t angle = node->get_rotation(); - node->set_rotation(snap_angle(angle + (dfrom - center).angle_to(dto - center), angle)); display_rotate_to = dto; - display_rotate_from = center; + display_rotate_from = center + node->get_pivot_offset().rotated(angle); + node->set_rotation(snap_angle(angle + (dfrom - display_rotate_from).angle_to(display_rotate_to - display_rotate_from), angle)); viewport->update(); } } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index dffb98e488..a3728a1d46 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1565,16 +1565,12 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); edit_menu->get_popup()->add_separator(); -#ifdef OSX_ENABLED - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); -#else + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); -#endif edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); @@ -1645,13 +1641,14 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0); ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0); ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K); - ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F); ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0); ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); #ifdef OSX_ENABLED + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CTRL | KEY_MASK_ALT | KEY_T); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 5e8eb06556..bd2cdf0dda 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -3827,9 +3827,6 @@ Object *SpatialEditor::_get_editor_data(Object *p_what) { si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario()); VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF); - if (Engine::get_singleton()->is_editor_hint()) - editor->call("edit_node", sp); - return si; } diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 36a578037e..e891850870 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -75,6 +75,9 @@ void TextureEditor::_notification(int p_what) { // In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient ofs_y = 0; tex_height = size.height; + } else if (Object::cast_to<GradientTexture>(*texture)) { + ofs_y = size.height / 4.0; + tex_height = size.height / 2.0; } draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height)); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 1dedf92452..dd5127181d 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -420,7 +420,7 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { p.push_back((int)SHAPE_DELETE); tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p); tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_DELETE]); - tool_containers[TOOLBAR_SHAPE]->add_change_receptor(memnew(VSeparator)); + tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator)); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton); tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); @@ -548,6 +548,10 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { preview->set_region(true); } +AutotileEditor::~AutotileEditor() { + memdelete(helper); +} + void AutotileEditor::_bind_methods() { ClassDB::bind_method("_on_autotile_selected", &AutotileEditor::_on_autotile_selected); diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 93d403deea..30f6e2b925 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -120,6 +120,7 @@ class AutotileEditor : public Control { AutotileEditorHelper *helper; AutotileEditor(EditorNode *p_editor); + ~AutotileEditor(); protected: static void _bind_methods(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 16605f2ebd..da283b16dc 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -152,7 +152,7 @@ private: return ""; } - } else if (mode == MODE_NEW) { + } else { // check if the specified folder is empty, even though this is not an error, it is good to check here d->list_dir_begin(); @@ -174,13 +174,6 @@ private: get_ok()->set_disabled(true); return ""; } - - } else if (d->file_exists("project.godot")) { - - set_message(TTR("Please choose a folder that does not contain a 'project.godot' file."), MESSAGE_ERROR); - memdelete(d); - get_ok()->set_disabled(true); - return ""; } set_message(""); @@ -531,11 +524,17 @@ public: set_title(TTR("Rename Project")); get_ok()->set_text(TTR("Rename")); name_container->show(); + status_rect->hide(); + msg->hide(); + get_ok()->set_disabled(false); ProjectSettings *current = memnew(ProjectSettings); if (current->setup(project_path->get_text(), "")) { set_message(TTR("Couldn't get project.godot in the project path."), MESSAGE_ERROR); + status_rect->show(); + msg->show(); + get_ok()->set_disabled(true); } else if (current->has_setting("application/config/name")) { project_name->set_text(current->get("application/config/name")); } @@ -543,8 +542,6 @@ public: project_name->call_deferred("grab_focus"); create_dir->hide(); - status_rect->hide(); - msg->hide(); } else { @@ -632,7 +629,7 @@ public: project_path->set_h_size_flags(SIZE_EXPAND_FILL); pphb->add_child(project_path); - // status button + // status icon status_rect = memnew(TextureRect); status_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); pphb->add_child(status_rect); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 704ec40e4c..058f517ae9 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -750,7 +750,16 @@ void ProjectSettingsEditor::_item_add() { String catname = category->get_text().strip_edges(); String propname = property->get_text().strip_edges(); - String name = catname != "" ? catname + "/" + propname : propname; + + if (propname.empty()) { + return; + } + + if (catname.empty()) { + catname = "global"; + } + + String name = catname + "/" + propname; undo_redo->create_action(TTR("Add Global Property")); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 629b5b63fb..d0527a13ea 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -612,7 +612,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (!EditorNode::get_log()->is_visible()) { if (EditorNode::get_singleton()->are_bottom_panels_hidden()) { - EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log()); + if (EDITOR_GET("run/output/always_open_output_on_play")) { + EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log()); + } } } EditorNode::get_log()->add_message(t); diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index cb483ed653..eedab7fdef 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -105,7 +105,7 @@ protected: void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh, const AABB &p_bounds = AABB()); void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1); void add_handles(const Vector<Vector3> &p_handles, bool p_billboard = false, bool p_secondary = false); - void add_solid_box(Ref<Material> &p_material, Vector3 size); + void add_solid_box(Ref<Material> &p_material, Vector3 p_size); void set_spatial_node(Spatial *p_node); const Spatial *get_spatial_node() const { return spatial_node; } @@ -376,13 +376,13 @@ public: class JointGizmosDrawer { public: - static Basis look_body(const Transform &joint_transform, const Transform &body_transform); + static Basis look_body(const Transform &p_joint_transform, const Transform &p_body_transform); static Basis look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform); - static Basis look_body_toward_x(const Transform &joint_transform, const Transform &body_transform); - static Basis look_body_toward_y(const Transform &joint_transform, const Transform &body_transform); + static Basis look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform); + static Basis look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform); /// Special function just used for physics joints, it that returns a basis constrained toward Joint Z axis /// with axis X and Y that are looking toward the body and oriented toward up - static Basis look_body_toward_z(const Transform &joint_transform, const Transform &body_transform); + static Basis look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform); // Draw circle around p_axis static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector<Vector3> &r_points, bool p_inverse = false); diff --git a/editor/translations/af.po b/editor/translations/af.po index 32a23e42f5..9d4ce1e29d 100644 --- a/editor/translations/af.po +++ b/editor/translations/af.po @@ -7684,7 +7684,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7818,7 +7818,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ar.po b/editor/translations/ar.po index d8a499c8f2..deb49f0619 100644 --- a/editor/translations/ar.po +++ b/editor/translations/ar.po @@ -19,8 +19,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:53+0000\n" -"Last-Translator: omar anwar aglan <omar.aglan91@yahoo.com>\n" +"PO-Revision-Date: 2018-01-24 11:45+0000\n" +"Last-Translator: anonymous <>\n" "Language-Team: Arabic <https://hosted.weblate.org/projects/godot-engine/" "godot/ar/>\n" "Language: ar\n" @@ -1150,7 +1150,7 @@ msgstr "تØديث" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "All Recognized" -msgstr "جميع الأنواع المعتمدة" +msgstr "جميع الأنواع المعتمدة\"المعروÙØ©\"" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "All Files (*)" @@ -1333,19 +1333,18 @@ msgid "Description" msgstr "الوصÙ" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "مستندات الإنترنت" +msgstr "الدورس علي الإنترنت:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"لا يوجد Øاليا وص٠لهذه الطريقة. الرجاء المساعدة من خلال [color=$color][url=" -"$url]المساهمة واØد[/url][/color] !" +"ليس هناك دروس تعليمية ÙÙŠ هذا الÙصل، يمكنك [color=$color][url=$url] المساهمة " +"ÙÙŠ Ø¥Øداها [/url][/color] أو [color=$color][url=$url2]أطلب Ø£Øداها [/url][/" +"color]." #: editor/editor_help.cpp msgid "Properties" @@ -1843,7 +1842,7 @@ msgstr "تمكين/إيقا٠الوضع الخالي من الإلهاء." #: editor/editor_node.cpp msgid "Add a new scene." -msgstr "أض٠مشهد جديدة." +msgstr "أض٠مشهد جديد" #: editor/editor_node.cpp msgid "Scene" @@ -7735,7 +7734,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7871,7 +7870,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/bg.po b/editor/translations/bg.po index 3f841adcb9..ecef737d82 100644 --- a/editor/translations/bg.po +++ b/editor/translations/bg.po @@ -6,12 +6,13 @@ # Bojidar Marinov <bojidar.marinov.bg@gmail.com>, 2016. # Иван Пенев (Ðдмирал ÐнимЕ) <aeternus.arcis@gmail.com>, 2016-2017. # Любомир ВаÑилев <lyubomirv@abv.bg>, 2018. +# MaresPW <marespw206@gmail.com>, 2018. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-06 13:19+0000\n" -"Last-Translator: Любомир ВаÑилев <lyubomirv@abv.bg>\n" +"PO-Revision-Date: 2018-01-24 18:44+0000\n" +"Last-Translator: MaresPW <marespw206@gmail.com>\n" "Language-Team: Bulgarian <https://hosted.weblate.org/projects/godot-engine/" "godot/bg/>\n" "Language: bg\n" @@ -518,9 +519,8 @@ msgid "Change" msgstr "" #: editor/create_dialog.cpp -#, fuzzy msgid "Create New %s" -msgstr "Създаване на нов проект" +msgstr "Създайте нов/а %s" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -708,9 +708,8 @@ msgid "Lead Developer" msgstr "" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "ДиÑпечер на проектите" +msgstr "Ръководител на проекта " #: editor/editor_about.cpp msgid "Developers" @@ -781,9 +780,8 @@ msgid "Error opening package file, not in zip format." msgstr "" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Uncompressing Assets" -msgstr "Извършва Ñе повторно внаÑÑне" +msgstr "Разархивиране на активи" #: editor/editor_asset_installer.cpp editor/project_manager.cpp msgid "Package Installed Successfully!" @@ -7751,7 +7749,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7904,7 +7902,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/bn.po b/editor/translations/bn.po index aaef88f5f8..b50fc38439 100644 --- a/editor/translations/bn.po +++ b/editor/translations/bn.po @@ -8169,7 +8169,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8323,7 +8323,7 @@ msgstr "ছবিসমূহ বà§à¦²à¦¿à¦Ÿà¦¿à¦‚ (Blitting) করা হচà msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ca.po b/editor/translations/ca.po index 682fbf0db4..91d1d55e64 100644 --- a/editor/translations/ca.po +++ b/editor/translations/ca.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-08 06:50+0000\n" +"PO-Revision-Date: 2018-01-24 02:50+0000\n" "Last-Translator: Roger Blanco Ribera <roger.blancoribera@gmail.com>\n" "Language-Team: Catalan <https://hosted.weblate.org/projects/godot-engine/" "godot/ca/>\n" @@ -1330,19 +1330,18 @@ msgid "Description" msgstr "Descripció" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Documentació en lÃnia" +msgstr "Tutorials en lÃnia:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Aquest mètode no disposa de cap descripció. Podeu contribuir [color=$color]" -"[url=$url] tot aportant-ne una[/url][/color]!" +"Aquesta classe no disposa encara de cap Tutorial. Podeu contribuir [color=" +"$color][url=$url] tot aportant-ne un[/url][/color] o [color=$color][url=" +"$url2]sol·licitant-lo[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -2635,7 +2634,7 @@ msgstr "No es pot moure/reanomenar l'arrel dels recursos." #: editor/filesystem_dock.cpp msgid "Cannot move a folder into itself." -msgstr "No es pot moure un directori dins si mateix:" +msgstr "No es pot moure un directori dins si mateix." #: editor/filesystem_dock.cpp msgid "Error moving:" @@ -3776,7 +3775,7 @@ msgstr "D'acord" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "No es poden instanciar múltiples nodes sense cap arrel." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5837,9 +5836,8 @@ msgid "Select current edited sub-tile." msgstr "Selecciona la sub-tessel·la en edició." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." -msgstr "Selecciona sub-tessel·la per a modificar-ne la prioritat." +msgstr "Selecciona una sub-tessel·la per a modificar-ne la prioritat." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5953,9 +5951,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Selecciona un fitxer 'projecte.godot'." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Selecciona un fitxer 'projecte.godot'." +msgstr "Selecciona un directori buit." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7815,8 +7812,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"En no disposar de cap fill del tipus Shape, aquest node no pot interactuar " +"amb l'espai.\n" +"Considereu afegir-hi nodes fill del tipus CollisionShape2D o " +"CollisionPolygon2D per definir-ne la forma." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7978,8 +7979,12 @@ msgstr "Il·luminant les Malles: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"En no disposar de cap fill del tipus Shape, aquest node no pot interactuar " +"amb l'espai.\n" +"Considereu afegir-hi nodes fill del tipus CollisionShape2D o " +"CollisionPolygon2D per definir-ne la forma." #: scene/3d/collision_polygon.cpp msgid "" @@ -8119,7 +8124,6 @@ msgid "(Other)" msgstr "(Altres)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/cs.po b/editor/translations/cs.po index 083bf9adcc..ce645b139b 100644 --- a/editor/translations/cs.po +++ b/editor/translations/cs.po @@ -6,21 +6,22 @@ # Fadex <vitekpaulik@gmail.com>, 2017. # Jan 'spl!te' KondelÃk <j.kondelik@centrum.cz>, 2016. # Jiri Hysek <contact@jirihysek.com>, 2017. -# LudÄ›k Novotný <gladosicek@gmail.com>, 2016. +# LudÄ›k Novotný <gladosicek@gmail.com>, 2016, 2018. # Martin Novák <maidx@seznam.cz>, 2017. +# zxey <r.hozak@seznam.cz>, 2018. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2017-12-09 19:45+0000\n" -"Last-Translator: Martin Novák <maidx@seznam.cz>\n" +"PO-Revision-Date: 2018-01-24 12:07+0000\n" +"Last-Translator: zxey <r.hozak@seznam.cz>\n" "Language-Team: Czech <https://hosted.weblate.org/projects/godot-engine/godot/" "cs/>\n" "Language: cs\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"X-Generator: Weblate 2.18-dev\n" +"X-Generator: Weblate 2.19-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -337,7 +338,7 @@ msgstr "Odstranit neplatné klÃÄe" #: editor/animation_editor.cpp msgid "Remove unresolved and empty tracks" -msgstr "Odstranit nevyÅ™eÅ¡ené a prázdné stopy" +msgstr "Odstranit neurÄené a prázdné stopy" #: editor/animation_editor.cpp msgid "Clean-up all animations" @@ -499,9 +500,8 @@ msgid "Connecting Signal:" msgstr "PÅ™ipojuji signál:" #: editor/connections_dialog.cpp -#, fuzzy msgid "Disconnect '%s' from '%s'" -msgstr "PÅ™ipojit '%s' k '%s'" +msgstr "Odpojit '%s' od '%s'" #: editor/connections_dialog.cpp msgid "Connect.." @@ -517,9 +517,8 @@ msgid "Signals" msgstr "Signály" #: editor/create_dialog.cpp -#, fuzzy msgid "Change %s Type" -msgstr "ZmÄ›nit typ hodnot pole" +msgstr "ZmÄ›nit typ %d" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -527,9 +526,8 @@ msgid "Change" msgstr "ZmÄ›nit" #: editor/create_dialog.cpp -#, fuzzy msgid "Create New %s" -msgstr "VytvoÅ™it nový" +msgstr "VytvoÅ™it nový %s" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -639,9 +637,8 @@ msgstr "" "PÅ™esto je chcete smazat? (nelze vrátit zpÄ›t)" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" -msgstr "Nelze odstranit:\n" +msgstr "Nelze odstranit:" #: editor/dependency_editor.cpp msgid "Error loading:" @@ -716,18 +713,16 @@ msgid "Godot Engine contributors" msgstr "PÅ™ispÃvajÃcà do Godot Enginu" #: editor/editor_about.cpp -#, fuzzy msgid "Project Founders" -msgstr "Nastavenà projektu" +msgstr "Zakladatelé projektu" #: editor/editor_about.cpp msgid "Lead Developer" msgstr "Vedoucà vývojář" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "Správce projektů" +msgstr "Správce projektu " #: editor/editor_about.cpp msgid "Developers" @@ -786,14 +781,12 @@ msgstr "" "popisy autorských práv a s licenÄnÃmi podmÃnkami." #: editor/editor_about.cpp -#, fuzzy msgid "All Components" -msgstr "Spojité" +msgstr "VÅ¡echny komponenty" #: editor/editor_about.cpp -#, fuzzy msgid "Components" -msgstr "Spojité" +msgstr "Komponenty" #: editor/editor_about.cpp msgid "Licenses" @@ -834,22 +827,20 @@ msgid "Add Effect" msgstr "PÅ™idat efekt" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Rename Audio Bus" -msgstr "PÅ™ejmenovat AutoLoad" +msgstr "PÅ™ejmenovat Audio Bus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Change Audio Bus Volume" -msgstr "ZmÄ›nit hodnotu pole" +msgstr "ZmÄ›nit hlasitost Audio Busu" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Solo" -msgstr "" +msgstr "Hraje pouze tento Audio Bus" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Mute" -msgstr "" +msgstr "Ztlumit tento Audio Bus" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Bypass Effects" @@ -861,16 +852,15 @@ msgstr "" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus Effect" -msgstr "" +msgstr "PÅ™idat Audio Bus efekt" #: editor/editor_audio_buses.cpp msgid "Move Bus Effect" -msgstr "" +msgstr "PÅ™esunout Bus efekt" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Delete Bus Effect" -msgstr "Smazat vybraný" +msgstr "Smazat Bus efekt" #: editor/editor_audio_buses.cpp msgid "Audio Bus, Drag and Drop to rearrange." @@ -878,11 +868,11 @@ msgstr "" #: editor/editor_audio_buses.cpp msgid "Solo" -msgstr "" +msgstr "Solo" #: editor/editor_audio_buses.cpp msgid "Mute" -msgstr "" +msgstr "Ztlumit" #: editor/editor_audio_buses.cpp msgid "Bypass" @@ -890,77 +880,72 @@ msgstr "" #: editor/editor_audio_buses.cpp msgid "Bus options" -msgstr "" +msgstr "Možnosti Busu" #: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp #: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Duplicate" -msgstr "" +msgstr "Duplikovat" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Reset Volume" -msgstr "Obnovit původnà pÅ™iblÞenÃ" +msgstr "Resetovat hlasitost" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Delete Effect" -msgstr "Smazat vybraný" +msgstr "Smazat efekt" #: editor/editor_audio_buses.cpp msgid "Audio" -msgstr "" +msgstr "Zvuk" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus" -msgstr "" +msgstr "PÅ™idat Audio Bus" #: editor/editor_audio_buses.cpp msgid "Master bus can't be deleted!" -msgstr "" +msgstr "Master bus nelze smazat!" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Delete Audio Bus" -msgstr "Optimalizovat animaci" +msgstr "Smazat Audio Bus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Duplicate Audio Bus" -msgstr "Duplikovat výbÄ›r" +msgstr "Duplikovat Audio Bus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Reset Bus Volume" -msgstr "Obnovit původnà pÅ™iblÞenÃ" +msgstr "Obnovit hlasitost Busu" #: editor/editor_audio_buses.cpp msgid "Move Audio Bus" -msgstr "" +msgstr "PÅ™esunout Audio Bus" #: editor/editor_audio_buses.cpp msgid "Save Audio Bus Layout As.." -msgstr "" +msgstr "Uložit rozloženà Audio Busu jako.." #: editor/editor_audio_buses.cpp msgid "Location for New Layout.." -msgstr "" +msgstr "UmÃstÄ›nà pro nové rozloženÃ.." #: editor/editor_audio_buses.cpp msgid "Open Audio Bus Layout" -msgstr "" +msgstr "OtevÅ™Ãt rozloženà Audio Busu" #: editor/editor_audio_buses.cpp msgid "There is no 'res://default_bus_layout.tres' file." -msgstr "" +msgstr "Soubor 'res://default_bus_layout.tres' neexistuje." #: editor/editor_audio_buses.cpp msgid "Invalid file, not an audio bus layout." -msgstr "" +msgstr "Neplatný soubor, neni to rozloženà Audio Busu." #: editor/editor_audio_buses.cpp msgid "Add Bus" -msgstr "" +msgstr "PÅ™idat bus" #: editor/editor_audio_buses.cpp msgid "Create a new Bus Layout." @@ -969,7 +954,7 @@ msgstr "" #: editor/editor_audio_buses.cpp editor/property_editor.cpp #: editor/script_create_dialog.cpp msgid "Load" -msgstr "" +msgstr "NaÄÃst" #: editor/editor_audio_buses.cpp msgid "Load an existing Bus Layout." @@ -986,7 +971,7 @@ msgstr "" #: editor/editor_audio_buses.cpp editor/import_dock.cpp msgid "Load Default" -msgstr "" +msgstr "NaÄÃst výchozÃ" #: editor/editor_audio_buses.cpp msgid "Load the default Bus Layout." @@ -1078,31 +1063,31 @@ msgstr "Singleton" #: editor/editor_data.cpp msgid "Updating Scene" -msgstr "" +msgstr "Aktualizuji scénu" #: editor/editor_data.cpp msgid "Storing local changes.." -msgstr "" +msgstr "Ukládám lokálnà zmÄ›ny.." #: editor/editor_data.cpp msgid "Updating scene.." -msgstr "" +msgstr "Aktualizuji scénu.." #: editor/editor_data.cpp msgid "[empty]" -msgstr "" +msgstr "[prázdné]" #: editor/editor_data.cpp msgid "[unsaved]" -msgstr "" +msgstr "[neuloženo]" #: editor/editor_dir_dialog.cpp msgid "Please select a base directory first" -msgstr "" +msgstr "Nejprve vyberte výchozà složku" #: editor/editor_dir_dialog.cpp msgid "Choose a Directory" -msgstr "" +msgstr "Vyberte složku" #: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp @@ -1123,45 +1108,43 @@ msgstr "Nelze vytvoÅ™it složku." #: editor/editor_dir_dialog.cpp msgid "Choose" -msgstr "" +msgstr "Vyberte" #: editor/editor_export.cpp msgid "Storing File:" -msgstr "" +msgstr "Ukládám soubor:" #: editor/editor_export.cpp msgid "Packing" -msgstr "" +msgstr "BalÃm" #: editor/editor_export.cpp platform/javascript/export/export.cpp msgid "Template file not found:" -msgstr "" +msgstr "Soubor Å¡ablony nenalezen:" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "File Exists, Overwrite?" msgstr "Soubor už existuje. PÅ™epsat?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "Select Current Folder" -msgstr "VytvoÅ™it složku" +msgstr "Vybrat stávajÃcà složku" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Copy Path" -msgstr "" +msgstr "KopÃrovat cestu" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Show In File Manager" -msgstr "" +msgstr "Ukázat ve správci souborů" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp -#, fuzzy msgid "New Folder.." -msgstr "VytvoÅ™it složku" +msgstr "Nová složka.." #: editor/editor_file_dialog.cpp msgid "Refresh" -msgstr "" +msgstr "Obnovit" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "All Recognized" @@ -1199,44 +1182,45 @@ msgstr "Uložit soubor" #: editor/editor_file_dialog.cpp msgid "Go Back" -msgstr "" +msgstr "JÃt zpÄ›t" #: editor/editor_file_dialog.cpp msgid "Go Forward" -msgstr "" +msgstr "Jit dopÅ™edu" #: editor/editor_file_dialog.cpp msgid "Go Up" -msgstr "" +msgstr "JÃt o úroveň výš" #: editor/editor_file_dialog.cpp msgid "Toggle Hidden Files" -msgstr "" +msgstr "Zobrazit skryté soubory" #: editor/editor_file_dialog.cpp msgid "Toggle Favorite" -msgstr "" +msgstr "Zobrazit oblÃbené" #: editor/editor_file_dialog.cpp msgid "Toggle Mode" -msgstr "" +msgstr "PÅ™epnout režim" #: editor/editor_file_dialog.cpp msgid "Focus Path" msgstr "" #: editor/editor_file_dialog.cpp +#, fuzzy msgid "Move Favorite Up" -msgstr "" +msgstr "PÅ™esunout oblÃbenou položku o úroveň výš" #: editor/editor_file_dialog.cpp +#, fuzzy msgid "Move Favorite Down" -msgstr "" +msgstr "PÅ™esunout oblÃbenou položku o úroveň nÞ" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "Go to parent folder" -msgstr "Nelze vytvoÅ™it složku." +msgstr "JÃt na nadÅ™azenou složku" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Directories & Files:" @@ -1244,7 +1228,7 @@ msgstr "Složky a soubory:" #: editor/editor_file_dialog.cpp msgid "Preview:" -msgstr "" +msgstr "Náhled:" #: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp #: scene/gui/file_dialog.cpp @@ -1270,11 +1254,11 @@ msgstr "Prohledat nápovÄ›du" #: editor/editor_help.cpp msgid "Class List:" -msgstr "" +msgstr "Seznam tÅ™Ãd:" #: editor/editor_help.cpp msgid "Search Classes" -msgstr "" +msgstr "Hledat tÅ™Ãdy" #: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp msgid "Top" @@ -1282,37 +1266,35 @@ msgstr "" #: editor/editor_help.cpp editor/property_editor.cpp msgid "Class:" -msgstr "" +msgstr "TÅ™Ãda:" #: editor/editor_help.cpp editor/scene_tree_editor.cpp msgid "Inherits:" -msgstr "" +msgstr "DÄ›dà z:" #: editor/editor_help.cpp msgid "Inherited by:" -msgstr "" +msgstr "DÄ›dÄ›ná z:" #: editor/editor_help.cpp msgid "Brief Description:" -msgstr "" +msgstr "StruÄný popis:" #: editor/editor_help.cpp -#, fuzzy msgid "Members" -msgstr "ÄŒlenové:" +msgstr "ÄŒlenové" #: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp msgid "Members:" msgstr "ÄŒlenové:" #: editor/editor_help.cpp -#, fuzzy msgid "Public Methods" -msgstr "Vybrat vÅ¡e" +msgstr "VeÅ™ejné metody" #: editor/editor_help.cpp msgid "Public Methods:" -msgstr "" +msgstr "VeÅ™ejné metody:" #: editor/editor_help.cpp msgid "GUI Theme Items" @@ -1327,36 +1309,32 @@ msgid "Signals:" msgstr "Signály:" #: editor/editor_help.cpp -#, fuzzy msgid "Enumerations" -msgstr "Funkce:" +msgstr "VýÄty" #: editor/editor_help.cpp -#, fuzzy msgid "Enumerations:" -msgstr "Funkce:" +msgstr "VýÄty:" #: editor/editor_help.cpp msgid "enum " -msgstr "" +msgstr "výÄet " #: editor/editor_help.cpp -#, fuzzy msgid "Constants" -msgstr "KonstantnÃ" +msgstr "Konstanty" #: editor/editor_help.cpp msgid "Constants:" -msgstr "" +msgstr "Konstanty:" #: editor/editor_help.cpp -#, fuzzy msgid "Description" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "Popis" #: editor/editor_help.cpp msgid "Online Tutorials:" -msgstr "" +msgstr "Online návody:" #: editor/editor_help.cpp msgid "" @@ -1364,40 +1342,45 @@ msgid "" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" +"V souÄasné dobÄ› pro tuto tÅ™Ãdu neexistujà žádné návody, můžete nÄ›jaký [color=" +"$color][url=$url]vytvoÅ™it[/url][/color] nebo o nÄ›j [color=$color][url=" +"$url2]zažádat[/url][/color]." #: editor/editor_help.cpp msgid "Properties" -msgstr "" +msgstr "Vlastnosti" #: editor/editor_help.cpp -#, fuzzy msgid "Property Description:" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "Popis vlastnosti:" #: editor/editor_help.cpp msgid "" "There is currently no description for this property. Please help us by " "[color=$color][url=$url]contributing one[/url][/color]!" msgstr "" +"V souÄasné dobÄ› neexistuje žádný popis pro tuto vlastnost. ProsÃm pomozte " +"nám tÃm, že ho[color=$color][url=$url]vytvoÅ™Ãte[/url][/color]!" #: editor/editor_help.cpp -#, fuzzy msgid "Methods" -msgstr "Seznam metod:" +msgstr "Metody" #: editor/editor_help.cpp msgid "Method Description:" -msgstr "" +msgstr "Popis metody:" #: editor/editor_help.cpp msgid "" "There is currently no description for this method. Please help us by [color=" "$color][url=$url]contributing one[/url][/color]!" msgstr "" +"V souÄasné dobÄ› neexistuje žádný popis pro tuto metodu. ProsÃm pomozte nám " +"tÃm, že ho [color=$color][url=$url]vytvoÅ™Ãte[/url][/color]!" #: editor/editor_help.cpp msgid "Search Text" -msgstr "" +msgstr "Prohledat text" #: editor/editor_help.cpp msgid "Find" @@ -1415,9 +1398,8 @@ msgid "Clear" msgstr "VyÄistit" #: editor/editor_log.cpp -#, fuzzy msgid "Clear Output" -msgstr "Výstup" +msgstr "Vymazat výstup" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" @@ -1430,67 +1412,63 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp #: editor/scene_tree_dock.cpp msgid "I see.." -msgstr "" +msgstr "Chápu.." #: editor/editor_node.cpp msgid "Can't open file for writing:" -msgstr "" +msgstr "Nelze otevÅ™Ãt soubor pro zápis:" #: editor/editor_node.cpp msgid "Requested file format unknown:" -msgstr "" +msgstr "Žádaný formát souboru je neznámý:" #: editor/editor_node.cpp msgid "Error while saving." -msgstr "" +msgstr "Chyba pÅ™i ukládánÃ." #: editor/editor_node.cpp -#, fuzzy msgid "Can't open '%s'." -msgstr "PÅ™ipojit.." +msgstr "Nelze otevÅ™Ãt '%s'." #: editor/editor_node.cpp -#, fuzzy msgid "Error while parsing '%s'." -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba pÅ™i parsovánà '%s'." #: editor/editor_node.cpp msgid "Unexpected end of file '%s'." -msgstr "" +msgstr "NeoÄekávaný konec souboru '%s'." #: editor/editor_node.cpp msgid "Missing '%s' or its dependencies." -msgstr "" +msgstr "Chybà '%s' nebo jeho závislosti." #: editor/editor_node.cpp -#, fuzzy msgid "Error while loading '%s'." -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba pÅ™i nahrávánà '%s'." #: editor/editor_node.cpp msgid "Saving Scene" -msgstr "" +msgstr "Ukládám scénu" #: editor/editor_node.cpp msgid "Analyzing" -msgstr "" +msgstr "Analyzuji" #: editor/editor_node.cpp msgid "Creating Thumbnail" -msgstr "" +msgstr "VytvářÃm náhled" #: editor/editor_node.cpp msgid "This operation can't be done without a tree root." msgstr "" #: editor/editor_node.cpp -#, fuzzy msgid "" "Couldn't save scene. Likely dependencies (instances or inheritance) couldn't " "be satisfied." msgstr "" "NepodaÅ™ilo se uložit scénu. NejspÃÅ¡e se nepodaÅ™ilo uspokojit závislosti " -"(instance)." +"(instance nebo dÄ›diÄnosti)." #: editor/editor_node.cpp msgid "Failed to load resource." @@ -1526,7 +1504,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Restored default layout to base settings." -msgstr "" +msgstr "Obnoveno výchozà rozloženà na základnà nastavenÃ." #: editor/editor_node.cpp msgid "" @@ -1564,19 +1542,19 @@ msgstr "" #: editor/editor_node.cpp msgid "Expand all properties" -msgstr "" +msgstr "Rozbalit vÅ¡echny vlastnosti" #: editor/editor_node.cpp msgid "Collapse all properties" -msgstr "" +msgstr "Sbalit vÅ¡echny vlastnosti" #: editor/editor_node.cpp msgid "Copy Params" -msgstr "" +msgstr "KopÃrovat parametry" #: editor/editor_node.cpp msgid "Paste Params" -msgstr "" +msgstr "Vložit parametry" #: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp msgid "Paste Resource" @@ -1600,7 +1578,7 @@ msgstr "OtevÅ™Ãt v nápovÄ›dÄ›" #: editor/editor_node.cpp msgid "There is no defined scene to run." -msgstr "" +msgstr "Neexistuje žádná scéna pro spuÅ¡tÄ›nÃ." #: editor/editor_node.cpp msgid "" @@ -1625,11 +1603,11 @@ msgstr "" #: editor/editor_node.cpp msgid "Current scene was never saved, please save it prior to running." -msgstr "" +msgstr "Aktuálnà scéna nebyla nikdy uložena, prosÃm uložte jà pÅ™ed spuÅ¡tÄ›nÃm." #: editor/editor_node.cpp msgid "Could not start subprocess!" -msgstr "" +msgstr "Nelze spustit podproces!" #: editor/editor_node.cpp msgid "Open Scene" @@ -1637,7 +1615,7 @@ msgstr "OtevÅ™Ãt scénu" #: editor/editor_node.cpp msgid "Open Base Scene" -msgstr "" +msgstr "OtevÅ™Ãt základnà scénu" #: editor/editor_node.cpp msgid "Quick Open Scene.." @@ -1648,13 +1626,12 @@ msgid "Quick Open Script.." msgstr "Rychlé otevÅ™enà skriptu.." #: editor/editor_node.cpp -#, fuzzy msgid "Save & Close" -msgstr "Uložit soubor" +msgstr "Uložit a zavÅ™Ãt" #: editor/editor_node.cpp msgid "Save changes to '%s' before closing?" -msgstr "" +msgstr "Uložit zmÄ›ny '%s' pÅ™ed zavÅ™enÃm?" #: editor/editor_node.cpp msgid "Save Scene As.." @@ -1662,23 +1639,23 @@ msgstr "Uložit scénu jako.." #: editor/editor_node.cpp msgid "No" -msgstr "" +msgstr "Ne" #: editor/editor_node.cpp msgid "Yes" -msgstr "" +msgstr "Ano" #: editor/editor_node.cpp msgid "This scene has never been saved. Save before running?" -msgstr "" +msgstr "Tato scéna nebyla nikdy uložena. Uložit pÅ™ed spuÅ¡tÄ›nÃm?" #: editor/editor_node.cpp editor/scene_tree_dock.cpp msgid "This operation can't be done without a scene." -msgstr "" +msgstr "Tato operace nemůže být provedena bez scény." #: editor/editor_node.cpp msgid "Export Mesh Library" -msgstr "" +msgstr "Exportovat Mesh Library" #: editor/editor_node.cpp msgid "This operation can't be done without a root node." @@ -1686,7 +1663,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Export Tile Set" -msgstr "" +msgstr "Exportovat Tile Set" #: editor/editor_node.cpp msgid "This operation can't be done without a selected node." @@ -1694,27 +1671,27 @@ msgstr "" #: editor/editor_node.cpp msgid "Current scene not saved. Open anyway?" -msgstr "" +msgstr "Aktuálnà scéna neuložena. PÅ™esto otevÅ™Ãt?" #: editor/editor_node.cpp msgid "Can't reload a scene that was never saved." -msgstr "" +msgstr "Nelze naÄÃst scénu, která nebyla nikdy uložena." #: editor/editor_node.cpp msgid "Revert" -msgstr "" +msgstr "Vrátit zpÄ›t" #: editor/editor_node.cpp msgid "This action cannot be undone. Revert anyway?" -msgstr "" +msgstr "Tuto akci nelze vrátit zpÄ›t. PokraÄovat?" #: editor/editor_node.cpp msgid "Quick Run Scene.." -msgstr "" +msgstr "Rychlé spuÅ¡tÄ›nà scény..." #: editor/editor_node.cpp msgid "Quit" -msgstr "" +msgstr "UkonÄit" #: editor/editor_node.cpp msgid "Exit the editor?" @@ -1722,43 +1699,45 @@ msgstr "UkonÄit editor?" #: editor/editor_node.cpp msgid "Open Project Manager?" -msgstr "" +msgstr "OtevÅ™Ãt Správce projektu?" #: editor/editor_node.cpp -#, fuzzy msgid "Save & Quit" -msgstr "Uložit soubor" +msgstr "Uložit a ukonÄit" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before quitting?" -msgstr "" +msgstr "Uložit zmÄ›ny následujÃcÃch scén pÅ™ed ukonÄenÃm?" #: editor/editor_node.cpp msgid "Save changes the following scene(s) before opening Project Manager?" -msgstr "" +msgstr "Uložit zmÄ›ny následujÃcÃch scén pÅ™ed otevÅ™enÃm Správce projektu?" #: editor/editor_node.cpp msgid "" "This option is deprecated. Situations where refresh must be forced are now " "considered a bug. Please report." msgstr "" +"Tato možnost se již nepoužÃvá. Situace, kdy obnova musà být vynucena jsou " +"nynà považovány za chybu. ProsÃm nahlaÅ¡te." #: editor/editor_node.cpp msgid "Pick a Main Scene" -msgstr "" +msgstr "Vybrat hlavnà scénu" #: editor/editor_node.cpp msgid "Unable to enable addon plugin at: '%s' parsing of config failed." msgstr "" +"Nelze povolit rozÅ¡iÅ™ujÃcà plugin: '%s' parsovánà konfigurace se nezdaÅ™ilo." #: editor/editor_node.cpp msgid "Unable to find script field for addon plugin at: 'res://addons/%s'." msgstr "" +"Nelze najÃt záznam skriptu pro rozÅ¡iÅ™ujÃcà plugin v: 'res://addons/%s'." #: editor/editor_node.cpp -#, fuzzy msgid "Unable to load addon script from path: '%s'." -msgstr "Chyba nahrávánà fontu." +msgstr "Nelze naÄÃst skript rozÅ¡ÃÅ™enà z cesty: '%s'." #: editor/editor_node.cpp msgid "" @@ -1770,6 +1749,8 @@ msgstr "" #: editor/editor_node.cpp msgid "Unable to load addon script from path: '%s' Script is not in tool mode." msgstr "" +"Nelze naÄÃst skript rozÅ¡ÃÅ™enà z cesty: '%s'. Skript nenà v režimu nástroje " +"(tool)." #: editor/editor_node.cpp msgid "" @@ -1780,92 +1761,92 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Ugh" -msgstr "" +msgstr "Ups" #: editor/editor_node.cpp msgid "" "Error loading scene, it must be inside the project path. Use 'Import' to " "open the scene, then save it inside the project path." msgstr "" +"Chyba pÅ™i nahrávánà scény, musà být v cestÄ› projektu. POužijte 'Importovat' " +"k otevÅ™enà scény, pak ji uložte uvnitÅ™ projektu." #: editor/editor_node.cpp msgid "Scene '%s' has broken dependencies:" -msgstr "" +msgstr "Scéna '%s' má rozbité závislosti:" #: editor/editor_node.cpp msgid "Clear Recent Scenes" -msgstr "" +msgstr "Vymazat nedávné scény" #: editor/editor_node.cpp msgid "Save Layout" -msgstr "" +msgstr "Uložit rozloženÃ" #: editor/editor_node.cpp msgid "Delete Layout" -msgstr "" +msgstr "Odstranit rozloženÃ" #: editor/editor_node.cpp editor/import_dock.cpp #: editor/script_create_dialog.cpp msgid "Default" -msgstr "" +msgstr "VýchozÃ" #: editor/editor_node.cpp msgid "Switch Scene Tab" -msgstr "" +msgstr "PÅ™epnout záložku scény" #: editor/editor_node.cpp msgid "%d more files or folders" -msgstr "" +msgstr "%d vÃce souborů nebo složek" #: editor/editor_node.cpp -#, fuzzy msgid "%d more folders" -msgstr "Nelze vytvoÅ™it složku." +msgstr "%d vÃce složek" #: editor/editor_node.cpp msgid "%d more files" -msgstr "" +msgstr "%d vÃce souborů" #: editor/editor_node.cpp msgid "Dock Position" -msgstr "" +msgstr "Pozice doku" #: editor/editor_node.cpp msgid "Distraction Free Mode" -msgstr "" +msgstr "NerozptylujÃcà režim" #: editor/editor_node.cpp msgid "Toggle distraction-free mode." -msgstr "" +msgstr "Zapnout nerozptylujÃcà režim." #: editor/editor_node.cpp -#, fuzzy msgid "Add a new scene." -msgstr "PÅ™idat nové stopy." +msgstr "PÅ™idat novou scénu." #: editor/editor_node.cpp msgid "Scene" -msgstr "" +msgstr "Scéna" #: editor/editor_node.cpp msgid "Go to previously opened scene." -msgstr "" +msgstr "PÅ™ejÃt na pÅ™edchozà scénu." #: editor/editor_node.cpp msgid "Next tab" -msgstr "" +msgstr "DalÅ¡Ã záložka" #: editor/editor_node.cpp msgid "Previous tab" -msgstr "" +msgstr "PÅ™edchozà záložka" #: editor/editor_node.cpp msgid "Filter Files.." -msgstr "" +msgstr "Filtrovat soubory..." #: editor/editor_node.cpp msgid "Operations with scene files." -msgstr "" +msgstr "Možnostà scén." #: editor/editor_node.cpp msgid "New Scene" @@ -1885,7 +1866,7 @@ msgstr "Uložit scénu" #: editor/editor_node.cpp msgid "Save all Scenes" -msgstr "" +msgstr "Uložit vÅ¡echny scény" #: editor/editor_node.cpp msgid "Close Scene" @@ -1901,11 +1882,11 @@ msgstr "Konvertovat na.." #: editor/editor_node.cpp msgid "MeshLibrary.." -msgstr "" +msgstr "MeshLibrary.." #: editor/editor_node.cpp msgid "TileSet.." -msgstr "" +msgstr "TileSet.." #: editor/editor_node.cpp editor/plugins/script_text_editor.cpp #: scene/gui/line_edit.cpp scene/gui/text_edit.cpp @@ -1919,16 +1900,15 @@ msgstr "Znovu" #: editor/editor_node.cpp msgid "Revert Scene" -msgstr "" +msgstr "Vrátit scénu" #: editor/editor_node.cpp msgid "Miscellaneous project or scene-wide tools." -msgstr "" +msgstr "Různé nástroje pro projekt nebo scény." #: editor/editor_node.cpp -#, fuzzy msgid "Project" -msgstr "Nastavenà projektu" +msgstr "Projekt" #: editor/editor_node.cpp msgid "Project Settings" @@ -1940,11 +1920,11 @@ msgstr "Spustit skript" #: editor/editor_node.cpp editor/project_export.cpp msgid "Export" -msgstr "" +msgstr "Exportovat" #: editor/editor_node.cpp msgid "Tools" -msgstr "" +msgstr "Nástroje" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -1956,13 +1936,15 @@ msgstr "LadÄ›nÃ" #: editor/editor_node.cpp msgid "Deploy with Remote Debug" -msgstr "" +msgstr "Nasazenà se vzdáleným ladÄ›nÃm" #: editor/editor_node.cpp msgid "" "When exporting or deploying, the resulting executable will attempt to " "connect to the IP of this computer in order to be debugged." msgstr "" +"PÅ™i exportu nebo nasazenÃ, se výsledný spustitelný soubor pokusà pÅ™ipojit k " +"IP tohoto poÄÃtaÄe, aby ho bylo možné ladit." #: editor/editor_node.cpp msgid "Small Deploy with Network FS" @@ -1977,6 +1959,11 @@ msgid "" "On Android, deploy will use the USB cable for faster performance. This " "option speeds up testing for games with a large footprint." msgstr "" +"Když je tato možnost povolena, export nebo nasazenà bude vytvářet minimálnà " +"spustitelný soubor.\n" +"Souborový systém bude poskytnut editorem projektu pÅ™es sÃt.\n" +"Pro nasazenà na Android bude použÃt USB kabel pro dosaženà vyÅ¡Å¡Ãho výkonu. " +"Tato možnost urychluje testovánà objemných her." #: editor/editor_node.cpp msgid "Visible Collision Shapes" @@ -2000,7 +1987,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Sync Scene Changes" -msgstr "" +msgstr "Synchronizovat zmÄ›ny scény" #: editor/editor_node.cpp msgid "" @@ -2009,10 +1996,14 @@ msgid "" "When used remotely on a device, this is more efficient with network " "filesystem." msgstr "" +"Když je zapnuta tato možnost, vÅ¡echny zmÄ›ny provedené ve scénÄ› v editoru " +"budou replikovány v běžÃcà hÅ™e.\n" +"PÅ™i použità se vzdáleným spuÅ¡tÄ›nÃm je toto vÃce efektivnà pÅ™i použità " +"sÃÅ¥ového souborového systému." #: editor/editor_node.cpp msgid "Sync Script Changes" -msgstr "" +msgstr "Synchornizace zmÄ›n skriptu" #: editor/editor_node.cpp msgid "" @@ -2021,6 +2012,10 @@ msgid "" "When used remotely on a device, this is more efficient with network " "filesystem." msgstr "" +"Když je zapnuta tato volba, jakýkoliv skript, který je uložen bude znovu " +"nahrán do spuÅ¡tÄ›né hry.\n" +"PÅ™i použità se vzdáleným spuÅ¡tÄ›nÃm je toto vÃce efektivnà pÅ™i použità " +"sÃÅ¥ového souborového systému." #: editor/editor_node.cpp msgid "Editor" @@ -2048,7 +2043,7 @@ msgstr "NápovÄ›da" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Classes" -msgstr "" +msgstr "TÅ™Ãdy" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp #: editor/plugins/script_editor_plugin.cpp @@ -2059,15 +2054,16 @@ msgstr "Hledat" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Online Docs" -msgstr "" +msgstr "Online dokumentace" #: editor/editor_node.cpp msgid "Q&A" -msgstr "" +msgstr "Q&A" #: editor/editor_node.cpp +#, fuzzy msgid "Issue Tracker" -msgstr "" +msgstr "Správa chyb" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp msgid "Community" @@ -2075,68 +2071,67 @@ msgstr "Komunita" #: editor/editor_node.cpp msgid "About" -msgstr "" +msgstr "O aplikaci" #: editor/editor_node.cpp msgid "Play the project." -msgstr "" +msgstr "Spustit projekt." #: editor/editor_node.cpp msgid "Play" -msgstr "" +msgstr "Spustit" #: editor/editor_node.cpp msgid "Pause the scene" -msgstr "" +msgstr "Pozastavit scénu" #: editor/editor_node.cpp msgid "Pause Scene" -msgstr "" +msgstr "Pozastavit scénu" #: editor/editor_node.cpp msgid "Stop the scene." -msgstr "" +msgstr "Zastavit scénu." #: editor/editor_node.cpp msgid "Stop" -msgstr "" +msgstr "Zastavit" #: editor/editor_node.cpp msgid "Play the edited scene." -msgstr "" +msgstr "Spustit upravenou scénu." #: editor/editor_node.cpp msgid "Play Scene" -msgstr "" +msgstr "Spustit scénu" #: editor/editor_node.cpp msgid "Play custom scene" msgstr "PÅ™ehrát vlastnà scénu" #: editor/editor_node.cpp -#, fuzzy msgid "Play Custom Scene" msgstr "PÅ™ehrát vlastnà scénu" #: editor/editor_node.cpp msgid "Spins when the editor window repaints!" -msgstr "" +msgstr "ToÄà se, když se okno pÅ™ekresluje!" #: editor/editor_node.cpp msgid "Update Always" -msgstr "" +msgstr "Aktualizovat vždy" #: editor/editor_node.cpp msgid "Update Changes" -msgstr "" +msgstr "Akualizovat zmÄ›ny" #: editor/editor_node.cpp msgid "Disable Update Spinner" -msgstr "" +msgstr "Vypnout aktualizaÄnà koleÄko" #: editor/editor_node.cpp msgid "Inspector" -msgstr "" +msgstr "Inspektor" #: editor/editor_node.cpp msgid "Create a new resource in memory and edit it." @@ -2152,7 +2147,7 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Save As.." -msgstr "" +msgstr "Uložit jako.." #: editor/editor_node.cpp msgid "Go to the previous edited object in history." @@ -2164,20 +2159,20 @@ msgstr "" #: editor/editor_node.cpp msgid "History of recently edited objects." -msgstr "" +msgstr "Historie naposledy upravených objektů." #: editor/editor_node.cpp msgid "Object properties." -msgstr "" +msgstr "Vlastnosti objektu." #: editor/editor_node.cpp msgid "Changes may be lost!" -msgstr "" +msgstr "ZmÄ›ny mohou být ztraceny!" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp #: editor/project_manager.cpp msgid "Import" -msgstr "" +msgstr "Importovat" #: editor/editor_node.cpp msgid "Node" @@ -2185,7 +2180,7 @@ msgstr "" #: editor/editor_node.cpp msgid "FileSystem" -msgstr "" +msgstr "Souborový systém" #: editor/editor_node.cpp msgid "Output" @@ -2193,7 +2188,7 @@ msgstr "Výstup" #: editor/editor_node.cpp msgid "Don't Save" -msgstr "" +msgstr "Neukládat" #: editor/editor_node.cpp msgid "Import Templates From ZIP File" @@ -2201,63 +2196,59 @@ msgstr "Importovat Å¡ablony ze ZIP souboru" #: editor/editor_node.cpp editor/project_export.cpp msgid "Export Project" -msgstr "" +msgstr "Exportovat projekt" #: editor/editor_node.cpp msgid "Export Library" -msgstr "" +msgstr "Exportovat knihovnu" #: editor/editor_node.cpp msgid "Merge With Existing" -msgstr "" +msgstr "SlouÄit s existujÃcÃ" #: editor/editor_node.cpp msgid "Password:" -msgstr "" +msgstr "Heslo:" #: editor/editor_node.cpp msgid "Open & Run a Script" -msgstr "" +msgstr "OtevÅ™Ãt a spustit skript" #: editor/editor_node.cpp msgid "New Inherited" -msgstr "" +msgstr "Nové zdÄ›dÄ›né" #: editor/editor_node.cpp msgid "Load Errors" -msgstr "" +msgstr "NaÄÃst chyby" #: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp msgid "Select" -msgstr "" +msgstr "Vybrat" #: editor/editor_node.cpp -#, fuzzy msgid "Open 2D Editor" -msgstr "OtevÅ™Ãt složku" +msgstr "OtevÅ™Ãt 2D editor" #: editor/editor_node.cpp -#, fuzzy msgid "Open 3D Editor" -msgstr "OtevÅ™Ãt složku" +msgstr "OtevÅ™Ãt 3D editor" #: editor/editor_node.cpp -#, fuzzy msgid "Open Script Editor" -msgstr "Editor závislostÃ" +msgstr "OtevÅ™Ãt editor skriptů" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" msgstr "" #: editor/editor_node.cpp -#, fuzzy msgid "Open the next Editor" -msgstr "Editor závislostÃ" +msgstr "OtevÅ™Ãt dalÅ¡Ã editor" #: editor/editor_node.cpp msgid "Open the previous Editor" -msgstr "" +msgstr "OtevÅ™Ãt pÅ™edchozà editor" #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -2265,60 +2256,62 @@ msgstr "" #: editor/editor_plugin.cpp msgid "Thumbnail.." -msgstr "" +msgstr "Náhled.." #: editor/editor_plugin_settings.cpp msgid "Installed Plugins:" -msgstr "" +msgstr "Nainstalované pluginy:" #: editor/editor_plugin_settings.cpp msgid "Update" -msgstr "" +msgstr "Aktualizovat" #: editor/editor_plugin_settings.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Version:" -msgstr "" +msgstr "Verze:" #: editor/editor_plugin_settings.cpp msgid "Author:" -msgstr "" +msgstr "Autor:" #: editor/editor_plugin_settings.cpp msgid "Status:" -msgstr "" +msgstr "Stav:" #: editor/editor_profiler.cpp msgid "Stop Profiling" -msgstr "" +msgstr "Zastavit profilovánÃ" #: editor/editor_profiler.cpp msgid "Start Profiling" -msgstr "" +msgstr "Spustit profilovánÃ" #: editor/editor_profiler.cpp msgid "Measure:" -msgstr "" +msgstr "MěřenÃ:" #: editor/editor_profiler.cpp msgid "Frame Time (sec)" -msgstr "" +msgstr "ÄŒas snÃmku (sek.)" #: editor/editor_profiler.cpp msgid "Average Time (sec)" -msgstr "" +msgstr "PrůmÄ›rný Äas (sek.)" #: editor/editor_profiler.cpp +#, fuzzy msgid "Frame %" -msgstr "" +msgstr "% snÃmku" #: editor/editor_profiler.cpp +#, fuzzy msgid "Physics Frame %" -msgstr "" +msgstr "% fyzikálnÃho snÃmku" #: editor/editor_profiler.cpp editor/script_editor_debugger.cpp msgid "Time:" -msgstr "" +msgstr "ÄŒas:" #: editor/editor_profiler.cpp msgid "Inclusive" @@ -2329,43 +2322,46 @@ msgid "Self" msgstr "" #: editor/editor_profiler.cpp +#, fuzzy msgid "Frame #:" -msgstr "" +msgstr "SnÃmek Ä.:" #: editor/editor_profiler.cpp msgid "Time" -msgstr "" +msgstr "ÄŒas" #: editor/editor_profiler.cpp -#, fuzzy msgid "Calls" -msgstr "Zavolat" +msgstr "VolánÃ" #: editor/editor_run_native.cpp msgid "Select device from the list" -msgstr "" +msgstr "Vyberte zaÅ™Ãzenà ze seznamu" #: editor/editor_run_native.cpp msgid "" "No runnable export preset found for this platform.\n" "Please add a runnable preset in the export menu." msgstr "" +"Nenalezen žádný spustitelný preset pro export pro tuto platformu.\n" +"rosÃm pÅ™idejte spustitelný preset v menu exportu." #: editor/editor_run_script.cpp msgid "Write your logic in the _run() method." -msgstr "" +msgstr "NapiÅ¡te svůj kód v _run() metodÄ›." #: editor/editor_run_script.cpp +#, fuzzy msgid "There is an edited scene already." -msgstr "" +msgstr "NÄ›jaka scéna už je upravována." #: editor/editor_run_script.cpp msgid "Couldn't instance script:" -msgstr "" +msgstr "NepodaÅ™ilo se instancovat skript:" #: editor/editor_run_script.cpp msgid "Did you forget the 'tool' keyword?" -msgstr "" +msgstr "NezapomnÄ›li jste na klÃÄové slovo 'tool'?" #: editor/editor_run_script.cpp msgid "Couldn't run script:" @@ -2443,9 +2439,8 @@ msgid "No version.txt found inside templates." msgstr "Nenalezena version.txt uvnitÅ™ Å¡ablon." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for templates:" -msgstr "Chyba pÅ™i vytvářenà cesty pro Å¡ablony:\n" +msgstr "Chyba pÅ™i vytvářenà cesty pro Å¡ablony:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -2468,9 +2463,8 @@ msgstr "" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Can't connect." -msgstr "PÅ™ipojit.." +msgstr "Nelze se pÅ™ipojit." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2479,9 +2473,8 @@ msgstr "" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Request Failed." -msgstr "Testované" +msgstr "Požadavek se nezdaÅ™il." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2494,28 +2487,25 @@ msgid "Failed:" msgstr "" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't write file." -msgstr "Nelze vytvoÅ™it složku." +msgstr "Nelze zapsat soubor." #: editor/export_template_manager.cpp msgid "Download Complete." msgstr "" #: editor/export_template_manager.cpp -#, fuzzy msgid "Error requesting url: " -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba požadavku o url: " #: editor/export_template_manager.cpp #, fuzzy msgid "Connecting to Mirror.." -msgstr "PÅ™ipojit.." +msgstr "PÅ™ipojuji se k mirroru.." #: editor/export_template_manager.cpp -#, fuzzy msgid "Disconnected" -msgstr "Odpojit" +msgstr "Odpojeno" #: editor/export_template_manager.cpp msgid "Resolving" @@ -2527,35 +2517,30 @@ msgstr "" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Connecting.." -msgstr "PÅ™ipojit.." +msgstr "PÅ™ipojuji.." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't Connect" -msgstr "PÅ™ipojit.." +msgstr "Nelze se pÅ™ipojit" #: editor/export_template_manager.cpp #, fuzzy msgid "Connected" -msgstr "PÅ™ipojit" +msgstr "PÅ™ipojeno" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Requesting.." -msgstr "Testované" +msgstr "PosÃlá se žádost.." #: editor/export_template_manager.cpp -#, fuzzy msgid "Downloading" -msgstr "Chyba pÅ™i naÄÃtánÃ:" +msgstr "Stahuji" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connection Error" -msgstr "PÅ™ipojit.." +msgstr "Chyba pÅ™ipojenÃ" #: editor/export_template_manager.cpp msgid "SSL Handshake Error" @@ -2574,14 +2559,12 @@ msgid "Install From File" msgstr "" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove Template" -msgstr "Odstranit výbÄ›r" +msgstr "Odstranit Å¡ablonu" #: editor/export_template_manager.cpp -#, fuzzy msgid "Select template file" -msgstr "Odstranit vybrané soubory?" +msgstr "Vybrat soubor Å¡ablony" #: editor/export_template_manager.cpp msgid "Export Template Manager" @@ -2624,19 +2607,16 @@ msgid "Cannot move a folder into itself." msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "Chyba pÅ™i naÄÃtánÃ:" +msgstr "Chyba pÅ™esouvánÃ:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error duplicating:" -msgstr "Chyba pÅ™i naÄÃtánÃ:" +msgstr "Chyba duplikovánÃ:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Unable to update dependencies:" -msgstr "Scénu se nepodaÅ™ilo naÄÃst kvůli chybÄ›jÃcÃm závislostem:" +msgstr "NepodaÅ™ilo se aktualizovat závisloti:" #: editor/filesystem_dock.cpp msgid "No name provided" @@ -2651,27 +2631,24 @@ msgid "No name provided." msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Name contains invalid characters." -msgstr "Platné znaky:" +msgstr "Jméno obsahuje neplatné znaky." #: editor/filesystem_dock.cpp msgid "A file or folder with this name already exists." msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Renaming file:" -msgstr "PÅ™ejmenovat promÄ›nnou" +msgstr "PÅ™ejmenovávánà souboru:" #: editor/filesystem_dock.cpp msgid "Renaming folder:" msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicating file:" -msgstr "PÅ™ejmenovat promÄ›nnou" +msgstr "Duplikace souboru:" #: editor/filesystem_dock.cpp msgid "Duplicating folder:" @@ -2711,9 +2688,8 @@ msgid "View Owners.." msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicate.." -msgstr "Animace: duplikovat klÃÄe" +msgstr "Duplikovat.." #: editor/filesystem_dock.cpp msgid "Previous Directory" @@ -2844,21 +2820,20 @@ msgid "Clear Default for '%s'" msgstr "" #: editor/import_dock.cpp -#, fuzzy msgid " Files" -msgstr "Soubor:" +msgstr " Soubory" #: editor/import_dock.cpp msgid "Import As:" -msgstr "" +msgstr "Importovat jako:" #: editor/import_dock.cpp editor/property_editor.cpp msgid "Preset.." -msgstr "" +msgstr "Preset.." #: editor/import_dock.cpp msgid "Reimport" -msgstr "" +msgstr "Znovu importovat" #: editor/multi_node_edit.cpp msgid "MultiNode Set" @@ -2866,7 +2841,7 @@ msgstr "" #: editor/node_dock.cpp msgid "Groups" -msgstr "" +msgstr "Skupiny" #: editor/node_dock.cpp msgid "Select a Node to edit Signals and Groups." @@ -2912,7 +2887,7 @@ msgstr "" #: editor/plugins/abstract_polygon_2d_editor.cpp #, fuzzy msgid "Delete points" -msgstr "Odstranit" +msgstr "Odstranit body" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Toggle Autoplay" @@ -2931,32 +2906,31 @@ msgid "Change Animation Name:" msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp -#, fuzzy msgid "Delete Animation?" -msgstr "Optimalizovat animaci" +msgstr "Smazat animaci?" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Remove Animation" -msgstr "" +msgstr "Smazat animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: Invalid animation name!" -msgstr "" +msgstr "Chyba: Neplatné jméno animace!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: Animation name already exists!" -msgstr "" +msgstr "Chyba: Jméno animace už existuje!" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Rename Animation" -msgstr "" +msgstr "PÅ™ejmenovat animaci" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Add Animation" -msgstr "" +msgstr "PÅ™idat animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Blend Next Changed" @@ -2968,15 +2942,15 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load Animation" -msgstr "" +msgstr "NaÄÃst animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Duplicate Animation" -msgstr "" +msgstr "Duplikovat animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation to copy!" -msgstr "" +msgstr "ERROR: Nevybrána animace pro kopÃrovánÃ!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation resource on clipboard!" @@ -2984,15 +2958,15 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Pasted Animation" -msgstr "" +msgstr "Vložená animace" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Paste Animation" -msgstr "" +msgstr "Vložit animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation to edit!" -msgstr "" +msgstr "ERROR: Nevybrána animace pro úpravu!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation backwards from current pos. (A)" @@ -3004,15 +2978,15 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Stop animation playback. (S)" -msgstr "" +msgstr "Zastavit pÅ™ehrávánà animace. (S)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation from start. (Shift+D)" -msgstr "" +msgstr "PÅ™ehrát vybranou animaci od zaÄátku. (Shift+D)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation from current pos. (D)" -msgstr "" +msgstr "PÅ™ehrát vybranou animaci od vybrané pozice. (D)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation position (in seconds)." @@ -3028,15 +3002,15 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load animation from disk." -msgstr "" +msgstr "NaÄÃst animaci z disku." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load an animation from disk." -msgstr "" +msgstr "NaÄÃst animaci z disku." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Save the current animation" -msgstr "" +msgstr "Uložit vybranou animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Display list of animations in player." @@ -3052,11 +3026,11 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation Tools" -msgstr "" +msgstr "Nástroje pro animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Copy Animation" -msgstr "" +msgstr "KopÃrovat animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Onion Skinning" @@ -3069,36 +3043,37 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp #, fuzzy msgid "Directions" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "SmÄ›ry" #: editor/plugins/animation_player_editor_plugin.cpp #, fuzzy msgid "Past" -msgstr "Vložit" +msgstr "Minulý" #: editor/plugins/animation_player_editor_plugin.cpp +#, fuzzy msgid "Future" -msgstr "" +msgstr "BudoucÃ" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Depth" -msgstr "" +msgstr "Hloubka" #: editor/plugins/animation_player_editor_plugin.cpp msgid "1 step" -msgstr "" +msgstr "1 krok" #: editor/plugins/animation_player_editor_plugin.cpp msgid "2 steps" -msgstr "" +msgstr "2 kroky" #: editor/plugins/animation_player_editor_plugin.cpp msgid "3 steps" -msgstr "" +msgstr "3 kroky" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Differences Only" -msgstr "" +msgstr "Pouze rozdÃly" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Force White Modulate" @@ -3110,18 +3085,18 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Create New Animation" -msgstr "" +msgstr "VytvoÅ™it novou animaci" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation Name:" -msgstr "" +msgstr "Jméno animace:" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/resource_preloader_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp #: editor/script_create_dialog.cpp msgid "Error!" -msgstr "" +msgstr "Chyba!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Blend Times:" @@ -3138,21 +3113,20 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Animation" -msgstr "" +msgstr "Animace" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "New name:" -msgstr "" +msgstr "Nové jméno:" #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "Edit Filters" -msgstr "Soubor:" +msgstr "Editovat filtry" #: editor/plugins/animation_tree_editor_plugin.cpp #: editor/plugins/multimesh_editor_plugin.cpp msgid "Scale:" -msgstr "" +msgstr "ZvÄ›tÅ¡enÃ:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Fade In (s):" @@ -3184,24 +3158,26 @@ msgstr "" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Start!" -msgstr "" +msgstr "Start!" #: editor/plugins/animation_tree_editor_plugin.cpp #: editor/plugins/multimesh_editor_plugin.cpp msgid "Amount:" -msgstr "" +msgstr "MnožstvÃ:" #: editor/plugins/animation_tree_editor_plugin.cpp +#, fuzzy msgid "Blend:" -msgstr "" +msgstr "ProlÃnánÃ:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend 0:" -msgstr "" +msgstr "ProlÃnánà 0:" #: editor/plugins/animation_tree_editor_plugin.cpp +#, fuzzy msgid "Blend 1:" -msgstr "" +msgstr "ProlÃnánà 1:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "X-Fade Time (s):" @@ -3209,11 +3185,11 @@ msgstr "" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Current:" -msgstr "" +msgstr "AktuálnÃ:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Add Input" -msgstr "" +msgstr "PÅ™idat vstup" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Clear Auto-Advance" @@ -3225,15 +3201,15 @@ msgstr "" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Delete Input" -msgstr "" +msgstr "Odstranit vstup" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Animation tree is valid." -msgstr "" +msgstr "Strom animace je platný." #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Animation tree is invalid." -msgstr "" +msgstr "Strom animace je neplatný." #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Animation Node" @@ -3273,7 +3249,7 @@ msgstr "" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Import Animations.." -msgstr "" +msgstr "Importovat animace.." #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Edit Node Filters" @@ -3281,7 +3257,7 @@ msgstr "" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Filters.." -msgstr "" +msgstr "Filtry.." #: editor/plugins/animation_tree_editor_plugin.cpp #, fuzzy @@ -3289,59 +3265,57 @@ msgid "AnimationTree" msgstr "PÅ™iblÞenà animace." #: editor/plugins/asset_library_editor_plugin.cpp +#, fuzzy msgid "Free" -msgstr "" +msgstr "Uvolnit" #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Contents:" -msgstr "Spojité" +msgstr "Obsah:" #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "View Files" -msgstr "Soubor:" +msgstr "Zobrazit soubory" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Can't resolve hostname:" -msgstr "" +msgstr "Nelze pÅ™eložit název hostitele:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Connection error, please try again." -msgstr "" +msgstr "Chyba pÅ™ipojenÃ, zkuste to prosÃm znovu." #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Can't connect to host:" -msgstr "PÅ™ipojit k uzlu:" +msgstr "Nelze se pÅ™ipojit k hostiteli:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "No response from host:" -msgstr "" +msgstr "Žádná odpovÄ›Ä od hostitele:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Request failed, return code:" -msgstr "" +msgstr "Požadavek se nezdaÅ™il, návratový kód:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Request failed, too many redirects" -msgstr "" +msgstr "Požadavek se nezdaÅ™il, pÅ™ÃliÅ¡ mnoho pÅ™esmÄ›rovánÃ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Bad download hash, assuming file has been tampered with." -msgstr "" +msgstr "Å patný hash staženého souboru, soubor byl nejspÃÅ¡e zfalÅ¡ován." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Expected:" -msgstr "" +msgstr "OÄekáváno:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Got:" -msgstr "" +msgstr "Staženo:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Failed sha256 hash check" -msgstr "" +msgstr "Neúspěšná kontrola sha256 hashe" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Asset Download Error:" @@ -3349,28 +3323,27 @@ msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Fetching:" -msgstr "" +msgstr "Stahuji:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Resolving.." -msgstr "" +msgstr "ZjiÅ¡Å¥ovánÃ.." #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Error making request" -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba pÅ™i vytvářenà požadavku" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Idle" -msgstr "" +msgstr "NeÄinný" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Retry" -msgstr "" +msgstr "Opakovat" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download Error" -msgstr "" +msgstr "Chyba pÅ™i stahovánÃ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download for this asset is already in progress!" @@ -3378,19 +3351,19 @@ msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp msgid "first" -msgstr "" +msgstr "prvnÃ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "prev" -msgstr "" +msgstr "pÅ™edchozÃ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "next" -msgstr "" +msgstr "následujÃcÃ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "last" -msgstr "" +msgstr "poslednÃ" #: editor/plugins/asset_library_editor_plugin.cpp #: modules/gdnative/gdnative_library_editor_plugin.cpp @@ -3400,7 +3373,7 @@ msgstr "VÅ¡echny" #: editor/plugins/asset_library_editor_plugin.cpp #: editor/project_settings_editor.cpp msgid "Plugins" -msgstr "" +msgstr "Pluginy" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Sort:" @@ -3453,17 +3426,18 @@ msgid "Failed creating lightmap images, make sure path is writable." msgstr "" #: editor/plugins/baked_lightmap_editor_plugin.cpp +#, fuzzy msgid "Bake Lightmaps" -msgstr "" +msgstr "Zapéct lightmapy" #: editor/plugins/camera_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Preview" -msgstr "" +msgstr "Náhled" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap" -msgstr "" +msgstr "Nastavenà pÅ™ichycovánÃ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp @@ -3493,64 +3467,62 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move vertical guide" -msgstr "" +msgstr "PÅ™esunout svislé vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Create new vertical guide" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "VytvoÅ™it nové svislé vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Remove vertical guide" -msgstr "Odstranit promÄ›nnou" +msgstr "Odstranit svislé vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move horizontal guide" -msgstr "" +msgstr "PÅ™esunout vodorovné vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Create new horizontal guide" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "VytvoÅ™it nové vodorovné vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Remove horizontal guide" -msgstr "Odstranit neplatné klÃÄe" +msgstr "Odstranit vodorovné vodÃtko" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Create new horizontal and vertical guides" -msgstr "" +msgstr "VytvoÅ™it nové vodorovné a svislé vodÃtka" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Edit IK Chain" -msgstr "" +msgstr "Upravit IK Å™etÄ›zec" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Edit CanvasItem" msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Anchors only" -msgstr "" +msgstr "Pouze kotvy" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Change Anchors and Margins" -msgstr "" +msgstr "Upravit kotvy a okraje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Change Anchors" -msgstr "" +msgstr "Upravit kotvy" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Paste Pose" msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Select Mode" -msgstr "Vybrat vÅ¡e" +msgstr "Režim výbÄ›ru" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Drag: Rotate" @@ -3570,11 +3542,11 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move Mode" -msgstr "" +msgstr "Režim pÅ™esouvánÃ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Rotate Mode" -msgstr "" +msgstr "Režim otáÄenÃ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -3592,21 +3564,21 @@ msgid "Pan Mode" msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Toggles snapping" -msgstr "PÅ™epnout breakpoint" +msgstr "PÅ™epnout pÅ™ichycovánÃ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Snap" -msgstr "" +msgstr "PoužÃt pÅ™ichycovánÃ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snapping options" -msgstr "" +msgstr "Možnosti pÅ™ichytávánÃ" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Snap to grid" -msgstr "" +msgstr "PÅ™ichytit k mřÞce" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Rotation Snap" @@ -3614,7 +3586,7 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap..." -msgstr "" +msgstr "Nastavenà pÅ™ichytávánÃ..." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap Relative" @@ -3689,28 +3661,30 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "View" -msgstr "" +msgstr "Zobrazit" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Show Grid" -msgstr "" +msgstr "Zobrazit mřÞku" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Show helpers" -msgstr "" +msgstr "Zobrazit pomocné" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Show rulers" -msgstr "" +msgstr "Zobrazit pravÃtka" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Show guides" -msgstr "" +msgstr "Zobrazit vodÃtka" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Center Selection" -msgstr "" +msgstr "Vycentrovat výbÄ›r" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Frame Selection" @@ -3718,15 +3692,17 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Layout" -msgstr "" +msgstr "RozloženÃ" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Insert Keys" -msgstr "" +msgstr "Vložit klÃÄe" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Insert Key" -msgstr "" +msgstr "Vložit klÃÄ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Insert Key (Existing Tracks)" @@ -3734,11 +3710,11 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Copy Pose" -msgstr "" +msgstr "KopÃrovat pózu" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Clear Pose" -msgstr "" +msgstr "Vymazat pózu" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Drag pivot from mouse position" @@ -3759,15 +3735,15 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" -msgstr "" +msgstr "PÅ™idat %s" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Adding %s..." -msgstr "" +msgstr "PÅ™idávám %s..." #: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Ok" -msgstr "" +msgstr "Ok" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." @@ -3781,12 +3757,11 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Error instancing scene from %s" -msgstr "" +msgstr "Chyba instancovánà scény z %s" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Change default type" -msgstr "ZmÄ›nit typ hodnot pole" +msgstr "ZmÄ›nit výchozà typ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "" @@ -3818,11 +3793,11 @@ msgstr "" #: editor/plugins/cube_grid_theme_editor_plugin.cpp msgid "Import from Scene" -msgstr "" +msgstr "Importovat ze scény" #: editor/plugins/cube_grid_theme_editor_plugin.cpp msgid "Update from Scene" -msgstr "" +msgstr "Aktualizovat ze scény" #: editor/plugins/curve_editor_plugin.cpp msgid "Flat0" @@ -3858,14 +3833,12 @@ msgid "Load Curve Preset" msgstr "" #: editor/plugins/curve_editor_plugin.cpp -#, fuzzy msgid "Add point" -msgstr "PÅ™idat signál" +msgstr "PÅ™idat bod" #: editor/plugins/curve_editor_plugin.cpp -#, fuzzy msgid "Remove point" -msgstr "Odstranit signál" +msgstr "Odstranit bod" #: editor/plugins/curve_editor_plugin.cpp #, fuzzy @@ -3878,12 +3851,11 @@ msgstr "" #: editor/plugins/curve_editor_plugin.cpp msgid "Load preset" -msgstr "" +msgstr "NaÄÃst preset" #: editor/plugins/curve_editor_plugin.cpp -#, fuzzy msgid "Remove Curve Point" -msgstr "Odstranit signál" +msgstr "Odstranit bod kÅ™ivky" #: editor/plugins/curve_editor_plugin.cpp msgid "Toggle Curve Linear Tangent" @@ -3912,7 +3884,7 @@ msgstr "" #: editor/plugins/item_list_editor_plugin.cpp msgid "Items" -msgstr "" +msgstr "Položky" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item List Editor" @@ -3934,23 +3906,23 @@ msgstr "" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Edit existing polygon:" -msgstr "" +msgstr "Upravit existujÃcà polygon:" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "LMB: Move Point." -msgstr "" +msgstr "LMB: PÅ™esunout bod." #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Ctrl+LMB: Split Segment." -msgstr "" +msgstr "Ctrl+LMB: RozdÄ›lit segment." #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "RMB: Erase Point." -msgstr "" +msgstr "RMB: Vymazat bod." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh is empty!" -msgstr "" +msgstr "Mesh je prázdný!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Static Trimesh Body" @@ -3994,23 +3966,23 @@ msgstr "" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" -msgstr "" +msgstr "MeshInstance nemá Mesh!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh has not surface to create outlines from!" -msgstr "" +msgstr "Mesh némá povrch z jakého vytvoÅ™it obrysy!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Could not create outline!" -msgstr "" +msgstr "Nelze vytvoÅ™it obrys!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline" -msgstr "" +msgstr "VytvoÅ™it obrys" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "" +msgstr "Mesh" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -4033,26 +4005,26 @@ msgid "Create Outline Mesh.." msgstr "" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "View UV1" -msgstr "Soubor:" +msgstr "Zobrazit UV1" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "View UV2" -msgstr "Soubor:" +msgstr "Zobrazit UV2" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Unwrap UV2 for Lightmap/AO" -msgstr "" +msgstr "Rozbalit UV2 pro Lightmapu/AO" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Outline Mesh" -msgstr "" +msgstr "VytvoÅ™it mesh obrysu" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Outline Size:" -msgstr "" +msgstr "Velikost obrysu:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "No mesh source specified (and no MultiMesh set in node)." @@ -4064,7 +4036,7 @@ msgstr "" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (invalid path)." -msgstr "" +msgstr "Zdroj meshe je neplatný (neplatná cesta)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (not a MeshInstance)." @@ -4448,7 +4420,7 @@ msgstr "" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Create UV Map" -msgstr "" +msgstr "VytvoÅ™it UV mapu" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Transform UV Map" @@ -4460,7 +4432,7 @@ msgstr "" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Move Point" -msgstr "" +msgstr "PÅ™esunout bod" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Ctrl: Rotate" @@ -4468,19 +4440,20 @@ msgstr "" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Shift: Move All" -msgstr "" +msgstr "Shift: PÅ™esunout vÅ¡e" #: editor/plugins/polygon_2d_editor_plugin.cpp +#, fuzzy msgid "Shift+Ctrl: Scale" -msgstr "" +msgstr "Shift+Ctrl: ZvÄ›tÅ¡enÃ" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Move Polygon" -msgstr "" +msgstr "PÅ™esunout polygon" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Rotate Polygon" -msgstr "" +msgstr "OtoÄit polygon" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Scale Polygon" @@ -4496,54 +4469,54 @@ msgstr "Upravit" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Polygon->UV" -msgstr "" +msgstr "Polygon->UV" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "UV->Polygon" -msgstr "" +msgstr "UV->Polygon" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Clear UV" -msgstr "" +msgstr "Vymazat UV" #: editor/plugins/polygon_2d_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Snap" -msgstr "" +msgstr "PÅ™ichytit" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Enable Snap" -msgstr "" +msgstr "Povolit pÅ™ichytávánÃ" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid" -msgstr "" +msgstr "MřÞka" #: editor/plugins/resource_preloader_editor_plugin.cpp msgid "ERROR: Couldn't load resource!" -msgstr "" +msgstr "Chyba: Nelze naÄÃst zdroj!" #: editor/plugins/resource_preloader_editor_plugin.cpp msgid "Add Resource" -msgstr "" +msgstr "PÅ™idat zdroj" #: editor/plugins/resource_preloader_editor_plugin.cpp msgid "Rename Resource" -msgstr "" +msgstr "PÅ™ejmenovat zdroj" #: editor/plugins/resource_preloader_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Delete Resource" -msgstr "" +msgstr "Smazat zdroj" #: editor/plugins/resource_preloader_editor_plugin.cpp msgid "Resource clipboard is empty!" -msgstr "" +msgstr "Schránka zdroje je prázdná!" #: editor/plugins/resource_preloader_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Load Resource" -msgstr "" +msgstr "NaÄÃst zdroj" #: editor/plugins/resource_preloader_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp @@ -4553,82 +4526,81 @@ msgid "Paste" msgstr "Vložit" #: editor/plugins/resource_preloader_editor_plugin.cpp -#, fuzzy msgid "ResourcePreloader" msgstr "Zdroj" #: editor/plugins/script_editor_plugin.cpp msgid "Clear Recent Files" -msgstr "" +msgstr "Vymazat nedávné soubory" #: editor/plugins/script_editor_plugin.cpp msgid "Close and save changes?" -msgstr "" +msgstr "ZavÅ™Ãt a uložit zmÄ›ny?" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme" -msgstr "" +msgstr "Chyba pÅ™i ukládánà motivu" #: editor/plugins/script_editor_plugin.cpp msgid "Error saving" -msgstr "" +msgstr "Chyba pÅ™i ukládánÃ" #: editor/plugins/script_editor_plugin.cpp msgid "Error importing theme" -msgstr "" +msgstr "Chyba pÅ™i importu motivu" #: editor/plugins/script_editor_plugin.cpp msgid "Error importing" -msgstr "" +msgstr "Chyba pÅ™i importu" #: editor/plugins/script_editor_plugin.cpp msgid "Import Theme" -msgstr "" +msgstr "Importovat motiv" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme As.." -msgstr "" +msgstr "Uložit motiv jako.." #: editor/plugins/script_editor_plugin.cpp +#, fuzzy msgid " Class Reference" -msgstr "" +msgstr " ReferenÄnà tÅ™Ãdy" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Sort" -msgstr "Řadit:" +msgstr "SeÅ™adit" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Move Up" -msgstr "" +msgstr "PÅ™esunout nahoru" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Move Down" -msgstr "" +msgstr "PÅ™esunout dolů" #: editor/plugins/script_editor_plugin.cpp msgid "Next script" -msgstr "" +msgstr "DalÅ¡Ã skript" #: editor/plugins/script_editor_plugin.cpp msgid "Previous script" -msgstr "" +msgstr "PÅ™edchozà skript" #: editor/plugins/script_editor_plugin.cpp msgid "File" -msgstr "" +msgstr "Soubor" #: editor/plugins/script_editor_plugin.cpp msgid "New" -msgstr "" +msgstr "Nový" #: editor/plugins/script_editor_plugin.cpp msgid "Save All" -msgstr "" +msgstr "Uložit vÅ¡e" #: editor/plugins/script_editor_plugin.cpp msgid "Soft Reload Script" @@ -4641,44 +4613,43 @@ msgstr "ZkopÃrovat uzly" #: editor/plugins/script_editor_plugin.cpp msgid "Show In File System" -msgstr "" +msgstr "Zobrazit v systému souborů" #: editor/plugins/script_editor_plugin.cpp msgid "History Prev" -msgstr "" +msgstr "Historie pÅ™edchozÃ" #: editor/plugins/script_editor_plugin.cpp msgid "History Next" -msgstr "" +msgstr "Historie dalÅ¡Ã" #: editor/plugins/script_editor_plugin.cpp msgid "Reload Theme" -msgstr "" +msgstr "Znovu naÄÃst motiv" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme" -msgstr "" +msgstr "Uložit motiv" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme As" -msgstr "" +msgstr "Uložit motiv jako" #: editor/plugins/script_editor_plugin.cpp msgid "Close Docs" -msgstr "" +msgstr "ZavÅ™Ãt dokumentaci" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Close All" -msgstr "ZavÅ™Ãt" +msgstr "ZavÅ™Ãt vÅ¡e" #: editor/plugins/script_editor_plugin.cpp msgid "Close Other Tabs" -msgstr "" +msgstr "ZavÅ™Ãt ostatnà záložky" #: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp msgid "Run" -msgstr "" +msgstr "Spustit" #: editor/plugins/script_editor_plugin.cpp msgid "Toggle Scripts Panel" @@ -4687,20 +4658,21 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp msgid "Find.." -msgstr "" +msgstr "NajÃt.." #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp msgid "Find Next" -msgstr "" +msgstr "NajÃt dalÅ¡Ã" #: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp msgid "Step Over" -msgstr "" +msgstr "PÅ™eskoÄit" #: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp +#, fuzzy msgid "Step Into" -msgstr "" +msgstr "Vstoupit" #: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp msgid "Break" @@ -4709,45 +4681,43 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp #: editor/script_editor_debugger.cpp msgid "Continue" -msgstr "" +msgstr "PokraÄovat" #: editor/plugins/script_editor_plugin.cpp msgid "Keep Debugger Open" msgstr "" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Debug with external editor" -msgstr "Editor závislostÃ" +msgstr "Debugovat externÃm editorem" #: editor/plugins/script_editor_plugin.cpp msgid "Open Godot online documentation" -msgstr "" +msgstr "OtevÅ™Ãt Godot online dokumentaci" #: editor/plugins/script_editor_plugin.cpp msgid "Search the class hierarchy." -msgstr "" +msgstr "Hledat v hierarchii tÅ™Ãd." #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." -msgstr "" +msgstr "Hledat v referenÄnà dokumentaci." #: editor/plugins/script_editor_plugin.cpp msgid "Go to previous edited document." -msgstr "" +msgstr "PÅ™ejÃt na pÅ™edchozà upravovaný dokument." #: editor/plugins/script_editor_plugin.cpp msgid "Go to next edited document." -msgstr "" +msgstr "PÅ™ejÃt na dalÅ¡Ã upravovaný dokument." #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Discard" -msgstr "DiskrétnÃ" +msgstr "Zahodit" #: editor/plugins/script_editor_plugin.cpp msgid "Create Script" -msgstr "" +msgstr "VytvoÅ™it skript" #: editor/plugins/script_editor_plugin.cpp msgid "" @@ -4757,20 +4727,21 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp msgid "Reload" -msgstr "" +msgstr "Znovu naÄÃst" #: editor/plugins/script_editor_plugin.cpp msgid "Resave" -msgstr "" +msgstr "Znovu uložit" #: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp msgid "Debugger" -msgstr "" +msgstr "Ladicà program" #: editor/plugins/script_editor_plugin.cpp msgid "" "Built-in scripts can only be edited when the scene they belong to is loaded" msgstr "" +"VestavÄ›né skripty lze editovat pouze pokud scéna, které náležÃ, je naÄtená" #: editor/plugins/script_text_editor.cpp msgid "Only resources from filesystem can be dropped." @@ -4778,23 +4749,24 @@ msgstr "" #: editor/plugins/script_text_editor.cpp msgid "Pick Color" -msgstr "" +msgstr "Vyberte barvu" #: editor/plugins/script_text_editor.cpp +#, fuzzy msgid "Convert Case" -msgstr "" +msgstr "PÅ™evest pÃsmena" #: editor/plugins/script_text_editor.cpp msgid "Uppercase" -msgstr "" +msgstr "Velká pÃsmena" #: editor/plugins/script_text_editor.cpp msgid "Lowercase" -msgstr "" +msgstr "Malá pÃsmena" #: editor/plugins/script_text_editor.cpp msgid "Capitalize" -msgstr "" +msgstr "Velká pÃsmena" #: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp #: scene/gui/text_edit.cpp @@ -4813,17 +4785,16 @@ msgid "Select All" msgstr "Vybrat vÅ¡e" #: editor/plugins/script_text_editor.cpp -#, fuzzy msgid "Delete Line" -msgstr "Odstranit" +msgstr "Odstranit řádek" #: editor/plugins/script_text_editor.cpp msgid "Indent Left" -msgstr "" +msgstr "Odsadit zleva" #: editor/plugins/script_text_editor.cpp msgid "Indent Right" -msgstr "" +msgstr "Odsadit zprava" #: editor/plugins/script_text_editor.cpp msgid "Toggle Comment" @@ -4840,11 +4811,11 @@ msgstr "Běž na řádek" #: editor/plugins/script_text_editor.cpp msgid "Fold All Lines" -msgstr "" +msgstr "Složit vÅ¡echny řádky" #: editor/plugins/script_text_editor.cpp msgid "Unfold All Lines" -msgstr "" +msgstr "Rozložit vÅ¡echny řádky" #: editor/plugins/script_text_editor.cpp msgid "Complete Symbol" @@ -4852,19 +4823,19 @@ msgstr "" #: editor/plugins/script_text_editor.cpp msgid "Trim Trailing Whitespace" -msgstr "" +msgstr "Osekat koncové mezery" #: editor/plugins/script_text_editor.cpp msgid "Convert Indent To Spaces" -msgstr "" +msgstr "PÅ™evést odsazenà na mezery" #: editor/plugins/script_text_editor.cpp msgid "Convert Indent To Tabs" -msgstr "" +msgstr "PÅ™evést odsazenà na taby" #: editor/plugins/script_text_editor.cpp msgid "Auto Indent" -msgstr "" +msgstr "Automatické odsazenÃ" #: editor/plugins/script_text_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -4873,15 +4844,15 @@ msgstr "PÅ™epnout breakpoint" #: editor/plugins/script_text_editor.cpp msgid "Remove All Breakpoints" -msgstr "" +msgstr "Odstranit vÅ¡echny breakpointy" #: editor/plugins/script_text_editor.cpp msgid "Goto Next Breakpoint" -msgstr "" +msgstr "PÅ™ejÃt na dalÅ¡Ã breakpoint" #: editor/plugins/script_text_editor.cpp msgid "Goto Previous Breakpoint" -msgstr "" +msgstr "PÅ™ejÃt na pÅ™edchozà breakpoint" #: editor/plugins/script_text_editor.cpp msgid "Convert To Uppercase" @@ -4893,31 +4864,31 @@ msgstr "Konvertovat na malá pÃsmena" #: editor/plugins/script_text_editor.cpp msgid "Find Previous" -msgstr "" +msgstr "NajÃt pÅ™edchozÃ" #: editor/plugins/script_text_editor.cpp msgid "Replace.." -msgstr "" +msgstr "Nahradit.." #: editor/plugins/script_text_editor.cpp msgid "Goto Function.." -msgstr "" +msgstr "PÅ™ejÃt na funkci.." #: editor/plugins/script_text_editor.cpp msgid "Goto Line.." -msgstr "" +msgstr "PÅ™ejÃt na řádek.." #: editor/plugins/script_text_editor.cpp msgid "Contextual Help" -msgstr "" +msgstr "Kontextová nápovÄ›da" #: editor/plugins/shader_editor_plugin.cpp msgid "Shader" -msgstr "" +msgstr "Shader" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Constant" -msgstr "" +msgstr "ZmÄ›nit skalárnà konstantu" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Constant" @@ -4925,11 +4896,11 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change RGB Constant" -msgstr "" +msgstr "ZmÄ›na RGB konstanty" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Operator" -msgstr "" +msgstr "ZmÄ›nit skalárnà operátor" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Operator" @@ -4941,7 +4912,7 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change RGB Operator" -msgstr "" +msgstr "ZmÄ›nit RGB operátor" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Toggle Rot Only" @@ -4949,7 +4920,7 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Function" -msgstr "" +msgstr "ZmÄ›nit skalárnà funkci" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Function" @@ -4969,7 +4940,7 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Default Value" -msgstr "" +msgstr "ZmÄ›nit výchozà hodnotu" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change XForm Uniform" @@ -4985,7 +4956,7 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Comment" -msgstr "" +msgstr "ZmÄ›nit komentář" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Add/Remove to Color Ramp" @@ -4997,19 +4968,19 @@ msgstr "" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Modify Curve Map" -msgstr "" +msgstr "Upravit mapu kÅ™ivky" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Input Name" -msgstr "" +msgstr "ZmÄ›nit název vstupu" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Connect Graph Nodes" -msgstr "" +msgstr "Propojit uzly grafu" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Disconnect Graph Nodes" -msgstr "" +msgstr "Odpojit uzly grafu" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Remove Shader Graph Node" @@ -5041,11 +5012,11 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Orthogonal" -msgstr "" +msgstr "OrtogonálnÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Perspective" -msgstr "" +msgstr "PerspektivnÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Aborted." @@ -5078,7 +5049,7 @@ msgstr "PÅ™echod" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotating %s degrees." -msgstr "" +msgstr "Rotuji %s stupňů." #: editor/plugins/spatial_editor_plugin.cpp msgid "Keying is disabled (no key inserted)." @@ -5090,84 +5061,84 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Objects Drawn" -msgstr "" +msgstr "Objekty vykreslené" #: editor/plugins/spatial_editor_plugin.cpp msgid "Material Changes" -msgstr "" +msgstr "ZmÄ›ny materiálu" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes" -msgstr "ZmÄ›nit" +msgstr "ZmÄ›ny shaderu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Surface Changes" -msgstr "" +msgstr "ZmÄ›ny povrchu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Draw Calls" -msgstr "" +msgstr "Vykreslovacà volánÃ" #: editor/plugins/spatial_editor_plugin.cpp +#, fuzzy msgid "Vertices" -msgstr "" +msgstr "Vertexy" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS" -msgstr "" +msgstr "FPS" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." -msgstr "" +msgstr "Pohled shora." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View." -msgstr "" +msgstr "Pohled zdola." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom" -msgstr "" +msgstr "DolnÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Left View." -msgstr "" +msgstr "Pohled zleva." #: editor/plugins/spatial_editor_plugin.cpp msgid "Left" -msgstr "" +msgstr "Levý" #: editor/plugins/spatial_editor_plugin.cpp msgid "Right View." -msgstr "" +msgstr "Pohled zprava." #: editor/plugins/spatial_editor_plugin.cpp msgid "Right" -msgstr "" +msgstr "Pravý" #: editor/plugins/spatial_editor_plugin.cpp msgid "Front View." -msgstr "" +msgstr "ÄŒelnà pohled." #: editor/plugins/spatial_editor_plugin.cpp msgid "Front" -msgstr "" +msgstr "PÅ™ednÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear View." -msgstr "" +msgstr "Pohled zezadu." #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear" -msgstr "" +msgstr "ZadnÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Align with view" -msgstr "" +msgstr "Zarovnat s výhledem" #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "OK :(" -msgstr "" +msgstr "OK :(" #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "No parent to instance a child at." @@ -5175,7 +5146,7 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "This operation requires a single selected node." -msgstr "" +msgstr "Tato operace vyžaduje jeden vybraný uzel." #: editor/plugins/spatial_editor_plugin.cpp msgid "Display Normal" @@ -5194,8 +5165,9 @@ msgid "Display Unshaded" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp +#, fuzzy msgid "View Environment" -msgstr "" +msgstr "Zobrazenà prostÅ™edÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Gizmos" @@ -5203,17 +5175,15 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Information" -msgstr "" +msgstr "Zobrazit informace" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View FPS" -msgstr "Soubor:" +msgstr "Zobrazit FPS" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Half Resolution" -msgstr "ZmÄ›nit měřÃtko výbÄ›ru" +msgstr "PoloviÄnà rozliÅ¡enÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Audio Listener" @@ -5226,41 +5196,39 @@ msgstr "Povolit" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Left" -msgstr "" +msgstr "Volný pohled doleva" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Right" -msgstr "" +msgstr "Volný pohled doprava" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Forward" -msgstr "" +msgstr "Volný pohled vpÅ™ed" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Backwards" -msgstr "" +msgstr "Volný pohled dozadu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Up" -msgstr "" +msgstr "Volný pohled nahoru" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Freelook Down" -msgstr "KoleÄko dolů." +msgstr "Volný pohled dolů" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Speed Modifier" -msgstr "" +msgstr "Rychlost volného pohledu" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Select Mode (Q)" -msgstr "Vybrat vÅ¡e" +msgstr "Režim výbÄ›ru (Q)" #: editor/plugins/spatial_editor_plugin.cpp msgid "" @@ -5271,56 +5239,55 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Move Mode (W)" -msgstr "" +msgstr "Režim posunu (W)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Mode (E)" -msgstr "" +msgstr "Režim otáÄenà (E)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scale Mode (R)" -msgstr "" +msgstr "Režim zvÄ›tÅ¡ovánà (R)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Local Coords" -msgstr "" +msgstr "MÃstnà souÅ™adnice" #: editor/plugins/spatial_editor_plugin.cpp msgid "Local Space Mode (%s)" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Mode (%s)" -msgstr "Vybrat vÅ¡e" +msgstr "Režim pÅ™ichycenà (%s)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" -msgstr "" +msgstr "Pohled zdola" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View" -msgstr "" +msgstr "Pohled shora" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear View" -msgstr "" +msgstr "Pohled zezadu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Front View" -msgstr "" +msgstr "Pohled zepÅ™edu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Left View" -msgstr "" +msgstr "Pohled zleva" #: editor/plugins/spatial_editor_plugin.cpp msgid "Right View" -msgstr "" +msgstr "Pohled zprava" #: editor/plugins/spatial_editor_plugin.cpp msgid "Switch Perspective/Orthogonal view" -msgstr "" +msgstr "PÅ™epnout perspektivnÃ/ortogonálnà pohled" #: editor/plugins/spatial_editor_plugin.cpp msgid "Insert Animation Key" @@ -5339,26 +5306,24 @@ msgid "Align Selection With View" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Tool Select" -msgstr "VÅ¡echny vybrané" +msgstr "Nástroj VýbÄ›r" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Move" -msgstr "" +msgstr "Nástroj PÅ™esunout" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Rotate" -msgstr "" +msgstr "Nástroj OtoÄit" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Scale" -msgstr "" +msgstr "Nástroj ZvÄ›tÅ¡enÃ" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Freelook" -msgstr "PÅ™epnout breakpoint" +msgstr "PÅ™epnout volný pohled" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform" @@ -5366,7 +5331,7 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Configure Snap.." -msgstr "" +msgstr "Nastavit pÅ™ichycenÃ.." #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Dialog.." @@ -5407,7 +5372,7 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Settings" -msgstr "" +msgstr "NastavenÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Skeleton Gizmo visibility" @@ -5415,7 +5380,7 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Snap Settings" -msgstr "" +msgstr "Nastavenà pÅ™ichycenÃ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Translate Snap:" @@ -5423,19 +5388,20 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Snap (deg.):" -msgstr "" +msgstr "PÅ™ichycenà rotaze (stupnÄ›):" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scale Snap (%):" -msgstr "" +msgstr "PÅ™ichycenà zvÄ›tÅ¡enà (%):" #: editor/plugins/spatial_editor_plugin.cpp +#, fuzzy msgid "Viewport Settings" -msgstr "" +msgstr "Nastavenà viewportu" #: editor/plugins/spatial_editor_plugin.cpp msgid "Perspective FOV (deg.):" -msgstr "" +msgstr "Perspektivnà FOV (stupnÄ›):" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Z-Near:" @@ -5620,9 +5586,8 @@ msgid "Remove All Items" msgstr "Odstranit výbÄ›r" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All" -msgstr "Odebrat" +msgstr "Odebrat vÅ¡e" #: editor/plugins/theme_editor_plugin.cpp msgid "Edit theme.." @@ -5872,9 +5837,8 @@ msgid "Delete patch '%s' from list?" msgstr "Odstranit" #: editor/project_export.cpp -#, fuzzy msgid "Delete preset '%s'?" -msgstr "Odstranit vybrané soubory?" +msgstr "Odstranit preset '%s'?" #: editor/project_export.cpp msgid "Export templates for this platform are missing/corrupted: " @@ -5940,9 +5904,8 @@ msgid "Custom (comma-separated):" msgstr "" #: editor/project_export.cpp -#, fuzzy msgid "Feature List:" -msgstr "Seznam metod:" +msgstr "Seznam funkcÃ:" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -5961,9 +5924,8 @@ msgid "Export With Debug" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "The path does not exist." -msgstr "Soubor neexistuje." +msgstr "Cesta neexistuje." #: editor/project_manager.cpp msgid "Please choose a 'project.godot' file." @@ -5982,7 +5944,6 @@ msgid "Imported Project" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Couldn't create folder." msgstr "Nelze vytvoÅ™it složku." @@ -6015,9 +5976,8 @@ msgid "The following files failed extraction from package:" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Rename Project" -msgstr "Nastavenà projektu" +msgstr "PÅ™ejmenovat projekt" #: editor/project_manager.cpp msgid "Couldn't get project.godot in the project path." @@ -6040,25 +6000,22 @@ msgid "Create New Project" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Create & Edit" -msgstr "VytvoÅ™it" +msgstr "VytvoÅ™it a editovat" #: editor/project_manager.cpp msgid "Install Project:" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Install & Edit" -msgstr "Instalovat" +msgstr "Instalovat a editovat" #: editor/project_manager.cpp msgid "Project Name:" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Create folder" msgstr "VytvoÅ™it složku" @@ -6075,9 +6032,8 @@ msgid "Unnamed Project" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Can't open project" -msgstr "PÅ™ipojit.." +msgstr "Nelze otevÅ™Ãt projekt" #: editor/project_manager.cpp msgid "Are you sure to open more than one project?" @@ -6149,9 +6105,8 @@ msgid "Restart Now" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Can't run project" -msgstr "PÅ™ipojit.." +msgstr "Nelze spustit projekt" #: editor/project_manager.cpp msgid "" @@ -6301,9 +6256,8 @@ msgid "Wheel Down." msgstr "KoleÄko dolů." #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add Global Property" -msgstr "PÅ™idat vlastnost getter" +msgstr "PÅ™idat globálnà vlastnost" #: editor/project_settings_editor.cpp msgid "Select a setting item first!" @@ -6318,14 +6272,12 @@ msgid "Setting '%s' is internal, and it can't be deleted." msgstr "" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Delete Item" -msgstr "Odstranit" +msgstr "Odstranit položku" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Can't contain '/' or ':'" -msgstr "PÅ™ipojit k uzlu:" +msgstr "Nesmà obsaovat '/' nebo ':'" #: editor/project_settings_editor.cpp msgid "Already existing" @@ -6385,9 +6337,8 @@ msgid "Changed Locale Filter Mode" msgstr "" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Project Settings (project.godot)" -msgstr "Nastavenà projektu" +msgstr "Nastavenà projektu (project.godot)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "General" @@ -6458,9 +6409,8 @@ msgid "Show only selected locales" msgstr "" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Filter mode:" -msgstr "Filtr:" +msgstr "Režim filtru:" #: editor/project_settings_editor.cpp msgid "Locales:" @@ -6570,14 +6520,12 @@ msgid "Select Property" msgstr "PÅ™idat vlastnost setter" #: editor/property_selector.cpp -#, fuzzy msgid "Select Virtual Method" -msgstr "Vybrat vÅ¡e" +msgstr "Vybrat virtuálnà metodu" #: editor/property_selector.cpp -#, fuzzy msgid "Select Method" -msgstr "Vybrat vÅ¡e" +msgstr "Vybrat metodu" #: editor/pvrtc_compress.cpp msgid "Could not execute PVRTC tool:" @@ -6721,9 +6669,8 @@ msgid "Error duplicating scene to save it." msgstr "" #: editor/scene_tree_dock.cpp -#, fuzzy msgid "Sub-Resources" -msgstr "Zdroj" +msgstr "DÃlÄà zdroje" #: editor/scene_tree_dock.cpp msgid "Clear Inheritance" @@ -6754,9 +6701,8 @@ msgid "Attach Script" msgstr "" #: editor/scene_tree_dock.cpp -#, fuzzy msgid "Clear Script" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "Vymazat skript" #: editor/scene_tree_dock.cpp msgid "Merge From Scene" @@ -6769,7 +6715,7 @@ msgstr "" #: editor/scene_tree_dock.cpp #, fuzzy msgid "Copy Node Path" -msgstr "ZkopÃrovat uzly" +msgstr "KopÃrovat cestu uzlu" #: editor/scene_tree_dock.cpp msgid "Delete (No Confirm)" @@ -6788,7 +6734,7 @@ msgstr "" #: editor/scene_tree_dock.cpp #, fuzzy msgid "Filter nodes" -msgstr "Filtr:" +msgstr "Filtrovat uzly" #: editor/scene_tree_dock.cpp msgid "Attach a new or existing script for the selected node." @@ -6801,7 +6747,7 @@ msgstr "" #: editor/scene_tree_dock.cpp #, fuzzy msgid "Remote" -msgstr "Odebrat" +msgstr "Vzdálený" #: editor/scene_tree_dock.cpp msgid "Local" @@ -6850,9 +6796,8 @@ msgid "Instance:" msgstr "" #: editor/scene_tree_editor.cpp -#, fuzzy msgid "Open script" -msgstr "Spustit skript" +msgstr "OtevÅ™Ãt skript" #: editor/scene_tree_editor.cpp msgid "" @@ -6891,19 +6836,16 @@ msgid "Select a Node" msgstr "" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Error loading template '%s'" -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba pÅ™i nahrávánà šablony '%s'" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Error - Could not create script in filesystem." -msgstr "Nelze vytvoÅ™it složku." +msgstr "Chyba - Nelze vytvoÅ™it skript v souborovém systému." #: editor/script_create_dialog.cpp -#, fuzzy msgid "Error loading script from %s" -msgstr "Chyba nahrávánà fontu." +msgstr "Chyba nahrávánà skriptu z %s" #: editor/script_create_dialog.cpp msgid "N/A" @@ -6939,9 +6881,8 @@ msgid "Wrong extension chosen" msgstr "" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Invalid Path" -msgstr "Neplatná cesta." +msgstr "Neplatná cesta" #: editor/script_create_dialog.cpp msgid "Invalid class name" @@ -6965,9 +6906,8 @@ msgid "Built-in script (into scene file)" msgstr "" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Create new script file" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "VytvoÅ™it nový soubor skriptu" #: editor/script_create_dialog.cpp msgid "Load existing script file" @@ -6986,14 +6926,12 @@ msgid "Class Name" msgstr "" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Template" -msgstr "Odstranit výbÄ›r" +msgstr "Å ablona" #: editor/script_create_dialog.cpp -#, fuzzy msgid "Built-in Script" -msgstr "Spustit skript" +msgstr "VestavÄ›ný skript" #: editor/script_create_dialog.cpp msgid "Attach Node Script" @@ -7002,7 +6940,7 @@ msgstr "" #: editor/script_editor_debugger.cpp #, fuzzy msgid "Remote " -msgstr "Odebrat" +msgstr "Vzdálený " #: editor/script_editor_debugger.cpp msgid "Bytes:" @@ -7389,7 +7327,7 @@ msgstr "ZmÄ›nit měřÃtko výbÄ›ru" #: modules/gridmap/grid_map_editor_plugin.cpp #, fuzzy msgid "GridMap Settings" -msgstr "Nastavenà projektu" +msgstr "Nastavenà GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Pick Distance:" @@ -7406,12 +7344,12 @@ msgstr "" #: modules/mono/editor/godotsharp_editor.cpp #, fuzzy msgid "Failed to create solution." -msgstr "Nelze vytvoÅ™it složku." +msgstr "NepodaÅ™ilo se vytvoÅ™it Å™eÅ¡enÃ." #: modules/mono/editor/godotsharp_editor.cpp #, fuzzy msgid "Failed to save solution." -msgstr "ZmÄ›nit měřÃtko výbÄ›ru" +msgstr "NepodaÅ™ilo se uložit Å™eÅ¡enÃ." #: modules/mono/editor/godotsharp_editor.cpp msgid "Done" @@ -7428,16 +7366,15 @@ msgstr "" #: modules/mono/editor/godotsharp_editor.cpp #, fuzzy msgid "Create C# solution" -msgstr "VytvoÅ™it odbÄ›r" +msgstr "VytvoÅ™it C# Å™eÅ¡enÃ" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Builds" msgstr "" #: modules/mono/editor/mono_bottom_panel.cpp -#, fuzzy msgid "Build Project" -msgstr "Nastavenà projektu" +msgstr "Sestavit projekt" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Warnings" @@ -7481,9 +7418,8 @@ msgid "Stack overflow with stack depth: " msgstr "PÅ™eteÄenà zásobnÃku s hloubkou: " #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Signal Arguments" -msgstr "Upravit argumenty signálu:" +msgstr "Upravit argumenty signálu" #: modules/visual_script/visual_script_editor.cpp #, fuzzy @@ -7491,18 +7427,16 @@ msgid "Change Argument Type" msgstr "ZmÄ›nit typ hodnot pole" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Argument name" -msgstr "ZmÄ›nit hodnotu pole" +msgstr "ZmÄ›nit název argumentu" #: modules/visual_script/visual_script_editor.cpp msgid "Set Variable Default Value" msgstr "" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Set Variable Type" -msgstr "Upravit promÄ›nnou:" +msgstr "Nastavit typ promÄ›nné" #: modules/visual_script/visual_script_editor.cpp msgid "Functions:" @@ -7579,7 +7513,7 @@ msgstr "" #: modules/visual_script/visual_script_editor.cpp #, fuzzy msgid "Hold %s to drop a simple reference to the node." -msgstr "Podržte Meta k uvolnÄ›nà jednoduché reference na uzel." +msgstr "Podržte %s k uvolnÄ›nà jednoduché reference na uzel." #: modules/visual_script/visual_script_editor.cpp #, fuzzy @@ -7589,7 +7523,7 @@ msgstr "Podržte Ctrl k uvolnÄ›nà jednoduché reference na uzel." #: modules/visual_script/visual_script_editor.cpp #, fuzzy msgid "Hold %s to drop a Variable Setter." -msgstr "Podržte Meta k uvolnÄ›nà jednoduché reference na uzel." +msgstr "Podržte %s k uvolnÄ›nà jednoduché reference na uzel." #: modules/visual_script/visual_script_editor.cpp msgid "Hold Ctrl to drop a Variable Setter." @@ -7612,29 +7546,24 @@ msgid "Add Setter Property" msgstr "PÅ™idat vlastnost setter" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Base Type" -msgstr "ZmÄ›nit typ hodnot pole" +msgstr "ZmÄ›nit základnà typ" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Move Node(s)" -msgstr "ZkopÃrovat uzly" +msgstr "PÅ™esunout uzly" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Remove VisualScript Node" -msgstr "Odstranit promÄ›nnou" +msgstr "Odstranit VisualScript uzel" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Connect Nodes" -msgstr "PÅ™ipojit k uzlu:" +msgstr "PÅ™ipojit uzly" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Condition" -msgstr "PÅ™echod" +msgstr "PodmÃnka" #: modules/visual_script/visual_script_editor.cpp msgid "Sequence" @@ -7669,9 +7598,8 @@ msgid "Script already has function '%s'" msgstr "" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Input Value" -msgstr "ZmÄ›nit hodnotu pole" +msgstr "ZmÄ›nit vstupnà hodnotu" #: modules/visual_script/visual_script_editor.cpp msgid "Can't copy the function node." @@ -7682,27 +7610,24 @@ msgid "Clipboard is empty!" msgstr "" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Paste VisualScript Nodes" -msgstr "Vložit uzly" +msgstr "Vložit VisualScript uzly" #: modules/visual_script/visual_script_editor.cpp msgid "Remove Function" msgstr "Odstranit funkci" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Edit Variable" -msgstr "Upravit promÄ›nnou:" +msgstr "Upravit promÄ›nnou" #: modules/visual_script/visual_script_editor.cpp msgid "Remove Variable" msgstr "Odstranit promÄ›nnou" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Edit Signal" -msgstr "Úprava signálu:" +msgstr "Upravit signál" #: modules/visual_script/visual_script_editor.cpp msgid "Remove Signal" @@ -7741,7 +7666,6 @@ msgid "Delete Selected" msgstr "Smazat vybraný" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Find Node Type" msgstr "Vyhledat typ uzlu" @@ -7822,19 +7746,16 @@ msgid "Run exported HTML in the system's default browser." msgstr "" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not write file:" -msgstr "Nelze vytvoÅ™it složku." +msgstr "Nelze zapsat soubor:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not open template for export:" -msgstr "Nelze vytvoÅ™it složku." +msgstr "Nelze otevÅ™Ãt Å¡ablonu pro export:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Invalid export template:" -msgstr "Neplatné jméno vlastnosti." +msgstr "Neplatná Å¡ablona pro export:" #: platform/javascript/export/export.cpp #, fuzzy @@ -7872,7 +7793,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8022,7 +7943,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -8132,9 +8053,8 @@ msgid "Please Confirm..." msgstr "PotvrÄte prosÃm.." #: scene/gui/file_dialog.cpp -#, fuzzy msgid "Select this Folder" -msgstr "Vybrat vÅ¡e" +msgstr "Vybrat tuto složku" #: scene/gui/popup.cpp msgid "" diff --git a/editor/translations/da.po b/editor/translations/da.po index 279448cd41..786cabcbdc 100644 --- a/editor/translations/da.po +++ b/editor/translations/da.po @@ -7835,7 +7835,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7988,7 +7988,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/de.po b/editor/translations/de.po index 584c2cbdf3..d0fa1597a5 100644 --- a/editor/translations/de.po +++ b/editor/translations/de.po @@ -28,8 +28,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-01-20 08:53+0000\n" -"Last-Translator: Timo Schwarzer <account@timoschwarzer.com>\n" +"PO-Revision-Date: 2018-01-22 08:08+0000\n" +"Last-Translator: So Wieso <sowieso@dukun.de>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/" "godot/de/>\n" "Language: de\n" @@ -279,7 +279,7 @@ msgstr "Cursor Schrittraster (in Sekunden)." #: editor/animation_editor.cpp msgid "Enable/Disable looping in animation." -msgstr "Animationswiederholung aktivieren/deaktivieren" +msgstr "Animationswiederholung aktivieren/deaktivieren." #: editor/animation_editor.cpp msgid "Add new tracks." @@ -1355,18 +1355,16 @@ msgid "Description" msgstr "Beschreibung" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Anleitungen" +msgstr "Anleitungen im Netz:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Es gibt zurzeit keine Beschreibung dieser Methode. [color=$color][url=" +"Es gibt zurzeit keine Tutorials zu dieser Klasse. [color=$color][url=" "$url]Ergänzungen durch eigene Beiträge[/url][/color] sind sehr erwünscht!" #: editor/editor_help.cpp @@ -3813,7 +3811,7 @@ msgstr "Ok" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "InÂsÂtanÂziÂieÂren mehrerer Nodes nicht möglich ohne Wurzel-Node." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5878,7 +5876,6 @@ msgid "Select current edited sub-tile." msgstr "Speichere die so eben bearbeitete Unterkachel." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." msgstr "Unterkachel auswählen um ihre Priorität zu ändern." @@ -5993,9 +5990,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Eine ‚project.godot‘-Datei auswählen." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Eine ‚project.godot‘-Datei auswählen." +msgstr "Bitte einen leeren Ordner auswählen." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7863,8 +7859,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Dieses Node besitzt keine untergeordneten Formen, es kann deshalb nicht mit " +"dem Raum interagieren.\n" +"Es wird empfohlen CollisionShape2D oder CollisionPolygon2D Unterobjekte " +"hinzuzufügen um seine Form festzulegen." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -8036,8 +8036,12 @@ msgstr "Beleuchte Meshe: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Dieses Node besitzt keine untergeordneten Formen, es kann deshalb nicht mit " +"dem Raum interagieren.\n" +"Es wird empfohlen CollisionShape oder CollisionPolygon Unterobjekte " +"hinzuzufügen um seine Form festzulegen." #: scene/3d/collision_polygon.cpp msgid "" @@ -8182,7 +8186,6 @@ msgid "(Other)" msgstr "(Andere)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/de_CH.po b/editor/translations/de_CH.po index dcffac974d..e0d10c9264 100644 --- a/editor/translations/de_CH.po +++ b/editor/translations/de_CH.po @@ -7809,7 +7809,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7959,7 +7959,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index baa77fe842..a1e1b1d4ae 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -7619,7 +7619,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7753,7 +7753,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/el.po b/editor/translations/el.po index 6ef2c6a8c8..e8841a9338 100644 --- a/editor/translations/el.po +++ b/editor/translations/el.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-16 17:46+0000\n" +"PO-Revision-Date: 2018-01-24 19:46+0000\n" "Last-Translator: George Tsiamasiotis <gtsiam@windowslive.com>\n" "Language-Team: Greek <https://hosted.weblate.org/projects/godot-engine/godot/" "el/>\n" @@ -1326,19 +1326,18 @@ msgid "Description" msgstr "ΠεÏιγÏαφή" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Βοηθήματα" +msgstr "Online Tutorial:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Δεν υπάÏχει ακόμη πεÏιγÏαφή για αυτήν την μÎθοδο. ΠαÏακαλοÏμε βοηθήστε μας " -"[color=$color][url=$url]γÏάφοντας μία[/url][/color]!" +"Δεν υπάÏχει ακόμα βοήθεια για αυτήν την κλάση, μποÏείτε να την [color=$color]" +"[url=$url]γÏάψετε[/url][/color] ή να την [color=$color][url=$url2]ζητήσετε[/" +"url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -2096,7 +2095,7 @@ msgstr "ΗλεκτÏονική τεκμηÏίωση" #: editor/editor_node.cpp msgid "Q&A" -msgstr "ΕÏώτηση&Απάντηση" +msgstr "ΕÏωτήσεις & Απαντήσεις" #: editor/editor_node.cpp msgid "Issue Tracker" @@ -3790,7 +3789,7 @@ msgstr "Εντάξει" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "Δεν είναι δυνατή η δημιουÏγία στιγμιότυπου χωÏίς Ïίζα." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5857,9 +5856,8 @@ msgid "Select current edited sub-tile." msgstr "ΕπÎλεξε το Ï„ÏÎχων επεξεÏγαζόμενο υπο-πλακίδιο." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." -msgstr "ΕπÎλεξε υπο-πλακίδιο για να αλλάξεις την Ï€ÏοτεÏαιότητα του." +msgstr "ΕπιλÎξτε υπο-πλακίδιο για να αλλάξετε την Ï€ÏοτεÏαιότητα του." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5974,9 +5972,8 @@ msgid "Please choose a 'project.godot' file." msgstr "ΠαÏακαλοÏμε επιλÎκτε Îνα αÏχείο 'project.godot'." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "ΠαÏακαλοÏμε επιλÎκτε Îνα αÏχείο 'project.godot'." +msgstr "ΠαÏακαλοÏμε επιλÎξτε Îναν άδειο φάκελο." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7092,7 +7089,7 @@ msgstr "ΠαÏακολοÏθηση" #: editor/script_editor_debugger.cpp msgid "List of Video Memory Usage by Resource:" -msgstr "Λίστα χÏήσης βιντεο-μνήμης ανά πόÏο:" +msgstr "Λίστα χÏήσης βίντεο-μνήμης ανά πόÏο:" #: editor/script_editor_debugger.cpp msgid "Total:" @@ -7100,7 +7097,7 @@ msgstr "Συνολικά:" #: editor/script_editor_debugger.cpp msgid "Video Mem" -msgstr "βιντεο-μνήμη" +msgstr "Βίντεο μνήμη" #: editor/script_editor_debugger.cpp msgid "Resource Path" @@ -7849,8 +7846,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Αυτός ο κόμβος δεν Îχει παιδιά κόμβους σχήματος, οπότε δεν μποÏεί να " +"αντιδÏάσει με το πεÏιβάλλον.\n" +"Σκεφτείτε να Ï€ÏοσθÎσετε CollisionShape2D ή CollisionPolygon2D για να οÏίσετε " +"το σχήμα του." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -8016,8 +8017,12 @@ msgstr "Φώτηση πλεγμάτων: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Αυτός ο κόμβος δεν Îχει παιδιά κόμβους σχήματος, οπότε δεν μποÏεί να " +"αντιδÏάσει με το πεÏιβάλλον.\n" +"Σκεφτείτε να Ï€ÏοσθÎσετε CollisionShape ή CollisionPolygon για να οÏίσετε το " +"σχήμα του." #: scene/3d/collision_polygon.cpp msgid "" @@ -8162,7 +8167,6 @@ msgid "(Other)" msgstr "(Άλλο)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/es.po b/editor/translations/es.po index 1138f1efc3..df1a38d45b 100644 --- a/editor/translations/es.po +++ b/editor/translations/es.po @@ -8242,7 +8242,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8412,7 +8412,7 @@ msgstr "Copiando datos de imágenes" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po index 9fdc891074..55a1953d34 100644 --- a/editor/translations/es_AR.po +++ b/editor/translations/es_AR.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" -"Last-Translator: Roger Blanco Ribera <roger.blancoribera@gmail.com>\n" +"PO-Revision-Date: 2018-01-24 20:49+0000\n" +"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n" "Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/" "godot-engine/godot/es_AR/>\n" "Language: es_AR\n" @@ -515,9 +515,8 @@ msgid "Signals" msgstr "Señales" #: editor/create_dialog.cpp -#, fuzzy msgid "Change %s Type" -msgstr "Cambiar Tipo" +msgstr "Cambiar Tipo de %s" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -635,9 +634,8 @@ msgstr "" "Quitarlos de todos modos? (imposible deshacer)" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" -msgstr "No se puede remover:\n" +msgstr "No se puede remover:" #: editor/dependency_editor.cpp msgid "Error loading:" @@ -721,9 +719,8 @@ msgid "Lead Developer" msgstr "Desarrollador Principal" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "Gestor de Proyectos" +msgstr "Gestor de Proyectos " #: editor/editor_about.cpp msgid "Developers" @@ -1124,9 +1121,8 @@ msgid "Packing" msgstr "Empaquetando" #: editor/editor_export.cpp platform/javascript/export/export.cpp -#, fuzzy msgid "Template file not found:" -msgstr "Plantilla no encontrada:\n" +msgstr "Plantilla no encontrada:" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "File Exists, Overwrite?" @@ -1337,19 +1333,18 @@ msgid "Description" msgstr "Descripción" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Tutoriales" +msgstr "Tutoriales En Linea:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Actualmente no existe descripción para este método. Por favor ayudanos " -"[color=$color][url=$url]contribuyendo una[/url][/color]!" +"Actualmente no existen tutoriales para esta clase, podés [color=$color][url=" +"$url]contribuir uno[/url][/color] o [color=$color][url=$url2]solicitar uno[/" +"url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -1403,9 +1398,8 @@ msgid "Clear" msgstr "Limpiar" #: editor/editor_log.cpp -#, fuzzy msgid "Clear Output" -msgstr "Salida" +msgstr "Limpiar Salida" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" @@ -1469,13 +1463,12 @@ msgid "This operation can't be done without a tree root." msgstr "Esta operación no puede hacerse sin una raÃz de árbol." #: editor/editor_node.cpp -#, fuzzy msgid "" "Couldn't save scene. Likely dependencies (instances or inheritance) couldn't " "be satisfied." msgstr "" "No se pudo guardar la escena. Probablemente no se hayan podido satisfacer " -"dependencias (instancias)." +"dependencias (instancias o herencia)." #: editor/editor_node.cpp msgid "Failed to load resource." @@ -1573,11 +1566,11 @@ msgstr "Colapsar todas las propiedades" #: editor/editor_node.cpp msgid "Copy Params" -msgstr "Copiar Params" +msgstr "Copiar Parámetros" #: editor/editor_node.cpp msgid "Paste Params" -msgstr "Pegar Parametros" +msgstr "Pegar Parámetros" #: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp msgid "Paste Resource" @@ -2286,7 +2279,7 @@ msgstr "Abrir en Editor de Script" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" -msgstr "Exportar Libreria de Assets" +msgstr "Abrir Biblioteca de Assets" #: editor/editor_node.cpp msgid "Open the next Editor" @@ -2483,9 +2476,8 @@ msgid "No version.txt found inside templates." msgstr "No se encontro ningún version.txt dentro de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for templates:" -msgstr "Error creando ruta para las plantillas:\n" +msgstr "Error creando rutas para las plantillas:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -2643,36 +2635,30 @@ msgid "View items as a list" msgstr "Ver items como una lista" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Status: Import of file failed. Please fix file and reimport manually." msgstr "" -"\n" -"Estado: Falló la importación del archivo. Por favor arregle el archivo y " -"reimporta manualmente." +"Estado: Falló la importación del archivo. Por favor arreglá el archivo y " +"reimportá manualmente." #: editor/filesystem_dock.cpp msgid "Cannot move/rename resources root." msgstr "No se puede mover/renombrar la raiz de recursos." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Cannot move a folder into itself." -msgstr "No se puede mover una carpeta dento de si misma.\n" +msgstr "No se puede mover una carpeta dentro de si misma." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "Error al mover:\n" +msgstr "Error al mover:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error duplicating:" -msgstr "Error duplicando:\n" +msgstr "Error al duplicar:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Unable to update dependencies:" -msgstr "No se pudieron actualizar las dependencias:\n" +msgstr "No se pudieron actualizar las dependencias:" #: editor/filesystem_dock.cpp msgid "No name provided" @@ -3318,9 +3304,8 @@ msgid "Filters.." msgstr "Filtros.." #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "AnimationTree" -msgstr "Animación" +msgstr "AnimationTree" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Free" @@ -3805,7 +3790,7 @@ msgstr "Ok" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "No se puede instanciar múltiples nodos sin raÃz." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -4190,9 +4175,8 @@ msgid "Bake!" msgstr "Hacer Bake!" #: editor/plugins/navigation_mesh_editor_plugin.cpp -#, fuzzy msgid "Bake the navigation mesh." -msgstr "Hacer bake de mesh de navegación.\n" +msgstr "Hacer bake de mesh de navegación." #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Clear the navigation mesh." @@ -4582,20 +4566,16 @@ msgid "Paste" msgstr "Pegar" #: editor/plugins/resource_preloader_editor_plugin.cpp -#, fuzzy msgid "ResourcePreloader" -msgstr "Ruta de Recursos" +msgstr "ResourcePreloader" #: editor/plugins/script_editor_plugin.cpp msgid "Clear Recent Files" msgstr "Restablecer Archivos Recientes" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Close and save changes?" -msgstr "" -"Cerrar y guardar cambios?\n" -"\"" +msgstr "¿Cerrar y guardar cambios?" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme" @@ -4670,7 +4650,6 @@ msgid "Copy Script Path" msgstr "Copiar Ruta de Script" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Show In File System" msgstr "Mostrar en Sistema de Archivos" @@ -5282,9 +5261,8 @@ msgid "XForm Dialog" msgstr "Dialogo XForm" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Select Mode (Q)" -msgstr "Modo Seleccionar (Q)\n" +msgstr "Modo Seleccionar (Q)" #: editor/plugins/spatial_editor_plugin.cpp msgid "" @@ -5562,18 +5540,16 @@ msgid "Move (After)" msgstr "Mover (Despues)" #: editor/plugins/sprite_frames_editor_plugin.cpp -#, fuzzy msgid "SpriteFrames" -msgstr "Frames del Stack" +msgstr "SpriteFrames" #: editor/plugins/style_box_editor_plugin.cpp msgid "StyleBox Preview:" msgstr "Vista Previa de StyleBox:" #: editor/plugins/style_box_editor_plugin.cpp -#, fuzzy msgid "StyleBox" -msgstr "Estilo" +msgstr "StyleBox" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Set Region Rect" @@ -5853,30 +5829,32 @@ msgid "Error" msgstr "Error" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Autotiles" -msgstr "Auto Rebanar" +msgstr "Autotiles" #: editor/plugins/tile_set_editor_plugin.cpp msgid "" "Select sub-tile to use as icon, this will be also used on invalid autotile " "bindings." msgstr "" +"Selectionar sub-tile para usar como icono, esta también sera usada en " +"bindings inválidos de autotile." #: editor/plugins/tile_set_editor_plugin.cpp msgid "" "LMB: set bit on.\n" "RMB: set bit off." msgstr "" +"Click izq: Activar bit.\n" +"Click der: Desactivar bit." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select current edited sub-tile." -msgstr "Guardar el recurso editado actualmente." +msgstr "Seleccionar sub-tile editado actualmente." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Select sub-tile to change its priority." -msgstr "" +msgstr "Seleccionar sub-tile para cambiar su prioridad." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5993,9 +5971,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Por favor elegà un archivo 'project.godot'." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Por favor elegà un archivo 'project.godot'." +msgstr "Por favor elegà una carpeta vacÃa." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -6007,13 +5984,12 @@ msgid "Imported Project" msgstr "Proyecto Importado" #: editor/project_manager.cpp -#, fuzzy msgid "Couldn't create folder." msgstr "No se pudo crear la carpeta." #: editor/project_manager.cpp msgid "There is already a folder in this path with the specified name." -msgstr "" +msgstr "Ya hay una carpeta en esta ruta con el nombre especificado." #: editor/project_manager.cpp msgid "It would be a good idea to name your project." @@ -6056,27 +6032,24 @@ msgid "Import Existing Project" msgstr "Importar Proyecto Existente" #: editor/project_manager.cpp -#, fuzzy msgid "Import & Edit" -msgstr "Importar y Abrir" +msgstr "Importar y Editar" #: editor/project_manager.cpp msgid "Create New Project" msgstr "Crear Proyecto Nuevo" #: editor/project_manager.cpp -#, fuzzy msgid "Create & Edit" -msgstr "Crear Emisor" +msgstr "Crear y Editar" #: editor/project_manager.cpp msgid "Install Project:" msgstr "Instalar Proyecto:" #: editor/project_manager.cpp -#, fuzzy msgid "Install & Edit" -msgstr "Instalar" +msgstr "Instalar y Editar" #: editor/project_manager.cpp msgid "Project Name:" @@ -6752,9 +6725,8 @@ msgid "Error duplicating scene to save it." msgstr "Error al duplicar escena para guardarla." #: editor/scene_tree_dock.cpp -#, fuzzy msgid "Sub-Resources" -msgstr "Sub-Recursos:" +msgstr "Sub-Recursos" #: editor/scene_tree_dock.cpp msgid "Clear Inheritance" @@ -7066,9 +7038,8 @@ msgid "Child Process Connected" msgstr "Proceso Hijo Conectado" #: editor/script_editor_debugger.cpp -#, fuzzy msgid "Copy Error" -msgstr "Erroes de carga" +msgstr "Erroes de Copia" #: editor/script_editor_debugger.cpp msgid "Inspect Previous Instance" @@ -7421,56 +7392,48 @@ msgid "Pick Distance:" msgstr "Elegir Instancia:" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Generating solution..." -msgstr "Creando contornos..." +msgstr "Generando solución..." #: modules/mono/editor/godotsharp_editor.cpp msgid "Generating C# project..." -msgstr "" +msgstr "Generando proyecto en C#..." #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to create solution." -msgstr "No se pudo crear el outline!" +msgstr "No se pudo crear la solución." #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to save solution." -msgstr "Fallo al cargar recurso." +msgstr "No se pudo guardar la solución." #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Done" -msgstr "Hecho!" +msgstr "Hecho" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to create C# project." -msgstr "Fallo al cargar recurso." +msgstr "No se pudo crear el proyecto en C#" #: modules/mono/editor/godotsharp_editor.cpp msgid "Mono" msgstr "Mono" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Create C# solution" -msgstr "Crear Outline" +msgstr "Crear solución en C#" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Builds" msgstr "Builds" #: modules/mono/editor/mono_bottom_panel.cpp -#, fuzzy msgid "Build Project" -msgstr "Proyecto" +msgstr "Construir Proyecto" #: modules/mono/editor/mono_bottom_panel.cpp -#, fuzzy msgid "Warnings" -msgstr "Advertencia" +msgstr "Advertencias" #: modules/visual_script/visual_script.cpp msgid "" @@ -7832,34 +7795,28 @@ msgid "Run exported HTML in the system's default browser." msgstr "Ejecutar HTML exportado en el navegador por defecto del sistema." #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not write file:" -msgstr "No se pudo escribir el archivo:\n" +msgstr "No se pudo escribir el archivo:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not open template for export:" -msgstr "No se pudo abrir la plantilla para exportar:\n" +msgstr "No se pudo abrir la plantilla para exportar:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Invalid export template:" -msgstr "Plantilla de exportación inválida:\n" +msgstr "Plantilla de exportación inválida:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read custom HTML shell:" -msgstr "No se pudo leer el shell HTML personalizado:\n" +msgstr "No se pudo leer el shell HTML personalizado:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read boot splash image file:" -msgstr "No se pudo leer la imagen de boot splash:\n" +msgstr "No se pudo leer la imagen de boot splash:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Using default boot splash image." -msgstr "No se pudo leer la imagen de boot splash:\n" +msgstr "Usando imagen boot splash por defecto." #: scene/2d/animated_sprite.cpp msgid "" @@ -7882,8 +7839,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Este nodo no tiene hijos de tipo shape, por lo tanto no puede interactuar " +"con el espacio.\n" +"Considerá agregarle nodos hijos de tipo CollisionShape2D o " +"CollisionPolygon2D para definir su forma." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -8045,8 +8006,12 @@ msgstr "Iluminando Meshes: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Este nodo no tiene hijos de tipo shape, asi que no puede interactuar con el " +"espacio.\n" +"Considerá agregarle nodos hijos de tipo CollisionShape o CollisionPolygon " +"para definir su forma." #: scene/3d/collision_polygon.cpp msgid "" @@ -8185,12 +8150,11 @@ msgid "(Other)" msgstr "(Otro)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." msgstr "" -"El Entorno por Defecto especificado en Configuracion del Editor (Rendering -" +"El Entorno por Defecto especificado en Configuración del Editor (Rendering -" "> Viewport -> Entorno por Defecto) no pudo ser cargado." #: scene/main/viewport.cpp diff --git a/editor/translations/fa.po b/editor/translations/fa.po index 8401de8e8a..3d6b98f49d 100644 --- a/editor/translations/fa.po +++ b/editor/translations/fa.po @@ -7805,7 +7805,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7961,7 +7961,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/fi.po b/editor/translations/fi.po index fc47f07508..8e8636973e 100644 --- a/editor/translations/fi.po +++ b/editor/translations/fi.po @@ -5,21 +5,21 @@ # # basse <basse@roiske.org>, 2017. # Bastian Salmela <bastian.salmela@gmail.com>, 2017. -# ekeimaja <ekeimaja@gmail.com>, 2017. +# ekeimaja <ekeimaja@gmail.com>, 2017-2018. # Jarmo Riikonen <amatrelan@gmail.com>, 2017. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2017-12-03 11:31+0000\n" -"Last-Translator: Bastian Salmela <bastian.salmela@gmail.com>\n" +"PO-Revision-Date: 2018-01-24 12:10+0000\n" +"Last-Translator: ekeimaja <ekeimaja@gmail.com>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" "Language: fi\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2.18-dev\n" +"X-Generator: Weblate 2.19-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -1104,7 +1104,7 @@ msgstr "(tyhjä)" #: editor/editor_data.cpp msgid "[unsaved]" -msgstr "" +msgstr "[ei tallennettu]" #: editor/editor_dir_dialog.cpp msgid "Please select a base directory first" @@ -1234,8 +1234,9 @@ msgid "Toggle Mode" msgstr "Näytä/piilota" #: editor/editor_file_dialog.cpp +#, fuzzy msgid "Focus Path" -msgstr "" +msgstr "Kohdista polku" #: editor/editor_file_dialog.cpp msgid "Move Favorite Up" @@ -3496,7 +3497,7 @@ msgstr "edellinen" #: editor/plugins/asset_library_editor_plugin.cpp msgid "next" -msgstr "" +msgstr "seuraava" #: editor/plugins/asset_library_editor_plugin.cpp msgid "last" @@ -4293,11 +4294,11 @@ msgstr "" #: editor/plugins/navigation_mesh_generator.cpp msgid "Setting up Configuration..." -msgstr "" +msgstr "Luodaan konfiguraatiota..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Calculating grid size..." -msgstr "" +msgstr "Lasketaan ruudukon kokoa..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Creating heightfield..." @@ -6512,7 +6513,7 @@ msgstr "Yhdistä Nodeen:" #: editor/project_settings_editor.cpp msgid "Already existing" -msgstr "" +msgstr "On jo olemassa" #: editor/project_settings_editor.cpp msgid "Add Input Action" @@ -6574,7 +6575,7 @@ msgstr "Projektin asetukset" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "General" -msgstr "" +msgstr "Yleinen" #: editor/project_settings_editor.cpp editor/property_editor.cpp msgid "Property:" @@ -6699,7 +6700,7 @@ msgstr "Uusi skripti" #: editor/property_editor.cpp msgid "New %s" -msgstr "" +msgstr "Uusi %s" #: editor/property_editor.cpp #, fuzzy @@ -6802,7 +6803,7 @@ msgstr "Pääskenen argumentit:" #: editor/run_settings_dialog.cpp msgid "Scene Run Settings" -msgstr "" +msgstr "Scenen suorittamisasetukset" #: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp #: scene/gui/dialogs.cpp @@ -6841,7 +6842,7 @@ msgstr "" #: editor/scene_tree_dock.cpp msgid "Duplicate Node(s)" -msgstr "" +msgstr "Monista node(t)" #: editor/scene_tree_dock.cpp msgid "Delete Node(s)?" @@ -7102,7 +7103,7 @@ msgstr "" #: editor/script_create_dialog.cpp msgid "Directory of the same name exists" -msgstr "" +msgstr "Samanniminen hakemisto on jo olemassa" #: editor/script_create_dialog.cpp #, fuzzy @@ -7256,7 +7257,7 @@ msgstr "" #: editor/script_editor_debugger.cpp msgid "Value" -msgstr "" +msgstr "Arvo" #: editor/script_editor_debugger.cpp msgid "Monitors" @@ -7268,7 +7269,7 @@ msgstr "" #: editor/script_editor_debugger.cpp msgid "Total:" -msgstr "" +msgstr "Yhteensä:" #: editor/script_editor_debugger.cpp msgid "Video Mem" @@ -7280,7 +7281,7 @@ msgstr "" #: editor/script_editor_debugger.cpp msgid "Type" -msgstr "" +msgstr "Tyyppi" #: editor/script_editor_debugger.cpp msgid "Format" @@ -7288,7 +7289,7 @@ msgstr "Muoto" #: editor/script_editor_debugger.cpp msgid "Usage" -msgstr "" +msgstr "Käyttö" #: editor/script_editor_debugger.cpp msgid "Misc" @@ -7385,11 +7386,11 @@ msgstr "" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Platform:" -msgstr "" +msgstr "Alusta:" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Platform" -msgstr "" +msgstr "Alusta" #: modules/gdnative/gdnative_library_editor_plugin.cpp #, fuzzy @@ -7417,7 +7418,7 @@ msgstr "Tila:" #: modules/gdnative/gdnative_library_singleton_editor.cpp msgid "Libraries: " -msgstr "" +msgstr "Kirjastot: " #: modules/gdnative/register_types.cpp msgid "GDNative" @@ -7517,15 +7518,15 @@ msgstr "" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit X Axis" -msgstr "" +msgstr "Muokkaa X-akselia" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit Y Axis" -msgstr "" +msgstr "Muokkaa Y-akselia" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit Z Axis" -msgstr "" +msgstr "Muokkaa Z-akselia" #: modules/gridmap/grid_map_editor_plugin.cpp #, fuzzy @@ -7593,7 +7594,7 @@ msgstr "" #: modules/mono/editor/godotsharp_editor.cpp msgid "Generating C# project..." -msgstr "" +msgstr "Luodaan C# projekti..." #: modules/mono/editor/godotsharp_editor.cpp #, fuzzy @@ -8040,7 +8041,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8176,7 +8177,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -8294,7 +8295,7 @@ msgstr "" #: scene/gui/tree.cpp msgid "(Other)" -msgstr "" +msgstr "(Muu)" #: scene/main/scene_tree.cpp #, fuzzy diff --git a/editor/translations/fr.po b/editor/translations/fr.po index f0eb523e56..7bb65f8056 100644 --- a/editor/translations/fr.po +++ b/editor/translations/fr.po @@ -10,7 +10,7 @@ # Chenebel Dorian <LoubiTek54@gmail.com>, 2016-2017. # derderder77 <derderder77380@gmail.com>, 2016. # finkiki <specialpopol@gmx.fr>, 2016. -# Gilles Roudiere <gilles.roudiere@gmail.com>, 2017. +# Gilles Roudiere <gilles.roudiere@gmail.com>, 2017-2018. # Hugo Locurcio <hugo.l@openmailbox.org>, 2016-2017. # Kanabenki <lucien.menassol@gmail.com>, 2017. # keltwookie <keltwookie@protonmail.com>, 2017. @@ -35,8 +35,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-01-06 13:19+0000\n" -"Last-Translator: Arthur Templé <tuturtemple@gmail.com>\n" +"PO-Revision-Date: 2018-01-22 08:08+0000\n" +"Last-Translator: Gilles Roudiere <gilles.roudiere@gmail.com>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/" "godot/fr/>\n" "Language: fr\n" @@ -539,9 +539,8 @@ msgid "Signals" msgstr "Signaux" #: editor/create_dialog.cpp -#, fuzzy msgid "Change %s Type" -msgstr "Changer le type" +msgstr "Changer le type de %s" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -662,9 +661,8 @@ msgstr "" "Les supprimer tout de même ? (annulation impossible)" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" -msgstr "Impossible à enlever :\n" +msgstr "Impossible à enlever :" #: editor/dependency_editor.cpp msgid "Error loading:" @@ -747,9 +745,8 @@ msgid "Lead Developer" msgstr "Développeur principal" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "Gestionnaire de projets" +msgstr "Gestionnaire de projets " #: editor/editor_about.cpp msgid "Developers" @@ -858,9 +855,8 @@ msgid "Rename Audio Bus" msgstr "Renommer bus audio" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Change Audio Bus Volume" -msgstr "Basculer vers transport audio solo" +msgstr "Modifier le volume audio du bus" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Solo" @@ -1106,7 +1102,6 @@ msgid "Updating scene.." msgstr "Mise à jour de la scène…" #: editor/editor_data.cpp -#, fuzzy msgid "[empty]" msgstr "(vide)" @@ -1152,9 +1147,8 @@ msgid "Packing" msgstr "Empaquetage" #: editor/editor_export.cpp platform/javascript/export/export.cpp -#, fuzzy msgid "Template file not found:" -msgstr "Fichier modèle introuvable :\n" +msgstr "Fichier modèle introuvable :" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "File Exists, Overwrite?" @@ -1365,9 +1359,8 @@ msgid "Description" msgstr "Description" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Tutoriels" +msgstr "Tutoriels en ligne :" #: editor/editor_help.cpp #, fuzzy @@ -1433,7 +1426,7 @@ msgstr "Effacer" #: editor/editor_log.cpp #, fuzzy msgid "Clear Output" -msgstr "Sortie" +msgstr "Effacer la sortie" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" @@ -1497,13 +1490,12 @@ msgid "This operation can't be done without a tree root." msgstr "Cette opération ne peut être réalisée sans une arborescence racine." #: editor/editor_node.cpp -#, fuzzy msgid "" "Couldn't save scene. Likely dependencies (instances or inheritance) couldn't " "be satisfied." msgstr "" -"Impossible d'enregistrer la scène. Les dépendances (instances) n'ont sans " -"doute pas pu être satisfaites." +"Impossible d'enregistrer la scène. Les dépendances (instances ou héritage) " +"n'ont sans doute pas pu être satisfaites." #: editor/editor_node.cpp msgid "Failed to load resource." @@ -1592,12 +1584,10 @@ msgstr "" "comprendre ce fonctionnement." #: editor/editor_node.cpp -#, fuzzy msgid "Expand all properties" msgstr "Développer tout" #: editor/editor_node.cpp -#, fuzzy msgid "Collapse all properties" msgstr "Réduire tout" @@ -2407,14 +2397,12 @@ msgid "Frame #:" msgstr "Frame # :" #: editor/editor_profiler.cpp -#, fuzzy msgid "Time" msgstr "Temps :" #: editor/editor_profiler.cpp -#, fuzzy msgid "Calls" -msgstr "Appel" +msgstr "Appels" #: editor/editor_run_native.cpp msgid "Select device from the list" @@ -2521,9 +2509,8 @@ msgid "No version.txt found inside templates." msgstr "Aucun version.txt n'a été trouvé dans les modèles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for templates:" -msgstr "Erreur lors de la création du chemin pour les modèles:\n" +msgstr "Erreur lors de la création du chemin pour les modèles :" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -2559,9 +2546,8 @@ msgstr "Pas de réponse." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Request Failed." -msgstr "Req. a Échoué." +msgstr "Requête échouée." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2598,7 +2584,6 @@ msgid "Resolving" msgstr "Résolution" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't Resolve" msgstr "Impossible à résoudre." @@ -2608,7 +2593,6 @@ msgid "Connecting.." msgstr "Connexion en cours.." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't Connect" msgstr "Connexion impossible" @@ -2684,10 +2668,8 @@ msgid "View items as a list" msgstr "Afficher les éléments sous forme de liste" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Status: Import of file failed. Please fix file and reimport manually." msgstr "" -"\n" "Statut : L'importation du fichier a échoué. Veuillez corriger le fichier et " "le réimporter manuellement." @@ -2696,24 +2678,20 @@ msgid "Cannot move/rename resources root." msgstr "Impossible de déplacer / renommer les ressources root." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Cannot move a folder into itself." -msgstr "Impossible de déplacer un dossier dans lui-même.\n" +msgstr "Impossible de déplacer un dossier dans lui-même." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "Erreur lors du déplacement :\n" +msgstr "Erreur lors du déplacement :" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error duplicating:" -msgstr "Erreur au chargement :" +msgstr "Erreur à la duplication :" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Unable to update dependencies:" -msgstr "Impossible de mettre à jour les dépendences :\n" +msgstr "Impossible de mettre à jour les dépendences :" #: editor/filesystem_dock.cpp msgid "No name provided" @@ -2744,14 +2722,12 @@ msgid "Renaming folder:" msgstr "Renommer le dossier :" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicating file:" -msgstr "Dupliquer" +msgstr "Duplication du fichier :" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicating folder:" -msgstr "Renommer le dossier :" +msgstr "Duplication du dossier :" #: editor/filesystem_dock.cpp msgid "Expand all" @@ -2770,9 +2746,8 @@ msgid "Move To.." msgstr "Déplacer vers…" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Open Scene(s)" -msgstr "Ouvrir une scène" +msgstr "Ouvrir une(des) scène(s)" #: editor/filesystem_dock.cpp msgid "Instance" @@ -2888,14 +2863,12 @@ msgid "Importing Scene.." msgstr "Importation de la scène…" #: editor/import/resource_importer_scene.cpp -#, fuzzy msgid "Generating Lightmaps" -msgstr "Transfert vers des lightmaps :" +msgstr "Génération des lightmaps :" #: editor/import/resource_importer_scene.cpp -#, fuzzy msgid "Generating for Mesh: " -msgstr "Générer AABB" +msgstr "Généreration pour le Mesh : " #: editor/import/resource_importer_scene.cpp msgid "Running Custom Script.." @@ -3158,7 +3131,7 @@ msgstr "Directions" #: editor/plugins/animation_player_editor_plugin.cpp #, fuzzy msgid "Past" -msgstr "Coller" +msgstr "Passé" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Future" @@ -3367,9 +3340,8 @@ msgid "Filters.." msgstr "Filtres…" #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "AnimationTree" -msgstr "Animation" +msgstr "AnimationTree" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Free" @@ -3540,9 +3512,8 @@ msgstr "" "accessible en écriture." #: editor/plugins/baked_lightmap_editor_plugin.cpp -#, fuzzy msgid "Bake Lightmaps" -msgstr "Transfert vers des lightmaps :" +msgstr "Précalculer les lightmaps :" #: editor/plugins/camera_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -3827,9 +3798,8 @@ msgid "Clear Pose" msgstr "Vider la pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Drag pivot from mouse position" -msgstr "Déplacer le point de pivot à partir de la position de la souris" +msgstr "Déplacer le point de pivot à la position de la souris" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Set pivot at mouse position" @@ -3856,8 +3826,9 @@ msgid "Ok" msgstr "OK" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "Impossible d'instancier plusieurs nÅ“uds sans nÅ“ud racine." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -4119,12 +4090,12 @@ msgstr "Créer un maillage de contour…" #: editor/plugins/mesh_instance_editor_plugin.cpp #, fuzzy msgid "View UV1" -msgstr "Affichage" +msgstr "Afficher l'UV1" #: editor/plugins/mesh_instance_editor_plugin.cpp #, fuzzy msgid "View UV2" -msgstr "Affichage" +msgstr "Afficher l'UV2" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Unwrap UV2 for Lightmap/AO" @@ -4248,9 +4219,8 @@ msgid "Bake!" msgstr "Calculer !" #: editor/plugins/navigation_mesh_editor_plugin.cpp -#, fuzzy msgid "Bake the navigation mesh." -msgstr "Créer un maillage de navigation\n" +msgstr "Précalculer le maillage de navigation." #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Clear the navigation mesh." @@ -4267,21 +4237,19 @@ msgstr "Calcul de la taille de la grille..." #: editor/plugins/navigation_mesh_generator.cpp #, fuzzy msgid "Creating heightfield..." -msgstr "Création de l'octree de lumière" +msgstr "Création de la heightmap..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Marking walkable triangles..." msgstr "Marquage des triangles parcourables..." #: editor/plugins/navigation_mesh_generator.cpp -#, fuzzy msgid "Constructing compact heightfield..." -msgstr "Construction d'un terrain compact..." +msgstr "Construction d'une heightmap compacte..." #: editor/plugins/navigation_mesh_generator.cpp -#, fuzzy msgid "Eroding walkable area..." -msgstr "Abrasion de la zone parcourable..." +msgstr "Réduction de la zone parcourable..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Partitioning..." @@ -4292,7 +4260,6 @@ msgid "Creating contours..." msgstr "Création des coutours..." #: editor/plugins/navigation_mesh_generator.cpp -#, fuzzy msgid "Creating polymesh..." msgstr "Création d'un maillage de contour…" @@ -4519,9 +4486,8 @@ msgid "Set Curve In Position" msgstr "Définir courbe en position" #: editor/plugins/path_editor_plugin.cpp -#, fuzzy msgid "Set Curve Out Position" -msgstr "Définir courbe hors position" +msgstr "Définir la position de sortie de la courbe" #: editor/plugins/path_editor_plugin.cpp msgid "Split Path" @@ -4655,9 +4621,8 @@ msgid "Clear Recent Files" msgstr "Effacer les fichiers récents" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Close and save changes?" -msgstr "Quitter et sauvegarder les modifications?" +msgstr "Quitter et sauvegarder les modifications ?" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme" @@ -4728,14 +4693,12 @@ msgid "Soft Reload Script" msgstr "Recharger le script (mode doux)" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Copy Script Path" -msgstr "Copier le chemin" +msgstr "Copier le chemin du script" #: editor/plugins/script_editor_plugin.cpp -#, fuzzy msgid "Show In File System" -msgstr "Montrer dans le système de fichiers" +msgstr "Afficher dans le système de fichiers" #: editor/plugins/script_editor_plugin.cpp msgid "History Prev" @@ -4928,9 +4891,8 @@ msgid "Clone Down" msgstr "Cloner en dessous" #: editor/plugins/script_text_editor.cpp -#, fuzzy msgid "Fold/Unfold Line" -msgstr "Dérouler la ligne" +msgstr "Réduire/Développer la ligne" #: editor/plugins/script_text_editor.cpp msgid "Fold All Lines" @@ -5348,9 +5310,8 @@ msgid "XForm Dialog" msgstr "Dialogue XForm" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Select Mode (Q)" -msgstr "Sélectionner le mode (Q)\n" +msgstr "Sélectionner le mode (Q)" #: editor/plugins/spatial_editor_plugin.cpp msgid "" @@ -5379,14 +5340,12 @@ msgid "Local Coords" msgstr "Coordonnées locales" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Local Space Mode (%s)" -msgstr "Mode de mise à l'échelle (R)" +msgstr "Mode d'échelle local (%s)" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Mode (%s)" -msgstr "Mode d'aimantation :" +msgstr "Mode d'aimantation (%s)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -5633,7 +5592,7 @@ msgstr "Déplacer (Après)" #: editor/plugins/sprite_frames_editor_plugin.cpp #, fuzzy msgid "SpriteFrames" -msgstr "Pile des appels" +msgstr "Images du sprite" #: editor/plugins/style_box_editor_plugin.cpp msgid "StyleBox Preview:" @@ -5642,7 +5601,7 @@ msgstr "Aperçu de la StyleBox :" #: editor/plugins/style_box_editor_plugin.cpp #, fuzzy msgid "StyleBox" -msgstr "Style" +msgstr "Style Box" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Set Region Rect" @@ -7963,7 +7922,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8136,7 +8095,7 @@ msgstr "Tracer les maillages" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/he.po b/editor/translations/he.po index 79e919ba3c..57ec12e002 100644 --- a/editor/translations/he.po +++ b/editor/translations/he.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:54+0000\n" +"PO-Revision-Date: 2018-01-25 22:27+0000\n" "Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n" "Language-Team: Hebrew <https://hosted.weblate.org/projects/godot-engine/" "godot/he/>\n" @@ -1427,7 +1427,7 @@ msgstr "מתבצע × ×™×ª×•×—" #: editor/editor_node.cpp msgid "Creating Thumbnail" -msgstr "" +msgstr "× ×•×¦×¨×ª ×ª×ž×•× ×” ממוזערת" #: editor/editor_node.cpp msgid "This operation can't be done without a tree root." @@ -1495,6 +1495,8 @@ msgid "" "This resource was imported, so it's not editable. Change its settings in the " "import panel and then re-import." msgstr "" +"מש×ב ×–×” עבר יבו×, לכן ×ין ×פשרות לערוך ×ותו. יש ×œ×©× ×•×ª ×ת ההגדרות שלו ×‘×—×œ×•× ×™×ª " +"×”×™×™×‘×•× ×•××– ×œ×™×™×‘× ×©×•×‘." #: editor/editor_node.cpp msgid "" @@ -1513,27 +1515,27 @@ msgstr "" #: editor/editor_node.cpp msgid "Expand all properties" -msgstr "" +msgstr "הרחבת כל המ××¤×™×™× ×™×" #: editor/editor_node.cpp msgid "Collapse all properties" -msgstr "" +msgstr "×¦×ž×¦×•× ×›×œ המ××¤×™×™× ×™×" #: editor/editor_node.cpp msgid "Copy Params" -msgstr "" +msgstr "העתקת ×ž×©×ª× ×™×" #: editor/editor_node.cpp msgid "Paste Params" -msgstr "" +msgstr "הדבקת ×ž×©×ª× ×™×" #: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp msgid "Paste Resource" -msgstr "" +msgstr "הדבקת מש×ב" #: editor/editor_node.cpp msgid "Copy Resource" -msgstr "" +msgstr "העתקת מש×ב" #: editor/editor_node.cpp msgid "Make Built-In" @@ -1545,11 +1547,11 @@ msgstr "" #: editor/editor_node.cpp msgid "Open in Help" -msgstr "" +msgstr "פתיחה בעזרה" #: editor/editor_node.cpp msgid "There is no defined scene to run." -msgstr "" +msgstr "×ין ×¡×¦× ×” מוגדרת להרצה." #: editor/editor_node.cpp msgid "" @@ -1574,55 +1576,55 @@ msgstr "" #: editor/editor_node.cpp msgid "Current scene was never saved, please save it prior to running." -msgstr "" +msgstr "×”×¡×¦× ×” ×”× ×•×›×—×™×ª ×ž×¢×•×œ× ×œ× × ×©×ž×¨×”, × × ×œ×©×ž×•×¨ ×ותה ×‘×˜×¨× ×”×”×¨×¦×”." #: editor/editor_node.cpp msgid "Could not start subprocess!" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ להפעיל תהליך ×ž×©× ×”!" #: editor/editor_node.cpp msgid "Open Scene" -msgstr "" +msgstr "פתיחת ×¡×¦× ×”" #: editor/editor_node.cpp msgid "Open Base Scene" -msgstr "" +msgstr "פתיחת ×¡×¦× ×ª בסיס" #: editor/editor_node.cpp msgid "Quick Open Scene.." -msgstr "" +msgstr "פתיחת ×¡×¦× ×” מהירה…" #: editor/editor_node.cpp msgid "Quick Open Script.." -msgstr "" +msgstr "פתיחת סקריפט מהירה…" #: editor/editor_node.cpp msgid "Save & Close" -msgstr "" +msgstr "שמירה וסגירה" #: editor/editor_node.cpp msgid "Save changes to '%s' before closing?" -msgstr "" +msgstr "לשמור ×ת ×”×©×™× ×•×™×™× ×œÖ¾â€š%s’ ×œ×¤× ×™ הסגירה?" #: editor/editor_node.cpp msgid "Save Scene As.." -msgstr "" +msgstr "שמירת ×¡×¦× ×” בש×…" #: editor/editor_node.cpp msgid "No" -msgstr "" +msgstr "ל×" #: editor/editor_node.cpp msgid "Yes" -msgstr "" +msgstr "כן" #: editor/editor_node.cpp msgid "This scene has never been saved. Save before running?" -msgstr "" +msgstr "×¡×¦× ×” ×–×ת ×ž×¢×•×œ× ×œ× × ×©×ž×¨×”. לשמור ×œ×¤× ×™ ההרצה?" #: editor/editor_node.cpp editor/scene_tree_dock.cpp msgid "This operation can't be done without a scene." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לבצע פעולה זו ×œ×œ× ×¡×¦× ×”." #: editor/editor_node.cpp msgid "Export Mesh Library" @@ -1630,7 +1632,7 @@ msgstr "" #: editor/editor_node.cpp msgid "This operation can't be done without a root node." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לבצע פעולה זו ×œ×œ× ×ž×¤×¨×§ עליון." #: editor/editor_node.cpp msgid "Export Tile Set" @@ -1638,23 +1640,23 @@ msgstr "" #: editor/editor_node.cpp msgid "This operation can't be done without a selected node." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לבצע פעולה זו ×œ×œ× ×‘×—×™×¨×” של מפרק." #: editor/editor_node.cpp msgid "Current scene not saved. Open anyway?" -msgstr "" +msgstr "×”×¡×¦× ×” ×”× ×•×›×—×™×ª ×œ× × ×©×ž×¨×”. לפתוח בכל ×–×ת?" #: editor/editor_node.cpp msgid "Can't reload a scene that was never saved." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ ×œ×¨×¢× ×Ÿ ×¡×¦× ×” ×©×ž×¢×•×œ× ×œ× × ×©×ž×¨×”." #: editor/editor_node.cpp msgid "Revert" -msgstr "" +msgstr "שחזור" #: editor/editor_node.cpp msgid "This action cannot be undone. Revert anyway?" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לבטל פעולה זו. לשחזר בכל ×–×ת?" #: editor/editor_node.cpp msgid "Quick Run Scene.." @@ -1662,49 +1664,51 @@ msgstr "" #: editor/editor_node.cpp msgid "Quit" -msgstr "" +msgstr "יצי××”" #: editor/editor_node.cpp msgid "Exit the editor?" -msgstr "" +msgstr "לצ×ת מהעורך?" #: editor/editor_node.cpp msgid "Open Project Manager?" -msgstr "" +msgstr "לפתוח ×ת ×ž× ×”×œ המיזמי×?" #: editor/editor_node.cpp msgid "Save & Quit" -msgstr "" +msgstr "לשמור ולצ×ת" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before quitting?" -msgstr "" +msgstr "לשמור ×ת ×”×©×™× ×•×™×™× ×œ×¡×¦× ×•×ª הב×ות ×œ×¤× ×™ היצי××”?" #: editor/editor_node.cpp msgid "Save changes the following scene(s) before opening Project Manager?" -msgstr "" +msgstr "לשמור ×ת ×”×¡×¦× ×•×ª הב×ות ×œ×¤× ×™ פתיחת ×ž× ×”×œ המיזמי×?" #: editor/editor_node.cpp msgid "" "This option is deprecated. Situations where refresh must be forced are now " "considered a bug. Please report." msgstr "" +"×פשרות זו ××™× ×” ×–×ž×™× ×” עוד. ×ž×¦×‘×™× ×‘×”× ×™×© ל×לץ ×¨×¢× ×•×Ÿ × ×—×©×‘×™× ×œ×‘××’×™×. × × ×œ×“×•×•×— " +"עליה×." #: editor/editor_node.cpp msgid "Pick a Main Scene" -msgstr "" +msgstr "× × ×œ×‘×—×•×¨ ×¡×¦× ×” ר×שית" #: editor/editor_node.cpp msgid "Unable to enable addon plugin at: '%s' parsing of config failed." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לפתוח ×ת תוסף ההרחבות תחת: ‚%s’ ×¤×¢× ×•×— ההגדרות × ×›×©×œ." #: editor/editor_node.cpp msgid "Unable to find script field for addon plugin at: 'res://addons/%s'." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ ×œ×ž×¦×•× ×©×“×” סקריפט עבור תוסף הרחבה תחת ‚res://addons/%s’." #: editor/editor_node.cpp msgid "Unable to load addon script from path: '%s'." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לטעון סקריפט הרחבה ×ž×”× ×ª×™×‘: ‚%s’." #: editor/editor_node.cpp msgid "" @@ -1783,23 +1787,23 @@ msgstr "" #: editor/editor_node.cpp msgid "Add a new scene." -msgstr "" +msgstr "הוספת ×¡×¦× ×” חדשה." #: editor/editor_node.cpp msgid "Scene" -msgstr "" +msgstr "×¡×¦× ×”" #: editor/editor_node.cpp msgid "Go to previously opened scene." -msgstr "" +msgstr "מעבר ×œ×¡×¦× ×” ×©× ×¤×ª×—×” ×§×•×“× ×œ×›×Ÿ." #: editor/editor_node.cpp msgid "Next tab" -msgstr "" +msgstr "×”×œ×©×•× ×™×ª הב××”" #: editor/editor_node.cpp msgid "Previous tab" -msgstr "" +msgstr "×”×œ×©×•× ×™×ª הקודמת" #: editor/editor_node.cpp msgid "Filter Files.." @@ -1807,39 +1811,39 @@ msgstr "" #: editor/editor_node.cpp msgid "Operations with scene files." -msgstr "" +msgstr "פעולות ×¢× ×§×•×‘×¦×™ ×¡×¦× ×•×ª." #: editor/editor_node.cpp msgid "New Scene" -msgstr "" +msgstr "×¡×¦× ×” חדשה" #: editor/editor_node.cpp msgid "New Inherited Scene.." -msgstr "" +msgstr "×¡×¦× ×” חדשה בירושה…" #: editor/editor_node.cpp msgid "Open Scene.." -msgstr "" +msgstr "פתיחת ×¡×¦× ×”â€¦" #: editor/editor_node.cpp msgid "Save Scene" -msgstr "" +msgstr "שמירת ×¡×¦× ×”" #: editor/editor_node.cpp msgid "Save all Scenes" -msgstr "" +msgstr "שמירת כל ×”×¡×¦× ×•×ª" #: editor/editor_node.cpp msgid "Close Scene" -msgstr "" +msgstr "סגירת ×¡×¦× ×”" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Open Recent" -msgstr "" +msgstr "פתיחה מה××—×¨×•× ×™×" #: editor/editor_node.cpp msgid "Convert To.." -msgstr "" +msgstr "המרה ×ל…" #: editor/editor_node.cpp msgid "MeshLibrary.." @@ -1852,62 +1856,64 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/script_text_editor.cpp #: scene/gui/line_edit.cpp scene/gui/text_edit.cpp msgid "Undo" -msgstr "" +msgstr "ביטול" #: editor/editor_node.cpp editor/plugins/script_text_editor.cpp #: scene/gui/line_edit.cpp msgid "Redo" -msgstr "" +msgstr "ביצוע חוזר" #: editor/editor_node.cpp msgid "Revert Scene" -msgstr "" +msgstr "שחזור ×¡×¦× ×”" #: editor/editor_node.cpp msgid "Miscellaneous project or scene-wide tools." -msgstr "" +msgstr "×›×œ×™× ×©×•× ×™× ×œ×ž×™×–× ×ו למגוון ×¡×¦× ×•×ª." #: editor/editor_node.cpp msgid "Project" -msgstr "" +msgstr "מיז×" #: editor/editor_node.cpp msgid "Project Settings" -msgstr "" +msgstr "הגדרות מיז×" #: editor/editor_node.cpp msgid "Run Script" -msgstr "" +msgstr "הרצת סקריפט" #: editor/editor_node.cpp editor/project_export.cpp msgid "Export" -msgstr "" +msgstr "ייצו×" #: editor/editor_node.cpp msgid "Tools" -msgstr "" +msgstr "כלי×" #: editor/editor_node.cpp msgid "Quit to Project List" -msgstr "" +msgstr "יצי××” לרשימת המיזמי×" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Debug" -msgstr "" +msgstr "× ×™×¤×•×™ שגי×ות" #: editor/editor_node.cpp msgid "Deploy with Remote Debug" -msgstr "" +msgstr "הטעמה ×¢× × ×™×¤×•×™ שגי×ות מרחוק" #: editor/editor_node.cpp msgid "" "When exporting or deploying, the resulting executable will attempt to " "connect to the IP of this computer in order to be debugged." msgstr "" +"בעת ×™×™×¦×•× ×ו הטמעה, קובץ ההפעלה ×™× ×¡×” להתחבר לכתובת ×”Ö¾IP של המחשב ×”×–×” לצורך " +"× ×™×¤×•×™ שגי×ות." #: editor/editor_node.cpp msgid "Small Deploy with Network FS" -msgstr "" +msgstr "הטמעה ×§×˜× ×” ×¢× ×ž×¢×¨×›×ª ×§×‘×¦×™× ×‘×¨×©×ª" #: editor/editor_node.cpp msgid "" @@ -1931,7 +1937,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Visible Navigation" -msgstr "" +msgstr "× ×™×•×•×˜ גלוי" #: editor/editor_node.cpp msgid "" @@ -1941,7 +1947,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Sync Scene Changes" -msgstr "" +msgstr "×¡× ×›×¨×•×Ÿ ×”×©×™× ×•×™×™× ×‘×¡×¦× ×”" #: editor/editor_node.cpp msgid "" @@ -1953,7 +1959,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Sync Script Changes" -msgstr "" +msgstr "×¡× ×›×¨×•×Ÿ ×”×©×™× ×•×™×™× ×‘×¡×§×¨×™×¤×˜" #: editor/editor_node.cpp msgid "" @@ -1965,31 +1971,31 @@ msgstr "" #: editor/editor_node.cpp msgid "Editor" -msgstr "" +msgstr "עורך" #: editor/editor_node.cpp editor/settings_config_dialog.cpp msgid "Editor Settings" -msgstr "" +msgstr "הגדרות עורך" #: editor/editor_node.cpp msgid "Editor Layout" -msgstr "" +msgstr "פריסת עורך" #: editor/editor_node.cpp msgid "Toggle Fullscreen" -msgstr "" +msgstr "×›× ×™×¡×” ×ל/יצי××” ממסך מל×" #: editor/editor_node.cpp editor/project_export.cpp msgid "Manage Export Templates" -msgstr "" +msgstr "× ×™×”×•×œ ×ª×‘× ×™×•×ª ייצו×" #: editor/editor_node.cpp msgid "Help" -msgstr "" +msgstr "עזרה" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Classes" -msgstr "" +msgstr "מחלקות" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp #: editor/plugins/script_editor_plugin.cpp @@ -2000,188 +2006,188 @@ msgstr "חיפוש" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Online Docs" -msgstr "" +msgstr "×ž×¡×ž×›×™× ×ž×§×•×•× ×™×" #: editor/editor_node.cpp msgid "Q&A" -msgstr "" +msgstr "שו״ת" #: editor/editor_node.cpp msgid "Issue Tracker" -msgstr "" +msgstr "עוקב תקלות" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp msgid "Community" -msgstr "" +msgstr "קהילה" #: editor/editor_node.cpp msgid "About" -msgstr "" +msgstr "על ×ודות" #: editor/editor_node.cpp msgid "Play the project." -msgstr "" +msgstr "× ×’×™× ×ª המיז×…" #: editor/editor_node.cpp msgid "Play" -msgstr "" +msgstr "× ×’×™× ×”" #: editor/editor_node.cpp msgid "Pause the scene" -msgstr "" +msgstr "השהיית ×”×¡×¦× ×”" #: editor/editor_node.cpp msgid "Pause Scene" -msgstr "" +msgstr "השהיית ×¡×¦× ×”" #: editor/editor_node.cpp msgid "Stop the scene." -msgstr "" +msgstr "עצירת ×”×¡×¦× ×”." #: editor/editor_node.cpp msgid "Stop" -msgstr "" +msgstr "עצירה" #: editor/editor_node.cpp msgid "Play the edited scene." -msgstr "" +msgstr "× ×’×™× ×ª ×”×¡×¦× ×” ×©× ×¢×¨×›×”." #: editor/editor_node.cpp msgid "Play Scene" -msgstr "" +msgstr "× ×’×™× ×ª ×”×¡×¦× ×”" #: editor/editor_node.cpp msgid "Play custom scene" -msgstr "" +msgstr "× ×’×™× ×ª ×¡×¦× ×” מות×מת ×ישית" #: editor/editor_node.cpp msgid "Play Custom Scene" -msgstr "" +msgstr "× ×’×™× ×ª ×¡×¦× ×” בהת×מה ×ישית" #: editor/editor_node.cpp msgid "Spins when the editor window repaints!" -msgstr "" +msgstr "מסתובב ×›×שר חלון העורך מצויר מחדש!" #: editor/editor_node.cpp msgid "Update Always" -msgstr "" +msgstr "לעדכן תמיד" #: editor/editor_node.cpp msgid "Update Changes" -msgstr "" +msgstr "עדכון ×©×™× ×•×™×™×" #: editor/editor_node.cpp msgid "Disable Update Spinner" -msgstr "" +msgstr "השבתת שבשבת עדכון" #: editor/editor_node.cpp msgid "Inspector" -msgstr "" +msgstr "חוקר" #: editor/editor_node.cpp msgid "Create a new resource in memory and edit it." -msgstr "" +msgstr "יצירת מש×ב חדש בזיכרון ועריכתו." #: editor/editor_node.cpp msgid "Load an existing resource from disk and edit it." -msgstr "" +msgstr "×˜×¢×™× ×ª מש×ב ×§×™×™× ×ž×”×›×•× ×Ÿ ועריכתו." #: editor/editor_node.cpp msgid "Save the currently edited resource." -msgstr "" +msgstr "שמירת המש×ב ×©× ×¢×¨×š כרגע." #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Save As.." -msgstr "" +msgstr "שמירה בש×…" #: editor/editor_node.cpp msgid "Go to the previous edited object in history." -msgstr "" +msgstr "מעבר לפריט ×”×חרון ×©× ×¢×¨×š מההיסטוריה." #: editor/editor_node.cpp msgid "Go to the next edited object in history." -msgstr "" +msgstr "מעבר לפריט ×”×‘× ×©× ×¢×¨×š מההיסטוריה." #: editor/editor_node.cpp msgid "History of recently edited objects." -msgstr "" +msgstr "היסטוריה של ×”×¤×¨×™×˜×™× ×©× ×¢×¨×›×• ל××—×¨×•× ×”." #: editor/editor_node.cpp msgid "Object properties." -msgstr "" +msgstr "מ××¤×™×™× ×™ פריט." #: editor/editor_node.cpp msgid "Changes may be lost!" -msgstr "" +msgstr "×”×©×™× ×•×™×™× ×¢×©×•×™×™× ×œ×œ×›×ª ל×יבוד!" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp #: editor/project_manager.cpp msgid "Import" -msgstr "" +msgstr "ייבו×" #: editor/editor_node.cpp msgid "Node" -msgstr "" +msgstr "מפרק" #: editor/editor_node.cpp msgid "FileSystem" -msgstr "" +msgstr "מערכת קבצי×" #: editor/editor_node.cpp msgid "Output" -msgstr "" +msgstr "פלט" #: editor/editor_node.cpp msgid "Don't Save" -msgstr "" +msgstr "×œ× ×œ×©×ž×•×¨" #: editor/editor_node.cpp msgid "Import Templates From ZIP File" -msgstr "" +msgstr "×™×™×‘×•× ×ª×‘× ×™×•×ª מקובץ ZIP" #: editor/editor_node.cpp editor/project_export.cpp msgid "Export Project" -msgstr "" +msgstr "×™×™×¦×•× ×ž×™×–×" #: editor/editor_node.cpp msgid "Export Library" -msgstr "" +msgstr "×™×™×¦×•× ×¡×¤×¨×™×”" #: editor/editor_node.cpp msgid "Merge With Existing" -msgstr "" +msgstr "מיזוג ×¢× × ×•×›×—×™×™×" #: editor/editor_node.cpp msgid "Password:" -msgstr "" +msgstr "ססמה:" #: editor/editor_node.cpp msgid "Open & Run a Script" -msgstr "" +msgstr "פתיחה והרצה של סקריפט" #: editor/editor_node.cpp msgid "New Inherited" -msgstr "" +msgstr "חדש בירושה" #: editor/editor_node.cpp msgid "Load Errors" -msgstr "" +msgstr "שגי×ות ×˜×¢×™× ×”" #: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp msgid "Select" -msgstr "" +msgstr "בחירה" #: editor/editor_node.cpp msgid "Open 2D Editor" -msgstr "" +msgstr "פתיחת עורך דו־ממד" #: editor/editor_node.cpp msgid "Open 3D Editor" -msgstr "" +msgstr "פתיחת עורך תלת־ממד" #: editor/editor_node.cpp msgid "Open Script Editor" -msgstr "" +msgstr "פתיחת עורך סקריפטי×" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" @@ -2189,11 +2195,11 @@ msgstr "" #: editor/editor_node.cpp msgid "Open the next Editor" -msgstr "" +msgstr "פתיחת העורך הב×" #: editor/editor_node.cpp msgid "Open the previous Editor" -msgstr "" +msgstr "פתיחת העורך הקוד×" #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -2201,28 +2207,28 @@ msgstr "" #: editor/editor_plugin.cpp msgid "Thumbnail.." -msgstr "" +msgstr "×ª×ž×•× ×” ממוזערת…" #: editor/editor_plugin_settings.cpp msgid "Installed Plugins:" -msgstr "" +msgstr "×ª×•×¡×¤×™× ×ž×•×ª×§× ×™×:" #: editor/editor_plugin_settings.cpp msgid "Update" -msgstr "" +msgstr "עדכון" #: editor/editor_plugin_settings.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Version:" -msgstr "" +msgstr "גרסה:" #: editor/editor_plugin_settings.cpp msgid "Author:" -msgstr "" +msgstr "יוצר:" #: editor/editor_plugin_settings.cpp msgid "Status:" -msgstr "" +msgstr "מצב:" #: editor/editor_profiler.cpp msgid "Stop Profiling" @@ -2234,27 +2240,27 @@ msgstr "" #: editor/editor_profiler.cpp msgid "Measure:" -msgstr "" +msgstr "מדידה:" #: editor/editor_profiler.cpp msgid "Frame Time (sec)" -msgstr "" +msgstr "זמן שקופית (×©× ×™×•×ª)" #: editor/editor_profiler.cpp msgid "Average Time (sec)" -msgstr "" +msgstr "זמן ממוצע (×©× ×™×•×ª)" #: editor/editor_profiler.cpp msgid "Frame %" -msgstr "" +msgstr "שקופית %" #: editor/editor_profiler.cpp msgid "Physics Frame %" -msgstr "" +msgstr "שקופית פיזיקלית %" #: editor/editor_profiler.cpp editor/script_editor_debugger.cpp msgid "Time:" -msgstr "" +msgstr "זמן:" #: editor/editor_profiler.cpp msgid "Inclusive" @@ -2262,23 +2268,23 @@ msgstr "" #: editor/editor_profiler.cpp msgid "Self" -msgstr "" +msgstr "עצמי" #: editor/editor_profiler.cpp msgid "Frame #:" -msgstr "" +msgstr "שקופית מס׳:" #: editor/editor_profiler.cpp msgid "Time" -msgstr "" +msgstr "זמן" #: editor/editor_profiler.cpp msgid "Calls" -msgstr "" +msgstr "קרי×ות" #: editor/editor_run_native.cpp msgid "Select device from the list" -msgstr "" +msgstr "× × ×œ×‘×—×•×¨ התקן מהרשימה" #: editor/editor_run_native.cpp msgid "" @@ -2288,11 +2294,11 @@ msgstr "" #: editor/editor_run_script.cpp msgid "Write your logic in the _run() method." -msgstr "" +msgstr "× ×™×ª×Ÿ לכתוב ×ת הלוגיקה שלך בשיטה ‎_run()‎." #: editor/editor_run_script.cpp msgid "There is an edited scene already." -msgstr "" +msgstr "כבר יש ×¡×¦× ×” בעריכה." #: editor/editor_run_script.cpp msgid "Couldn't instance script:" @@ -2300,55 +2306,55 @@ msgstr "" #: editor/editor_run_script.cpp msgid "Did you forget the 'tool' keyword?" -msgstr "" +msgstr "יכול להיות ששכחת ×ת מילת המפתח ‚tool’?" #: editor/editor_run_script.cpp msgid "Couldn't run script:" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ להריץ ×ת הסקריפט:" #: editor/editor_run_script.cpp msgid "Did you forget the '_run' method?" -msgstr "" +msgstr "שכחת ×ת השיטה ‚‎_run’?" #: editor/editor_settings.cpp msgid "Default (Same as Editor)" -msgstr "" +msgstr "בררת מחדל (כמו העורך)" #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" -msgstr "" +msgstr "× × ×œ×‘×—×•×¨ ×ž×¤×¨×§×™× ×œ×™×™×¦×•×" #: editor/editor_sub_scene.cpp msgid "Scene Path:" -msgstr "" +msgstr "× ×ª×™×‘ ×¡×¦× ×•×ª:" #: editor/editor_sub_scene.cpp msgid "Import From Node:" -msgstr "" +msgstr "×™×™×‘×•× ×ž×ž×¤×¨×§:" #: editor/export_template_manager.cpp msgid "Re-Download" -msgstr "" +msgstr "הורדה מחדש" #: editor/export_template_manager.cpp msgid "Uninstall" -msgstr "" +msgstr "הסרה" #: editor/export_template_manager.cpp msgid "(Installed)" -msgstr "" +msgstr "(מותקן)" #: editor/export_template_manager.cpp msgid "Download" -msgstr "" +msgstr "הורדה" #: editor/export_template_manager.cpp msgid "(Missing)" -msgstr "" +msgstr "(חסר)" #: editor/export_template_manager.cpp msgid "(Current)" -msgstr "" +msgstr "(× ×•×›×—×™)" #: editor/export_template_manager.cpp msgid "Retrieving mirrors, please wait.." @@ -2356,15 +2362,15 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Remove template version '%s'?" -msgstr "" +msgstr "להסיר ×ת גרסת ×”×ª×‘× ×™×ª ‚%s’?" #: editor/export_template_manager.cpp msgid "Can't open export templates zip." -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ ×œ×™×™×¦× zip של ×ª×‘× ×™×•×ª." #: editor/export_template_manager.cpp msgid "Invalid version.txt format inside templates." -msgstr "" +msgstr "×ª×‘× ×™×ª ×”Ö¾version.txt שגויה בתוך ×”×ª×‘× ×™×•×ª." #: editor/export_template_manager.cpp msgid "" @@ -7513,23 +7519,23 @@ msgstr "" #: modules/visual_script/visual_script_editor.cpp msgid "Find Node Type" -msgstr "" +msgstr "×יתור סוג מפרק" #: modules/visual_script/visual_script_editor.cpp msgid "Copy Nodes" -msgstr "" +msgstr "העתקת מפרקי×" #: modules/visual_script/visual_script_editor.cpp msgid "Cut Nodes" -msgstr "" +msgstr "גזירת מפרקי×" #: modules/visual_script/visual_script_editor.cpp msgid "Paste Nodes" -msgstr "" +msgstr "הדבקת מפרקי×" #: modules/visual_script/visual_script_flow_control.cpp msgid "Input type not iterable: " -msgstr "" +msgstr "סוג הקלט ×œ× ×–×ž×™×Ÿ למחזוריות: " #: modules/visual_script/visual_script_flow_control.cpp msgid "Iterator became invalid" @@ -7541,15 +7547,15 @@ msgstr "" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Invalid index property name." -msgstr "" +msgstr "×©× ×ž×פיין ×”××™× ×“×§×¡ שגוי." #: modules/visual_script/visual_script_func_nodes.cpp msgid "Base object is not a Node!" -msgstr "" +msgstr "×¢×¦× ×”×‘×¡×™×¡ ××™× × ×• מפרק!" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Path does not lead Node!" -msgstr "" +msgstr "×”× ×ª×™×‘ ×œ× ×ž×•×‘×™×œ מפרק!" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Invalid index property name '%s' in node %s." @@ -7557,23 +7563,23 @@ msgstr "" #: modules/visual_script/visual_script_nodes.cpp msgid ": Invalid argument of type: " -msgstr "" +msgstr ": ××¨×’×•×ž× ×˜ שגוי מסוג: " #: modules/visual_script/visual_script_nodes.cpp msgid ": Invalid arguments: " -msgstr "" +msgstr ": ××¨×’×•×ž× ×˜×™× ×©×’×•×™×™×: " #: modules/visual_script/visual_script_nodes.cpp msgid "VariableGet not found in script: " -msgstr "" +msgstr "×œ× × ×ž×¦× VariableGet בסקריפט: " #: modules/visual_script/visual_script_nodes.cpp msgid "VariableSet not found in script: " -msgstr "" +msgstr "×œ× × ×ž×¦× VariableSet בסקריפט: " #: modules/visual_script/visual_script_nodes.cpp msgid "Custom node has no _step() method, can't process graph." -msgstr "" +msgstr "למפרק המות×× ×ין שיטת ‎_step()‎, ×ין ×פשרות לעבד תרשי×." #: modules/visual_script/visual_script_nodes.cpp msgid "" @@ -7583,35 +7589,35 @@ msgstr "" #: platform/javascript/export/export.cpp msgid "Run in Browser" -msgstr "" +msgstr "הפעלה בדפדפן" #: platform/javascript/export/export.cpp msgid "Run exported HTML in the system's default browser." -msgstr "" +msgstr "הפעלת ×”Ö¾HTML ×”×ž×™×•×¦× ×‘×“×¤×“×¤×Ÿ בררת המחדל של המערכת." #: platform/javascript/export/export.cpp msgid "Could not write file:" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לכתוב קובץ:" #: platform/javascript/export/export.cpp msgid "Could not open template for export:" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ לפתוח ×ª×‘× ×™×ª לייצו×:" #: platform/javascript/export/export.cpp msgid "Invalid export template:" -msgstr "" +msgstr "×ª×‘× ×™×ª ×™×¦×•× ×©×’×•×™×”:" #: platform/javascript/export/export.cpp msgid "Could not read custom HTML shell:" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ž×¢×˜×¤×ª HTML מות×מת:" #: platform/javascript/export/export.cpp msgid "Could not read boot splash image file:" -msgstr "" +msgstr "×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×§×•×‘×¥ ×ª×ž×•× ×ª פתיח:" #: platform/javascript/export/export.cpp msgid "Using default boot splash image." -msgstr "" +msgstr "× ×¢×©×” שימוש ×‘×ª×ž×•× ×ª הפתיח כבררת מחדל." #: scene/2d/animated_sprite.cpp msgid "" @@ -7629,7 +7635,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7687,16 +7693,17 @@ msgstr "" msgid "" "ParallaxLayer node only works when set as child of a ParallaxBackground node." msgstr "" +"מפרק ParallaxLayer עובד רק ×›×שר ×”×•× ×ž×•×’×“×¨ כצ××¦× ×©×œ מפרק ParallaxBackground." #: scene/2d/particles_2d.cpp scene/3d/particles.cpp msgid "" "A material to process the particles is not assigned, so no behavior is " "imprinted." -msgstr "" +msgstr "×œ× ×ž×•×§×¦×” חומר לעיבוד חלקיקי×, לכן ×œ× ×ª×•×˜×‘×¢ ×”×ª× ×”×’×•×ª." #: scene/2d/path_2d.cpp msgid "PathFollow2D only works when set as a child of a Path2D node." -msgstr "" +msgstr "PathFollow2D עובד רק ×›×שר ×”×•× ×ž×•×’×“×¨ כצ××¦× ×©×œ מפרק Path2D." #: scene/2d/physics_body_2d.cpp msgid "" @@ -7717,7 +7724,7 @@ msgstr "" #: scene/3d/arvr_nodes.cpp msgid "ARVRCamera must have an ARVROrigin node as its parent" -msgstr "" +msgstr "ל־ARVRCamera חייב להיות מפרק ARVROrigin כהורה שלו" #: scene/3d/arvr_nodes.cpp msgid "ARVRController must have an ARVROrigin node as its parent" @@ -7741,7 +7748,7 @@ msgstr "" #: scene/3d/arvr_nodes.cpp msgid "ARVROrigin requires an ARVRCamera child node" -msgstr "" +msgstr "ARVROrigin דורש מפרק צ××¦× ×ž×¡×•×’ ARVRCamera" #: scene/3d/baked_lightmap.cpp msgid "Plotting Meshes: " @@ -7763,7 +7770,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -7775,7 +7782,7 @@ msgstr "" #: scene/3d/collision_polygon.cpp msgid "An empty CollisionPolygon has no effect on collision." -msgstr "" +msgstr "ל־CollisionPolygon ריק ×ין כל השפעה על ×”×ª× ×’×©×•×ª." #: scene/3d/collision_shape.cpp msgid "" @@ -7895,11 +7902,11 @@ msgstr "" #: scene/resources/dynamic_font.cpp msgid "Unknown font format." -msgstr "" +msgstr "×ž×‘× ×” הגופן ×œ× ×™×“×•×¢." #: scene/resources/dynamic_font.cpp msgid "Error loading font." -msgstr "" +msgstr "שגי××” ×‘×˜×¢×™× ×ª הגופן." #: scene/resources/dynamic_font.cpp msgid "Invalid font size." diff --git a/editor/translations/hi.po b/editor/translations/hi.po index 0e7b325800..c124727d74 100644 --- a/editor/translations/hi.po +++ b/editor/translations/hi.po @@ -7680,7 +7680,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7814,7 +7814,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/hu.po b/editor/translations/hu.po index 0afd037bf6..9d89e98f7e 100644 --- a/editor/translations/hu.po +++ b/editor/translations/hu.po @@ -7737,7 +7737,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7871,7 +7871,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/id.po b/editor/translations/id.po index 538d49a628..ae7fd98ae2 100644 --- a/editor/translations/id.po +++ b/editor/translations/id.po @@ -8021,7 +8021,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8181,7 +8181,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/is.po b/editor/translations/is.po index 3bcd2c4004..3cccf15a6c 100644 --- a/editor/translations/is.po +++ b/editor/translations/is.po @@ -7628,7 +7628,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7762,7 +7762,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/it.po b/editor/translations/it.po index e068bcb758..86700a094b 100644 --- a/editor/translations/it.po +++ b/editor/translations/it.po @@ -8041,7 +8041,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8217,7 +8217,7 @@ msgstr "Bliting Immagini" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ja.po b/editor/translations/ja.po index e7ee2be567..061a3909e9 100644 --- a/editor/translations/ja.po +++ b/editor/translations/ja.po @@ -8723,7 +8723,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8887,7 +8887,7 @@ msgstr "イメージをé…ç½®(Blit)" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ko.po b/editor/translations/ko.po index 9d553c4405..29518c9797 100644 --- a/editor/translations/ko.po +++ b/editor/translations/ko.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-01-20 08:54+0000\n" -"Last-Translator: paijai 송 (fivejobi) <xotjq237@gmail.com>\n" +"PO-Revision-Date: 2018-01-20 17:48+0000\n" +"Last-Translator: 박한얼 <volzhs@gmail.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/" "godot/ko/>\n" "Language: ko\n" @@ -1328,19 +1328,18 @@ msgid "Description" msgstr "설명" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "íŠœí† ë¦¬ì–¼" +msgstr "온ë¼ì¸ íŠœí† ë¦¬ì–¼:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"현재 ì´ ë©”ì„œë“œì— ëŒ€í•œ ìƒì„¸ì„¤ëª…ì´ ì—†ìŠµë‹ˆë‹¤. [color=$color][url=$url]ê´€ë ¨ ì •ë³´" -"를 기여하여[/url][/color] ë” ë‚˜ì•„ì§€ê²Œ ë„와주세요!" +"현재 ì´ í´ëž˜ìŠ¤ì— 대한 íŠœí† ë¦¬ì–¼ì´ ì—†ìŠµë‹ˆë‹¤. [color=$color][url=$url]ë„ì›€ì„ ì£¼" +"시거나[/url][/color] [color=$color][url=$url2]ìš”ì² í•˜ì‹¤ 수[/url][/color] 있습" +"니다." #: editor/editor_help.cpp msgid "Properties" @@ -3760,7 +3759,7 @@ msgstr "확ì¸" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "루트 ë…¸ë“œì—†ì´ ì—¬ëŸ¬ê°œì˜ ë…¸ë“œë¥¼ ìƒì„±í• 수 없습니다." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5819,7 +5818,6 @@ msgid "Select current edited sub-tile." msgstr "현재 íŽ¸ì§‘ëœ ì„œë¸Œ íƒ€ì¼ ì„ íƒ." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." msgstr "서브 타ì¼ì„ ì„ íƒí•´ ìš°ì„ ìˆœìœ„ë¥¼ 바꿉니다." @@ -5930,9 +5928,8 @@ msgid "Please choose a 'project.godot' file." msgstr "'project.godot' 파ì¼ì„ ì„ íƒí•˜ì„¸ìš”." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "'project.godot' 파ì¼ì„ ì„ íƒí•˜ì„¸ìš”." +msgstr "비어있는 í´ë”를 ì„ íƒí•˜ì„¸ìš”." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7782,8 +7779,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"ì´ ë…¸ë“œëŠ” ëª¨ì–‘ì„ ê°–ëŠ” ìžì‹ 노드가 없어서, 공간ìƒì—ì„œ ìƒí˜¸ìž‘ìš©í• ìˆ˜ 없습니" +"다.\n" +"CollisionShape2D ë˜ëŠ” CollisionPolygon2Dì„ ìžì‹ 노드로 추가하여 ëª¨ì–‘ì„ ì •ì˜í•˜" +"세요." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7937,8 +7938,12 @@ msgstr "ë©”ì‹œì— ë¼ì´íŒ… 중: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"ì´ ë…¸ë“œëŠ” ëª¨ì–‘ì„ ê°–ëŠ” ìžì‹ 노드가 없어서, 공간ìƒì—ì„œ ìƒí˜¸ìž‘ìš©í• ìˆ˜ 없습니" +"다.\n" +"CollisionShape ë˜ëŠ” CollisionPolygonì„ ìžì‹ 노드로 추가하여 ëª¨ì–‘ì„ ì •ì˜í•˜ì„¸" +"ìš”." #: scene/3d/collision_polygon.cpp msgid "" @@ -8074,7 +8079,6 @@ msgid "(Other)" msgstr "(기타)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/lt.po b/editor/translations/lt.po index bc55b05326..5e5400d1e7 100644 --- a/editor/translations/lt.po +++ b/editor/translations/lt.po @@ -7650,7 +7650,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7784,7 +7784,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/nb.po b/editor/translations/nb.po index 0514473a4d..623898fcb5 100644 --- a/editor/translations/nb.po +++ b/editor/translations/nb.po @@ -7876,7 +7876,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8010,7 +8010,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/nl.po b/editor/translations/nl.po index 06afd6c7ab..c44ef3ebc8 100644 --- a/editor/translations/nl.po +++ b/editor/translations/nl.po @@ -10,26 +10,27 @@ # Cornee Traas <corneetraas@hotmail.com>, 2017. # Daeran Wereld <daeran@gmail.com>, 2017. # Dzejkop <jakubtrad@gmail.com>, 2017. +# Ferdinand de Coninck <ferdinand.deconinck@gmail.com>, 2018. # Maikel <maikel_martens_1@hotmail.com>, 2017. -# Pieter-Jan Briers <pieterjan.briers@gmail.com>, 2017. +# Pieter-Jan Briers <pieterjan.briers@gmail.com>, 2017-2018. # Robin Arys <robinarys@hotmail.com>, 2017. # Senno Kaasjager <senno.kaasjager@gmail.com>, 2017. -# Uxilo <jmolendijk93@gmail.com>, 2017. +# Uxilo <jmolendijk93@gmail.com>, 2017-2018. # Wout Standaert <wout@blobkat.com>, 2017. # Zatherz <zatherz@linux.pl>, 2017. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2017-12-20 15:43+0000\n" -"Last-Translator: Christophe Swolfs <swolfschristophe@gmail.com>\n" +"PO-Revision-Date: 2018-01-22 08:07+0000\n" +"Last-Translator: Pieter-Jan Briers <pieterjan.briers@gmail.com>\n" "Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/" "nl/>\n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2.18\n" +"X-Generator: Weblate 2.19-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -40,9 +41,8 @@ msgid "All Selection" msgstr "Alle Selectie" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Time" -msgstr "Anim Wijzig Waarde" +msgstr "Anim Wijzig Keyframe Waarde" #: editor/animation_editor.cpp msgid "Anim Change Transition" @@ -53,9 +53,8 @@ msgid "Anim Change Transform" msgstr "Anim Wijzig Transform" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Value" -msgstr "Anim Wijzig Waarde" +msgstr "Anim Wijzig Keyframe Waarde" #: editor/animation_editor.cpp msgid "Anim Change Call" @@ -508,9 +507,8 @@ msgid "Connecting Signal:" msgstr "Signaal aan het Verbinden:" #: editor/connections_dialog.cpp -#, fuzzy msgid "Disconnect '%s' from '%s'" -msgstr "Verbind '%s' met '%s'" +msgstr "Ontkoppel '%s' van '%s'" #: editor/connections_dialog.cpp msgid "Connect.." @@ -526,9 +524,8 @@ msgid "Signals" msgstr "Signalen" #: editor/create_dialog.cpp -#, fuzzy msgid "Change %s Type" -msgstr "Wijzig Array Waarde Type" +msgstr "Wijzig %s Type" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -536,9 +533,8 @@ msgid "Change" msgstr "Wijzig" #: editor/create_dialog.cpp -#, fuzzy msgid "Create New %s" -msgstr "Nieuwe Maken" +msgstr "Maake Nieuwe %s" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -651,9 +647,8 @@ msgstr "" "Toch verwijderen? (Kan niet ongedaan worden.)" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" -msgstr "Niet wisbaar:\n" +msgstr "Niet te verwijderen:" #: editor/dependency_editor.cpp msgid "Error loading:" @@ -736,9 +731,8 @@ msgid "Lead Developer" msgstr "Hoofdontwikkelaar" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "Project Manager" +msgstr "Project Manager " #: editor/editor_about.cpp msgid "Developers" @@ -847,9 +841,8 @@ msgid "Rename Audio Bus" msgstr "Hernoem audiobus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Change Audio Bus Volume" -msgstr "Verander audiobus solo" +msgstr "Verander Audio Bus Volume" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Solo" @@ -864,9 +857,8 @@ msgid "Toggle Audio Bus Bypass Effects" msgstr "Verander audiobusomleiding" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Select Audio Bus Send" -msgstr "Selecteer audiobus verzend" +msgstr "Selecteer Audiobus Verzend" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus Effect" @@ -915,7 +907,7 @@ msgstr "Effect Verwijderen" #: editor/editor_audio_buses.cpp msgid "Audio" -msgstr "" +msgstr "Audio" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus" @@ -1094,11 +1086,11 @@ msgstr "Scene aan het updaten.." #: editor/editor_data.cpp msgid "[empty]" -msgstr "" +msgstr "[leeg]" #: editor/editor_data.cpp msgid "[unsaved]" -msgstr "" +msgstr "[niet opgeslagen]" #: editor/editor_dir_dialog.cpp msgid "Please select a base directory first" @@ -1138,9 +1130,8 @@ msgid "Packing" msgstr "Inpakken" #: editor/editor_export.cpp platform/javascript/export/export.cpp -#, fuzzy msgid "Template file not found:" -msgstr "Template bestand niet gevonden:\n" +msgstr "Template bestand niet gevonden:" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "File Exists, Overwrite?" @@ -1351,19 +1342,18 @@ msgid "Description" msgstr "Beschrijving" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Online Documentatie" +msgstr "Online Documentatie:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Er is momenteel geen beschrijving voor deze methode. Help ons alsjeblieft " -"door [color=$color][url=$url]een toe te voegen[/url][/color]!" +"Er is momenteel geen handleiding voor deze methode. Help ons alsjeblieft " +"door [color=$color][url=$url]een toe te voegen[/url][/color] of [color=" +"$color][url=$url2]een aan te vragen[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -1417,9 +1407,8 @@ msgid "Clear" msgstr "Leegmaken" #: editor/editor_log.cpp -#, fuzzy msgid "Clear Output" -msgstr "Output" +msgstr "Maak Uitvoer Leeg" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" @@ -1483,13 +1472,12 @@ msgid "This operation can't be done without a tree root." msgstr "Deze operatie kan niet gedaan worden zonder boomwortel." #: editor/editor_node.cpp -#, fuzzy msgid "" "Couldn't save scene. Likely dependencies (instances or inheritance) couldn't " "be satisfied." msgstr "" -"Kon scene niet opslaan. Waarschijnlijk konden afhankelijkheden (instanties) " -"niet voldaan worden." +"Kon de scene niet opslaan. Waarschijnlijk konden afhankelijkheden " +"(instanties of erfelijkheden) niet voldaan worden." #: editor/editor_node.cpp msgid "Failed to load resource." @@ -1579,14 +1567,12 @@ msgstr "" "begrijpen." #: editor/editor_node.cpp -#, fuzzy msgid "Expand all properties" -msgstr "Klap alles uit" +msgstr "Klap alle eigenschappen uit" #: editor/editor_node.cpp -#, fuzzy msgid "Collapse all properties" -msgstr "Klap alles in" +msgstr "Klap alle eigenschappen in" #: editor/editor_node.cpp msgid "Copy Params" @@ -1707,6 +1693,7 @@ msgid "Export Mesh Library" msgstr "Exporteer Mesh Library" #: editor/editor_node.cpp +#, fuzzy msgid "This operation can't be done without a root node." msgstr "Deze bewerking is niet mogelijk zonder een hoofdknoop." @@ -2287,25 +2274,22 @@ msgid "Select" msgstr "Selecteer" #: editor/editor_node.cpp -#, fuzzy msgid "Open 2D Editor" -msgstr "Open 2D Editor" +msgstr "Open 2D Bewerker" #: editor/editor_node.cpp msgid "Open 3D Editor" msgstr "Open 3D Editor" #: editor/editor_node.cpp -#, fuzzy msgid "Open Script Editor" -msgstr "Open Script Editor" +msgstr "Open Script Bewerker" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" msgstr "Open Asset Bibliotheek" #: editor/editor_node.cpp -#, fuzzy msgid "Open the next Editor" msgstr "Open de volgende Editor" @@ -2388,14 +2372,12 @@ msgid "Frame #:" msgstr "Frame #:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Time" -msgstr "Tijd:" +msgstr "Tijd" #: editor/editor_profiler.cpp -#, fuzzy msgid "Calls" -msgstr "Aanroep" +msgstr "Aanroepen" #: editor/editor_run_native.cpp msgid "Select device from the list" @@ -2502,9 +2484,8 @@ msgid "No version.txt found inside templates." msgstr "Geen version.txt gevonden in sjablonen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for templates:" -msgstr "Fout bij het maken van een pad voor sjablonen:\n" +msgstr "Fout bij het maken van een pad voor sjablonen:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -2539,9 +2520,8 @@ msgstr "Geen antwoord." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp -#, fuzzy msgid "Request Failed." -msgstr "Aanv. Mislukt." +msgstr "Aanvraag Mislukt." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2587,7 +2567,6 @@ msgid "Connecting.." msgstr "Verbinden.." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't Connect" msgstr "Kan niet verbinden" @@ -2665,36 +2644,30 @@ msgid "View items as a list" msgstr "Bekijk objecten als een lijst" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Status: Import of file failed. Please fix file and reimport manually." msgstr "" -"\n" "Status: Importeren van bestand mislukt. Repareer het bestand en importeer " -"handmatig opnieuw." +"opnieuw handmatig." #: editor/filesystem_dock.cpp msgid "Cannot move/rename resources root." msgstr "Kan de hoofdmap voor resources niet verplaatsen of hernoemen." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Cannot move a folder into itself." -msgstr "Het is niet mogelijk om een map in zichzelf te stoppen.\n" +msgstr "Het is niet mogelijk om een map in zichzelf te verplaatsen." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "Fout bij het verplaatsen:\n" +msgstr "Fout bij het verplaatsen:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error duplicating:" -msgstr "Error bij het laden van:" +msgstr "Fout bij het dupliceren:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Unable to update dependencies:" -msgstr "Kon afhankelijkheden niet verversen:\n" +msgstr "Kon afhankelijkheden niet updaten:" #: editor/filesystem_dock.cpp msgid "No name provided" @@ -2725,22 +2698,18 @@ msgid "Renaming folder:" msgstr "Hernoemen folder:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicating file:" -msgstr "Dupliceren" +msgstr "Bestand dupliceren:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicating folder:" -msgstr "Hernoemen folder:" +msgstr "Folder dupliceren:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Expand all" msgstr "Klap alles uit" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Collapse all" msgstr "Klap alles in" @@ -2753,9 +2722,8 @@ msgid "Move To.." msgstr "Verplaats Naar.." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Open Scene(s)" -msgstr "Scene Openen" +msgstr "Scene(s) Openen" #: editor/filesystem_dock.cpp msgid "Instance" @@ -2770,9 +2738,8 @@ msgid "View Owners.." msgstr "Bekijk eigenaren.." #: editor/filesystem_dock.cpp -#, fuzzy msgid "Duplicate.." -msgstr "Dupliceren" +msgstr "Dupliceren.." #: editor/filesystem_dock.cpp msgid "Previous Directory" @@ -2872,11 +2839,11 @@ msgstr "Scene Importeren.." #: editor/import/resource_importer_scene.cpp msgid "Generating Lightmaps" -msgstr "" +msgstr "Bouw Lightmappen" #: editor/import/resource_importer_scene.cpp msgid "Generating for Mesh: " -msgstr "" +msgstr "Bouw voor Mesh: " #: editor/import/resource_importer_scene.cpp msgid "Running Custom Script.." @@ -3105,7 +3072,6 @@ msgid "Save the current animation" msgstr "Sla de huidige animatie op" #: editor/plugins/animation_player_editor_plugin.cpp -#, fuzzy msgid "Display list of animations in player." msgstr "Geef lijst van animaties weer in speler." @@ -3115,7 +3081,7 @@ msgstr "Automatisch afspelen bij laden" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Edit Target Blend Times" -msgstr "" +msgstr "Bewerk Doel Mengtijden" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation Tools" @@ -3127,7 +3093,7 @@ msgstr "Kopieer Animatie" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Onion Skinning" -msgstr "" +msgstr "Ui Schillen" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Enable Onion Skinning" @@ -3169,7 +3135,7 @@ msgstr "Alleen verschillen" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Force White Modulate" -msgstr "" +msgstr "Forceer Witte Modulatie" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Include Gizmos (3D)" @@ -3241,14 +3207,12 @@ msgid "Auto Restart:" msgstr "Automatische herstart:" #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "Restart (s):" -msgstr "Herstart (en):" +msgstr "Herstarten (s):" #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "Random Restart (s):" -msgstr "Willekeurige herstart (en):" +msgstr "Willekeurige Herstart (s):" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Start!" @@ -3265,15 +3229,15 @@ msgstr "Mengen" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend 0:" -msgstr "" +msgstr "Meng 0:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend 1:" -msgstr "" +msgstr "Meng 1:" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "X-Fade Time (s):" -msgstr "" +msgstr "X-Fade Tijd (en):" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Current:" @@ -3285,11 +3249,11 @@ msgstr "Voeg invoer toe" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Clear Auto-Advance" -msgstr "" +msgstr "Verwijder Automatische Voortgang." #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Set Auto-Advance" -msgstr "" +msgstr "Automatische voortgang instellen" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Delete Input" @@ -3309,35 +3273,35 @@ msgstr "Animatie Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "OneShot Node" -msgstr "" +msgstr "OneShot Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Mix Node" -msgstr "" +msgstr "Mix Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend2 Node" -msgstr "" +msgstr "Blend2 Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend3 Node" -msgstr "" +msgstr "Blend3 Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Blend4 Node" -msgstr "" +msgstr "Blend4 Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "TimeScale Node" -msgstr "" +msgstr "TimeScale Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "TimeSeek Node" -msgstr "" +msgstr "TimeSeek Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Transition Node" -msgstr "" +msgstr "Transition Node" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Import Animations.." @@ -3349,12 +3313,11 @@ msgstr "Wijzig Node Filters" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Filters.." -msgstr "" +msgstr "Filters..." #: editor/plugins/animation_tree_editor_plugin.cpp -#, fuzzy msgid "AnimationTree" -msgstr "Animatie" +msgstr "AnimatieBoom" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Free" @@ -3426,7 +3389,7 @@ msgstr "Fout bij opvragen" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Idle" -msgstr "" +msgstr "Inactief" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Retry" @@ -3464,7 +3427,7 @@ msgstr "Alle" #: editor/plugins/asset_library_editor_plugin.cpp #: editor/project_settings_editor.cpp msgid "Plugins" -msgstr "" +msgstr "Plugins" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Sort:" @@ -3506,20 +3469,27 @@ msgid "" "Save your scene (for images to be saved in the same dir), or pick a save " "path from the BakedLightmap properties." msgstr "" +"Kan geen opslag pad voor de lichtmappen bepalen.\n" +"Sla jouw scène op (om lichtmappen op te slaan in dezelfde map) of kies een " +"opslag pad vanaf de BakedLightmap eigenschappen." #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" "No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake " "Light' flag is on." msgstr "" +"Geen meshes om te bakken. Zorg ervoor dat ze een UV2 kanaal bevatten en dat " +"'Bake Light' vlag aan staat." #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "Failed creating lightmap images, make sure path is writable." msgstr "" +"Creëren van lichtmap afbeeldingen mislukt, zorg ervoor dat het pad " +"schrijfbaar is." #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "Bake Lightmaps" -msgstr "" +msgstr "Bak Lichtmappen" #: editor/plugins/camera_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -3528,37 +3498,37 @@ msgstr "Voorbeeld" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap" -msgstr "" +msgstr "Configureer Snap" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Offset:" -msgstr "" +msgstr "Raster Verplaatsing:" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Step:" -msgstr "" +msgstr "Raster Stap:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Rotation Offset:" -msgstr "" +msgstr "Rotatie Verplaatsing:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Rotation Step:" -msgstr "" +msgstr "Rotatie Stap:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move Pivot" -msgstr "" +msgstr "Verplaats Draaipunt" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move Action" -msgstr "" +msgstr "Verplaats Actie" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move vertical guide" -msgstr "" +msgstr "Verplaats vertical gids" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Create new vertical guide" @@ -3586,7 +3556,7 @@ msgstr "Maak nieuwe horizontale en verticale gidsen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Edit IK Chain" -msgstr "" +msgstr "Bewerk IK Ketting" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Edit CanvasItem" @@ -3606,7 +3576,7 @@ msgstr "Wijzig Ankers" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Paste Pose" -msgstr "" +msgstr "Plak Houding" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Select Mode" @@ -3614,7 +3584,7 @@ msgstr "Selecteer Modus" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Drag: Rotate" -msgstr "" +msgstr "Sleep: Roteer" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Alt+Drag: Move" @@ -3623,6 +3593,8 @@ msgstr "Alt + Slepen : Verplaatsen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)." msgstr "" +"Druk 'v' om het draaipunt aan te passen, 'Shift+v' om het draaipunt te " +"slepen (tijdens het bewegen)." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Alt+RMB: Depth list selection" @@ -3647,11 +3619,11 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Click to change object's rotation pivot." -msgstr "" +msgstr "Klik om het draaipunt van het object aan te passen." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Pan Mode" -msgstr "" +msgstr "Verschuif Modus" #: editor/plugins/canvas_item_editor_plugin.cpp #, fuzzy @@ -3660,11 +3632,11 @@ msgstr "Breekpunt Aan- of Uitschakelen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Snap" -msgstr "" +msgstr "Gebruik Uitlijnen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snapping options" -msgstr "" +msgstr "Uitlijnen opties" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to grid" @@ -3676,35 +3648,38 @@ msgstr "Gebruik Rotatie Snap" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap..." -msgstr "" +msgstr "Configureer Uitlijnen..." #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Snap Relative" -msgstr "" +msgstr "Uitlijn Relatief" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Pixel Snap" -msgstr "" +msgstr "Gebruik Pixel Uitlijnen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Smart snapping" msgstr "Slim Uitlijnen" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Snap to parent" -msgstr "" +msgstr "Uitlijnen naar ouder" #: editor/plugins/canvas_item_editor_plugin.cpp +#, fuzzy msgid "Snap to node anchor" -msgstr "" +msgstr "Uitlijnen naar node anker" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to node sides" -msgstr "" +msgstr "Uitlijnen naar node zijden" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to other nodes" -msgstr "" +msgstr "Uitlijnen naar andere nodes" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to guides" @@ -3724,30 +3699,33 @@ msgstr "Ontgrendel het geselecteerde object (kan verplaatst worden)." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Makes sure the object's children are not selectable." msgstr "" +"Zorgt ervoor dat de kinderen van dit object niet geselecteerd kunnen worden." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Restores the object's children's ability to be selected." msgstr "" +"Herstelt de mogelijkheid van selecteerbaarheid bij de kinderen van het " +"object." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Make Bones" -msgstr "" +msgstr "Maak Botten" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Clear Bones" -msgstr "" +msgstr "Maak Botten Leeg" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Show Bones" -msgstr "" +msgstr "Laat Botten Zien" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Make IK Chain" -msgstr "" +msgstr "Maak IK Ketting" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Clear IK Chain" -msgstr "" +msgstr "Maak IK Ketting Leeg" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -3773,39 +3751,39 @@ msgstr "Toon hulplijnen" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Center Selection" -msgstr "" +msgstr "Centreer Selectie" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Frame Selection" -msgstr "" +msgstr "Raam Selectie" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Layout" -msgstr "" +msgstr "Indeling" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Insert Keys" -msgstr "" +msgstr "Voer Sleutels In" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Insert Key" -msgstr "" +msgstr "Voer Sleutel in" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Insert Key (Existing Tracks)" -msgstr "" +msgstr "Voeg Sleutel in (Bestaande Banen)" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Copy Pose" -msgstr "" +msgstr "Kopieer Houding" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Clear Pose" -msgstr "" +msgstr "Maak Houding Leeg" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Drag pivot from mouse position" -msgstr "" +msgstr "Versleep draaipunt vanaf muispositie" #: editor/plugins/canvas_item_editor_plugin.cpp #, fuzzy @@ -3814,27 +3792,27 @@ msgstr "Verwijder Signaal" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" -msgstr "" +msgstr "Vermenigvuldig rasterstap met 2" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Divide grid step by 2" -msgstr "" +msgstr "Deel rasterstap door 2" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" -msgstr "" +msgstr "Voeg %s Toe" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Adding %s..." -msgstr "" +msgstr "%s aan het toevoegen..." #: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Ok" -msgstr "" +msgstr "Oké" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "Kan niet meerdere knooppunten instantiëren zonder een wortel." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -3844,7 +3822,7 @@ msgstr "Creëer Node" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Error instancing scene from %s" -msgstr "" +msgstr "Er is iets misgegaan bij het instantiëren van scene vanaf %s" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Change default type" @@ -3855,6 +3833,8 @@ msgid "" "Drag & drop + Shift : Add node as sibling\n" "Drag & drop + Alt : Change node type" msgstr "" +"Sleep & laat los + Shift : voeg knooppunt toe als kind\n" +"Sleep & laat los + Alt : Verander node type" #: editor/plugins/collision_polygon_editor_plugin.cpp msgid "Create Poly3D" @@ -3862,7 +3842,7 @@ msgstr "Maak Poly3D" #: editor/plugins/collision_shape_2d_editor_plugin.cpp msgid "Set Handle" -msgstr "" +msgstr "Stel Handgreep In" #: editor/plugins/cube_grid_theme_editor_plugin.cpp msgid "Remove item %d?" @@ -3880,19 +3860,19 @@ msgstr "Verwijder Geselecteerde Item" #: editor/plugins/cube_grid_theme_editor_plugin.cpp msgid "Import from Scene" -msgstr "" +msgstr "Importeer vanaf de Scene" #: editor/plugins/cube_grid_theme_editor_plugin.cpp msgid "Update from Scene" -msgstr "" +msgstr "Werk bij vanaf de Scene" #: editor/plugins/curve_editor_plugin.cpp msgid "Flat0" -msgstr "" +msgstr "Plat0" #: editor/plugins/curve_editor_plugin.cpp msgid "Flat1" -msgstr "" +msgstr "Plat1" #: editor/plugins/curve_editor_plugin.cpp #, fuzzy @@ -3901,23 +3881,23 @@ msgstr "Schaal Selectie" #: editor/plugins/curve_editor_plugin.cpp msgid "Ease out" -msgstr "" +msgstr "Neem af naar buiten" #: editor/plugins/curve_editor_plugin.cpp msgid "Smoothstep" -msgstr "" +msgstr "Gelijke stap" #: editor/plugins/curve_editor_plugin.cpp msgid "Modify Curve Point" -msgstr "" +msgstr "Wijzig Curve Punt" #: editor/plugins/curve_editor_plugin.cpp msgid "Modify Curve Tangent" -msgstr "" +msgstr "Wijzig Curve Raaklijn" #: editor/plugins/curve_editor_plugin.cpp msgid "Load Curve Preset" -msgstr "" +msgstr "Laad Curve Preset" #: editor/plugins/curve_editor_plugin.cpp msgid "Add point" @@ -3934,11 +3914,11 @@ msgstr "Lineair" #: editor/plugins/curve_editor_plugin.cpp msgid "Right linear" -msgstr "" +msgstr "Rechtslijnig" #: editor/plugins/curve_editor_plugin.cpp msgid "Load preset" -msgstr "" +msgstr "Laad voorinstelling" #: editor/plugins/curve_editor_plugin.cpp #, fuzzy @@ -3947,98 +3927,108 @@ msgstr "Verwijder Signaal" #: editor/plugins/curve_editor_plugin.cpp msgid "Toggle Curve Linear Tangent" -msgstr "" +msgstr "Schakel Curve Lineaire Raaklijn" #: editor/plugins/curve_editor_plugin.cpp msgid "Hold Shift to edit tangents individually" -msgstr "" +msgstr "Houd Shift ingedrukt om de raaklijnen individueel te bewerken" #: editor/plugins/gi_probe_editor_plugin.cpp +#, fuzzy msgid "Bake GI Probe" -msgstr "" +msgstr "Bak GI Probe" #: editor/plugins/gradient_editor_plugin.cpp +#, fuzzy msgid "Add/Remove Color Ramp Point" -msgstr "" +msgstr "Verwijder/Voeg Kleuren Hellings Punt Toe" #: editor/plugins/gradient_editor_plugin.cpp #: editor/plugins/shader_graph_editor_plugin.cpp +#, fuzzy msgid "Modify Color Ramp" -msgstr "" +msgstr "Wijzig Kleuren Helling" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item %d" -msgstr "" +msgstr "Item %d" #: editor/plugins/item_list_editor_plugin.cpp msgid "Items" -msgstr "" +msgstr "Items" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item List Editor" -msgstr "" +msgstr "Item Lijst Editor" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "" "No OccluderPolygon2D resource on this node.\n" "Create and assign one?" msgstr "" +"Geen OccluderPolygon2D resource op deze node.\n" +"Creëer en wijs een toe?" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Create Occluder Polygon" -msgstr "" +msgstr "Creëer Occluder Polygon" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Create a new polygon from scratch." -msgstr "" +msgstr "Creëer een compleet nieuwe polygon." #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Edit existing polygon:" -msgstr "" +msgstr "Wijzig bestaande polygon:" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "LMB: Move Point." -msgstr "" +msgstr "LMB: Verplaats Punt." #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Ctrl+LMB: Split Segment." -msgstr "" +msgstr "Ctrl+LMB: Splits Segment." #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "RMB: Erase Point." -msgstr "" +msgstr "RMB: Verwijder Punt." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh is empty!" -msgstr "" +msgstr "Mesh is leeg!" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Static Trimesh Body" -msgstr "" +msgstr "Creëer Statisch Trimesh Lichaam" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Static Convex Body" -msgstr "" +msgstr "Creëer Statisch Convex Lichaam" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "This doesn't work on scene root!" -msgstr "" +msgstr "Dit werkt niet op scene root!" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Trimesh Shape" -msgstr "" +msgstr "Creëer Trimesh Vorm" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Convex Shape" -msgstr "" +msgstr "Creëer Convexe Vorm" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Navigation Mesh" -msgstr "" +msgstr "Creëer Navigatie Mesh" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Contained Mesh is not of type ArrayMesh." -msgstr "" +msgstr "Bevatte Mesh is niet van type ArrayMesh" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "UV Unwrap failed, mesh may not be manifold?" @@ -4049,110 +4039,117 @@ msgid "No mesh to debug." msgstr "" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Model has no UV in this layer" -msgstr "" +msgstr "Model heeft geen UV in deze laag" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" -msgstr "" +msgstr "MeshInstance ontbreekt een Mesh!" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Mesh has not surface to create outlines from!" -msgstr "" +msgstr "Mesh heeft geen oppervlakte om omlijning van te maken!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Could not create outline!" -msgstr "" +msgstr "Kon omlijning niet maken!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline" -msgstr "" +msgstr "Creëer Omlijning" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "" +msgstr "Mesh" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" -msgstr "" +msgstr "Creëer Trimesh Statisch Lichaam" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Convex Static Body" -msgstr "" +msgstr "Creëer Convex Statisch Lichaam" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Trimesh Collision Sibling" -msgstr "" +msgstr "Creëer Trimesh Botsings Broer" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Convex Collision Sibling" -msgstr "" +msgstr "Creëer Convex Botsings Broer" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Outline Mesh.." -msgstr "" +msgstr "Creëer Omlijning Mesh.." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "View UV1" -msgstr "Weergeven" +msgstr "Geef UV1 Weer" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "View UV2" -msgstr "Weergeven" +msgstr "Geef UV2 Weer" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Unwrap UV2 for Lightmap/AO" msgstr "" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Create Outline Mesh" -msgstr "" +msgstr "Creëer Omlijning Mesh" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "Outline Size:" -msgstr "" +msgstr "Omlijning Grootte:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "No mesh source specified (and no MultiMesh set in node)." -msgstr "" +msgstr "Geen mesh bron gespecificeerd (en geen MultiMesh ingesteld in node)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "No mesh source specified (and MultiMesh contains no Mesh)." -msgstr "" +msgstr "Geen mesh bron gespecificeerd (en MultiMesh bevat geen Mesh)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (invalid path)." -msgstr "" +msgstr "Mesh bron is ongeldig (ongeldig pad)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (not a MeshInstance)." -msgstr "" +msgstr "Mesh bron is ongeldig (niet een MeshInstance)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (contains no Mesh resource)." -msgstr "" +msgstr "Mesh bron is ongeldig (bevat geen Mesh resource)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "No surface source specified." -msgstr "" +msgstr "Geen oppervlakte bron gespecificeerd." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Surface source is invalid (invalid path)." -msgstr "" +msgstr "Oppervlakte bron is ongeldig (ongeldig pad)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Surface source is invalid (no geometry)." -msgstr "" +msgstr "Oppervlakte bron is ongeldig (geen geometrie)." #: editor/plugins/multimesh_editor_plugin.cpp +#, fuzzy msgid "Surface source is invalid (no faces)." -msgstr "" +msgstr "Oppervlakte bron is ongeldig (geen vlakken)." #: editor/plugins/multimesh_editor_plugin.cpp +#, fuzzy msgid "Parent has no solid faces to populate." -msgstr "" +msgstr "Ouder heeft geen vaste vlakken om te bevolken." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Couldn't map area." @@ -4160,11 +4157,11 @@ msgstr "" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Source Mesh:" -msgstr "" +msgstr "Selecteer een Bron Mesh:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Target Surface:" -msgstr "" +msgstr "Selecteer een Doel Oppervlakte:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Populate Surface" @@ -4176,67 +4173,67 @@ msgstr "" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Target Surface:" -msgstr "" +msgstr "Doel Oppervlakte:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Source Mesh:" -msgstr "" +msgstr "Bron Mesh:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "X-Axis" -msgstr "" +msgstr "X-As" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Y-Axis" -msgstr "" +msgstr "Y-As" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Z-Axis" -msgstr "" +msgstr "Z-As" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh Up Axis:" -msgstr "" +msgstr "Mesh Boven As:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Random Rotation:" -msgstr "" +msgstr "Willekeurige Rotatie:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Random Tilt:" -msgstr "" +msgstr "Willekeurige Tilt:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Random Scale:" -msgstr "" +msgstr "Willekeurige Schaal:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Populate" -msgstr "" +msgstr "Bevolken" #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Bake!" -msgstr "" +msgstr "Bakken!" #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Bake the navigation mesh." -msgstr "" +msgstr "Bak de navigatie mesh." #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Clear the navigation mesh." -msgstr "" +msgstr "Leeg de navigatie mesh." #: editor/plugins/navigation_mesh_generator.cpp msgid "Setting up Configuration..." -msgstr "" +msgstr "Configuratie aan het instellen..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Calculating grid size..." -msgstr "" +msgstr "Raster groote uitrekenen..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Creating heightfield..." -msgstr "" +msgstr "Hoogteveld aan het creëeren..." #: editor/plugins/navigation_mesh_generator.cpp #, fuzzy @@ -4244,8 +4241,9 @@ msgid "Marking walkable triangles..." msgstr "Lokale wijziging aan het opslaan.." #: editor/plugins/navigation_mesh_generator.cpp +#, fuzzy msgid "Constructing compact heightfield..." -msgstr "" +msgstr "Compact hoogteveld aan het bouwen..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Eroding walkable area..." @@ -4253,15 +4251,15 @@ msgstr "" #: editor/plugins/navigation_mesh_generator.cpp msgid "Partitioning..." -msgstr "" +msgstr "Partitioneren..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Creating contours..." -msgstr "" +msgstr "Contouren aan het creëeren..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Creating polymesh..." -msgstr "" +msgstr "Polymesh aan het creëeren..." #: editor/plugins/navigation_mesh_generator.cpp msgid "Converting to native navigation mesh..." @@ -4277,16 +4275,16 @@ msgstr "" #: editor/plugins/navigation_mesh_generator.cpp msgid "Done!" -msgstr "" +msgstr "Klaar!" #: editor/plugins/navigation_polygon_editor_plugin.cpp msgid "Create Navigation Polygon" -msgstr "" +msgstr "Creëer Navigatie Polygoon" #: editor/plugins/particles_2d_editor_plugin.cpp #: editor/plugins/particles_editor_plugin.cpp msgid "Generating AABB" -msgstr "" +msgstr "AABB Genereren" #: editor/plugins/particles_2d_editor_plugin.cpp msgid "Can only set point into a ParticlesMaterial process material" @@ -7935,7 +7933,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8094,7 +8092,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/pl.po b/editor/translations/pl.po index 9274ab5628..f5b08d11c1 100644 --- a/editor/translations/pl.po +++ b/editor/translations/pl.po @@ -13,8 +13,9 @@ # Kajetan KuszczyÅ„ski <kajetanek99@gmail.com>, 2016. # Kamil Lewan <lewan.kamil@gmail.com>, 2016. # Karol Walasek <coreconviction@gmail.com>, 2016. -# Maksymilian Åšwiąć <maksymilian.swiac@gmail.com>, 2017. +# Maksymilian Åšwiąć <maksymilian.swiac@gmail.com>, 2017-2018. # Mietek SzczeÅ›niak <ravaging@go2.pl>, 2016. +# NeverK <neverkoxu@gmail.com>, 2018. # Rafal Brozio <rafal.brozio@gmail.com>, 2016. # RafaÅ‚ Ziemniak <synaptykq@gmail.com>, 2017. # Sebastian Krzyszkowiak <dos@dosowisko.net>, 2017. @@ -25,8 +26,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" -"Last-Translator: Daniel Lewan <vision360.daniel@gmail.com>\n" +"PO-Revision-Date: 2018-01-22 08:08+0000\n" +"Last-Translator: Maksymilian Åšwiąć <maksymilian.swiac@gmail.com>\n" "Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/" "godot/pl/>\n" "Language: pl\n" @@ -3190,7 +3191,7 @@ msgstr "Mieszanie" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Mix" -msgstr "Mix" +msgstr "Miks" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Auto Restart:" @@ -3309,7 +3310,7 @@ msgstr "Filtry.." #: editor/plugins/animation_tree_editor_plugin.cpp msgid "AnimationTree" -msgstr "AnimationTree" +msgstr "Drzewo animacji" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Free" @@ -4022,7 +4023,7 @@ msgstr "Model nie posiada UV w tej warstwie" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" -msgstr "MeshInstance nie posiada siatki! " +msgstr "MeshInstance nie posiada siatki!" #: editor/plugins/mesh_instance_editor_plugin.cpp #, fuzzy @@ -7888,7 +7889,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8048,7 +8049,7 @@ msgstr "OÅ›wietlanie siatek: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/pr.po b/editor/translations/pr.po index c2b5cd7a03..3fd24dd39f 100644 --- a/editor/translations/pr.po +++ b/editor/translations/pr.po @@ -7732,7 +7732,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7866,7 +7866,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po index 6ebd209730..6963d39352 100644 --- a/editor/translations/pt_BR.po +++ b/editor/translations/pt_BR.po @@ -11,6 +11,7 @@ # Guilherme Felipe C G Silva <guilhermefelipecgs@gmail.com>, 2017. # Joaquim Ferreira <joaquimferreira1996@bol.com.br>, 2016. # jonathan railarem <railarem@gmail.com>, 2017. +# Lucas Silva <lucasb.hpp@gmail.com>, 2018. # Luiz G. Correia <luizgabriell2.0@gmail.com>, 2017. # Mailson Silva Marins <mailsons335@gmail.com>, 2016. # MalcomRF <malcomkbk@gmail.com>, 2017. @@ -24,7 +25,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: 2016-05-30\n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" +"PO-Revision-Date: 2018-01-22 08:08+0000\n" "Last-Translator: Michael Alexsander Silva Dias <michaelalexsander@protonmail." "com>\n" "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/" @@ -176,7 +177,7 @@ msgstr "Constante" #: editor/animation_editor.cpp msgid "In" -msgstr "In" +msgstr "Em" #: editor/animation_editor.cpp msgid "Out" @@ -1343,19 +1344,18 @@ msgid "Description" msgstr "Descrição" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Tutoriais" +msgstr "Tutoriais Online:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Atualmente não existe descrição para este método. Por favor nos ajude [color=" -"$color][url=$url]contribuindo uma[/url][/color]!" +"Atualmente não há tutoriais para essa classe. Você pode [color=$color][url=" +"$url]contribuir criando um[/url][/color] ou [color=$color][url=" +"$url2]solicitar[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -3793,7 +3793,7 @@ msgstr "Ok" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "ImpossÃvel instanciar múltiplos nós sem uma raiz." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5857,7 +5857,6 @@ msgid "Select current edited sub-tile." msgstr "Selecione o sub-tile editado atual." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." msgstr "Selecione o sub-tile para alterar sua prioridade." @@ -5976,9 +5975,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Por favor, escolha um arquivo 'project.godot'." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Por favor, escolha um arquivo 'project.godot'." +msgstr "Por favor, escolha uma pasta vazia." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -6907,7 +6905,7 @@ msgstr "Erro ao carregar modelo '%s'" #: editor/script_create_dialog.cpp msgid "Error - Could not create script in filesystem." -msgstr "Erro - Não foi possÃvel criar o script no sistema de arquivos." +msgstr "Erro - Não se pôde criar o script no sistema de arquivos." #: editor/script_create_dialog.cpp msgid "Error loading script from %s" @@ -7836,8 +7834,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Este nó não possui filhos com formas, então não pode interagir com o " +"espaço.\n" +"Considere adicionar os nós CollisionShape2D ou CollisionPolygon2D como filho " +"para definir sua forma." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -8002,8 +8004,12 @@ msgstr "Iluminando Malhas: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Este nó não possui filhos com formas, então não pode interagir com o " +"espaço.\n" +"Considere adicionar os nós CollisionShape2D ou CollisionPolygon2D como filho " +"para definir sua forma." #: scene/3d/collision_polygon.cpp msgid "" @@ -8143,13 +8149,12 @@ msgid "(Other)" msgstr "(Outro)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." msgstr "" -"O Ambiente Padrão como especificado nas Configurações de Projeto (Rendering -" -"> Environment -> Default Environment) não pôde ser carregado." +"O Ambiente Padrão especificado nas Configurações de Projeto (Rendering -> " +"Environment -> Default Environment) não pôde ser carregado." #: scene/main/viewport.cpp msgid "" diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po index 574c801e46..7bddb64cd0 100644 --- a/editor/translations/pt_PT.po +++ b/editor/translations/pt_PT.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" +"PO-Revision-Date: 2018-01-22 08:08+0000\n" "Last-Translator: João Lopes <linux-man@hotmail.com>\n" "Language-Team: Portuguese (Portugal) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_PT/>\n" @@ -1336,19 +1336,18 @@ msgid "Description" msgstr "Descrição" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Documentação Online" +msgstr "Tutoriais Online:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Atualmente não existe descrição para este Método. Por favor ajude-nos [color=" -"$color][url=$url]contribuindo com uma[/url][/color]!" +"Atualmente não existem tutoriais para esta classe, pode [color=$color][url=" +"$url]contribuir com um[/url][/color] ou [color=$color][url=$url2]solicitar " +"um[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -2480,7 +2479,7 @@ msgstr "A Extrair os Modelos de Exportação" #: editor/export_template_manager.cpp msgid "Importing:" -msgstr "Importando:" +msgstr "A Importar:" #: editor/export_template_manager.cpp msgid "" @@ -3779,7 +3778,7 @@ msgstr "Ok" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "ImpossÃvel instanciar nós múltiplos sem raiz." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5840,9 +5839,8 @@ msgid "Select current edited sub-tile." msgstr "Selecionar o sub-tile editado." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." -msgstr "Selecionar sub-tile para alterar prioridade." +msgstr "Selecionar sub-tile para alterar a sua prioridade." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5957,9 +5955,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Escolha um Ficheiro 'project.godot'." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Escolha um Ficheiro 'project.godot'." +msgstr "Por favor escolha uma pasta vazia." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7815,8 +7812,12 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Este nó não tem formas filhos, não conseguindo assim interagir com o " +"espaço.\n" +"Considere adicionar nós filhos CollisionShape2D ou CollisionPolygon2D para " +"definir a sua forma." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7981,8 +7982,12 @@ msgstr "A iluminar Meshes: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Este nó não tem formas filhos, não conseguindo assim interagir com o " +"espaço.\n" +"Considere adicionar nós filhos CollisionShape ou CollisionPolygon para " +"definir a sua forma." #: scene/3d/collision_polygon.cpp msgid "" @@ -8124,7 +8129,6 @@ msgid "(Other)" msgstr "(Outro)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/ro.po b/editor/translations/ro.po index f56bba4b0d..9aa5fbeeff 100644 --- a/editor/translations/ro.po +++ b/editor/translations/ro.po @@ -7626,7 +7626,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7760,7 +7760,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ru.po b/editor/translations/ru.po index 3919cc31dc..cf82142c80 100644 --- a/editor/translations/ru.po +++ b/editor/translations/ru.po @@ -18,8 +18,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" -"Last-Translator: Artem Varaksa <aymfst@gmail.com>\n" +"PO-Revision-Date: 2018-01-24 12:27+0000\n" +"Last-Translator: ijet <my-ijet@mail.ru>\n" "Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/" "godot/ru/>\n" "Language: ru\n" @@ -1339,19 +1339,18 @@ msgid "Description" msgstr "ОпиÑание" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Уроки" +msgstr "Онлайн уроки:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ÑутÑтвует опиÑание Ñтого метода. ПожалуйÑта [color=" -"$color][url=$url]помогите нам[/url][/color]!" +"Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ÑутÑтвуют учебники Ð´Ð»Ñ Ñтого клаÑÑа, вы можете его " +"[color=$color][url=$url]добавить[/url][/color] или [color=$color][url=" +"$url2]запроÑить[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -3785,7 +3784,7 @@ msgstr "Ок" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "Ðе удаетÑÑ Ñоздать неÑколько узлов без корнÑ." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5848,9 +5847,8 @@ msgid "Select current edited sub-tile." msgstr "Выберите текущий редактированный вложенный тайл." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." -msgstr "Выберите вложенный тайл чтобы изменить его приоритет." +msgstr "Выберите вложенный тайл, Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ приоритета." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5961,9 +5959,8 @@ msgid "Please choose a 'project.godot' file." msgstr "ПожалуйÑта, выберите 'project.godot' файл." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "ПожалуйÑта, выберите 'project.godot' файл." +msgstr "ПожалуйÑта, выберите пуÑтую папку." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7819,7 +7816,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7987,7 +7984,7 @@ msgstr "ОÑвещение полиÑетки: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -8128,12 +8125,11 @@ msgid "(Other)" msgstr "(Другие)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." msgstr "" -"Среда по умолчанию, как определено в ÐаÑтройках проекта (Rendering -> " +"Среда по умолчанию, как определено в наÑтройках проекта (Rendering -> " "Environment -> Default Environment) не может быть загружена." #: scene/main/viewport.cpp diff --git a/editor/translations/sk.po b/editor/translations/sk.po index ecc8ac42d7..33c848f91b 100644 --- a/editor/translations/sk.po +++ b/editor/translations/sk.po @@ -7718,7 +7718,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7859,7 +7859,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/sl.po b/editor/translations/sl.po index 7864252dad..9e9db80294 100644 --- a/editor/translations/sl.po +++ b/editor/translations/sl.po @@ -5,13 +5,14 @@ # # matevž lapajne <sivar.lapajne@gmail.com>, 2016-2017. # Matjaž Vitas <matjaz.vitas@gmail.com>, 2017. +# Miha Komatar <miha.komatar@gmail.com>, 2018. # Simon Å ander <simon.sand3r@gmail.com>, 2017. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2017-12-07 02:49+0000\n" -"Last-Translator: Matjaž Vitas <matjaz.vitas@gmail.com>\n" +"PO-Revision-Date: 2018-01-25 22:27+0000\n" +"Last-Translator: Miha Komatar <miha.komatar@gmail.com>\n" "Language-Team: Slovenian <https://hosted.weblate.org/projects/godot-engine/" "godot/sl/>\n" "Language: sl\n" @@ -19,7 +20,7 @@ msgstr "" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" "%100==4 ? 2 : 3;\n" -"X-Generator: Weblate 2.18-dev\n" +"X-Generator: Weblate 2.19-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -35,7 +36,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Anim Change Transition" -msgstr "" +msgstr "Anim spremeni prehod" #: editor/animation_editor.cpp msgid "Anim Change Transform" @@ -47,7 +48,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Anim Change Call" -msgstr "" +msgstr "Anim izberi klic" #: editor/animation_editor.cpp msgid "Anim Add Track" @@ -7711,7 +7712,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7853,7 +7854,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -7993,7 +7994,7 @@ msgstr "Neznani format pisave." #: scene/resources/dynamic_font.cpp msgid "Error loading font." -msgstr "Napaka naložitve pisave." +msgstr "Napaka nalaganja pisave." #: scene/resources/dynamic_font.cpp msgid "Invalid font size." diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po index 33c4863f62..1c53884f32 100644 --- a/editor/translations/sr_Cyrl.po +++ b/editor/translations/sr_Cyrl.po @@ -7815,7 +7815,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7949,7 +7949,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/sv.po b/editor/translations/sv.po index 0d06c4538e..5779cb56b1 100644 --- a/editor/translations/sv.po +++ b/editor/translations/sv.po @@ -8279,7 +8279,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8433,7 +8433,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/ta.po b/editor/translations/ta.po index db9594e79f..16e646b2a0 100644 --- a/editor/translations/ta.po +++ b/editor/translations/ta.po @@ -7628,7 +7628,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7762,7 +7762,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/th.po b/editor/translations/th.po index 37fdefc46e..4f8b834985 100644 --- a/editor/translations/th.po +++ b/editor/translations/th.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" +"PO-Revision-Date: 2018-01-20 09:49+0000\n" "Last-Translator: Poommetee Ketson <poommetee@protonmail.com>\n" "Language-Team: Thai <https://hosted.weblate.org/projects/godot-engine/godot/" "th/>\n" @@ -1319,17 +1319,17 @@ msgid "Description" msgstr "รายละเà¸à¸µà¸¢à¸”" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "คู่มืà¸" +msgstr "สà¸à¸™à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸à¸à¸™à¹„ลน์:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." -msgstr "เมท็à¸à¸”นี้ยังไม่มีคำà¸à¸˜à¸´à¸šà¸²à¸¢ โปรดช่วย[color=$color][url=$url]à¹à¸à¹‰à¹„ข[/url][/color]!" +msgstr "" +"คลาสนี้ยังไม่มีà¸à¸²à¸£à¸ªà¸à¸™à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ ท่านสามารถ[color=$color][url=$url]ช่วยเขียน[/url][/" +"color] หรืภ[color=$color][url=$url2]ขà¸à¹ƒà¸«à¹‰à¸ˆà¸±à¸”ทำ[/url][/color]" #: editor/editor_help.cpp msgid "Properties" @@ -3712,7 +3712,7 @@ msgstr "ตà¸à¸¥à¸‡" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "à¸à¸´à¸™à¸ªà¹à¸•à¸™à¸‹à¹Œà¸«à¸¥à¸²à¸¢ ๆ โหนดโดยที่ไม่มีโหนดราà¸à¹„ม่ได้" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5769,7 +5769,6 @@ msgid "Select current edited sub-tile." msgstr "เลืà¸à¸à¹„ทล์ย่à¸à¸¢à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸›à¸£à¸±à¸šà¹à¸•à¹ˆà¸‡" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." msgstr "เลืà¸à¸à¹„ทล์ย่à¸à¸¢à¹€à¸žà¸·à¹ˆà¸à¸ˆà¸±à¸”ลำดับความสำคัà¸" @@ -5880,9 +5879,8 @@ msgid "Please choose a 'project.godot' file." msgstr "à¸à¸£à¸¸à¸“าเลืà¸à¸à¹„ฟล์ 'project.godot'" #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "à¸à¸£à¸¸à¸“าเลืà¸à¸à¹„ฟล์ 'project.godot'" +msgstr "à¸à¸£à¸¸à¸“าเลืà¸à¸à¹‚ฟลเดà¸à¸£à¹Œà¸§à¹ˆà¸²à¸‡à¹€à¸›à¸¥à¹ˆà¸²" #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7687,12 +7685,11 @@ msgstr "ไม่สามารถà¸à¹ˆà¸²à¸™à¹‚ครงสร้าง HTML: #: platform/javascript/export/export.cpp msgid "Could not read boot splash image file:" -msgstr "ไม่สามารถà¸à¹ˆà¸²à¸™à¹„ฟล์ภาพขณะบูต:" +msgstr "ไม่สามารถà¸à¹ˆà¸²à¸™à¹„ฟล์ภาพขณะเริ่มเà¸à¸¡:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Using default boot splash image." -msgstr "ใช้ภาพขณะบูตปริยาย" +msgstr "ใช้ภาพขณะเริ่มเà¸à¸¡à¸›à¸£à¸´à¸¢à¸²à¸¢" #: scene/2d/animated_sprite.cpp msgid "" @@ -7712,8 +7709,10 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"โหนดนี้ไม่มีโหนดรูปทรงเป็นโหนดลูภจึงไม่มีผลทางà¸à¸²à¸¢à¸ าพ\n" +"à¸à¸£à¸¸à¸“าเพิ่ม CollisionShape2D หรืภCollisionPolygon2D เป็นโหนดลูà¸à¹€à¸žà¸·à¹ˆà¸à¹ƒà¸«à¹‰à¸¡à¸µà¸£à¸¹à¸›à¸—รง" #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7857,8 +7856,10 @@ msgstr "ส่à¸à¸‡à¹à¸ªà¸‡à¸šà¸™à¸žà¸·à¹‰à¸™à¸œà¸´à¸§: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"โหนดนี้ไม่มีโหนดรูปทรงเป็นโหนดลูภจึงไม่มีผลทางà¸à¸²à¸¢à¸ าพ\n" +"à¸à¸£à¸¸à¸“าเพิ่ม CollisionShape หรืภCollisionPolygon เป็นโหนดลูà¸à¹€à¸žà¸·à¹ˆà¸à¹ƒà¸«à¹‰à¸¡à¸µà¸£à¸¹à¸›à¸—รง" #: scene/3d/collision_polygon.cpp msgid "" @@ -7983,7 +7984,6 @@ msgid "(Other)" msgstr "(à¸à¸·à¹ˆà¸™)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/tr.po b/editor/translations/tr.po index 83c6b284b8..934077d896 100644 --- a/editor/translations/tr.po +++ b/editor/translations/tr.po @@ -12,13 +12,13 @@ # M. Yavuz Uzun <myavuzuzun@yandex.com>, 2016. # Orkun Turan <holygatestudio@yandex.com>, 2016-2017. # razah <icnikerazah@gmail.com>, 2017-2018. -# stnmycri <satenmeycri@gmail.com>, 2017. +# stnmycri <satenmeycri@gmail.com>, 2017-2018. # Yavuz Günay <yavuzgunay@gmail.com>, 2017. # msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-20 08:55+0000\n" +"PO-Revision-Date: 2018-01-24 23:40+0000\n" "Last-Translator: razah <icnikerazah@gmail.com>\n" "Language-Team: Turkish <https://hosted.weblate.org/projects/godot-engine/" "godot/tr/>\n" @@ -1333,19 +1333,18 @@ msgid "Description" msgstr "Açıklama" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Öğreticiler" +msgstr "Çevrimiçi Rehberler:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Bu metot için henüz bir açıklama yok. Bize [color=$color][url=$url]katkıda " -"bulunarak[/url][/color] yardım edebilirsiniz!" +"Bu metot için henüz bir rehber yok. Siz de\n" +"[color=$color][url=$url]hazırlayabilir[/url][/color] ya da \n" +"[color=$color][url=$url2]öneride bulunabilirsiniz[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -3778,7 +3777,7 @@ msgstr "Tamam" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." -msgstr "" +msgstr "Kök olmadan çoklu düğüm oluÅŸturulamaz." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -5839,7 +5838,6 @@ msgid "Select current edited sub-tile." msgstr "Åžuanki düzenlenmiÅŸ alt-döşemeyi seç." #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." msgstr "ÖnceliÄŸini deÄŸiÅŸtirmek için alt-karo seçin." @@ -5954,9 +5952,8 @@ msgid "Please choose a 'project.godot' file." msgstr "Lütfen bir 'proje.godot' dosyası seçin." #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "Lütfen bir 'proje.godot' dosyası seçin." +msgstr "Lütfen boÅŸ bir klasör seçin." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -7809,8 +7806,11 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"Bu düğüm alt ÅŸekillere sahip deÄŸil, bu yüzden uzayla etkileÅŸime giremez.\n" +"Åžeklini belirlemek için CollisionShape2D ya da CollisionPolygon2D eklemeyi " +"düşünebilirsiniz." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7974,8 +7974,11 @@ msgstr "Örüntüler Haritalanıyor: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"Bu düğüm alt ÅŸekillere sahip deÄŸil, bu yüzden uzayla etkileÅŸime giremez.\n" +"Åžeklini belirlemek için CollisionShape ya da CollisionPolygon eklemeyi " +"düşünebilirsiniz." #: scene/3d/collision_polygon.cpp msgid "" @@ -8117,7 +8120,6 @@ msgid "(Other)" msgstr "(DiÄŸer)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." diff --git a/editor/translations/uk.po b/editor/translations/uk.po index afe2edcc14..d01686aec4 100644 --- a/editor/translations/uk.po +++ b/editor/translations/uk.po @@ -8,12 +8,13 @@ # Гидеон Теон <t.kudely94@gmail.com>, 2017. # ÐœÐ°Ñ€Ñ Ð¯Ð¼Ð±Ð°Ñ€ <mjambarmeta@gmail.com>, 2017-2018. # ОлекÑандр Пилипчук <pilipchukap@rambler.ru>, 2018. +# Yuri Chornoivan <yurchor@ukr.net>, 2018. # msgid "" msgstr "" -"Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-01-12 15:18+0000\n" -"Last-Translator: ÐœÐ°Ñ€Ñ Ð¯Ð¼Ð±Ð°Ñ€ <mjambarmeta@gmail.com>\n" +"Project-Id-Version: Ukrainian (Godot Engine)\n" +"PO-Revision-Date: 2018-01-24 17:51+0000\n" +"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/" "godot/uk/>\n" "Language: uk\n" @@ -25,11 +26,11 @@ msgstr "" #: editor/animation_editor.cpp msgid "Disabled" -msgstr "Відключено" +msgstr "Вимкнено" #: editor/animation_editor.cpp msgid "All Selection" -msgstr "УÑÑ– вибранні елементи" +msgstr "УÑе позначене" #: editor/animation_editor.cpp msgid "Anim Change Keyframe Time" @@ -969,11 +970,11 @@ msgstr "Зберегти цей макет шини у файлі." #: editor/editor_audio_buses.cpp editor/import_dock.cpp msgid "Load Default" -msgstr "Завантажити за промовчаннÑм" +msgstr "Завантажити типовий" #: editor/editor_audio_buses.cpp msgid "Load the default Bus Layout." -msgstr "Завантажити макет шини за промовчаннÑм." +msgstr "Завантажити типовий макет шини." #: editor/editor_autoload_settings.cpp msgid "Invalid name." @@ -1083,7 +1084,7 @@ msgstr "[не збережено]" #: editor/editor_dir_dialog.cpp msgid "Please select a base directory first" -msgstr "БудьлаÑка, виберіть Ñпочатку базову каталог" +msgstr "Будь лаÑка, виберіть Ñпочатку базовий каталог" #: editor/editor_dir_dialog.cpp msgid "Choose a Directory" @@ -1331,19 +1332,18 @@ msgid "Description" msgstr "ОпиÑ" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "Онлайн документаціÑ" +msgstr "Підручники у інтернеті:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"Ðа данний момент Ð¾Ð¿Ð¸Ñ Ñ†ÑŒÐ¾Ð³Ð¾ методу відÑутній. БудьлаÑка, [color=$color][url=" -"$url]допоможіть нам[/url][/color]!" +"ÐаÑтанов щодо цього клаÑу ще немає. Ви можете [color=$color][url=" +"$url]Ñтворити Ñ—Ñ…[/url][/color] або [color=$color][url=$url2]надіÑлати запит " +"щодо їхнього ÑтвореннÑ[/url][/color]." #: editor/editor_help.cpp msgid "Properties" @@ -1358,8 +1358,8 @@ msgid "" "There is currently no description for this property. Please help us by " "[color=$color][url=$url]contributing one[/url][/color]!" msgstr "" -"Ðа данний момент Ð¾Ð¿Ð¸Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– відÑутній. БудьлаÑка, [color=$color]" -"[url=$url]допоможіть нам[/url][/color]!" +"У поточній верÑÑ–Ñ— немає опиÑу цієї влаÑтивоÑÑ‚Ñ–. Будь лаÑка, [color=$color]" +"[url=$url]Ñтворіть його[/url][/color]!" #: editor/editor_help.cpp msgid "Methods" @@ -1374,8 +1374,8 @@ msgid "" "There is currently no description for this method. Please help us by [color=" "$color][url=$url]contributing one[/url][/color]!" msgstr "" -"Ðа данний момент Ð¾Ð¿Ð¸Ñ Ñ†ÑŒÐ¾Ð³Ð¾ методу відÑутній. БудьлаÑка, [color=$color][url=" -"$url]допоможіть нам[/url][/color]!" +"У поточній верÑÑ–Ñ— немає опиÑу цього методу. Будь лаÑка, [color=$color][url=" +"$url]Ñтворіть його[/url][/color]!" #: editor/editor_help.cpp msgid "Search Text" @@ -1435,7 +1435,7 @@ msgstr "Помилка при ÑинтакÑичному аналізі '%s'." #: editor/editor_node.cpp msgid "Unexpected end of file '%s'." -msgstr "Ðеочикуваний кінець Ñ€Ñдку '%s'." +msgstr "Ðеочікуваний кінець Ñ€Ñдка «%s»." #: editor/editor_node.cpp msgid "Missing '%s' or its dependencies." @@ -1455,7 +1455,7 @@ msgstr "Ðналіз" #: editor/editor_node.cpp msgid "Creating Thumbnail" -msgstr "Створити екÑкіз" +msgstr "Створюємо мініатюру" #: editor/editor_node.cpp msgid "This operation can't be done without a tree root." @@ -1495,7 +1495,7 @@ msgstr "Помилка при Ñпробі зберегти макет!" #: editor/editor_node.cpp msgid "Default editor layout overridden." -msgstr "Макет редактора за промовчуваннÑм перевизначено." +msgstr "Типове ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð° перевизначено." #: editor/editor_node.cpp msgid "Layout name not found!" @@ -1503,7 +1503,7 @@ msgstr "Ðазву макета не знайдено!" #: editor/editor_node.cpp msgid "Restored default layout to base settings." -msgstr "Поновити макет за промовчуваннÑм до базових налаштувань." +msgstr "Відновлено типове ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° базовими параметрами." #: editor/editor_node.cpp msgid "" @@ -1819,7 +1819,7 @@ msgstr "Видалити макет" #: editor/editor_node.cpp editor/import_dock.cpp #: editor/script_create_dialog.cpp msgid "Default" -msgstr "За промовчаннÑм" +msgstr "Типовий" #: editor/editor_node.cpp msgid "Switch Scene Tab" @@ -2401,7 +2401,7 @@ msgstr "Ви забули метод \"_run\"?" #: editor/editor_settings.cpp msgid "Default (Same as Editor)" -msgstr "За промовчаннÑм (так Ñамо, Ñк редактор)" +msgstr "Типовий (так Ñамо, Ñк редактор)" #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -2548,7 +2548,7 @@ msgstr "Ðе вдаєтьÑÑ Ð²Ð¸Ñ€Ñ–ÑˆÐ¸Ñ‚Ð¸" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Connecting.." -msgstr "З’єданнÑ.." +msgstr "З’єднаннÑ.." #: editor/export_template_manager.cpp msgid "Can't Connect" @@ -3352,7 +3352,7 @@ msgstr "Помилка перевірки Ñ…ÐµÑˆÑƒÐ²Ð°Ð½Ð½Ñ sha256" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Asset Download Error:" -msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°:" +msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ñƒ:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Fetching:" @@ -3380,7 +3380,7 @@ msgstr "Помилка завантаженнÑ" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download for this asset is already in progress!" -msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ актива вже виконуєтьÑÑ!" +msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ активу вже виконуєтьÑÑ!" #: editor/plugins/asset_library_editor_plugin.cpp msgid "first" @@ -3782,6 +3782,7 @@ msgstr "Гаразд" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Cannot instantiate multiple nodes without root." msgstr "" +"Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÐºÐ·ÐµÐ¼Ð¿Ð»Ñрів декількох вузлів Ñ” неможливим без кореневого запиÑу." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -3795,7 +3796,7 @@ msgstr "Помилка Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñцени з %s" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Change default type" -msgstr "Змінити тип за промовчаннÑм" +msgstr "Змінити типовий тип" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "" @@ -3982,7 +3983,7 @@ msgstr "Створити вигнуту форму" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Navigation Mesh" -msgstr "Створити навигаційну Ñітку" +msgstr "Створити навігаційну Ñітку" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Contained Mesh is not of type ArrayMesh." @@ -4847,11 +4848,11 @@ msgstr "Розгорнути вÑÑ– Ñ€Ñдки" #: editor/plugins/script_text_editor.cpp msgid "Complete Symbol" -msgstr "" +msgstr "Завершити Ñимвол" #: editor/plugins/script_text_editor.cpp msgid "Trim Trailing Whitespace" -msgstr "" +msgstr "Обрізати кінцевий пробіл" #: editor/plugins/script_text_editor.cpp msgid "Convert Indent To Spaces" @@ -4916,7 +4917,7 @@ msgstr "Шейдер" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Constant" -msgstr "" +msgstr "Змінити чиÑлову Ñталу" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Constant" @@ -4924,47 +4925,47 @@ msgstr "Змінити векторну конÑтанту" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change RGB Constant" -msgstr "" +msgstr "Змінити Ñталу RGB" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Operator" -msgstr "" +msgstr "Змінити чиÑловий оператор" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Operator" -msgstr "" +msgstr "Змінити векторний оператор" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Scalar Operator" -msgstr "" +msgstr "Змінити векторно-чиÑловий оператор" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change RGB Operator" -msgstr "" +msgstr "Змінити оператор RGB" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Toggle Rot Only" -msgstr "" +msgstr "Перемкнути лише поворот" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Function" -msgstr "" +msgstr "Змінити ÑкалÑрну функцію" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Function" -msgstr "" +msgstr "Змінити векторну функцію" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Scalar Uniform" -msgstr "" +msgstr "Змінити чиÑлову одиницю" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Vec Uniform" -msgstr "" +msgstr "Змінити векторну одиницю" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change RGB Uniform" -msgstr "" +msgstr "Змінити одиницю RGB" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Default Value" @@ -4972,15 +4973,15 @@ msgstr "Змінити Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° промовчаннÑм" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change XForm Uniform" -msgstr "" +msgstr "Змінити одиницю XForm" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Texture Uniform" -msgstr "" +msgstr "Змінити одиницю текÑтури" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Cubemap Uniform" -msgstr "" +msgstr "Змінити одиницю кубічної мапи" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Comment" @@ -4988,11 +4989,11 @@ msgstr "Змінити коментар" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Add/Remove to Color Ramp" -msgstr "" +msgstr "Додати до рампи кольорів або вилучити з неї" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Add/Remove to Curve Map" -msgstr "" +msgstr "Додати до карти кривих або вилучити з неї" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Modify Curve Map" @@ -5000,167 +5001,167 @@ msgstr "Змінити карту кривої" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Change Input Name" -msgstr "" +msgstr "Змінити назву входу" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Connect Graph Nodes" -msgstr "" +msgstr "З'єднати вузли графу" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Disconnect Graph Nodes" -msgstr "" +msgstr "Роз'єднати вузли графу" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Remove Shader Graph Node" -msgstr "" +msgstr "Вилучити вузол графу шейдера" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Move Shader Graph Node" -msgstr "" +msgstr "ПереÑунути вузол графу шейдера" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Duplicate Graph Node(s)" -msgstr "" +msgstr "Дублювати вузли графу" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Delete Shader Graph Node(s)" -msgstr "" +msgstr "Вилучити взули графу шейдера" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Error: Cyclic Connection Link" -msgstr "" +msgstr "Помилка: циклічне поÑиланнÑ" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Error: Missing Input Connections" -msgstr "Помилка: ВідÑутні вхідні з'єднаннÑ" +msgstr "Помилка: пропущено вхідні з'єднаннÑ" #: editor/plugins/shader_graph_editor_plugin.cpp msgid "Add Shader Graph Node" -msgstr "" +msgstr "Додати вузол графу шейдера" #: editor/plugins/spatial_editor_plugin.cpp msgid "Orthogonal" -msgstr "" +msgstr "Ортогонально" #: editor/plugins/spatial_editor_plugin.cpp msgid "Perspective" -msgstr "" +msgstr "ПерÑпектива" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Aborted." -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾." #: editor/plugins/spatial_editor_plugin.cpp msgid "X-Axis Transform." -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð° віÑÑÑŽ X." #: editor/plugins/spatial_editor_plugin.cpp msgid "Y-Axis Transform." -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð° віÑÑÑŽ Y." #: editor/plugins/spatial_editor_plugin.cpp msgid "Z-Axis Transform." -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð° віÑÑÑŽ Z." #: editor/plugins/spatial_editor_plugin.cpp msgid "View Plane Transform." -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð»Ð¾Ñ‰Ð¸Ð½Ð¸ переглÑду." #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " -msgstr "" +msgstr "МаÑштаб: " #: editor/plugins/spatial_editor_plugin.cpp msgid "Translating: " -msgstr "" +msgstr "ПеренеÑеннÑ: " #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotating %s degrees." -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° %s градуÑів." #: editor/plugins/spatial_editor_plugin.cpp msgid "Keying is disabled (no key inserted)." -msgstr "" +msgstr "Ð’ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° вимкнено (ключ не вÑтавлено)." #: editor/plugins/spatial_editor_plugin.cpp msgid "Animation Key Inserted." -msgstr "" +msgstr "Ð’Ñтавлено ключ анімації." #: editor/plugins/spatial_editor_plugin.cpp msgid "Objects Drawn" -msgstr "" +msgstr "Ðамальовано об'єктів" #: editor/plugins/spatial_editor_plugin.cpp msgid "Material Changes" -msgstr "" +msgstr "Зміни матеріалу" #: editor/plugins/spatial_editor_plugin.cpp msgid "Shader Changes" -msgstr "" +msgstr "Зміни шейдерів" #: editor/plugins/spatial_editor_plugin.cpp msgid "Surface Changes" -msgstr "" +msgstr "Зміни поверхонь" #: editor/plugins/spatial_editor_plugin.cpp msgid "Draw Calls" -msgstr "" +msgstr "Виклики заÑобу малюваннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Vertices" -msgstr "" +msgstr "Вершини" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS" -msgstr "" +msgstr "Кадри за Ñекунду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." -msgstr "" +msgstr "ВиглÑд згори." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View." -msgstr "" +msgstr "ВиглÑд знизу." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom" -msgstr "" +msgstr "Знизу" #: editor/plugins/spatial_editor_plugin.cpp msgid "Left View." -msgstr "" +msgstr "ВиглÑд зліва." #: editor/plugins/spatial_editor_plugin.cpp msgid "Left" -msgstr "" +msgstr "Зліва" #: editor/plugins/spatial_editor_plugin.cpp msgid "Right View." -msgstr "" +msgstr "ВиглÑд Ñправа." #: editor/plugins/spatial_editor_plugin.cpp msgid "Right" -msgstr "" +msgstr "Справа" #: editor/plugins/spatial_editor_plugin.cpp msgid "Front View." -msgstr "" +msgstr "ВиглÑд Ñпереду." #: editor/plugins/spatial_editor_plugin.cpp msgid "Front" -msgstr "" +msgstr "Спереду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear View." -msgstr "" +msgstr "ВиглÑд ззаду." #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear" -msgstr "" +msgstr "Ззаду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Align with view" -msgstr "" +msgstr "ВирівнÑти з переглÑдом" #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "OK :(" @@ -5168,7 +5169,7 @@ msgstr "Гаразд :(" #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "No parent to instance a child at." -msgstr "" +msgstr "Ðемає батьківÑького запиÑу Ð´Ð»Ñ Ð´Ð¾Ñ‡Ñ–Ñ€Ð½ÑŒÐ¾Ð³Ð¾." #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "This operation requires a single selected node." @@ -5176,79 +5177,79 @@ msgstr "Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” одного обраного вуР#: editor/plugins/spatial_editor_plugin.cpp msgid "Display Normal" -msgstr "" +msgstr "Ðормальний переглÑд" #: editor/plugins/spatial_editor_plugin.cpp msgid "Display Wireframe" -msgstr "" +msgstr "ПереглÑд каркаÑу" #: editor/plugins/spatial_editor_plugin.cpp msgid "Display Overdraw" -msgstr "" +msgstr "Режим проÑвічуваннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Display Unshaded" -msgstr "" +msgstr "ПереглÑд без тіней" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Environment" -msgstr "" +msgstr "ПереглÑд Ñередовища" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Gizmos" -msgstr "" +msgstr "ПереглÑд гаджетів" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Information" -msgstr "" +msgstr "ПереглÑд відомоÑтей" #: editor/plugins/spatial_editor_plugin.cpp msgid "View FPS" -msgstr "" +msgstr "ПереглÑд чаÑтоти кадрів" #: editor/plugins/spatial_editor_plugin.cpp msgid "Half Resolution" -msgstr "" +msgstr "Половина роздільноÑÑ‚Ñ–" #: editor/plugins/spatial_editor_plugin.cpp msgid "Audio Listener" -msgstr "" +msgstr "ПроÑÐ»ÑƒÑ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Doppler Enable" -msgstr "" +msgstr "Ефект Доплера" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Left" -msgstr "" +msgstr "ОглÑд ліворуч" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Right" -msgstr "" +msgstr "ОглÑд праворуч" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Forward" -msgstr "" +msgstr "ОглÑд вперед" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Backwards" -msgstr "" +msgstr "ОглÑд назад" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Up" -msgstr "" +msgstr "ОглÑд вгору" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Down" -msgstr "" +msgstr "ОглÑд вниз" #: editor/plugins/spatial_editor_plugin.cpp msgid "Freelook Speed Modifier" -msgstr "" +msgstr "Коефіцієнт швидкоÑÑ‚Ñ– оглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" -msgstr "" +msgstr "Вікно XForm" #: editor/plugins/spatial_editor_plugin.cpp msgid "Select Mode (Q)" @@ -5260,488 +5261,491 @@ msgid "" "Alt+Drag: Move\n" "Alt+RMB: Depth list selection" msgstr "" +"ПеретÑгуваннÑ: Обертати\n" +"Alt+ПеретÑгуваннÑ: ПереÑунути\n" +"Alt+Права кнопка: Вибір у ÑпиÑку за глибиною" #: editor/plugins/spatial_editor_plugin.cpp msgid "Move Mode (W)" -msgstr "" +msgstr "Режим переÑÑƒÐ²Ð°Ð½Ð½Ñ (W)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Mode (E)" -msgstr "" +msgstr "Режим Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ (E)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scale Mode (R)" -msgstr "" +msgstr "Режим маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ (R)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Local Coords" -msgstr "" +msgstr "Локальні координати" #: editor/plugins/spatial_editor_plugin.cpp msgid "Local Space Mode (%s)" -msgstr "" +msgstr "Режим локального проÑтору (%s)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Snap Mode (%s)" -msgstr "" +msgstr "Режим Ð¿Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ (%s)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" -msgstr "" +msgstr "ВиглÑд знизу" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View" -msgstr "" +msgstr "ВиглÑд згори" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear View" -msgstr "" +msgstr "ВиглÑд ззаду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Front View" -msgstr "" +msgstr "ВиглÑд Ñпереду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Left View" -msgstr "" +msgstr "ВиглÑд зліва" #: editor/plugins/spatial_editor_plugin.cpp msgid "Right View" -msgstr "" +msgstr "ВиглÑд Ñправа" #: editor/plugins/spatial_editor_plugin.cpp msgid "Switch Perspective/Orthogonal view" -msgstr "" +msgstr "Перемкнути переглÑд перÑпективи/ортогональний переглÑд" #: editor/plugins/spatial_editor_plugin.cpp msgid "Insert Animation Key" -msgstr "" +msgstr "Ð’Ñтавити ключ анімації" #: editor/plugins/spatial_editor_plugin.cpp msgid "Focus Origin" -msgstr "" +msgstr "Ð¤Ð¾ÐºÑƒÑ Ð½Ð° центрі" #: editor/plugins/spatial_editor_plugin.cpp msgid "Focus Selection" -msgstr "" +msgstr "ФокуÑувати позначене" #: editor/plugins/spatial_editor_plugin.cpp msgid "Align Selection With View" -msgstr "" +msgstr "ВирівнÑти позначене із переглÑдом" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Select" -msgstr "" +msgstr "ІнÑтрумент позначеннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Move" -msgstr "" +msgstr "ІнÑтрумент переÑуваннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Rotate" -msgstr "" +msgstr "ІнÑтрумент обертаннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Tool Scale" -msgstr "" +msgstr "ІнÑтрумент маÑштабуваннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Toggle Freelook" -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð¾Ð³Ð»Ñду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform" -msgstr "" +msgstr "ПеретвореннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Configure Snap.." -msgstr "" +msgstr "Ðалаштувати прилипаннÑ..." #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Dialog.." -msgstr "" +msgstr "Вікно перетвореннÑ..." #: editor/plugins/spatial_editor_plugin.cpp msgid "1 Viewport" -msgstr "" +msgstr "1 панель переглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "2 Viewports" -msgstr "" +msgstr "2 панелі переглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "2 Viewports (Alt)" -msgstr "" +msgstr "2 панелі переглÑду (альт.)" #: editor/plugins/spatial_editor_plugin.cpp msgid "3 Viewports" -msgstr "" +msgstr "3 панелі переглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "3 Viewports (Alt)" -msgstr "" +msgstr "3 панелі переглÑду (альт.)" #: editor/plugins/spatial_editor_plugin.cpp msgid "4 Viewports" -msgstr "" +msgstr "4 панелі переглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Origin" -msgstr "" +msgstr "ПереглÑд центра" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Grid" -msgstr "" +msgstr "ПереглÑд ґратки" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Settings" -msgstr "" +msgstr "Параметри" #: editor/plugins/spatial_editor_plugin.cpp msgid "Skeleton Gizmo visibility" -msgstr "" +msgstr "ВидиміÑÑ‚ÑŒ каркаÑу гаджетів" #: editor/plugins/spatial_editor_plugin.cpp msgid "Snap Settings" -msgstr "" +msgstr "Параметри прилипаннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Translate Snap:" -msgstr "" +msgstr "ПереÑувне прилипаннÑ:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Snap (deg.):" -msgstr "" +msgstr "Обертальне Ð¿Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ (град.):" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scale Snap (%):" -msgstr "" +msgstr "МаÑштабувальне Ð¿Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ (%):" #: editor/plugins/spatial_editor_plugin.cpp msgid "Viewport Settings" -msgstr "" +msgstr "Параметри панелі переглÑду" #: editor/plugins/spatial_editor_plugin.cpp msgid "Perspective FOV (deg.):" -msgstr "" +msgstr "Поле зору перÑпективи (град.):" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Z-Near:" -msgstr "" +msgstr "Ближнє Z переглÑду:" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Z-Far:" -msgstr "" +msgstr "Дальнє Z переглÑду:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Change" -msgstr "" +msgstr "Зміна перетвореннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Translate:" -msgstr "" +msgstr "ПеренеÑеннÑ:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate (deg.):" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ (град.):" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scale (ratio):" -msgstr "" +msgstr "МаÑштаб (коеф.):" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Type" -msgstr "" +msgstr "Тип перетвореннÑ" #: editor/plugins/spatial_editor_plugin.cpp msgid "Pre" -msgstr "" +msgstr "До" #: editor/plugins/spatial_editor_plugin.cpp msgid "Post" -msgstr "" +msgstr "ПіÑлÑ" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "ERROR: Couldn't load frame resource!" -msgstr "" +msgstr "Помилка: не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ реÑÑƒÑ€Ñ ÐºÐ°Ð´Ñ€Ñƒ!" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Add Frame" -msgstr "" +msgstr "Додати кадр" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Resource clipboard is empty or not a texture!" -msgstr "" +msgstr "Буфер реÑурÑів порожній або не міÑтить текÑтури!" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Paste Frame" -msgstr "" +msgstr "Ð’Ñтавити кадр" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Add Empty" -msgstr "" +msgstr "Додати порожній" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Change Animation Loop" -msgstr "" +msgstr "Змінити цикл анімації" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Change Animation FPS" -msgstr "" +msgstr "Змінити чаÑтоту кадрів анімації" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "(empty)" -msgstr "" +msgstr "(порожньо)" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Animations" -msgstr "" +msgstr "Ðнімації" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Speed (FPS):" -msgstr "" +msgstr "ЧаÑтота (кадри за Ñек.):" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Loop" -msgstr "" +msgstr "Зациклити" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Animation Frames" -msgstr "" +msgstr "Кадри анімації" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Insert Empty (Before)" -msgstr "" +msgstr "Ð’Ñтавити порожній (до)" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Insert Empty (After)" -msgstr "" +msgstr "Ð’Ñтавити порожній (піÑлÑ)" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Move (Before)" -msgstr "" +msgstr "ПереÑунути (до)" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Move (After)" -msgstr "" +msgstr "ПереÑунути (піÑлÑ)" #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "SpriteFrames" -msgstr "" +msgstr "Кадри Ñпрайта" #: editor/plugins/style_box_editor_plugin.cpp msgid "StyleBox Preview:" -msgstr "" +msgstr "ПереглÑд StyleBox:" #: editor/plugins/style_box_editor_plugin.cpp msgid "StyleBox" -msgstr "" +msgstr "Style Box" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Set Region Rect" -msgstr "" +msgstr "Ð’Ñтановити прÑмокутник облаÑÑ‚Ñ–" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Snap Mode:" -msgstr "" +msgstr "Режим прилипаннÑ:" #: editor/plugins/texture_region_editor_plugin.cpp msgid "<None>" -msgstr "" +msgstr "<Ðемає>" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Pixel Snap" -msgstr "" +msgstr "ÐŸÑ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ Ð´Ð¾ пікÑелів" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Grid Snap" -msgstr "" +msgstr "ÐŸÑ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ Ð´Ð¾ ґратки" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Auto Slice" -msgstr "" +msgstr "ÐвтонарізаннÑ" #: editor/plugins/texture_region_editor_plugin.cpp #: editor/plugins/tile_set_editor_plugin.cpp msgid "Offset:" -msgstr "" +msgstr "ЗÑув:" #: editor/plugins/texture_region_editor_plugin.cpp #: editor/plugins/tile_set_editor_plugin.cpp msgid "Step:" -msgstr "" +msgstr "Крок:" #: editor/plugins/texture_region_editor_plugin.cpp #: editor/plugins/tile_set_editor_plugin.cpp msgid "Separation:" -msgstr "" +msgstr "ВідокремленнÑ:" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Texture Region" -msgstr "" +msgstr "ОблаÑÑ‚ÑŒ текÑтури" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Texture Region Editor" -msgstr "" +msgstr "Редактор облаÑÑ‚Ñ– текÑтури" #: editor/plugins/theme_editor_plugin.cpp msgid "Can't save theme to file:" -msgstr "" +msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ тему до файла:" #: editor/plugins/theme_editor_plugin.cpp msgid "Add All Items" -msgstr "" +msgstr "Додати уÑÑ– елементи" #: editor/plugins/theme_editor_plugin.cpp msgid "Add All" -msgstr "" +msgstr "Додати уÑÑ–" #: editor/plugins/theme_editor_plugin.cpp #: editor/plugins/tile_set_editor_plugin.cpp msgid "Remove Item" -msgstr "" +msgstr "Вилучити елемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" -msgstr "" +msgstr "Вилучити уÑÑ– елементи" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All" -msgstr "" +msgstr "Вилучити уÑÑ–" #: editor/plugins/theme_editor_plugin.cpp msgid "Edit theme.." -msgstr "" +msgstr "Редагувати тему..." #: editor/plugins/theme_editor_plugin.cpp msgid "Theme editing menu." -msgstr "" +msgstr "Меню Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼Ð¸." #: editor/plugins/theme_editor_plugin.cpp msgid "Add Class Items" -msgstr "" +msgstr "Додати елементи клаÑу" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "" +msgstr "Вилучити елементи клаÑу" #: editor/plugins/theme_editor_plugin.cpp msgid "Create Empty Template" -msgstr "" +msgstr "Створити порожній шаблон" #: editor/plugins/theme_editor_plugin.cpp msgid "Create Empty Editor Template" -msgstr "" +msgstr "Створити порожній шаблон редактора" #: editor/plugins/theme_editor_plugin.cpp msgid "Create From Current Editor Theme" -msgstr "" +msgstr "Створити на оÑнові поточної теми редактора" #: editor/plugins/theme_editor_plugin.cpp msgid "CheckBox Radio1" -msgstr "" +msgstr "Варіант 1" #: editor/plugins/theme_editor_plugin.cpp msgid "CheckBox Radio2" -msgstr "" +msgstr "Варіант 2" #: editor/plugins/theme_editor_plugin.cpp msgid "Item" -msgstr "" +msgstr "Елемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Check Item" -msgstr "" +msgstr "Позначити елемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Checked Item" -msgstr "" +msgstr "Позначений елемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Has" -msgstr "" +msgstr "Має" #: editor/plugins/theme_editor_plugin.cpp msgid "Many" -msgstr "" +msgstr "Багато" #: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp msgid "Options" -msgstr "" +msgstr "Параметрів" #: editor/plugins/theme_editor_plugin.cpp msgid "Have,Many,Several,Options!" -msgstr "" +msgstr "Має,Багато,Декілька,Параметрів!" #: editor/plugins/theme_editor_plugin.cpp msgid "Tab 1" -msgstr "" +msgstr "Вкладка 1" #: editor/plugins/theme_editor_plugin.cpp msgid "Tab 2" -msgstr "" +msgstr "Вкладка 2" #: editor/plugins/theme_editor_plugin.cpp msgid "Tab 3" -msgstr "" +msgstr "Вкладка 3" #: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp #: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp msgid "Type:" -msgstr "" +msgstr "Тип:" #: editor/plugins/theme_editor_plugin.cpp msgid "Data Type:" -msgstr "" +msgstr "Тип даних:" #: editor/plugins/theme_editor_plugin.cpp msgid "Icon" -msgstr "" +msgstr "Піктограма" #: editor/plugins/theme_editor_plugin.cpp msgid "Style" -msgstr "" +msgstr "Стиль" #: editor/plugins/theme_editor_plugin.cpp msgid "Font" -msgstr "" +msgstr "Шрифт" #: editor/plugins/theme_editor_plugin.cpp msgid "Color" -msgstr "" +msgstr "Колір" #: editor/plugins/theme_editor_plugin.cpp msgid "Theme" -msgstr "" +msgstr "Тема" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" -msgstr "" +msgstr "Витерти позначене" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Paint TileMap" -msgstr "" +msgstr "Ðамалювати карту плиток" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Line Draw" -msgstr "" +msgstr "Ðамалювати лінію" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Rectangle Paint" -msgstr "" +msgstr "Ðамалювати прÑмокутник" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Bucket Fill" -msgstr "" +msgstr "ЗалиттÑ" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase TileMap" -msgstr "" +msgstr "Витерти карту плиток" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase selection" -msgstr "" +msgstr "Витерти позначене" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Find tile" @@ -5749,15 +5753,15 @@ msgstr "Знайти плитку" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Transpose" -msgstr "" +msgstr "ТранÑпонувати" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Mirror X" -msgstr "" +msgstr "Віддзеркалити за X" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Mirror Y" -msgstr "" +msgstr "Віддзеркалити за Y" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Paint Tile" @@ -5769,19 +5773,19 @@ msgstr "Вибрати плитку" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Rotate 0 degrees" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° 0 градуÑів" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Rotate 90 degrees" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° 90 градуÑів" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Rotate 180 degrees" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° 180 градуÑів" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Rotate 270 degrees" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° 270 градуÑів" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Could not find tile:" @@ -5789,15 +5793,15 @@ msgstr "Ðеможливо знайти плитку:" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Item name or ID:" -msgstr "" +msgstr "Ðазва або ідентифікатор елемента:" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Create from scene?" -msgstr "" +msgstr "Створити зі Ñцени?" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Merge from scene?" -msgstr "" +msgstr "Об'єднати зі Ñцени?" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Tile Set" @@ -5805,31 +5809,35 @@ msgstr "Ðабір плитки" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Create from Scene" -msgstr "" +msgstr "Створити зі Ñцени" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Merge from Scene" -msgstr "" +msgstr "Об'єднати зі Ñцени" #: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp msgid "Error" -msgstr "" +msgstr "Помилка" #: editor/plugins/tile_set_editor_plugin.cpp msgid "Autotiles" -msgstr "" +msgstr "Ðвтоплитки" #: editor/plugins/tile_set_editor_plugin.cpp msgid "" "Select sub-tile to use as icon, this will be also used on invalid autotile " "bindings." msgstr "" +"Виберіть підплитку Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñк піктограми. Її також буде викориÑтано " +"Ð´Ð»Ñ Ð½ÐµÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¸Ñ… прив'Ñзок у режимі автоплитки." #: editor/plugins/tile_set_editor_plugin.cpp msgid "" "LMB: set bit on.\n" "RMB: set bit off." msgstr "" +"Ліва кнопка: вÑтановити.\n" +"Права кнопка: знÑти." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Select current edited sub-tile." @@ -5837,7 +5845,7 @@ msgstr "Вибрати поточну редаговану вкладену пл #: editor/plugins/tile_set_editor_plugin.cpp msgid "Select sub-tile to change its priority." -msgstr "" +msgstr "Позначте підплитку Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð¸ Ñ—Ñ— пріоритетноÑÑ‚Ñ–." #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5845,117 +5853,123 @@ msgstr "СкаÑувати" #: editor/project_export.cpp msgid "Runnable" -msgstr "" +msgstr "Ðктивний" #: editor/project_export.cpp msgid "Delete patch '%s' from list?" -msgstr "" +msgstr "Вилучити латку «%s» зі ÑпиÑку?" #: editor/project_export.cpp msgid "Delete preset '%s'?" -msgstr "" +msgstr "Вилучити набір «%s»?" #: editor/project_export.cpp msgid "Export templates for this platform are missing/corrupted: " msgstr "" +"Ðе виÑтачає шаблонів екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ð¸ або шаблони пошкоджено: " #: editor/project_export.cpp msgid "Presets" -msgstr "" +msgstr "Ðабори" #: editor/project_export.cpp editor/project_settings_editor.cpp msgid "Add.." -msgstr "" +msgstr "Додати..." #: editor/project_export.cpp msgid "Resources" -msgstr "" +msgstr "РеÑурÑи" #: editor/project_export.cpp msgid "Export all resources in the project" -msgstr "" +msgstr "ЕкÑпортувати уÑÑ– реÑурÑи у проекті" #: editor/project_export.cpp msgid "Export selected scenes (and dependencies)" -msgstr "" +msgstr "ЕкÑпортувати позначені реÑурÑи (Ñ– залежноÑÑ‚Ñ–)" #: editor/project_export.cpp msgid "Export selected resources (and dependencies)" -msgstr "" +msgstr "ЕкÑпортувати позначені реÑурÑи (Ñ– залежноÑÑ‚Ñ–)" #: editor/project_export.cpp msgid "Export Mode:" -msgstr "" +msgstr "Режим екÑпортуваннÑ:" #: editor/project_export.cpp msgid "Resources to export:" -msgstr "" +msgstr "ЕкÑпортовані реÑурÑи:" #: editor/project_export.cpp msgid "" "Filters to export non-resource files (comma separated, e.g: *.json, *.txt)" msgstr "" +"Фільтри екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð², Ñкі не міÑÑ‚ÑÑ‚ÑŒ реÑурÑів (з відокремленнÑм " +"комами, приклад: *.json, *.txt)" #: editor/project_export.cpp msgid "" "Filters to exclude files from project (comma separated, e.g: *.json, *.txt)" msgstr "" +"Фільтри Ð²Ð¸ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² з проекту (з відокремленнÑм комами, приклад: *." +"json, *.txt)" #: editor/project_export.cpp msgid "Patches" -msgstr "" +msgstr "Латки" #: editor/project_export.cpp msgid "Make Patch" -msgstr "" +msgstr "Створити латку" #: editor/project_export.cpp msgid "Features" -msgstr "" +msgstr "МожливоÑÑ‚Ñ–" #: editor/project_export.cpp msgid "Custom (comma-separated):" -msgstr "" +msgstr "Ðетипове (з відокремленнÑм комами):" #: editor/project_export.cpp msgid "Feature List:" -msgstr "" +msgstr "СпиÑок можливоÑтей:" #: editor/project_export.cpp msgid "Export PCK/Zip" -msgstr "" +msgstr "ЕкÑпортувати PCK/Zip" #: editor/project_export.cpp msgid "Export templates for this platform are missing:" -msgstr "" +msgstr "Ðемає шаблонів екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— платформи:" #: editor/project_export.cpp msgid "Export templates for this platform are missing/corrupted:" msgstr "" +"Ðе виÑтачає шаблонів екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ð¸ або шаблони пошкоджено:" #: editor/project_export.cpp msgid "Export With Debug" -msgstr "" +msgstr "ЕкÑпортувати із діагноÑтикою" #: editor/project_manager.cpp msgid "The path does not exist." -msgstr "" +msgstr "ШлÑху не Ñ–Ñнує." #: editor/project_manager.cpp msgid "Please choose a 'project.godot' file." -msgstr "" +msgstr "Будь лаÑка, виберіть файл «project.godot»." #: editor/project_manager.cpp msgid "Please choose an empty folder." -msgstr "" +msgstr "Будь лаÑка, виберіть порожню теку." #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." -msgstr "" +msgstr "Будь лаÑка, виберіть теку, у Ñкій не міÑтитьÑÑ Ñ„Ð°Ð¹Ð»Ð° «project.godot»." #: editor/project_manager.cpp msgid "Imported Project" -msgstr "" +msgstr "Імпортований проект" #: editor/project_manager.cpp msgid "Couldn't create folder." @@ -5963,47 +5977,47 @@ msgstr "Ðеможливо Ñтворити теку." #: editor/project_manager.cpp msgid "There is already a folder in this path with the specified name." -msgstr "" +msgstr "У вказаному каталозі вже міÑтитьÑÑ Ñ‚ÐµÐºÐ° із вказано назвою." #: editor/project_manager.cpp msgid "It would be a good idea to name your project." -msgstr "" +msgstr "Вам варто дати назву вашому проекту." #: editor/project_manager.cpp msgid "Invalid project path (changed anything?)." -msgstr "" +msgstr "Ðекоректний шлÑÑ… до проекту (щоÑÑŒ змінилоÑÑ?)." #: editor/project_manager.cpp msgid "Couldn't get project.godot in project path." -msgstr "" +msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ project.godot з каталогу проекту." #: editor/project_manager.cpp msgid "Couldn't edit project.godot in project path." -msgstr "" +msgstr "Ðе вдалоÑÑ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ñ‚Ð¸ project.godot у каталозі проекту." #: editor/project_manager.cpp msgid "Couldn't create project.godot in project path." -msgstr "" +msgstr "Ðе вдалоÑÑ Ñтворити project.godot у каталозі проекту." #: editor/project_manager.cpp msgid "The following files failed extraction from package:" -msgstr "" +msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ такі файли з пакунка:" #: editor/project_manager.cpp msgid "Rename Project" -msgstr "" +msgstr "Перейменувати проект" #: editor/project_manager.cpp msgid "Couldn't get project.godot in the project path." -msgstr "" +msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ project.godot у каталозі проекту." #: editor/project_manager.cpp msgid "New Game Project" -msgstr "" +msgstr "Ðовий проект гри" #: editor/project_manager.cpp msgid "Import Existing Project" -msgstr "" +msgstr "Імпортувати наÑвний проект" #: editor/project_manager.cpp msgid "Import & Edit" @@ -6011,7 +6025,7 @@ msgstr "Імпортувати та редагувати" #: editor/project_manager.cpp msgid "Create New Project" -msgstr "" +msgstr "Створити новий проект" #: editor/project_manager.cpp msgid "Create & Edit" @@ -6019,7 +6033,7 @@ msgstr "Створити та змінити" #: editor/project_manager.cpp msgid "Install Project:" -msgstr "" +msgstr "Ð’Ñтановити проект:" #: editor/project_manager.cpp msgid "Install & Edit" @@ -6027,31 +6041,31 @@ msgstr "Ð’Ñтановити та змінити" #: editor/project_manager.cpp msgid "Project Name:" -msgstr "" +msgstr "Ðазва проекту:" #: editor/project_manager.cpp msgid "Create folder" -msgstr "" +msgstr "Створити теку" #: editor/project_manager.cpp msgid "Project Path:" -msgstr "" +msgstr "ШлÑÑ… проекту:" #: editor/project_manager.cpp msgid "Browse" -msgstr "" +msgstr "Вибрати" #: editor/project_manager.cpp msgid "Unnamed Project" -msgstr "" +msgstr "Проект без назви" #: editor/project_manager.cpp msgid "Can't open project" -msgstr "" +msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ проект" #: editor/project_manager.cpp msgid "Are you sure to open more than one project?" -msgstr "" +msgstr "Ви Ñправді хочете відкрити декілька проектів одразу?" #: editor/project_manager.cpp msgid "" @@ -6059,32 +6073,42 @@ msgid "" "Please edit the project and set the main scene in \"Project Settings\" under " "the \"Application\" category." msgstr "" +"Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити проект: не визначено головної Ñцени.\n" +"Будь лаÑка, змініть проект, вÑтановивши головну Ñцену у категорії «Програма» " +"Ñторінки «Параметри проекту»." #: editor/project_manager.cpp msgid "" "Can't run project: Assets need to be imported.\n" "Please edit the project to trigger the initial import." msgstr "" +"Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити проект: Ñлід імпортувати вміÑÑ‚.\n" +"Будь лаÑка, змініть проект так, щоб увімкнути початкове імпортуваннÑ." #: editor/project_manager.cpp msgid "Are you sure to run more than one project?" -msgstr "" +msgstr "Ви Ñправді хочете запуÑтити декілька проектів одночаÑно?" #: editor/project_manager.cpp msgid "Remove project from the list? (Folder contents will not be modified)" -msgstr "" +msgstr "Вилучити проект зі ÑпиÑку? (ВміÑÑ‚ теки не буде змінено)" #: editor/project_manager.cpp msgid "" "Language changed.\n" "The UI will update next time the editor or project manager starts." msgstr "" +"Змінено мову.\n" +"Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð±ÑƒÐ´Ðµ оновлено під Ñ‡Ð°Ñ Ð½Ð°Ñтупного запуÑку редактора або заÑобу " +"ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°Ð¼Ð¸." #: editor/project_manager.cpp msgid "" "You are about the scan %s folders for existing Godot projects. Do you " "confirm?" msgstr "" +"Ви наказали розпочати ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ %s тек у пошуках наÑвних проектів Godot. " +"Підтверджуєте ÑкануваннÑ?" #: editor/project_manager.cpp msgid "Project Manager" @@ -6092,23 +6116,23 @@ msgstr "Керівник проекту" #: editor/project_manager.cpp msgid "Project List" -msgstr "" +msgstr "СпиÑок проектів" #: editor/project_manager.cpp msgid "Scan" -msgstr "" +msgstr "Сканувати" #: editor/project_manager.cpp msgid "Select a Folder to Scan" -msgstr "" +msgstr "Виберіть теку Ð´Ð»Ñ ÑкануваннÑ" #: editor/project_manager.cpp msgid "New Project" -msgstr "" +msgstr "Ðовий проект" #: editor/project_manager.cpp msgid "Templates" -msgstr "" +msgstr "Шаблони" #: editor/project_manager.cpp msgid "Exit" @@ -6127,6 +6151,8 @@ msgid "" "You don't currently have any projects.\n" "Would you like to explore the official example projects in the Asset Library?" msgstr "" +"Зараз проектів немає.\n" +"Хочете вивчити проекти офіційних прикладів з бібліотеки даних?" #: editor/project_settings_editor.cpp msgid "Key " @@ -6134,11 +6160,11 @@ msgstr "Клавіша " #: editor/project_settings_editor.cpp msgid "Joy Button" -msgstr "" +msgstr "Кнопка джойÑтика" #: editor/project_settings_editor.cpp msgid "Joy Axis" -msgstr "" +msgstr "Ð’Ñ–ÑÑŒ джойÑтика" #: editor/project_settings_editor.cpp msgid "Mouse Button" @@ -6146,31 +6172,31 @@ msgstr "Кнопка миші" #: editor/project_settings_editor.cpp msgid "Invalid action (anything goes but '/' or ':')." -msgstr "" +msgstr "Ðекоректна Ð´Ñ–Ñ (можна уÑе, окрім «/» або «:»)." #: editor/project_settings_editor.cpp msgid "Action '%s' already exists!" -msgstr "" +msgstr "Ð—Ð°Ð¿Ð¸Ñ Ð´Ñ–Ñ— «%s» вже Ñ–Ñнує!" #: editor/project_settings_editor.cpp msgid "Rename Input Action Event" -msgstr "" +msgstr "Перейменувати подію за вхідною дією" #: editor/project_settings_editor.cpp msgid "Add Input Action Event" -msgstr "" +msgstr "Додати подію за вхідною дією" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Shift+" -msgstr "" +msgstr "Shift+" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Alt+" -msgstr "" +msgstr "Alt+" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Control+" -msgstr "" +msgstr "Ctrl+" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key.." @@ -6178,27 +6204,27 @@ msgstr "ÐатиÑніть клавішу,..." #: editor/project_settings_editor.cpp msgid "Mouse Button Index:" -msgstr "" +msgstr "Ð†Ð½Ð´ÐµÐºÑ ÐºÐ½Ð¾Ð¿ÐºÐ¸ миші:" #: editor/project_settings_editor.cpp msgid "Left Button" -msgstr "" +msgstr "Ліва кнопка" #: editor/project_settings_editor.cpp msgid "Right Button" -msgstr "" +msgstr "Права кнопка" #: editor/project_settings_editor.cpp msgid "Middle Button" -msgstr "" +msgstr "Ð¡ÐµÑ€ÐµÐ´Ð½Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°" #: editor/project_settings_editor.cpp msgid "Wheel Up Button" -msgstr "" +msgstr "Кнопка коліщатка вгору" #: editor/project_settings_editor.cpp msgid "Wheel Down Button" -msgstr "" +msgstr "Кнопка коліщатка вниз" #: editor/project_settings_editor.cpp msgid "Button 6" @@ -6218,87 +6244,87 @@ msgstr "Кнопка 9" #: editor/project_settings_editor.cpp msgid "Joypad Axis Index:" -msgstr "" +msgstr "Ð†Ð½Ð´ÐµÐºÑ Ð²Ñ–ÑÑ– джойÑтика:" #: editor/project_settings_editor.cpp msgid "Axis" -msgstr "" +msgstr "Ð’Ñ–ÑÑŒ" #: editor/project_settings_editor.cpp msgid "Joypad Button Index:" -msgstr "" +msgstr "Ð†Ð½Ð´ÐµÐºÑ ÐºÐ½Ð¾Ð¿ÐºÐ¸ джойÑтика:" #: editor/project_settings_editor.cpp msgid "Erase Input Action" -msgstr "" +msgstr "Витерти Ð·Ð°Ð¿Ð¸Ñ Ð´Ñ–Ñ—" #: editor/project_settings_editor.cpp msgid "Erase Input Action Event" -msgstr "" +msgstr "Витерти подію за дією" #: editor/project_settings_editor.cpp msgid "Add Event" -msgstr "" +msgstr "Додати подію" #: editor/project_settings_editor.cpp msgid "Device" -msgstr "" +msgstr "ПриÑтрій" #: editor/project_settings_editor.cpp msgid "Button" -msgstr "" +msgstr "Кнопка" #: editor/project_settings_editor.cpp msgid "Left Button." -msgstr "" +msgstr "Ліва кнопка." #: editor/project_settings_editor.cpp msgid "Right Button." -msgstr "" +msgstr "Права кнопка." #: editor/project_settings_editor.cpp msgid "Middle Button." -msgstr "" +msgstr "Ð¡ÐµÑ€ÐµÐ´Ð½Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°." #: editor/project_settings_editor.cpp msgid "Wheel Up." -msgstr "" +msgstr "Коліщатко вгору." #: editor/project_settings_editor.cpp msgid "Wheel Down." -msgstr "" +msgstr "Коліщатко вниз." #: editor/project_settings_editor.cpp msgid "Add Global Property" -msgstr "" +msgstr "Додати загальну влаÑтивіÑÑ‚ÑŒ" #: editor/project_settings_editor.cpp msgid "Select a setting item first!" -msgstr "" +msgstr "Спочатку виберіть елемент параметра!" #: editor/project_settings_editor.cpp msgid "No property '%s' exists." -msgstr "" +msgstr "ВлаÑтивоÑÑ‚Ñ– «%s» не Ñ–Ñнує." #: editor/project_settings_editor.cpp msgid "Setting '%s' is internal, and it can't be deleted." -msgstr "" +msgstr "Параметр «%s» Ñ” внутрішнім. Його не можна вилучати." #: editor/project_settings_editor.cpp msgid "Delete Item" -msgstr "" +msgstr "Вилучити елемент" #: editor/project_settings_editor.cpp msgid "Can't contain '/' or ':'" -msgstr "" +msgstr "Ðе може міÑтити «/» або «:»" #: editor/project_settings_editor.cpp msgid "Already existing" -msgstr "" +msgstr "Вже Ñ–Ñнує" #: editor/project_settings_editor.cpp msgid "Add Input Action" -msgstr "" +msgstr "Додати дію" #: editor/project_settings_editor.cpp msgid "Error saving settings." @@ -6306,11 +6332,11 @@ msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ." #: editor/project_settings_editor.cpp msgid "Settings saved OK." -msgstr "" +msgstr "Параметри уÑпішно збережено." #: editor/project_settings_editor.cpp msgid "Override for Feature" -msgstr "" +msgstr "Перевизначено Ð´Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ÑÑ‚Ñ–" #: editor/project_settings_editor.cpp msgid "Add Translation" @@ -6318,67 +6344,67 @@ msgstr "Додати переклад" #: editor/project_settings_editor.cpp msgid "Remove Translation" -msgstr "" +msgstr "Вилучити переклад" #: editor/project_settings_editor.cpp msgid "Add Remapped Path" -msgstr "" +msgstr "Додати переÑпрÑмований шлÑÑ…" #: editor/project_settings_editor.cpp msgid "Resource Remap Add Remap" -msgstr "" +msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу додає переÑпрÑмуваннÑ" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" -msgstr "" +msgstr "Змінити мову переÑпрÑÐ¼Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу" #: editor/project_settings_editor.cpp msgid "Remove Resource Remap" -msgstr "" +msgstr "Вилучити переÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу" #: editor/project_settings_editor.cpp msgid "Remove Resource Remap Option" -msgstr "" +msgstr "Вилучити параметр переÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу" #: editor/project_settings_editor.cpp msgid "Changed Locale Filter" -msgstr "" +msgstr "Змінено фільтр локалі" #: editor/project_settings_editor.cpp msgid "Changed Locale Filter Mode" -msgstr "" +msgstr "Змінено режим Ñ„Ñ–Ð»ÑŒÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»Ñ–" #: editor/project_settings_editor.cpp msgid "Project Settings (project.godot)" -msgstr "" +msgstr "Параметри проекту (project.godot)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "General" -msgstr "" +msgstr "Загальне" #: editor/project_settings_editor.cpp editor/property_editor.cpp msgid "Property:" -msgstr "" +msgstr "ВлаÑтивіÑÑ‚ÑŒ:" #: editor/project_settings_editor.cpp msgid "Override For.." -msgstr "" +msgstr "Перевизначити на..." #: editor/project_settings_editor.cpp msgid "Input Map" -msgstr "" +msgstr "Карта введеннÑ" #: editor/project_settings_editor.cpp msgid "Action:" -msgstr "" +msgstr "ДіÑ:" #: editor/project_settings_editor.cpp msgid "Device:" -msgstr "" +msgstr "ПриÑтрій:" #: editor/project_settings_editor.cpp msgid "Index:" -msgstr "" +msgstr "ІндекÑ:" #: editor/project_settings_editor.cpp msgid "Localization" @@ -6394,67 +6420,67 @@ msgstr "Переклади:" #: editor/project_settings_editor.cpp msgid "Remaps" -msgstr "" +msgstr "ПереÑпрÑмуваннÑ" #: editor/project_settings_editor.cpp msgid "Resources:" -msgstr "" +msgstr "РеÑурÑи:" #: editor/project_settings_editor.cpp msgid "Remaps by Locale:" -msgstr "" +msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° локаллю:" #: editor/project_settings_editor.cpp msgid "Locale" -msgstr "" +msgstr "Локаль" #: editor/project_settings_editor.cpp msgid "Locales Filter" -msgstr "" +msgstr "Фільтр локалей" #: editor/project_settings_editor.cpp msgid "Show all locales" -msgstr "" +msgstr "Показати уÑÑ– локалі" #: editor/project_settings_editor.cpp msgid "Show only selected locales" -msgstr "" +msgstr "Показати лише позначені локалі" #: editor/project_settings_editor.cpp msgid "Filter mode:" -msgstr "" +msgstr "Режим фільтруваннÑ:" #: editor/project_settings_editor.cpp msgid "Locales:" -msgstr "" +msgstr "Локалі:" #: editor/project_settings_editor.cpp msgid "AutoLoad" -msgstr "" +msgstr "ÐвтозавантаженнÑ" #: editor/property_editor.cpp msgid "Pick a Viewport" -msgstr "" +msgstr "Виберіть панель переглÑду" #: editor/property_editor.cpp msgid "Ease In" -msgstr "" +msgstr "Перейти в" #: editor/property_editor.cpp msgid "Ease Out" -msgstr "" +msgstr "Перейти з" #: editor/property_editor.cpp msgid "Zero" -msgstr "" +msgstr "Ðуль" #: editor/property_editor.cpp msgid "Easing In-Out" -msgstr "" +msgstr "Перейти у-з" #: editor/property_editor.cpp msgid "Easing Out-In" -msgstr "" +msgstr "Перейти з-у" #: editor/property_editor.cpp msgid "File.." @@ -6462,15 +6488,15 @@ msgstr "Файл..." #: editor/property_editor.cpp msgid "Dir.." -msgstr "" +msgstr "Каталог..." #: editor/property_editor.cpp msgid "Assign" -msgstr "" +msgstr "Призначити" #: editor/property_editor.cpp msgid "Select Node" -msgstr "" +msgstr "Позначити вузол" #: editor/property_editor.cpp msgid "New Script" @@ -6478,91 +6504,92 @@ msgstr "Ðовий Ñкрипт" #: editor/property_editor.cpp msgid "New %s" -msgstr "" +msgstr "Ðовий %s" #: editor/property_editor.cpp msgid "Make Unique" -msgstr "" +msgstr "Зробити унікальним" #: editor/property_editor.cpp msgid "Show in File System" -msgstr "" +msgstr "Показати в файловій ÑиÑтемі" #: editor/property_editor.cpp msgid "Convert To %s" -msgstr "" +msgstr "Перетворити на %s" #: editor/property_editor.cpp msgid "Error loading file: Not a resource!" -msgstr "" +msgstr "Помилка при завантаженні файла: не Ñ” реÑурÑом!" #: editor/property_editor.cpp msgid "Selected node is not a Viewport!" -msgstr "" +msgstr "Позначений вузол не Ñ” панеллю переглÑду!" #: editor/property_editor.cpp msgid "Pick a Node" -msgstr "" +msgstr "Вибрати вузол" #: editor/property_editor.cpp msgid "Bit %d, val %d." -msgstr "" +msgstr "Біт %d, Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ %d." #: editor/property_editor.cpp msgid "On" -msgstr "" +msgstr "Увімкнено" #: editor/property_editor.cpp msgid "[Empty]" -msgstr "" +msgstr "[Порожньо]" #: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp msgid "Set" -msgstr "" +msgstr "Ð’Ñтановити" #: editor/property_editor.cpp msgid "Properties:" -msgstr "" +msgstr "ВлаÑтивоÑÑ‚Ñ–:" #: editor/property_selector.cpp msgid "Select Property" -msgstr "" +msgstr "Вибір влаÑтивоÑÑ‚Ñ–" #: editor/property_selector.cpp msgid "Select Virtual Method" -msgstr "" +msgstr "Вибір віртуального методу" #: editor/property_selector.cpp msgid "Select Method" -msgstr "" +msgstr "Вибір методу" #: editor/pvrtc_compress.cpp msgid "Could not execute PVRTC tool:" -msgstr "" +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ інÑтрумент PVRTC:" #: editor/pvrtc_compress.cpp msgid "Can't load back converted image using PVRTC tool:" msgstr "" +"Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ перетворене Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð° допомогою заÑобу PVRTC:" #: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp msgid "Reparent Node" -msgstr "" +msgstr "Змінити батьківÑький вузол" #: editor/reparent_dialog.cpp msgid "Reparent Location (Select new Parent):" -msgstr "" +msgstr "Змінити Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ (вибрати новий батьківÑький вузол):" #: editor/reparent_dialog.cpp msgid "Keep Global Transform" -msgstr "" +msgstr "Зберегти загальне перетвореннÑ" #: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp msgid "Reparent" -msgstr "" +msgstr "Змінити батьківÑький" #: editor/run_settings_dialog.cpp msgid "Run Mode:" -msgstr "" +msgstr "Режим виконаннÑ:" #: editor/run_settings_dialog.cpp msgid "Current Scene" @@ -6570,15 +6597,15 @@ msgstr "Поточна Ñцена" #: editor/run_settings_dialog.cpp msgid "Main Scene" -msgstr "" +msgstr "Головна Ñцена" #: editor/run_settings_dialog.cpp msgid "Main Scene Arguments:" -msgstr "" +msgstr "Ðргументи головної Ñцени:" #: editor/run_settings_dialog.cpp msgid "Scene Run Settings" -msgstr "" +msgstr "Параметри запуÑку Ñцени" #: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp #: scene/gui/dialogs.cpp @@ -6587,49 +6614,51 @@ msgstr "Гаразд" #: editor/scene_tree_dock.cpp msgid "No parent to instance the scenes at." -msgstr "" +msgstr "Ðемає батьківÑького вузла, на Ñкому заÑновано Ñцени." #: editor/scene_tree_dock.cpp msgid "Error loading scene from %s" -msgstr "" +msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби завантажити Ñцену з %s" #: editor/scene_tree_dock.cpp msgid "" "Cannot instance the scene '%s' because the current scene exists within one " "of its nodes." msgstr "" +"Ðе можна Ñтворити екземплÑÑ€ Ñцени «%s», оÑкільки поточна Ñцена Ñ–Ñнує у " +"одному з Ñ—Ñ— вузлів." #: editor/scene_tree_dock.cpp msgid "Instance Scene(s)" -msgstr "" +msgstr "Сцени екземплÑра" #: editor/scene_tree_dock.cpp msgid "This operation can't be done on the tree root." -msgstr "" +msgstr "Цю операцію не може бути виконано над коренем дерева." #: editor/scene_tree_dock.cpp msgid "Move Node In Parent" -msgstr "" +msgstr "ПереÑунути вузол у батьківÑький" #: editor/scene_tree_dock.cpp msgid "Move Nodes In Parent" -msgstr "" +msgstr "ПереÑунути вузли у батьківÑький" #: editor/scene_tree_dock.cpp msgid "Duplicate Node(s)" -msgstr "" +msgstr "Дублювати вузли" #: editor/scene_tree_dock.cpp msgid "Delete Node(s)?" -msgstr "" +msgstr "Вилучити вузли?" #: editor/scene_tree_dock.cpp msgid "Can not perform with the root node." -msgstr "" +msgstr "Ðе можна виконувати із кореневим вузлом." #: editor/scene_tree_dock.cpp msgid "This operation can't be done on instanced scenes." -msgstr "" +msgstr "Цю дію не можна виконувати над Ñценами з екземплÑрами." #: editor/scene_tree_dock.cpp msgid "Save New Scene As.." @@ -6637,37 +6666,39 @@ msgstr "Зберегти нову Ñцену Ñк..." #: editor/scene_tree_dock.cpp msgid "Editable Children" -msgstr "" +msgstr "Редагований дочірній елемент" #: editor/scene_tree_dock.cpp msgid "Load As Placeholder" -msgstr "" +msgstr "Завантажити Ñк заповнювач" #: editor/scene_tree_dock.cpp msgid "Discard Instancing" -msgstr "" +msgstr "Відкинути екземплÑри" #: editor/scene_tree_dock.cpp msgid "Makes Sense!" -msgstr "" +msgstr "У цьому Ñ” ÑенÑ!" #: editor/scene_tree_dock.cpp msgid "Can't operate on nodes from a foreign scene!" -msgstr "" +msgstr "Ðе можна працювати із вузлами зі Ñторонньої Ñцени!" #: editor/scene_tree_dock.cpp msgid "Can't operate on nodes the current scene inherits from!" -msgstr "" +msgstr "Ðе можна працювати з вузлами, Ñпадкоємцем Ñких Ñ” поточна Ñцена!" #: editor/scene_tree_dock.cpp msgid "Remove Node(s)" -msgstr "" +msgstr "Вилучити вузли" #: editor/scene_tree_dock.cpp msgid "" "Couldn't save new scene. Likely dependencies (instances) couldn't be " "satisfied." msgstr "" +"Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ нову Ñцену. Ймовірно, залежноÑÑ‚Ñ– (екземплÑри) не " +"задоволено." #: editor/scene_tree_dock.cpp msgid "Error saving scene." @@ -6675,7 +6706,7 @@ msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñцени." #: editor/scene_tree_dock.cpp msgid "Error duplicating scene to save it." -msgstr "" +msgstr "Помилка під Ñ‡Ð°Ñ Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ñцени Ð´Ð»Ñ Ñ—Ñ— збереженнÑ." #: editor/scene_tree_dock.cpp msgid "Sub-Resources" @@ -6683,7 +6714,7 @@ msgstr "ПідреÑурÑи" #: editor/scene_tree_dock.cpp msgid "Clear Inheritance" -msgstr "" +msgstr "УÑунути уÑпадкуваннÑ" #: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp msgid "Open in Editor" @@ -6691,35 +6722,35 @@ msgstr "Відкрити в редакторі" #: editor/scene_tree_dock.cpp msgid "Delete Node(s)" -msgstr "" +msgstr "Вилучити вузли" #: editor/scene_tree_dock.cpp msgid "Add Child Node" -msgstr "" +msgstr "Додати дочірній вузол" #: editor/scene_tree_dock.cpp msgid "Instance Child Scene" -msgstr "" +msgstr "Створити екземплÑÑ€ дочірньої Ñцени" #: editor/scene_tree_dock.cpp msgid "Change Type" -msgstr "" +msgstr "Змінити тип" #: editor/scene_tree_dock.cpp msgid "Attach Script" -msgstr "" +msgstr "Долучити Ñкрипт" #: editor/scene_tree_dock.cpp msgid "Clear Script" -msgstr "" +msgstr "Вилучити Ñкрипт" #: editor/scene_tree_dock.cpp msgid "Merge From Scene" -msgstr "" +msgstr "Об'єднати зі Ñцени" #: editor/scene_tree_dock.cpp msgid "Save Branch as Scene" -msgstr "" +msgstr "Зберегти гілку Ñк Ñцену" #: editor/scene_tree_dock.cpp msgid "Copy Node Path" @@ -6727,79 +6758,87 @@ msgstr "Копіювати вузол шлÑху" #: editor/scene_tree_dock.cpp msgid "Delete (No Confirm)" -msgstr "" +msgstr "Вилучити (без підтвердженнÑ)" #: editor/scene_tree_dock.cpp msgid "Add/Create a New Node" -msgstr "" +msgstr "Додати або Ñтворити новий вузол" #: editor/scene_tree_dock.cpp msgid "" "Instance a scene file as a Node. Creates an inherited scene if no root node " "exists." msgstr "" +"Створити екземплÑÑ€ файла Ñцени Ñк вузол. Створює уÑпадковану Ñцену, Ñкщо " +"кореневого вузла не Ñ–Ñнує." #: editor/scene_tree_dock.cpp msgid "Filter nodes" -msgstr "" +msgstr "Фільтрувати вузли" #: editor/scene_tree_dock.cpp msgid "Attach a new or existing script for the selected node." -msgstr "" +msgstr "Долучити новий або наÑвний Ñкрипт до позначеного вузла." #: editor/scene_tree_dock.cpp msgid "Clear a script for the selected node." -msgstr "" +msgstr "Вилучити Ñкрипт Ð´Ð»Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾Ð³Ð¾ вузла." #: editor/scene_tree_dock.cpp msgid "Remote" -msgstr "" +msgstr "Віддалений" #: editor/scene_tree_dock.cpp msgid "Local" -msgstr "" +msgstr "Локальний" #: editor/scene_tree_dock.cpp msgid "Clear Inheritance? (No Undo!)" -msgstr "" +msgstr "Вилучити уÑпадковуваннÑ? (Без можливоÑÑ‚Ñ– ÑкаÑувати!)" #: editor/scene_tree_dock.cpp msgid "Clear!" -msgstr "" +msgstr "Вилучено!" #: editor/scene_tree_editor.cpp msgid "Toggle Spatial Visible" -msgstr "" +msgstr "Перемкнути проÑторову видиміÑÑ‚ÑŒ" #: editor/scene_tree_editor.cpp msgid "Toggle CanvasItem Visible" -msgstr "" +msgstr "Перемкнути видиміÑÑ‚ÑŒ CanvasItem" #: editor/scene_tree_editor.cpp msgid "Node configuration warning:" -msgstr "" +msgstr "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ñ‰Ð¾Ð´Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²ÑƒÐ·Ð»Ð°:" #: editor/scene_tree_editor.cpp msgid "" "Node has connection(s) and group(s)\n" "Click to show signals dock." msgstr "" +"Вузол міÑтить з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ– групи\n" +"Клацніть, щоб переглÑнути панель Ñигналів." #: editor/scene_tree_editor.cpp msgid "" "Node has connections.\n" "Click to show signals dock." msgstr "" +"Вузол міÑтить з'єднаннÑ\n" +"Клацніть, щоб переглÑнути панель Ñигналів." #: editor/scene_tree_editor.cpp msgid "" "Node is in group(s).\n" "Click to show groups dock." msgstr "" +"Вузол належить групам.\n" +"Клацніть, щоб переглÑнути панель груп." #: editor/scene_tree_editor.cpp msgid "Instance:" -msgstr "" +msgstr "ЕкземплÑÑ€:" #: editor/scene_tree_editor.cpp msgid "Open script" @@ -6818,14 +6857,16 @@ msgid "" "Children are not selectable.\n" "Click to make selectable" msgstr "" +"Дочірні об'єкти не можна позначити.\n" +"Клацніть, щоб зробити Ñ—Ñ… придатними до позначеннÑ" #: editor/scene_tree_editor.cpp msgid "Toggle Visibility" -msgstr "" +msgstr "Перемкнути видиміÑÑ‚ÑŒ" #: editor/scene_tree_editor.cpp msgid "Invalid node name, the following characters are not allowed:" -msgstr "" +msgstr "Ðекоректна назва вузла. Ðе можна викориÑтовувати такі Ñимволи:" #: editor/scene_tree_editor.cpp msgid "Rename Node" @@ -6833,11 +6874,11 @@ msgstr "Перейменувати вузол" #: editor/scene_tree_editor.cpp msgid "Scene Tree (Nodes):" -msgstr "" +msgstr "Дерево Ñцени (вузли):" #: editor/scene_tree_editor.cpp msgid "Node Configuration Warning!" -msgstr "" +msgstr "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ñ‰Ð¾Ð´Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²ÑƒÐ·Ð»Ñ–Ð²!" #: editor/scene_tree_editor.cpp msgid "Select a Node" @@ -6845,15 +6886,15 @@ msgstr "Виберіть вузол" #: editor/script_create_dialog.cpp msgid "Error loading template '%s'" -msgstr "" +msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби завантажити шаблон «%s»" #: editor/script_create_dialog.cpp msgid "Error - Could not create script in filesystem." -msgstr "" +msgstr "Помилка: не вдалоÑÑ Ñтворити Ñкрипт у файловій ÑиÑтемі." #: editor/script_create_dialog.cpp msgid "Error loading script from %s" -msgstr "" +msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби завантажити Ñкрипт з %s" #: editor/script_create_dialog.cpp msgid "N/A" @@ -6861,31 +6902,31 @@ msgstr "Ð/З" #: editor/script_create_dialog.cpp msgid "Path is empty" -msgstr "" +msgstr "Порожній шлÑÑ…" #: editor/script_create_dialog.cpp msgid "Path is not local" -msgstr "" +msgstr "ШлÑÑ… не Ñ” локальним" #: editor/script_create_dialog.cpp msgid "Invalid base path" -msgstr "" +msgstr "Ðекоректний базовий шлÑÑ…" #: editor/script_create_dialog.cpp msgid "Directory of the same name exists" -msgstr "" +msgstr "Каталог із такою назвою вже Ñ–Ñнує" #: editor/script_create_dialog.cpp msgid "File exists, will be reused" -msgstr "" +msgstr "Файл вже Ñ–Ñнує, його буде викориÑтано повторно" #: editor/script_create_dialog.cpp msgid "Invalid extension" -msgstr "" +msgstr "Ðекоректний ÑуфікÑ" #: editor/script_create_dialog.cpp msgid "Wrong extension chosen" -msgstr "" +msgstr "Вибрано некоректний ÑуфікÑ" #: editor/script_create_dialog.cpp msgid "Invalid Path" @@ -6893,23 +6934,23 @@ msgstr "Ðеправильний шлÑÑ…" #: editor/script_create_dialog.cpp msgid "Invalid class name" -msgstr "" +msgstr "Ðекоректна назва клаÑу" #: editor/script_create_dialog.cpp msgid "Invalid inherited parent name or path" -msgstr "" +msgstr "Ðекоректна назва або шлÑÑ… до уÑпадкованого батьківÑького елемента" #: editor/script_create_dialog.cpp msgid "Script valid" -msgstr "" +msgstr "Скрипт Ñ” коректним" #: editor/script_create_dialog.cpp msgid "Allowed: a-z, A-Z, 0-9 and _" -msgstr "" +msgstr "Можна викориÑтовувати: a-z, A-Z, 0-9 Ñ– _" #: editor/script_create_dialog.cpp msgid "Built-in script (into scene file)" -msgstr "" +msgstr "Вбудований (до файла Ñцени) Ñкрипт" #: editor/script_create_dialog.cpp msgid "Create new script file" @@ -6925,31 +6966,31 @@ msgstr "Мова" #: editor/script_create_dialog.cpp msgid "Inherits" -msgstr "" +msgstr "УÑпадковує" #: editor/script_create_dialog.cpp msgid "Class Name" -msgstr "" +msgstr "Ðазва клаÑу" #: editor/script_create_dialog.cpp msgid "Template" -msgstr "" +msgstr "Шаблон" #: editor/script_create_dialog.cpp msgid "Built-in Script" -msgstr "" +msgstr "Вбудований Ñкрипт" #: editor/script_create_dialog.cpp msgid "Attach Node Script" -msgstr "" +msgstr "Долучити Ñкрипт вузла" #: editor/script_editor_debugger.cpp msgid "Remote " -msgstr "" +msgstr "Віддалений " #: editor/script_editor_debugger.cpp msgid "Bytes:" -msgstr "" +msgstr "Байтів:" #: editor/script_editor_debugger.cpp msgid "Warning" @@ -6969,7 +7010,7 @@ msgstr "ФункціÑ:" #: editor/script_editor_debugger.cpp msgid "Pick one or more items from the list to display the graph." -msgstr "" +msgstr "Виберіть один або декілька пунктів зі ÑпиÑку Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду графу." #: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp msgid "Errors" @@ -6977,7 +7018,7 @@ msgstr "Помилки" #: editor/script_editor_debugger.cpp msgid "Child Process Connected" -msgstr "" +msgstr "З'єднано дочірній процеÑ" #: editor/script_editor_debugger.cpp msgid "Copy Error" @@ -6985,15 +7026,15 @@ msgstr "Помилка копіюваннÑ" #: editor/script_editor_debugger.cpp msgid "Inspect Previous Instance" -msgstr "" +msgstr "ІнÑпектувати попередній екземплÑÑ€" #: editor/script_editor_debugger.cpp msgid "Inspect Next Instance" -msgstr "" +msgstr "ІнÑпектувати наÑтупний екземплÑÑ€" #: editor/script_editor_debugger.cpp msgid "Stack Frames" -msgstr "" +msgstr "СтоÑувати кадри" #: editor/script_editor_debugger.cpp msgid "Variable" @@ -7005,91 +7046,91 @@ msgstr "Помилки:" #: editor/script_editor_debugger.cpp msgid "Stack Trace (if applicable):" -msgstr "" +msgstr "ТраÑÑƒÐ²Ð°Ð½Ð½Ñ Ñтека (Ñкщо заÑтоÑовне):" #: editor/script_editor_debugger.cpp msgid "Profiler" -msgstr "" +msgstr "ЗаÑіб профілюваннÑ" #: editor/script_editor_debugger.cpp msgid "Monitor" -msgstr "" +msgstr "Монітор" #: editor/script_editor_debugger.cpp msgid "Value" -msgstr "" +msgstr "ЗначеннÑ" #: editor/script_editor_debugger.cpp msgid "Monitors" -msgstr "" +msgstr "Монітори" #: editor/script_editor_debugger.cpp msgid "List of Video Memory Usage by Resource:" -msgstr "" +msgstr "СпиÑок викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ÐµÐ¾Ð¿Ð°Ð¼'ÑÑ‚Ñ– за реÑурÑами:" #: editor/script_editor_debugger.cpp msgid "Total:" -msgstr "" +msgstr "Загалом:" #: editor/script_editor_debugger.cpp msgid "Video Mem" -msgstr "" +msgstr "Відеопам'ÑÑ‚ÑŒ" #: editor/script_editor_debugger.cpp msgid "Resource Path" -msgstr "" +msgstr "ШлÑÑ… до реÑурÑу" #: editor/script_editor_debugger.cpp msgid "Type" -msgstr "" +msgstr "Тип" #: editor/script_editor_debugger.cpp msgid "Format" -msgstr "" +msgstr "Формат" #: editor/script_editor_debugger.cpp msgid "Usage" -msgstr "" +msgstr "ВикориÑтаннÑ" #: editor/script_editor_debugger.cpp msgid "Misc" -msgstr "" +msgstr "Інше" #: editor/script_editor_debugger.cpp msgid "Clicked Control:" -msgstr "" +msgstr "ÐатиÑнутий елемент керуваннÑ:" #: editor/script_editor_debugger.cpp msgid "Clicked Control Type:" -msgstr "" +msgstr "ÐатиÑнутий тип керуваннÑ:" #: editor/script_editor_debugger.cpp msgid "Live Edit Root:" -msgstr "" +msgstr "Корінь інтерактивного редагуваннÑ:" #: editor/script_editor_debugger.cpp msgid "Set From Tree" -msgstr "" +msgstr "Ð’Ñтановити з дерева" #: editor/settings_config_dialog.cpp msgid "Shortcuts" -msgstr "" +msgstr "Клавіатурні ÑкороченнÑ" #: editor/settings_config_dialog.cpp msgid "Binding" -msgstr "" +msgstr "Палітурка" #: editor/spatial_editor_gizmos.cpp msgid "Change Light Radius" -msgstr "" +msgstr "Змінити Ñ€Ð°Ð´Ñ–ÑƒÑ Ð¾ÑвітленнÑ" #: editor/spatial_editor_gizmos.cpp msgid "Change AudioStreamPlayer3D Emission Angle" -msgstr "" +msgstr "Змінити кут Ð²Ð¸Ð¿Ñ€Ð¾Ð¼Ñ–Ð½ÑŽÐ²Ð°Ð½Ð½Ñ AudioStreamPlayer3D" #: editor/spatial_editor_gizmos.cpp msgid "Change Camera FOV" -msgstr "" +msgstr "Змінити поле зору камери" #: editor/spatial_editor_gizmos.cpp msgid "Change Camera Size" @@ -7097,43 +7138,43 @@ msgstr "Змінити розмір камери" #: editor/spatial_editor_gizmos.cpp msgid "Change Sphere Shape Radius" -msgstr "" +msgstr "Змінити Ñ€Ð°Ð´Ñ–ÑƒÑ Ñферичної форми" #: editor/spatial_editor_gizmos.cpp msgid "Change Box Shape Extents" -msgstr "" +msgstr "Змінити розміри кубічної форми" #: editor/spatial_editor_gizmos.cpp msgid "Change Capsule Shape Radius" -msgstr "" +msgstr "Змінити Ñ€Ð°Ð´Ñ–ÑƒÑ Ñ„Ð¾Ñ€Ð¼Ð¸ капÑули" #: editor/spatial_editor_gizmos.cpp msgid "Change Capsule Shape Height" -msgstr "" +msgstr "Змінити виÑоту форми капÑули" #: editor/spatial_editor_gizmos.cpp msgid "Change Ray Shape Length" -msgstr "" +msgstr "Змінити довжину форми променÑ" #: editor/spatial_editor_gizmos.cpp msgid "Change Notifier Extents" -msgstr "" +msgstr "Змінити розміри заÑобу ÑповіщеннÑ" #: editor/spatial_editor_gizmos.cpp msgid "Change Particles AABB" -msgstr "" +msgstr "Змінити AABB чаÑток" #: editor/spatial_editor_gizmos.cpp msgid "Change Probe Extents" -msgstr "" +msgstr "Змінити розміри зонду" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Select the dynamic library for this entry" -msgstr "" +msgstr "Виберіть динамічну бібліотеку Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиÑу" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Select dependencies of the library for this entry" -msgstr "" +msgstr "Виберіть залежноÑÑ‚Ñ– бібліотеки Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиÑу" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Remove current entry" @@ -7141,15 +7182,15 @@ msgstr "Видалити поточне поле" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Double click to create a new entry" -msgstr "" +msgstr "Двічі клацніть Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ запиÑу" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Platform:" -msgstr "" +msgstr "Платформа:" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Platform" -msgstr "" +msgstr "Платформа" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Dynamic Library" @@ -7157,7 +7198,7 @@ msgstr "Динамічна бібліотека" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "Add an architecture entry" -msgstr "" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð°Ñ€Ñ…Ñ–Ñ‚ÐµÐºÑ‚ÑƒÑ€Ð¸" #: modules/gdnative/gdnative_library_editor_plugin.cpp msgid "GDNativeLibrary" @@ -7177,25 +7218,26 @@ msgstr "Бібліотеки: " #: modules/gdnative/register_types.cpp msgid "GDNative" -msgstr "" +msgstr "GD Native" #: modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp msgid "Invalid type argument to convert(), use TYPE_* constants." msgstr "" +"Ðекоректний аргумент типу у convert(), Ñлід викориÑтовувати Ñталі TYPE_*." #: modules/gdscript/gdscript_functions.cpp modules/mono/glue/glue_header.h #: modules/visual_script/visual_script_builtin_funcs.cpp msgid "Not enough bytes for decoding bytes, or invalid format." -msgstr "" +msgstr "ÐедоÑтатньо байтів Ð´Ð»Ñ Ð´ÐµÐºÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ вказано некоректний формат." #: modules/gdscript/gdscript_functions.cpp msgid "step argument is zero!" -msgstr "" +msgstr "аргумент кроку дорівнює нулеві!" #: modules/gdscript/gdscript_functions.cpp msgid "Not a script with an instance" -msgstr "" +msgstr "Ðе Ñкрипт з екземплÑром" #: modules/gdscript/gdscript_functions.cpp msgid "Not based on a script" @@ -7207,127 +7249,129 @@ msgstr "Ðе заÑнований на файлі реÑурÑів" #: modules/gdscript/gdscript_functions.cpp msgid "Invalid instance dictionary format (missing @path)" -msgstr "" +msgstr "Ðекоректний формат Ñловника екземплÑра (пропущено @path)" #: modules/gdscript/gdscript_functions.cpp msgid "Invalid instance dictionary format (can't load script at @path)" msgstr "" +"Ðекоректний формат Ñловника екземплÑра (не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ Ñкрипт у " +"@path)" #: modules/gdscript/gdscript_functions.cpp msgid "Invalid instance dictionary format (invalid script at @path)" -msgstr "" +msgstr "Ðекоректний формат Ñловника екземплÑра (некоректний Ñкрипт у @path)" #: modules/gdscript/gdscript_functions.cpp msgid "Invalid instance dictionary (invalid subclasses)" -msgstr "" +msgstr "Ðекоректний Ñловник екземплÑра (некоректні підклаÑи)" #: modules/gdscript/gdscript_functions.cpp msgid "Object can't provide a length." -msgstr "" +msgstr "Об'єкт не може надавати довжину." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Delete Selection" -msgstr "" +msgstr "Ð’Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾Ð³Ð¾ GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Duplicate Selection" -msgstr "" +msgstr "Ð”ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾Ð³Ð¾ GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Floor:" -msgstr "" +msgstr "Поверх:" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" -msgstr "" +msgstr "Карта Ñітки" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Snap View" -msgstr "" +msgstr "Миттєвий переглÑд" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Previous Floor" -msgstr "" +msgstr "Попередній поверх" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Floor" -msgstr "" +msgstr "ÐаÑтупний поверх" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Disabled" -msgstr "" +msgstr "ÐžÐ±Ñ€Ñ–Ð·Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð¾" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Above" -msgstr "" +msgstr "Обрізати вище" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Below" -msgstr "" +msgstr "Обрізати нижче" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit X Axis" -msgstr "" +msgstr "Редагувати віÑÑŒ X" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit Y Axis" -msgstr "" +msgstr "Редагувати віÑÑŒ Y" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit Z Axis" -msgstr "" +msgstr "Редагувати віÑÑŒ Z" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Rotate X" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо X" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Rotate Y" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо Y" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Rotate Z" -msgstr "" +msgstr "ÐžÐ±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо Z" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Back Rotate X" -msgstr "" +msgstr "Зворотне Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо X" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Back Rotate Y" -msgstr "" +msgstr "Зворотне Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо Y" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Back Rotate Z" -msgstr "" +msgstr "Зворотне Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð²ÐºÐ°Ð·Ñ–Ð²Ð½Ð¸ÐºÐ° навколо Z" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Cursor Clear Rotation" -msgstr "" +msgstr "ЗнÑти Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð· вказівника" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Create Area" -msgstr "" +msgstr "Створити облаÑÑ‚ÑŒ" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Create Exterior Connector" -msgstr "" +msgstr "Створити зовнішнє з'єднаннÑ" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Erase Area" -msgstr "" +msgstr "Витерти облаÑÑ‚ÑŒ" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clear Selection" -msgstr "ОчиÑтити виділене" +msgstr "ОчиÑтити позначене" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Settings" -msgstr "" +msgstr "Параметри GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Pick Distance:" -msgstr "" +msgstr "ВідÑтань вибору:" #: modules/mono/editor/godotsharp_editor.cpp msgid "Generating solution..." @@ -7335,7 +7379,7 @@ msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð²'Ñзку..." #: modules/mono/editor/godotsharp_editor.cpp msgid "Generating C# project..." -msgstr "" +msgstr "Створюємо проект C#..." #: modules/mono/editor/godotsharp_editor.cpp msgid "Failed to create solution." @@ -7355,7 +7399,7 @@ msgstr "Ðе вдалоÑÑ Ñтворити проект C#." #: modules/mono/editor/godotsharp_editor.cpp msgid "Mono" -msgstr "" +msgstr "Моно" #: modules/mono/editor/godotsharp_editor.cpp msgid "Create C# solution" @@ -7363,7 +7407,7 @@ msgstr "Створити розв'Ñзок C#" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Builds" -msgstr "" +msgstr "ЗбираннÑ" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Build Project" @@ -7378,50 +7422,56 @@ msgid "" "A node yielded without working memory, please read the docs on how to yield " "properly!" msgstr "" +"Вузол відпрацював без робочої пам'ÑÑ‚Ñ–. Будь лаÑка, ознайомтеÑÑ Ñ–Ð· " +"документацією щодо правильного ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²ÑƒÐ·Ð»Ñ–Ð²!" #: modules/visual_script/visual_script.cpp msgid "" "Node yielded, but did not return a function state in the first working " "memory." msgstr "" +"Вузол відпрацював, але не повернув Ñтану функції у першій робочій пам'ÑÑ‚Ñ–." #: modules/visual_script/visual_script.cpp msgid "" "Return value must be assigned to first element of node working memory! Fix " "your node please." msgstr "" +"Повернуте Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути пов'Ñзано із першим елементом у робочій пам'ÑÑ‚Ñ– " +"вузла! Будь лаÑка, виправте ваш вузол." #: modules/visual_script/visual_script.cpp msgid "Node returned an invalid sequence output: " -msgstr "" +msgstr "Вузлом повернуто некоректну поÑлідовніÑÑ‚ÑŒ даних: " #: modules/visual_script/visual_script.cpp msgid "Found sequence bit but not the node in the stack, report bug!" msgstr "" +"ВиÑвлено біт поÑлідовноÑÑ‚Ñ–, але не вузол у Ñтеку. Повідомте про цю ваду!" #: modules/visual_script/visual_script.cpp msgid "Stack overflow with stack depth: " -msgstr "" +msgstr "ÐŸÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñтека, глибина Ñтека: " #: modules/visual_script/visual_script_editor.cpp msgid "Change Signal Arguments" -msgstr "" +msgstr "Змірити аргументи Ñигналу" #: modules/visual_script/visual_script_editor.cpp msgid "Change Argument Type" -msgstr "" +msgstr "Змінити тип аргументу" #: modules/visual_script/visual_script_editor.cpp msgid "Change Argument name" -msgstr "" +msgstr "Змінити назву аргументу" #: modules/visual_script/visual_script_editor.cpp msgid "Set Variable Default Value" -msgstr "" +msgstr "Ð’Ñтановити типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ—" #: modules/visual_script/visual_script_editor.cpp msgid "Set Variable Type" -msgstr "" +msgstr "Ð’Ñтановити тип змінної" #: modules/visual_script/visual_script_editor.cpp msgid "Functions:" @@ -7433,11 +7483,11 @@ msgstr "Змінні:" #: modules/visual_script/visual_script_editor.cpp msgid "Name is not a valid identifier:" -msgstr "" +msgstr "Ðазва не Ñ” коректним ідентифікатором:" #: modules/visual_script/visual_script_editor.cpp msgid "Name already in use by another func/var/signal:" -msgstr "" +msgstr "Ðазва вже викориÑтовуєтьÑÑ Ñ–Ð½ÑˆÐ¾ÑŽ функцією, змінною чи Ñигналом:" #: modules/visual_script/visual_script_editor.cpp msgid "Rename Function" @@ -7473,35 +7523,39 @@ msgstr "Додати вузол" #: modules/visual_script/visual_script_editor.cpp msgid "Remove VisualScript Nodes" -msgstr "" +msgstr "Вилучити вузли VisualScript" #: modules/visual_script/visual_script_editor.cpp msgid "Duplicate VisualScript Nodes" -msgstr "" +msgstr "Дублювати вузли VisualScript" #: modules/visual_script/visual_script_editor.cpp msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature." msgstr "" +"Утримуйте натиÑнутою %s, щоб Ñкинути отримувач. Утримуйте натиÑнутою Shift, " +"щоб Ñкинути Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð´Ð¶ÐµÐ½ÐµÑ€Ñ–ÐºÐ°." #: modules/visual_script/visual_script_editor.cpp msgid "Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature." msgstr "" +"Утримуйте натиÑнутою Ctrl, щоб Ñкинути отримувач. Утримуйте натиÑнутою " +"Shift, щоб Ñкинути Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð´Ð¶ÐµÐ½ÐµÑ€Ñ–ÐºÐ°." #: modules/visual_script/visual_script_editor.cpp msgid "Hold %s to drop a simple reference to the node." -msgstr "" +msgstr "Утримуйте натиÑнутою %s, щоб Ñкинути проÑте поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° вузол." #: modules/visual_script/visual_script_editor.cpp msgid "Hold Ctrl to drop a simple reference to the node." -msgstr "" +msgstr "Утримуйте натиÑнутою Ctrl, щоб Ñкинути проÑте поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° вузол." #: modules/visual_script/visual_script_editor.cpp msgid "Hold %s to drop a Variable Setter." -msgstr "" +msgstr "Утримуйте натиÑнутою %s, щоб Ñкинути вÑтановлювач змінної." #: modules/visual_script/visual_script_editor.cpp msgid "Hold Ctrl to drop a Variable Setter." -msgstr "" +msgstr "Утримуйте натиÑнутою Ctrl, щоб Ñкинути вÑтановлювач змінної." #: modules/visual_script/visual_script_editor.cpp msgid "Add Preload Node" @@ -7509,15 +7563,15 @@ msgstr "Додати попередньо завантажений вузол" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" -msgstr "" +msgstr "Додати вузли з дерева" #: modules/visual_script/visual_script_editor.cpp msgid "Add Getter Property" -msgstr "" +msgstr "Додати влаÑтивіÑÑ‚ÑŒ отримувача" #: modules/visual_script/visual_script_editor.cpp msgid "Add Setter Property" -msgstr "" +msgstr "Додати влаÑтивіÑÑ‚ÑŒ вÑтановлювача" #: modules/visual_script/visual_script_editor.cpp msgid "Change Base Type" @@ -7525,11 +7579,11 @@ msgstr "Змінити базовий тип" #: modules/visual_script/visual_script_editor.cpp msgid "Move Node(s)" -msgstr "" +msgstr "ПереÑунути вузли" #: modules/visual_script/visual_script_editor.cpp msgid "Remove VisualScript Node" -msgstr "" +msgstr "Вилучити вузол VisualScript" #: modules/visual_script/visual_script_editor.cpp msgid "Connect Nodes" @@ -7537,11 +7591,11 @@ msgstr "Приєднати вузли" #: modules/visual_script/visual_script_editor.cpp msgid "Condition" -msgstr "" +msgstr "Умова" #: modules/visual_script/visual_script_editor.cpp msgid "Sequence" -msgstr "" +msgstr "ПоÑлідовніÑÑ‚ÑŒ" #: modules/visual_script/visual_script_editor.cpp msgid "Switch" @@ -7581,7 +7635,7 @@ msgstr "Ðеможливо Ñкопіювати вузол функції." #: modules/visual_script/visual_script_editor.cpp msgid "Clipboard is empty!" -msgstr "" +msgstr "Буфер обміну порожній!" #: modules/visual_script/visual_script_editor.cpp msgid "Paste VisualScript Nodes" @@ -7681,7 +7735,7 @@ msgstr "ШлÑÑ… не веде до вузла!" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Invalid index property name '%s' in node %s." -msgstr "" +msgstr "Ðекоректна назва влаÑтивоÑÑ‚Ñ– індекÑу, «%s», у вузлі %s." #: modules/visual_script/visual_script_nodes.cpp msgid ": Invalid argument of type: " @@ -7693,21 +7747,23 @@ msgstr ": ÐеприпуÑтимі аргументи: " #: modules/visual_script/visual_script_nodes.cpp msgid "VariableGet not found in script: " -msgstr "" +msgstr "Ðе знайдено VariableGet у Ñкрипті: " #: modules/visual_script/visual_script_nodes.cpp msgid "VariableSet not found in script: " -msgstr "" +msgstr "Ðе знайдено VariableSet у Ñкрипті: " #: modules/visual_script/visual_script_nodes.cpp msgid "Custom node has no _step() method, can't process graph." -msgstr "" +msgstr "У нетиповому вузлі немає методу _step(). Обробка графу неможлива." #: modules/visual_script/visual_script_nodes.cpp msgid "" "Invalid return value from _step(), must be integer (seq out), or string " "(error)." msgstr "" +"_step() повертає некоректне значеннÑ. ЗначеннÑм має бути ціле чиÑло (seq " +"out) або Ñ€Ñдок (error)." #: platform/javascript/export/export.cpp msgid "Run in Browser" @@ -7746,19 +7802,27 @@ msgid "" "A SpriteFrames resource must be created or set in the 'Frames' property in " "order for AnimatedSprite to display frames." msgstr "" +"Щоб AnimatedSprite могла показувати кадри, має бути Ñтворено або вÑтановлено " +"у влаÑтивоÑÑ‚Ñ– «Frames» реÑÑƒÑ€Ñ SpriteFrames." #: scene/2d/canvas_modulate.cpp msgid "" "Only one visible CanvasModulate is allowed per scene (or set of instanced " "scenes). The first created one will work, while the rest will be ignored." msgstr "" +"У Ñцені (або наборі екземплÑрів Ñцен) може бути лише один видимий " +"CanvasModulate. Працюватиме перший зі Ñтворених, решту буде проігноровано." #: scene/2d/collision_object_2d.cpp msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"У цього вузла немає дочірніх форм, отже він не може взаємодіÑти із " +"проÑтором.\n" +"Спробуйте додати дочірні вузли CollisionShape2D або CollisionPolygon2D Ð´Ð»Ñ " +"Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¹Ð¾Ð³Ð¾ форми." #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7766,10 +7830,13 @@ msgid "" "CollisionObject2D derived node. Please only use it as a child of Area2D, " "StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape." msgstr "" +"CollisionPolygon2D призначено лише Ð´Ð»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ похідному " +"вузлу CollisionObject2D. Будь лаÑка, викориÑтовуйте його Ñк дочірній елемент " +"Area2D, StaticBody2D, RigidBody2D, KinematicBody2D тощо, щоб надати їм форми." #: scene/2d/collision_polygon_2d.cpp msgid "An empty CollisionPolygon2D has no effect on collision." -msgstr "" +msgstr "Порожній CollisionPolygon2D ніÑк не вплине на зіткненнÑ." #: scene/2d/collision_shape_2d.cpp msgid "" @@ -7777,54 +7844,72 @@ msgid "" "CollisionObject2D derived node. Please only use it as a child of Area2D, " "StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape." msgstr "" +"CollisionShape2D призначено лише Ð´Ð»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ похідному " +"вузлу CollisionObject2D. Будь лаÑка, викориÑтовуйте його Ñк дочірній елемент " +"Area2D, StaticBody2D, RigidBody2D, KinematicBody2D тощо, щоб надати їм форми." #: scene/2d/collision_shape_2d.cpp msgid "" "A shape must be provided for CollisionShape2D to function. Please create a " "shape resource for it!" msgstr "" +"Ð”Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÐµÐ·Ð´Ð°Ñ‚Ð½Ð¾ÑÑ‚Ñ– CollisionShape2D Ñлід надати форму. Будь " +"лаÑка, Ñтворіть реÑÑƒÑ€Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ елемента!" #: scene/2d/light_2d.cpp msgid "" "A texture with the shape of the light must be supplied to the 'texture' " "property." -msgstr "" +msgstr "Ð”Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «texture» Ñлід надати текÑтуру із формою оÑвітленнÑ." #: scene/2d/light_occluder_2d.cpp msgid "" "An occluder polygon must be set (or drawn) for this occluder to take effect." msgstr "" +"Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб ефект затулÑÐ½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÑŽÐ²Ð°Ð², Ñлід вÑтановити (або намалювати) " +"багатокутник затулÑннÑ." #: scene/2d/light_occluder_2d.cpp msgid "The occluder polygon for this occluder is empty. Please draw a polygon!" msgstr "" +"Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ затулÑÐ½Ð½Ñ Ð±Ð°Ð³Ð°Ñ‚Ð¾ÐºÑƒÑ‚Ð½Ð¸Ðº Ñ” порожнім. Будь лаÑка, намалюйте " +"багатокутник!" #: scene/2d/navigation_polygon.cpp msgid "" "A NavigationPolygon resource must be set or created for this node to work. " "Please set a property or draw a polygon." msgstr "" +"Щоб забезпечити працездатніÑÑ‚ÑŒ цього вузла, Ñлід вÑтановити або Ñтворити " +"реÑÑƒÑ€Ñ NavigationPolygon. Будь лаÑка, вÑтановіть Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– або " +"намалюйте багатокутник." #: scene/2d/navigation_polygon.cpp msgid "" "NavigationPolygonInstance must be a child or grandchild to a Navigation2D " "node. It only provides navigation data." msgstr "" +"NavigationPolygonInstance має бути дочірнім елементом вузла Navigation2D або " +"елементом ще нижчої підпорÑдкованоÑÑ‚Ñ–. Він надає лише навігаційні дані." #: scene/2d/parallax_layer.cpp msgid "" "ParallaxLayer node only works when set as child of a ParallaxBackground node." msgstr "" +"Вузол ParallaxLayer працює, лише Ñкщо його вÑтановлено Ñк дочірній Ð´Ð»Ñ Ð²ÑƒÐ·Ð»Ð° " +"ParallaxBackground." #: scene/2d/particles_2d.cpp scene/3d/particles.cpp msgid "" "A material to process the particles is not assigned, so no behavior is " "imprinted." msgstr "" +"Ðе визначено матеріалу Ð´Ð»Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ чаÑток, тому ніÑкої поведінки не " +"відтворюватиметьÑÑ." #: scene/2d/path_2d.cpp msgid "PathFollow2D only works when set as a child of a Path2D node." -msgstr "" +msgstr "PathFollow2D працюватиме лише Ñк дочірній елемент вузла Path2D." #: scene/2d/physics_body_2d.cpp msgid "" @@ -7832,44 +7917,55 @@ msgid "" "by the physics engine when running.\n" "Change the size in children collision shapes instead." msgstr "" +"Зміни розмірів RigidBody2D (у режимах character або rigid) буде " +"перевизначено фізичним рушієм під Ñ‡Ð°Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸.\n" +"ЗаміÑÑ‚ÑŒ цієї зміни, вам варто змінити розміри дочірніх форм зіткненнÑ." #: scene/2d/remote_transform_2d.cpp msgid "Path property must point to a valid Node2D node to work." msgstr "" +"Щоб уÑе працювало Ñк Ñлід, влаÑтивіÑÑ‚ÑŒ шлÑху (path) має вказувати на " +"коректний вузол Node2D." #: scene/2d/visibility_notifier_2d.cpp msgid "" "VisibilityEnable2D works best when used with the edited scene root directly " "as parent." msgstr "" +"VisibilityEnable2D найкраще працюватиме, Ñкщо його викориÑтано із " +"безпоÑереднім батьківÑьким елементом — редагованим коренем Ñцени." #: scene/3d/arvr_nodes.cpp msgid "ARVRCamera must have an ARVROrigin node as its parent" -msgstr "" +msgstr "ARVRCamera повинен мати батьківÑьким вузлом вузол ARVROrigin" #: scene/3d/arvr_nodes.cpp msgid "ARVRController must have an ARVROrigin node as its parent" -msgstr "" +msgstr "ARVRController повинен мати батьківÑьким вузлом вузол ARVROrigin" #: scene/3d/arvr_nodes.cpp msgid "" "The controller id must not be 0 or this controller will not be bound to an " "actual controller" msgstr "" +"Ідентифікатором контролера має бути значеннÑ, Ñке Ñ” відмінним від 0, інакше " +"цей контролер не буде пов'Ñзано із Ñправжнім елементом керуваннÑ" #: scene/3d/arvr_nodes.cpp msgid "ARVRAnchor must have an ARVROrigin node as its parent" -msgstr "" +msgstr "ARVRAnchor повинен мати батьківÑьким вузлом вузол ARVROrigin" #: scene/3d/arvr_nodes.cpp msgid "" "The anchor id must not be 0 or this anchor will not be bound to an actual " "anchor" msgstr "" +"Ідентифікатором прив'Ñзки має бути значеннÑ, Ñке Ñ” відмінним від 0, інакше " +"цю прив'Ñзку не буде пов'Ñзано із Ñправжньою прив'Ñзкою" #: scene/3d/arvr_nodes.cpp msgid "ARVROrigin requires an ARVRCamera child node" -msgstr "" +msgstr "ARVROrigin повинен мати дочірній вузол ARVRCamera" #: scene/3d/baked_lightmap.cpp msgid "Plotting Meshes: " @@ -7891,8 +7987,12 @@ msgstr "ОÑÐ²Ñ–Ñ‚Ð»ÐµÐ½Ð½Ñ Ñітки: " msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"У цього вузла немає дочірніх форм, отже він не може взаємодіÑти із " +"проÑтором.\n" +"Спробуйте додати дочірні вузли CollisionShape або CollisionPolygon Ð´Ð»Ñ " +"Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¹Ð¾Ð³Ð¾ форми." #: scene/3d/collision_polygon.cpp msgid "" @@ -7900,10 +8000,13 @@ msgid "" "CollisionObject derived node. Please only use it as a child of Area, " "StaticBody, RigidBody, KinematicBody, etc. to give them a shape." msgstr "" +"CollisionPolygon призначено лише Ð´Ð»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ похідному " +"вузлу CollisionObject. Будь лаÑка, викориÑтовуйте його Ñк дочірній елемент " +"Area, StaticBody, RigidBody, KinematicBody тощо, щоб надати їм форми." #: scene/3d/collision_polygon.cpp msgid "An empty CollisionPolygon has no effect on collision." -msgstr "" +msgstr "Порожній CollisionPolygon ніÑк не вплине на зіткненнÑ." #: scene/3d/collision_shape.cpp msgid "" @@ -7911,12 +8014,17 @@ msgid "" "derived node. Please only use it as a child of Area, StaticBody, RigidBody, " "KinematicBody, etc. to give them a shape." msgstr "" +"CollisionShape призначено лише Ð´Ð»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ похідному " +"вузлу CollisionObject. Будь лаÑка, викориÑтовуйте його Ñк дочірній елемент " +"Area, StaticBody, RigidBody, KinematicBody тощо, щоб надати їм форми." #: scene/3d/collision_shape.cpp msgid "" "A shape must be provided for CollisionShape to function. Please create a " "shape resource for it!" msgstr "" +"Ð”Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÐµÐ·Ð´Ð°Ñ‚Ð½Ð¾ÑÑ‚Ñ– CollisionShape Ñлід надати форму. Будь " +"лаÑка, Ñтворіть реÑÑƒÑ€Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ елемента!" #: scene/3d/gi_probe.cpp msgid "Plotting Meshes" @@ -7933,11 +8041,14 @@ msgid "" "NavigationMeshInstance must be a child or grandchild to a Navigation node. " "It only provides navigation data." msgstr "" +"NavigationMeshInstance має бути дочірнім елементом вузла Navigation або " +"елементом ще нижчої підпорÑдкованоÑÑ‚Ñ–. Він надає лише навігаційні дані." #: scene/3d/particles.cpp msgid "" "Nothing is visible because meshes have not been assigned to draw passes." msgstr "" +"Ðічого не видно, оÑкільки Ñітки не було пов'Ñзано із проходами малюваннÑ." #: scene/3d/physics_body.cpp msgid "" @@ -7945,27 +8056,38 @@ msgid "" "by the physics engine when running.\n" "Change the size in children collision shapes instead." msgstr "" +"Зміни розмірів RigidBody (у режимах character або rigid) буде перевизначено " +"фізичним рушієм під Ñ‡Ð°Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸.\n" +"ЗаміÑÑ‚ÑŒ цієї зміни, вам варто змінити розміри дочірніх форм зіткненнÑ." #: scene/3d/remote_transform.cpp msgid "Path property must point to a valid Spatial node to work." msgstr "" +"Щоб уÑе працювало Ñк Ñлід, влаÑтивіÑÑ‚ÑŒ шлÑху (path) має вказувати на " +"коректний вузол Spatial." #: scene/3d/scenario_fx.cpp msgid "" "Only one WorldEnvironment is allowed per scene (or set of instanced scenes)." msgstr "" +"У Ñцені (або наборі екземплÑрів Ñцен) може бути лише один елемент " +"WorldEnvironment." #: scene/3d/sprite_3d.cpp msgid "" "A SpriteFrames resource must be created or set in the 'Frames' property in " "order for AnimatedSprite3D to display frames." msgstr "" +"Щоб AnimatedSprite могла показувати кадри, має бути Ñтворено або вÑтановлено " +"у влаÑтивоÑÑ‚Ñ– «Frames» реÑÑƒÑ€Ñ SpriteFrames." #: scene/3d/vehicle_body.cpp msgid "" "VehicleWheel serves to provide a wheel system to a VehicleBody. Please use " "it as a child of a VehicleBody." msgstr "" +"VehicleWheel Ñлугує Ð´Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ ÑиÑтеми ÐºÐ¾Ð»Ñ–Ñ Ñƒ VehicleBody. " +"Будь лаÑка, викориÑтовуйте цей елемент Ñк дочірній елемент вузла VehicleBody." #: scene/gui/color_picker.cpp msgid "Raw Mode" @@ -7977,11 +8099,11 @@ msgstr "Додати поточний колір в ÑкоÑÑ‚Ñ– преÑету" #: scene/gui/dialogs.cpp msgid "Alert!" -msgstr "СповіщеннÑ!" +msgstr "Увага!" #: scene/gui/dialogs.cpp msgid "Please Confirm..." -msgstr "Будь-лаÑка підтвердіть..." +msgstr "Будь лаÑка, підтвердьте..." #: scene/gui/file_dialog.cpp msgid "Select this Folder" @@ -7993,6 +8115,9 @@ msgid "" "functions. Making them visible for editing is fine though, but they will " "hide upon running." msgstr "" +"КонтекÑтні підказки типово буде приховано, Ñкщо ви не викличете popup() або " +"ÑкуÑÑŒ із функцій popup*(). Втім, робити Ñ—Ñ… видимими Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ â€” звична " +"практика. Втім, Ñлід пам'Ñтати, що під Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку Ñ—Ñ… буде приховано." #: scene/gui/scroll_container.cpp msgid "" @@ -8000,6 +8125,10 @@ msgid "" "Use a container as child (VBox,HBox,etc), or a Control and set the custom " "minimum size manually." msgstr "" +"ScrollContainer призначено Ð´Ð»Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ із одинарним дочірнім заÑобом " +"керуваннÑ.\n" +"СкориÑтайтеÑÑ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ð¾Ð¼ Ñк дочірнім об'єктом (VBox, HBox тощо) або вузлом " +"Control Ñ– вÑтановіть нетиповий мінімальний розмір вручну." #: scene/gui/tree.cpp msgid "(Other)" @@ -8010,6 +8139,8 @@ msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." msgstr "" +"Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ типове Ñередовище, Ñк його визначено у параметрах " +"проекту (Обробка -> Середовище -> Типове Ñередовище)." #: scene/main/viewport.cpp msgid "" @@ -8018,6 +8149,10 @@ msgid "" "obtain a size. Otherwise, make it a RenderTarget and assign its internal " "texture to some node for display." msgstr "" +"Ð¦Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ показу не Ñ” ціллю обробки зображеннÑ. Якщо ви хочете, щоб Ñ—Ñ— вміÑÑ‚ " +"було безпоÑередньо показано на екрані, зробіть Ñ—Ñ— дочірньою Ð´Ð»Ñ Ð²ÑƒÐ·Ð»Ð° " +"Control, щоб у неї був розмір. Крім того, можна зробити Ñ—Ñ— RenderTarget Ñ– " +"пов'Ñзати Ñ—Ñ— внутрішню текÑтуру з одним із вузлів Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ." #: scene/resources/dynamic_font.cpp msgid "Error initializing FreeType." @@ -8033,7 +8168,7 @@ msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ." #: scene/resources/dynamic_font.cpp msgid "Invalid font size." -msgstr "ÐедійÑний розмір шрифту." +msgstr "Ðекоректний розмір шрифту." #~ msgid "Next" #~ msgstr "Далі" diff --git a/editor/translations/ur_PK.po b/editor/translations/ur_PK.po index 61ed68262b..9fae520b55 100644 --- a/editor/translations/ur_PK.po +++ b/editor/translations/ur_PK.po @@ -7691,7 +7691,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7825,7 +7825,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/vi.po b/editor/translations/vi.po index 433c8429dd..018883dc91 100644 --- a/editor/translations/vi.po +++ b/editor/translations/vi.po @@ -3,6 +3,7 @@ # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. # +# Dlean Jeans <dleanjeans@gmail.com>, 2018. # Hai Le <dark.hades.1102@gmail.com>, 2017. # Nguyá»…n Tuấn Anh <anhnt.fami@gmail.com>, 2017. # Tung Le <tungkradle@gmail.com>, 2017. @@ -10,18 +11,17 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2017-11-25 13:51+0000\n" -"Last-Translator: Hai Le <dark.hades.1102@gmail.com>\n" +"PO-Revision-Date: 2018-01-24 12:35+0000\n" +"Last-Translator: Dlean Jeans <dleanjeans@gmail.com>\n" "Language-Team: Vietnamese <https://hosted.weblate.org/projects/godot-engine/" "godot/vi/>\n" "Language: vi\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2.18-dev\n" +"X-Generator: Weblate 2.19-dev\n" #: editor/animation_editor.cpp -#, fuzzy msgid "Disabled" msgstr "Tắt" @@ -30,34 +30,32 @@ msgid "All Selection" msgstr "Chá»n tất cả" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Time" -msgstr "Äổi giá trị" +msgstr "Äổi thá»i gian khung hình" #: editor/animation_editor.cpp msgid "Anim Change Transition" -msgstr "" +msgstr "Äổi Transition Animation" #: editor/animation_editor.cpp msgid "Anim Change Transform" -msgstr "" +msgstr "Äổi Transform Animation" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Value" -msgstr "Äổi giá trị" +msgstr "Äổi giá trị khung hình" #: editor/animation_editor.cpp msgid "Anim Change Call" -msgstr "" +msgstr "Äổi Function Gá»i Animation" #: editor/animation_editor.cpp msgid "Anim Add Track" -msgstr "" +msgstr "Thêm Track Animation" #: editor/animation_editor.cpp msgid "Anim Duplicate Keys" -msgstr "" +msgstr "Nhân đôi Các Key của Animation" #: editor/animation_editor.cpp msgid "Move Anim Track Up" @@ -73,7 +71,7 @@ msgstr "Xóa Anim Track" #: editor/animation_editor.cpp msgid "Set Transitions to:" -msgstr "" +msgstr "Äặt Transtions thà nh:" #: editor/animation_editor.cpp msgid "Anim Track Rename" @@ -106,7 +104,7 @@ msgstr "Xóa phÃm Anim" #: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Duplicate Selection" -msgstr "" +msgstr "Nhân đôi lá»±a chá»n" #: editor/animation_editor.cpp msgid "Duplicate Transposed" @@ -163,7 +161,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "In" -msgstr "" +msgstr "Trong" #: editor/animation_editor.cpp msgid "Out" @@ -239,7 +237,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Anim Add Call Track" -msgstr "" +msgstr "Thêm Track Gá»i Function" #: editor/animation_editor.cpp msgid "Animation zoom." @@ -247,7 +245,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Length (s):" -msgstr "" +msgstr "Äá»™ dà i (giây):" #: editor/animation_editor.cpp msgid "Animation length (in seconds)." @@ -291,7 +289,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Anim. Optimizer" -msgstr "" +msgstr "Tối Æ°u hóa Animation" #: editor/animation_editor.cpp msgid "Max. Linear Error:" @@ -311,7 +309,7 @@ msgstr "" #: editor/animation_editor.cpp msgid "Select an AnimationPlayer from the Scene Tree to edit animations." -msgstr "" +msgstr "Chá»n má»™t AnimationPlayer từ Scene Tree để chỉnh sá»a animation." #: editor/animation_editor.cpp msgid "Key" @@ -521,9 +519,8 @@ msgid "Change" msgstr "" #: editor/create_dialog.cpp -#, fuzzy msgid "Create New %s" -msgstr "Tạo" +msgstr "Tạo %s Má»›i" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -609,7 +606,7 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp #: editor/quick_open.cpp scene/gui/file_dialog.cpp msgid "Open" -msgstr "" +msgstr "Mở" #: editor/dependency_editor.cpp msgid "Owners Of:" @@ -680,7 +677,7 @@ msgstr "" #: editor/project_export.cpp editor/project_settings_editor.cpp #: editor/scene_tree_dock.cpp msgid "Delete" -msgstr "" +msgstr "Xóa" #: editor/dictionary_property_edit.cpp msgid "Change Dictionary Key" @@ -696,7 +693,7 @@ msgstr "" #: editor/editor_about.cpp msgid "Thanks!" -msgstr "" +msgstr "Cảm Æ¡n!" #: editor/editor_about.cpp msgid "Godot Engine contributors" @@ -1576,7 +1573,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Current scene was never saved, please save it prior to running." -msgstr "" +msgstr "Scene hiện tại chÆ°a được lÆ°u, hãy lÆ°u nó trÆ°á»›c khi chạy." #: editor/editor_node.cpp msgid "Could not start subprocess!" @@ -1608,7 +1605,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Save Scene As.." -msgstr "" +msgstr "LÆ°u Scene vá»›i tên.." #: editor/editor_node.cpp msgid "No" @@ -1620,7 +1617,7 @@ msgstr "" #: editor/editor_node.cpp msgid "This scene has never been saved. Save before running?" -msgstr "" +msgstr "Scene nà y chÆ°a được lÆ°u. LÆ°u trÆ°á»›c khi chạy?" #: editor/editor_node.cpp editor/scene_tree_dock.cpp msgid "This operation can't be done without a scene." @@ -1680,11 +1677,11 @@ msgstr "" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before quitting?" -msgstr "" +msgstr "LÆ°u thay đổi trong các scene sau trÆ°á»›c khi thoát?" #: editor/editor_node.cpp msgid "Save changes the following scene(s) before opening Project Manager?" -msgstr "" +msgstr "LÆ°u thay đổi trong các scene sau trÆ°á»›c khi mở Project Manager?" #: editor/editor_node.cpp msgid "" @@ -1722,6 +1719,8 @@ msgid "" "Scene '%s' was automatically imported, so it can't be modified.\n" "To make changes to it, a new inherited scene can be created." msgstr "" +"Scene '%s' được load tá»± Ä‘á»™ng, vì váºy không thể chỉnh sá»a.\n" +"Tạo má»™t scene con để chỉnh sá»a." #: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -1785,7 +1784,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Add a new scene." -msgstr "" +msgstr "Thêm má»™t scene má»›i." #: editor/editor_node.cpp msgid "Scene" @@ -1813,11 +1812,11 @@ msgstr "" #: editor/editor_node.cpp msgid "New Scene" -msgstr "" +msgstr "Tạo Scene Má»›i" #: editor/editor_node.cpp msgid "New Inherited Scene.." -msgstr "" +msgstr "Tạo Scene Con.." #: editor/editor_node.cpp msgid "Open Scene.." @@ -2119,7 +2118,7 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp #: editor/project_manager.cpp msgid "Import" -msgstr "" +msgstr "Nháºp từ bên ngoà i" #: editor/editor_node.cpp msgid "Node" @@ -2139,7 +2138,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Import Templates From ZIP File" -msgstr "" +msgstr "Nháºp Template từ file ZIP" #: editor/editor_node.cpp editor/project_export.cpp msgid "Export Project" @@ -2318,7 +2317,7 @@ msgstr "" #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" -msgstr "" +msgstr "Chá»n Node để Nháºp" #: editor/editor_sub_scene.cpp msgid "Scene Path:" @@ -2326,7 +2325,7 @@ msgstr "" #: editor/editor_sub_scene.cpp msgid "Import From Node:" -msgstr "" +msgstr "Nháºp từ Node:" #: editor/export_template_manager.cpp msgid "Re-Download" @@ -2358,7 +2357,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Remove template version '%s'?" -msgstr "" +msgstr "Xóa template phiên bản '%s'?" #: editor/export_template_manager.cpp msgid "Can't open export templates zip." @@ -2545,9 +2544,8 @@ msgid "Cannot move a folder into itself." msgstr "" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "Lá»—i tải font." +msgstr "Lá»—i di chuyển:" #: editor/filesystem_dock.cpp msgid "Error duplicating:" @@ -2571,79 +2569,79 @@ msgstr "" #: editor/filesystem_dock.cpp msgid "Name contains invalid characters." -msgstr "" +msgstr "Tên có kà tá»± không hợp lệ." #: editor/filesystem_dock.cpp msgid "A file or folder with this name already exists." -msgstr "" +msgstr "Äã có má»™t file hoặc folder trùng tên." #: editor/filesystem_dock.cpp msgid "Renaming file:" -msgstr "" +msgstr "Äổi tên file:" #: editor/filesystem_dock.cpp msgid "Renaming folder:" -msgstr "" +msgstr "Äổi tên folder:" #: editor/filesystem_dock.cpp msgid "Duplicating file:" -msgstr "" +msgstr "Tạo bản sao file:" #: editor/filesystem_dock.cpp msgid "Duplicating folder:" -msgstr "" +msgstr "Tạo bản sao folder:" #: editor/filesystem_dock.cpp msgid "Expand all" -msgstr "" +msgstr "Mở rá»™ng tất cả" #: editor/filesystem_dock.cpp msgid "Collapse all" -msgstr "" +msgstr "Thu gá»n tất cả" #: editor/filesystem_dock.cpp msgid "Rename.." -msgstr "" +msgstr "Äổi tên..." #: editor/filesystem_dock.cpp msgid "Move To.." -msgstr "" +msgstr "Di chuyển đến..." #: editor/filesystem_dock.cpp msgid "Open Scene(s)" -msgstr "" +msgstr "Mở Scene" #: editor/filesystem_dock.cpp msgid "Instance" -msgstr "" +msgstr "Thêm và o scene" #: editor/filesystem_dock.cpp msgid "Edit Dependencies.." -msgstr "" +msgstr "Chỉnh sá»a các File phụ thuá»™c.." #: editor/filesystem_dock.cpp msgid "View Owners.." -msgstr "" +msgstr "Xem các scene sở hữu.." #: editor/filesystem_dock.cpp msgid "Duplicate.." -msgstr "" +msgstr "Nhân đôi..." #: editor/filesystem_dock.cpp msgid "Previous Directory" -msgstr "" +msgstr "ThÆ° mục trÆ°á»›c" #: editor/filesystem_dock.cpp msgid "Next Directory" -msgstr "" +msgstr "ThÆ° mục tiếp theo" #: editor/filesystem_dock.cpp msgid "Re-Scan Filesystem" -msgstr "" +msgstr "Quét lại hệ thống táºp tin" #: editor/filesystem_dock.cpp msgid "Toggle folder status as Favorite" -msgstr "" +msgstr "(Bá») Chá»n thÆ° mục Hay sá» dụng" #: editor/filesystem_dock.cpp msgid "Instance the selected scene(s) as child of the selected node." @@ -2654,23 +2652,25 @@ msgid "" "Scanning Files,\n" "Please Wait.." msgstr "" +"Äang quét file,\n" +"Chá» môt chút.." #: editor/filesystem_dock.cpp msgid "Move" -msgstr "" +msgstr "Di chuyển" #: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp #: editor/project_manager.cpp msgid "Rename" -msgstr "" +msgstr "Äổi tên" #: editor/groups_editor.cpp msgid "Add to Group" -msgstr "" +msgstr "Thêm và o Nhóm" #: editor/groups_editor.cpp msgid "Remove from Group" -msgstr "" +msgstr "Xóa khá»i Nhóm" #: editor/import/resource_importer_scene.cpp msgid "Import as Single Scene" @@ -2678,31 +2678,31 @@ msgstr "" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Animations" -msgstr "" +msgstr "Load vá»›i các Animation riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Materials" -msgstr "" +msgstr "Load vá»›i các Material riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects" -msgstr "" +msgstr "Load vá»›i các Object riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Materials" -msgstr "" +msgstr "Load vá»›i các Object+Material riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Animations" -msgstr "" +msgstr "Load vá»›i các Object+Animation riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Materials+Animations" -msgstr "" +msgstr "Load vá»›i Material+Animation riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import with Separate Objects+Materials+Animations" -msgstr "" +msgstr "Load vá»›i các Object+Material+Animation riêng biệt" #: editor/import/resource_importer_scene.cpp msgid "Import as Multiple Scenes" @@ -2849,85 +2849,85 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Remove Animation" -msgstr "" +msgstr "Xóa Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: Invalid animation name!" -msgstr "" +msgstr "Lá»–I: Tên animation không hợp lệ!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: Animation name already exists!" -msgstr "" +msgstr "Lá»–I: Tên animation trùng lặp!" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Rename Animation" -msgstr "" +msgstr "Äổi tên Animation" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp msgid "Add Animation" -msgstr "" +msgstr "Thêm Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Blend Next Changed" -msgstr "" +msgstr "Chuyển đổi animation Tiếp theo Thay đổi" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Change Blend Time" -msgstr "" +msgstr "Äổi Thá»i gian Chuyển Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load Animation" -msgstr "" +msgstr "Load Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Duplicate Animation" -msgstr "" +msgstr "Tạo Animation bản sao" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation to copy!" -msgstr "" +msgstr "Lá»–I: Không có animation để copy!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation resource on clipboard!" -msgstr "" +msgstr "Lá»–I: Không có animation trên clipboard!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Pasted Animation" -msgstr "" +msgstr "Animation đã Dán" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Paste Animation" -msgstr "" +msgstr "Dán Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: No animation to edit!" -msgstr "" +msgstr "Lá»–I: Không có animation để chỉnh!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation backwards from current pos. (A)" -msgstr "" +msgstr "Chạy animation ngược lại từ vị trà hiện tại. (A)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation backwards from end. (Shift+A)" -msgstr "" +msgstr "Chạy animation ngược lại từ cuối. (Shift+A)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Stop animation playback. (S)" -msgstr "" +msgstr "NgÆ°ng chạy animation. (S)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation from start. (Shift+D)" -msgstr "" +msgstr "Chạy animation từ đầu. (Shift+D)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Play selected animation from current pos. (D)" -msgstr "" +msgstr "Chạy animation từ vị trà hiện tại. (D)" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation position (in seconds)." -msgstr "" +msgstr "Vị trà animation (Ä‘Æ¡n vị giây)." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Scale animation playback globally for the node." @@ -2935,35 +2935,35 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Create new animation in player." -msgstr "" +msgstr "Tạo animation má»›i trong player." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load animation from disk." -msgstr "" +msgstr "Load animation từ disk." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Load an animation from disk." -msgstr "" +msgstr "Load má»™t animation từ disk." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Save the current animation" -msgstr "" +msgstr "LÆ°u animation nà y" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Display list of animations in player." -msgstr "" +msgstr "Hiển thị danh sách các animation trong player." #: editor/plugins/animation_player_editor_plugin.cpp msgid "Autoplay on Load" -msgstr "" +msgstr "Tá»± Ä‘á»™ng chạy khi Load" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Edit Target Blend Times" -msgstr "" +msgstr "Chỉnh Thá»i gian Chuyển Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Animation Tools" -msgstr "" +msgstr "Các Công cụ Animation" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Copy Animation" @@ -2971,11 +2971,11 @@ msgstr "" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Onion Skinning" -msgstr "" +msgstr "Khung hình Liên tiếp" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Enable Onion Skinning" -msgstr "" +msgstr "Xem Khung hình Liên tiếp" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Directions" @@ -4941,7 +4941,7 @@ msgstr "" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Plane Transform." -msgstr "" +msgstr "Xem Transform của Plane." #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -5891,9 +5891,8 @@ msgid "Create New Project" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Create & Edit" -msgstr "Tạo" +msgstr "Tạo & Sá»a" #: editor/project_manager.cpp msgid "Install Project:" @@ -5970,41 +5969,43 @@ msgstr "" #: editor/project_manager.cpp msgid "Project List" -msgstr "" +msgstr "List Project" #: editor/project_manager.cpp msgid "Scan" -msgstr "" +msgstr "Quét" #: editor/project_manager.cpp msgid "Select a Folder to Scan" -msgstr "" +msgstr "Chá»n má»™t Folder để Quét" #: editor/project_manager.cpp msgid "New Project" -msgstr "" +msgstr "Tạo Project" #: editor/project_manager.cpp msgid "Templates" -msgstr "" +msgstr "Khung project" #: editor/project_manager.cpp msgid "Exit" -msgstr "" +msgstr "Thoát" #: editor/project_manager.cpp msgid "Restart Now" -msgstr "" +msgstr "Restart ngay" #: editor/project_manager.cpp msgid "Can't run project" -msgstr "" +msgstr "Không thể chạy project" #: editor/project_manager.cpp msgid "" "You don't currently have any projects.\n" "Would you like to explore the official example projects in the Asset Library?" msgstr "" +"Hiện giá» bạn không có project nà o.\n" +"Bạn có muốn xem các project official và dụ trên Asset Library không?" #: editor/project_settings_editor.cpp msgid "Key " @@ -7553,7 +7554,7 @@ msgstr "" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Path does not lead Node!" -msgstr "" +msgstr "Path không chỉ đến Node!" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Invalid index property name '%s' in node %s." @@ -7633,7 +7634,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7767,7 +7768,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp @@ -7859,7 +7860,7 @@ msgstr "Xin hãy xác nháºn..." #: scene/gui/file_dialog.cpp msgid "Select this Folder" -msgstr "" +msgstr "Chá»n folder nà y" #: scene/gui/popup.cpp msgid "" @@ -7867,6 +7868,9 @@ msgid "" "functions. Making them visible for editing is fine though, but they will " "hide upon running." msgstr "" +"Các popup sẽ mặc định là ẩn trừ khi bạn gá»i popup() hoặc bất kì function nà o " +"có dạng popup*(). Có thể để popup nhìn thấy được để chỉnh sá»a, nhÆ°ng chúng " +"sẽ ẩn khi chạy." #: scene/gui/scroll_container.cpp msgid "" diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po index ff8813624d..debb1d60f3 100644 --- a/editor/translations/zh_CN.po +++ b/editor/translations/zh_CN.po @@ -22,8 +22,8 @@ msgid "" msgstr "" "Project-Id-Version: Chinese (Simplified) (Godot Engine)\n" -"POT-Creation-Date: 2018-01-06 09:18+0200\n" -"PO-Revision-Date: 2018-01-20 08:53+0000\n" +"POT-Creation-Date: 2018-01-20 12:15+0200\n" +"PO-Revision-Date: 2018-01-20 12:54+0000\n" "Last-Translator: Geequlim <geequlim@gmail.com>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hans/>\n" @@ -503,7 +503,7 @@ msgstr "连接'%s'到'%s'" #: editor/connections_dialog.cpp msgid "Connecting Signal:" -msgstr "连接事件:" +msgstr "连接信å·:" #: editor/connections_dialog.cpp msgid "Disconnect '%s' from '%s'" @@ -511,12 +511,12 @@ msgstr "å–消'%s'的连接'%s'" #: editor/connections_dialog.cpp msgid "Connect.." -msgstr "连接事件。" +msgstr "连接信å·.." #: editor/connections_dialog.cpp #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Disconnect" -msgstr "åˆ é™¤äº‹ä»¶è¿žæŽ¥" +msgstr "åˆ é™¤ä¿¡å·è¿žæŽ¥" #: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp msgid "Signals" @@ -637,7 +637,6 @@ msgid "" msgstr "è¦åˆ 除的文件被其他资æºæ‰€ä¾èµ–,ä»ç„¶è¦åˆ 除å—ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ï¼‰" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" msgstr "æ— æ³•ç§»é™¤:" @@ -1116,7 +1115,6 @@ msgid "Packing" msgstr "打包ä¸" #: editor/editor_export.cpp platform/javascript/export/export.cpp -#, fuzzy msgid "Template file not found:" msgstr "找ä¸åˆ°æ¨¡æ¿æ–‡ä»¶:" @@ -1302,7 +1300,7 @@ msgstr "GUI主题:" #: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp msgid "Signals:" -msgstr "事件:" +msgstr "ä¿¡å·:" #: editor/editor_help.cpp msgid "Enumerations" @@ -1329,19 +1327,17 @@ msgid "Description" msgstr "æè¿°" #: editor/editor_help.cpp -#, fuzzy msgid "Online Tutorials:" -msgstr "教程" +msgstr "在线教程:" #: editor/editor_help.cpp -#, fuzzy msgid "" "There are currently no tutorials for this class, you can [color=$color][url=" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"当å‰æ²¡æœ‰æ¤æ–¹æ³•çš„说明。请帮助我们通过 [color=$color] [url=$url] 贡献一个 [/" -"url][/color]!" +"暂时没有æ¤ç±»åž‹çš„说明。请帮通过[color=$color][url=$url] 补充文档或æ交请求 [/" +"url][/color]çš„æ–¹å¼å¸®åŠ©æˆ‘们完善文档!" #: editor/editor_help.cpp msgid "Properties" @@ -2593,24 +2589,20 @@ msgid "Cannot move/rename resources root." msgstr "æ— æ³•ç§»åŠ¨/é‡å‘½åæ ¹èµ„æºã€‚" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Cannot move a folder into itself." msgstr "æ— æ³•å°†æ–‡ä»¶å¤¹ç§»åŠ¨åˆ°å…¶è‡ªèº«ã€‚" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error moving:" -msgstr "移动时出错:" +msgstr "移动出错:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Error duplicating:" msgstr "å¤åˆ¶å‡ºé”™:" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Unable to update dependencies:" -msgstr "æ— æ³•æ›´æ–°ä¾èµ–关系:" +msgstr "æ— æ³•æ›´æ–°ä¾èµ–:" #: editor/filesystem_dock.cpp msgid "No name provided" @@ -4116,9 +4108,8 @@ msgid "Bake!" msgstr "烘焙ï¼" #: editor/plugins/navigation_mesh_editor_plugin.cpp -#, fuzzy msgid "Bake the navigation mesh." -msgstr "çƒ˜ç„™å¯¼èˆªç½‘æ ¼(mesh)." +msgstr "çƒ˜ç„™å¯¼èˆªç½‘æ ¼(mesh)。" #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Clear the navigation mesh." @@ -5792,9 +5783,8 @@ msgid "Select current edited sub-tile." msgstr "ä¿å˜å½“å‰ç¼–辑的ååœ°ç –(sub-tile)。" #: editor/plugins/tile_set_editor_plugin.cpp -#, fuzzy msgid "Select sub-tile to change its priority." -msgstr "选择修改优先级的ååœ°ç –ï¼ˆsub-tile)。" +msgstr "选择è¦ä¿®æ”¹ä¼˜å…ˆçº§çš„ååœ°ç –ï¼ˆsub-tile)。" #: editor/progress_dialog.cpp scene/gui/dialogs.cpp msgid "Cancel" @@ -5903,9 +5893,8 @@ msgid "Please choose a 'project.godot' file." msgstr "请选择一个'project.godot'文件。" #: editor/project_manager.cpp -#, fuzzy msgid "Please choose an empty folder." -msgstr "请选择一个'project.godot'文件。" +msgstr "请选择一个空目录。" #: editor/project_manager.cpp msgid "Please choose a folder that does not contain a 'project.godot' file." @@ -5916,13 +5905,12 @@ msgid "Imported Project" msgstr "已导入的项目" #: editor/project_manager.cpp -#, fuzzy msgid "Couldn't create folder." -msgstr "æ— æ³•åˆ›å»ºç›®å½•ã€‚" +msgstr "æ— æ³•åˆ›å»ºæ–‡ä»¶å¤¹ã€‚" #: editor/project_manager.cpp msgid "There is already a folder in this path with the specified name." -msgstr "" +msgstr "å·²å˜åœ¨ä¸Žç»™å®šå称相åŒçš„目录。" #: editor/project_manager.cpp msgid "It would be a good idea to name your project." @@ -5965,7 +5953,6 @@ msgid "Import Existing Project" msgstr "导入现有项目" #: editor/project_manager.cpp -#, fuzzy msgid "Import & Edit" msgstr "导入|打开" @@ -5974,18 +5961,16 @@ msgid "Create New Project" msgstr "新建项目" #: editor/project_manager.cpp -#, fuzzy msgid "Create & Edit" -msgstr "创建å‘射器(Emitter)" +msgstr "创建|编辑" #: editor/project_manager.cpp msgid "Install Project:" msgstr "安装项目:" #: editor/project_manager.cpp -#, fuzzy msgid "Install & Edit" -msgstr "安装" +msgstr "安装|编辑" #: editor/project_manager.cpp msgid "Project Name:" @@ -6256,7 +6241,7 @@ msgstr "'%s'是内置设定,ä¸èƒ½åˆ 除。" #: editor/project_settings_editor.cpp msgid "Delete Item" -msgstr "åˆ é™¤è¾“å…¥äº‹ä»¶" +msgstr "åˆ é™¤æ¡ç›®" #: editor/project_settings_editor.cpp msgid "Can't contain '/' or ':'" @@ -6436,11 +6421,11 @@ msgstr "目录.." #: editor/property_editor.cpp msgid "Assign" -msgstr "分é…(Assign)" +msgstr "分é…" #: editor/property_editor.cpp msgid "Select Node" -msgstr "选择一个节点" +msgstr "选择节点" #: editor/property_editor.cpp msgid "New Script" @@ -6521,7 +6506,7 @@ msgstr "é‡è®¾çˆ¶èŠ‚点" #: editor/reparent_dialog.cpp msgid "Reparent Location (Select new Parent):" -msgstr "é‡è®¾ä½ç½®ï¼ˆé€‰æ‹©çˆ¶èŠ‚点):" +msgstr "é‡è®¾ä½ç½®ï¼ˆé€‰æ‹©æ–°çš„父节点):" #: editor/reparent_dialog.cpp msgid "Keep Global Transform" @@ -6616,7 +6601,7 @@ msgstr "åŠ è½½ä¸ºå ä½ç¬¦" #: editor/scene_tree_dock.cpp msgid "Discard Instancing" -msgstr "废弃实例化" +msgstr "放弃实例化" #: editor/scene_tree_dock.cpp msgid "Makes Sense!" @@ -6649,9 +6634,8 @@ msgid "Error duplicating scene to save it." msgstr "å¤åˆ¶åœºæ™¯å‡ºé”™ã€‚" #: editor/scene_tree_dock.cpp -#, fuzzy msgid "Sub-Resources" -msgstr "å资æº:" +msgstr "å资æº" #: editor/scene_tree_dock.cpp msgid "Clear Inheritance" @@ -6846,7 +6830,7 @@ msgstr "文件路径为空" #: editor/script_create_dialog.cpp msgid "Path is not local" -msgstr "必须是项目路径" +msgstr "必须是项目内的路径" #: editor/script_create_dialog.cpp msgid "Invalid base path" @@ -6962,9 +6946,8 @@ msgid "Child Process Connected" msgstr "å进程已连接" #: editor/script_editor_debugger.cpp -#, fuzzy msgid "Copy Error" -msgstr "åŠ è½½é”™è¯¯" +msgstr "å¤åˆ¶é”™è¯¯ä¿¡æ¯" #: editor/script_editor_debugger.cpp msgid "Inspect Previous Instance" @@ -6976,7 +6959,7 @@ msgstr "编辑下一个实例" #: editor/script_editor_debugger.cpp msgid "Stack Frames" -msgstr "å †æ ˆå¸§ï¼ˆFrames)" +msgstr "å †æ ˆå¸§ï¼ˆStack Frames)" #: editor/script_editor_debugger.cpp msgid "Variable" @@ -6988,7 +6971,7 @@ msgstr "错误:" #: editor/script_editor_debugger.cpp msgid "Stack Trace (if applicable):" -msgstr "è°ƒç”¨å †æ ˆ:" +msgstr "è°ƒç”¨å †æ ˆ(若适用):" #: editor/script_editor_debugger.cpp msgid "Profiler" @@ -7186,7 +7169,7 @@ msgstr "没有基于脚本" #: modules/gdscript/gdscript_functions.cpp msgid "Not based on a resource file" -msgstr "没有基于一个资æºæ–‡ä»¶" +msgstr "没有基于资æºæ–‡ä»¶" #: modules/gdscript/gdscript_functions.cpp msgid "Invalid instance dictionary format (missing @path)" @@ -7206,11 +7189,11 @@ msgstr "éžæ³•çš„å—典实例(派生类éžæ³•ï¼‰" #: modules/gdscript/gdscript_functions.cpp msgid "Object can't provide a length." -msgstr "对象ä¸èƒ½æ供长度。" +msgstr "å¯¹è±¡æ— æ³•æ供长度。" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Delete Selection" -msgstr "åˆ é™¤é€‰æ‹©çš„æ …æ ¼å›¾" +msgstr "åˆ é™¤é€‰æ‹©çš„GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Duplicate Selection" @@ -7218,7 +7201,7 @@ msgstr "å¤åˆ¶é€‰ä¸é¡¹" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Floor:" -msgstr "地æ¿:" +msgstr "层:" #: modules/gridmap/grid_map_editor_plugin.cpp #, fuzzy @@ -7236,7 +7219,7 @@ msgstr "上一个目录" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Floor" -msgstr "下一个目录" +msgstr "下一层" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Disabled" @@ -7308,61 +7291,53 @@ msgstr "清空选ä¸" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Settings" -msgstr "æ …æ ¼å›¾è®¾ç½®" +msgstr "GridMap设置" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Pick Distance:" msgstr "拾å–è·ç¦»:" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Generating solution..." -msgstr "æ£åœ¨åˆ›å»ºè½®å»“... " +msgstr "æ£åœ¨åˆ›ç”Ÿæˆå†³æ–¹æ¡ˆ... " #: modules/mono/editor/godotsharp_editor.cpp msgid "Generating C# project..." -msgstr "" +msgstr "æ£åœ¨ç”ŸæˆC#项目..." #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to create solution." -msgstr "æ— æ³•åˆ›å»ºè½®å»“(outlines)ï¼" +msgstr "创建解决方案失败。" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to save solution." -msgstr "åŠ è½½èµ„æºå¤±è´¥ã€‚" +msgstr "ä¿å˜è§£å†³æ–¹æ¡ˆå¤±è´¥ã€‚" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Done" -msgstr "å®Œæˆ !" +msgstr "完æˆ" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Failed to create C# project." -msgstr "åŠ è½½èµ„æºå¤±è´¥ã€‚" +msgstr "创建C#项目失败" #: modules/mono/editor/godotsharp_editor.cpp msgid "Mono" -msgstr "å•å£°é“" +msgstr "Mono" #: modules/mono/editor/godotsharp_editor.cpp -#, fuzzy msgid "Create C# solution" -msgstr "创建轮廓(outlines)" +msgstr "创建C#解决方案" #: modules/mono/editor/mono_bottom_panel.cpp msgid "Builds" msgstr "构建" #: modules/mono/editor/mono_bottom_panel.cpp -#, fuzzy msgid "Build Project" -msgstr "项目" +msgstr "构建项目" #: modules/mono/editor/mono_bottom_panel.cpp -#, fuzzy msgid "Warnings" msgstr "è¦å‘Š" @@ -7431,7 +7406,7 @@ msgstr "å称ä¸æ˜¯æœ‰æ•ˆçš„æ ‡è¯†ç¬¦ï¼š" #: modules/visual_script/visual_script_editor.cpp msgid "Name already in use by another func/var/signal:" -msgstr "å称已ç»è¢«å…¶ä»–的函数/å˜é‡/事件å 用:" +msgstr "å称已ç»è¢«å…¶ä»–的函数/å˜é‡/ä¿¡å·å 用:" #: modules/visual_script/visual_script_editor.cpp msgid "Rename Function" @@ -7443,7 +7418,7 @@ msgstr "é‡å‘½åå˜é‡" #: modules/visual_script/visual_script_editor.cpp msgid "Rename Signal" -msgstr "é‡å‘½å事件" +msgstr "é‡å‘½åä¿¡å·" #: modules/visual_script/visual_script_editor.cpp msgid "Add Function" @@ -7455,7 +7430,7 @@ msgstr "æ·»åŠ å˜é‡" #: modules/visual_script/visual_script_editor.cpp msgid "Add Signal" -msgstr "æ·»åŠ äº‹ä»¶" +msgstr "æ·»åŠ ä¿¡å·" #: modules/visual_script/visual_script_editor.cpp msgid "Change Expression" @@ -7503,15 +7478,15 @@ msgstr "æ·»åŠ Preload节点" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" -msgstr "从场景导入节点" +msgstr "ä»Žæ ‘ä¸æ·»åŠ 节点" #: modules/visual_script/visual_script_editor.cpp msgid "Add Getter Property" -msgstr "æ·»åŠ Getter Property" +msgstr "æ·»åŠ å±žæ€§Getter" #: modules/visual_script/visual_script_editor.cpp msgid "Add Setter Property" -msgstr "æ·»åŠ Setter Property" +msgstr "æ·»åŠ å±žæ€§Setter" #: modules/visual_script/visual_script_editor.cpp msgid "Change Base Type" @@ -7531,31 +7506,31 @@ msgstr "连接节点" #: modules/visual_script/visual_script_editor.cpp msgid "Condition" -msgstr "æ¡ä»¶èŠ‚点(Condition)" +msgstr "æ¡ä»¶ï¼ˆCondition)" #: modules/visual_script/visual_script_editor.cpp msgid "Sequence" -msgstr "åºåˆ—节点(Sequence)" +msgstr "åºåˆ—(Sequence)" #: modules/visual_script/visual_script_editor.cpp msgid "Switch" -msgstr "选择节点(Switch)" +msgstr "选择(Switch)" #: modules/visual_script/visual_script_editor.cpp msgid "Iterator" -msgstr "é历节点(Iterator)" +msgstr "é历(Iterator)" #: modules/visual_script/visual_script_editor.cpp msgid "While" -msgstr "æ¡ä»¶å¾ªçŽ¯èŠ‚点(While)" +msgstr "æ¡ä»¶å¾ªçŽ¯ï¼ˆWhile)" #: modules/visual_script/visual_script_editor.cpp msgid "Return" -msgstr "返回节点(Return)" +msgstr "返回" #: modules/visual_script/visual_script_editor.cpp msgid "Call" -msgstr "调用到" +msgstr "调用" #: modules/visual_script/visual_script_editor.cpp msgid "Get" @@ -7567,7 +7542,7 @@ msgstr "脚本已å˜åœ¨å‡½æ•° '%s'" #: modules/visual_script/visual_script_editor.cpp msgid "Change Input Value" -msgstr "更改输入的值" +msgstr "更改输入值" #: modules/visual_script/visual_script_editor.cpp msgid "Can't copy the function node." @@ -7599,7 +7574,7 @@ msgstr "编辑信å·" #: modules/visual_script/visual_script_editor.cpp msgid "Remove Signal" -msgstr "åˆ é™¤äº‹ä»¶" +msgstr "åˆ é™¤ä¿¡å·" #: modules/visual_script/visual_script_editor.cpp msgid "Editing Variable:" @@ -7607,7 +7582,7 @@ msgstr "编辑å˜é‡ï¼š" #: modules/visual_script/visual_script_editor.cpp msgid "Editing Signal:" -msgstr "编辑事件:" +msgstr "编辑信å·:" #: modules/visual_script/visual_script_editor.cpp msgid "Base Type:" @@ -7619,11 +7594,11 @@ msgstr "有效节点:" #: modules/visual_script/visual_script_editor.cpp msgid "Select or create a function to edit graph" -msgstr "选择或创建一个函数æ¥ç¼–辑图" +msgstr "选择或创建一个函数æ¥ç¼–辑" #: modules/visual_script/visual_script_editor.cpp msgid "Edit Signal Arguments:" -msgstr "编辑事件å‚æ•°:" +msgstr "编辑信å·å‚æ•°:" #: modules/visual_script/visual_script_editor.cpp msgid "Edit Variable:" @@ -7631,7 +7606,7 @@ msgstr "编辑å˜é‡:" #: modules/visual_script/visual_script_editor.cpp msgid "Delete Selected" -msgstr "åˆ é™¤é€‰æ‹©çš„èŠ‚ç‚¹" +msgstr "åˆ é™¤å·²é€‰ä¸" #: modules/visual_script/visual_script_editor.cpp msgid "Find Node Type" @@ -7675,7 +7650,7 @@ msgstr "路径必须指å‘节点ï¼" #: modules/visual_script/visual_script_func_nodes.cpp msgid "Invalid index property name '%s' in node %s." -msgstr "节点 '%s' çš„ '%s' ä¸ºæ— æ•ˆç´¢å¼•å±žæ€§å。" +msgstr "'%s'这个属性å的在节点'%s'ä¸ä¸å˜åœ¨ã€‚" #: modules/visual_script/visual_script_nodes.cpp msgid ": Invalid argument of type: " @@ -7709,37 +7684,31 @@ msgstr "在æµè§ˆå™¨ä¸è¿è¡Œ" #: platform/javascript/export/export.cpp msgid "Run exported HTML in the system's default browser." -msgstr "使用默认æµè§ˆå™¨æ‰“开导出的HTML文件." +msgstr "使用默认æµè§ˆå™¨æ‰“开导出的HTML文件。" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not write file:" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:\n" +msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not open template for export:" -msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š\n" +msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Invalid export template:" -msgstr "æ— æ•ˆçš„å¯¼å‡ºæ¨¡æ¿ï¼š\n" +msgstr "导出模æ¿æ— 效:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read custom HTML shell:" -msgstr "æ— æ³•è¯»å–自定义HTML命令:\n" +msgstr "æ— æ³•è¯»å–自定义HTML命令:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read boot splash image file:" -msgstr "æ— æ³•è¯»å–å¯åŠ¨å›¾ç‰‡æ–‡ä»¶ï¼š\n" +msgstr "æ— æ³•è¯»å–å¯åŠ¨å›¾ç‰‡:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Using default boot splash image." -msgstr "æ— æ³•è¯»å–å¯åŠ¨å›¾ç‰‡æ–‡ä»¶ï¼š\n" +msgstr "使用默认å¯åŠ¨å›¾ç‰‡ã€‚" #: scene/2d/animated_sprite.cpp msgid "" @@ -7761,8 +7730,10 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" +"该节点没有æ述其形状的åèŠ‚ç‚¹ï¼Œå› æ¤å®ƒå°†æ— 法进行碰撞交互。\n" +"è¯·æ·»åŠ CollisionShape2D或CollisionPolygon2D类型的å节点æ¥å®šä¹‰å®ƒçš„形状。" #: scene/2d/collision_polygon_2d.cpp msgid "" @@ -7836,7 +7807,7 @@ msgstr "ç²’åæè´¨æ²¡æœ‰æŒ‡å®šï¼Œè¯¥è¡Œä¸ºæ— æ•ˆã€‚" #: scene/2d/path_2d.cpp msgid "PathFollow2D only works when set as a child of a Path2D node." -msgstr "PathFollow2D类型的节点åªæœ‰æ”¾åœ¨Path2D节点下æ‰èƒ½æ£å¸¸å·¥ä½œã€‚" +msgstr "PathFollow2D类型的节点åªæœ‰ä½œä¸ºPath2Dçš„å节点节æ‰èƒ½æ£å¸¸å·¥ä½œã€‚" #: scene/2d/physics_body_2d.cpp msgid "" @@ -7844,13 +7815,13 @@ msgid "" "by the physics engine when running.\n" "Change the size in children collision shapes instead." msgstr "" -"è¿è¡Œæ—¶ï¼Œä¿®æ”¹RigidBody2D (character或rigid模å¼ï¼‰çš„尺寸,会修改物ç†å¼•æ“Žçš„大å°" -"尺寸。\n" -"修改å节点碰撞形状的大å°ä½œä¸ºä»£æ›¿ã€‚" +"对RigidBody2D (在character或rigid模å¼æƒ³ï¼‰çš„尺寸修改在è¿è¡Œæ—¶ä¼šè¢«ç‰©ç†å¼•æ“Žçš„覆" +"盖。\n" +"建议您修改å节点的碰撞形状。" #: scene/2d/remote_transform_2d.cpp msgid "Path property must point to a valid Node2D node to work." -msgstr "path属性必须指å‘一个åˆæ³•çš„Node2D节点æ‰èƒ½æ£å¸¸å·¥ä½œã€‚" +msgstr "Path属性必须指å‘一个åˆæ³•çš„Node2D节点æ‰èƒ½æ£å¸¸å·¥ä½œã€‚" #: scene/2d/visibility_notifier_2d.cpp msgid "" @@ -7909,8 +7880,10 @@ msgstr "æ£åœ¨ç»˜åˆ¶ç½‘æ ¼" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" +"该节点没有æ述其形状的åèŠ‚ç‚¹ï¼Œå› æ¤å®ƒå°†æ— 法进行碰撞交互。\n" +"è¯·æ·»åŠ CollisionShape或CollisionPolygon类型的å节点æ¥å®šä¹‰å®ƒçš„形状。" #: scene/3d/collision_polygon.cpp msgid "" @@ -7948,7 +7921,7 @@ msgstr "æ£åœ¨ç»˜åˆ¶ç½‘æ ¼" #: scene/3d/navigation_mesh.cpp msgid "A NavigationMesh resource must be set or created for this node to work." -msgstr "æ¤èŠ‚点需è¦è®¾ç½®NavigationMesh资æºæ‰èƒ½å·¥ä½œã€‚" +msgstr "æ¤èŠ‚点需è¦è®¾ç½®NavigationMesh资æºæ‰èƒ½æ£å¸¸å·¥ä½œã€‚" #: scene/3d/navigation_mesh.cpp msgid "" @@ -7960,7 +7933,7 @@ msgstr "" #: scene/3d/particles.cpp msgid "" "Nothing is visible because meshes have not been assigned to draw passes." -msgstr "ç²’åä¸å¯è§ï¼Œå› ä¸ºæ²¡æœ‰ç½‘æ ¼(meshes)指定到绘制通é“(draw passes)。" +msgstr "ç²’åä¸å¯è§ï¼Œå› ä¸ºæ²¡æœ‰ç½‘æ ¼(meshe)指定到绘制通é“(draw passes)。" #: scene/3d/physics_body.cpp msgid "" @@ -7968,9 +7941,9 @@ msgid "" "by the physics engine when running.\n" "Change the size in children collision shapes instead." msgstr "" -"è¿è¡Œæ—¶ï¼Œä¿®æ”¹RigidBody(character或rigid模å¼ï¼‰çš„尺寸,会修改物ç†å¼•æ“Žçš„大å°å°º" -"寸。\n" -"修改å节点碰撞形状的大å°ä½œä¸ºä»£æ›¿ã€‚" +"对RigidBody(在character或rigid模å¼ä¸‹ï¼‰çš„尺寸修改,在è¿è¡Œæ—¶ä¼šè¢«ç‰©ç†å¼•æ“Žçš„覆" +"盖。\n" +"建议您修改å节点的碰撞形状。" #: scene/3d/remote_transform.cpp msgid "Path property must point to a valid Spatial node to work." @@ -7994,7 +7967,8 @@ msgid "" "VehicleWheel serves to provide a wheel system to a VehicleBody. Please use " "it as a child of a VehicleBody." msgstr "" -"VehicleWheel 为 VehicleBody æ供一个车轮系统。请将它作为VehicleBodyçš„å节点。" +"VehicleWheel 为 VehicleBody æ供一个车轮系统(Wheel System)。请将它作为" +"VehicleBodyçš„å节点。" #: scene/gui/color_picker.cpp msgid "Raw Mode" @@ -8022,8 +7996,8 @@ msgid "" "functions. Making them visible for editing is fine though, but they will " "hide upon running." msgstr "" -"Popup对象默认ä¿æŒéšè—,除éžä½ 调用popup()方法。编辑时å¯ä»¥è®©å®ƒä»¬ä¿æŒå¯è§ï¼Œä½†è¿" -"行时它们会自动éšè—。" +"Popup对象默认ä¿æŒéšè—,除éžä½ 调用popup()或其他popup相关方法。编辑时å¯ä»¥è®©å®ƒä»¬" +"ä¿æŒå¯è§ï¼Œä½†å®ƒåœ¨è¿è¡Œæ—¶ä»¬ä¼šè‡ªåŠ¨éšè—。" #: scene/gui/scroll_container.cpp msgid "" @@ -8032,19 +8006,17 @@ msgid "" "minimum size manually." msgstr "" "ScrollContainer旨在与å•ä¸ªå控件é…åˆä½¿ç”¨ã€‚\n" -"使用Container(VBox,HBoxç‰ï¼‰ä½œä¸ºå…¶å控件并手动或设置Control的自定义最å°å°º" -"寸。" +"请使用Container(VBox,HBoxç‰ï¼‰ä½œä¸ºå…¶å控件或手动设置Control的最å°å°ºå¯¸ã€‚" #: scene/gui/tree.cpp msgid "(Other)" msgstr "(其它)" #: scene/main/scene_tree.cpp -#, fuzzy msgid "" "Default Environment as specified in Project Settings (Rendering -> " "Environment -> Default Environment) could not be loaded." -msgstr "项目设置ä¸çš„é»˜è®¤çŽ¯å¢ƒæ— æ³•åŠ è½½ï¼Œè¯¦è§ï¼ˆæ¸²æŸ“->视图->默认环境) 。" +msgstr "æ— æ³•åŠ è½½é¡¹ç›®è®¾ç½®ä¸çš„默认环境,详è§ï¼ˆæ¸²æŸ“->视图->默认环境)。" #: scene/main/viewport.cpp msgid "" @@ -8053,9 +8025,9 @@ msgid "" "obtain a size. Otherwise, make it a RenderTarget and assign its internal " "texture to some node for display." msgstr "" -"这个Viewport未设置为render targetã€‚å¦‚æžœä½ åˆ»æ„打算让其直接在å±å¹•ä¸Šæ˜¾ç¤ºå…¶å†…容," -"使其æˆä¸ºå控件的所以它å¯ä»¥æœ‰ä¸€ä¸ªå°ºå¯¸å¤§å°å€¼ã€‚å¦åˆ™è¯·è®¾ç½®ä¸ºRender target,并将其" -"内部纹ç†åˆ†é…给一些节点以显示。" +"这个Viewportæœªè®¾ç½®ä¸ºæ¸²æŸ“ç›®æ ‡(render target)ã€‚å¦‚æžœä½ åˆ»æ„打算让其直接在å±å¹•ä¸Šæ˜¾" +"示其内容,使其æˆä¸ºå控件的所以它å¯ä»¥æœ‰ä¸€ä¸ªå°ºå¯¸å¤§å°å€¼ã€‚å¦åˆ™è¯·è®¾ç½®ä¸ºRender " +"target,并将其内部纹ç†åˆ†é…给一些节点以显示。" #: scene/resources/dynamic_font.cpp msgid "Error initializing FreeType." diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po index a9444cac2b..3b380305d0 100644 --- a/editor/translations/zh_HK.po +++ b/editor/translations/zh_HK.po @@ -7963,7 +7963,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -8097,7 +8097,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po index 8bfac48d5b..abdbf6dc2b 100644 --- a/editor/translations/zh_TW.po +++ b/editor/translations/zh_TW.po @@ -7774,7 +7774,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape2D or CollisionPolygon2D children nodes to " -"define it's shape." +"define its shape." msgstr "" #: scene/2d/collision_polygon_2d.cpp @@ -7912,7 +7912,7 @@ msgstr "" msgid "" "This node has no children shapes, so it can't interact with the space.\n" "Consider adding CollisionShape or CollisionPolygon children nodes to define " -"it's shape." +"its shape." msgstr "" #: scene/3d/collision_polygon.cpp diff --git a/main/main.cpp b/main/main.cpp index d670b1d0b5..f6a8391447 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -903,7 +903,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->set_screen_orientation(OS::SCREEN_LANDSCAPE); } - Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/fixed_fps", 60)); + Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60)); Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); GLOBAL_DEF("debug/settings/stdout/print_fps", OS::get_singleton()->is_stdout_verbose()); @@ -1775,7 +1775,7 @@ bool Main::iteration() { if (frame > 1000000) { - if (GLOBAL_DEF("debug/settings/stdout/print_fps", OS::get_singleton()->is_stdout_verbose())) { + if (GLOBAL_DEF("debug/settings/stdout/print_fps", OS::get_singleton()->is_stdout_verbose()) && !editor) { print_line("FPS: " + itos(frames)); }; diff --git a/methods.py b/methods.py index a8f1fb8a5d..f4d33fb6d8 100644 --- a/methods.py +++ b/methods.py @@ -1477,7 +1477,7 @@ def use_windows_spawn_fix(self, platform=None): self['SPAWN'] = mySpawn -def split_lib(self, libname): +def split_lib(self, libname, src_list = None, env_lib = None): import string env = self @@ -1487,7 +1487,13 @@ def split_lib(self, libname): list = [] lib_list = [] - for f in getattr(env, libname + "_sources"): + if src_list == None: + src_list = getattr(env, libname + "_sources") + + if type(env_lib) == type(None): + env_lib = env + + for f in src_list: fname = "" if type(f) == type(""): fname = env.File(f).path @@ -1497,26 +1503,26 @@ def split_lib(self, libname): base = string.join(fname.split("/")[:2], "/") if base != cur_base and len(list) > max_src: if num > 0: - lib = env.add_library(libname + str(num), list) + lib = env_lib.add_library(libname + str(num), list) lib_list.append(lib) list = [] num = num + 1 cur_base = base list.append(f) - lib = env.add_library(libname + str(num), list) + lib = env_lib.add_library(libname + str(num), list) lib_list.append(lib) if len(lib_list) > 0: import os, sys if os.name == 'posix' and sys.platform == 'msys': env.Replace(ARFLAGS=['rcsT']) - lib = env.add_library(libname + "_collated", lib_list) + lib = env_lib.add_library(libname + "_collated", lib_list) lib_list = [lib] lib_base = [] - env.add_source_files(lib_base, "*.cpp") - lib = env.add_library(libname, lib_base) + env_lib.add_source_files(lib_base, "*.cpp") + lib = env_lib.add_library(libname, lib_base) lib_list.insert(0, lib) env.Prepend(LIBS=lib_list) diff --git a/misc/dist/linux/godot.6 b/misc/dist/linux/godot.6 index 946fa6c913..22836e03ed 100644 --- a/misc/dist/linux/godot.6 +++ b/misc/dist/linux/godot.6 @@ -1,4 +1,4 @@ -.TH GODOT "6" "September 2017" "godot 3.0-alpha" "Games" +.TH GODOT "6" "January 2018" "godot 3.0" "Games" .SH NAME godot \- multi\-platform 2D and 3D game engine with a feature\-rich editor .SH SYNOPSIS @@ -37,6 +37,9 @@ Use a specific locale (<locale> being a two\-letter code). \fB\-\-path\fR <directory> Path to a project (<directory> must contain a 'project.godot' file). .TP +\fB\-u\fR, \fB\-\-upwards\fR +Scan folders upwards for project.godot file. +.TP \fB\-\-main\-pack\fR <file> Path to a pack (.pck) file to load. .TP @@ -133,17 +136,17 @@ Generate JSON dump of the Godot API for GDNative bindings. \fB\-\-test\fR <test> Run a unit test ('string', 'containers', 'math', 'render', 'multimesh', 'gui', 'io', 'shaderlang', 'physics', 'oa_hash_map'). .SH FILES -~/.godot/ +XDG_DATA_CONFIG/godot/ or ~/.config/godot/ .RS -User\-specific configuration and cache folder, contains persistent editor and game configuration and saved files, temporary metadata, etc. +User\-specific configuration folder, contains persistent editor settings, script and text editor templates and projects metadata. .RE -~/.godot/app_userdata/ +XDG_DATA_HOME/godot/ or ~/.local/share/godot/ .RS -Contains the default configuration and user data folders for Godot\-made games (\fIuser://\fR path). +Contains the default configuration and user data folders for Godot\-made games (\fIuser://\fR path), as well as export templates. .RE -~/.godot/templates/ +XDG_DATA_CACHE/godot or ~/.cache/godot/ .RS -Installation folder for "export templates", compiled binaries of the engine to deploy on the many supported platforms. +Cache folder for generated thumbnails and scene previews, as well as temporary location for downloads. .RE /usr/share/doc/godot/ .RS diff --git a/misc/dist/osx_tools.app/Contents/Info.plist b/misc/dist/osx_tools.app/Contents/Info.plist index 895eda14db..828a40206d 100755 --- a/misc/dist/osx_tools.app/Contents/Info.plist +++ b/misc/dist/osx_tools.app/Contents/Info.plist @@ -19,11 +19,11 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>3.0-dev</string> + <string>3.0</string> <key>CFBundleSignature</key> <string>godot</string> <key>CFBundleVersion</key> - <string>3.0-dev</string> + <string>3.0</string> <key>NSHumanReadableCopyright</key> <string>© 2007-2018 Juan Linietsky, Ariel Manzur.</string> <key>LSMinimumSystemVersion</key> diff --git a/modules/SCsub b/modules/SCsub index 7a467676cd..74a5267355 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -18,7 +18,7 @@ for x in env.module_list: SConscript(x + "/SCsub") if env.split_modules: - env.split_lib("modules") + env.split_lib("modules", env_lib = env_modules) else: lib = env_modules.add_library("modules", env.modules_sources) diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 1c94428a2a..e0e46cd369 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -154,7 +154,7 @@ public: /// AREA_PARAM_GRAVITY_VECTOR /// Otherwise you can set area parameters virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value); - virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const; + virtual Variant area_get_param(RID p_area, AreaParameter p_param) const; virtual void area_set_transform(RID p_area, const Transform &p_transform); virtual Transform area_get_transform(RID p_area) const; @@ -301,7 +301,7 @@ public: virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B); virtual Vector3 pin_joint_get_local_b(RID p_joint) const; - virtual RID joint_create_hinge(RID p_body_A, const Transform &p_frame_A, RID p_body_B, const Transform &p_frame_B); + virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B); virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B); virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value); diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index f8732c5747..c7909c7d72 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.0-rc1"> +<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 5237556df3..a59abb0ebb 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.0-rc1"> +<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 709eed9e40..4a511b39a7 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -35,6 +35,8 @@ #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h> +#define RAY_STABILITY_MARGIN 0.1 + /** @author AndreaCatania */ @@ -97,10 +99,15 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo m_world->rayTestSingleInternal(ray_transform, to, other_co_wrapper, btResult); if (btResult.hasHit()) { - btVector3 ray_normal(to.getOrigin() - ray_transform.getOrigin()); + + btVector3 ray_normal(ray_transform.getOrigin() - to.getOrigin()); ray_normal.normalize(); - ray_normal *= -1; - resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); + btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); + + if (depth >= -RAY_STABILITY_MARGIN) + depth = 0; + + resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, depth); } } diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h index c716c1d88d..7383dad2bf 100644 --- a/modules/bullet/godot_ray_world_algorithm.h +++ b/modules/bullet/godot_ray_world_algorithm.h @@ -49,7 +49,7 @@ class GodotRayWorldAlgorithm : public btActivatingCollisionAlgorithm { bool m_isSwapped; public: - GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *m_world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped); + GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped); virtual ~GodotRayWorldAlgorithm(); virtual void processCollision(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut); diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index b18965a5b8..e1b0b1b421 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -205,6 +205,6 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult { return m_pointCollisionObject; } - virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorld, btScalar depth); + virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth); }; #endif // GODOT_RESULT_CALLBACKS_H diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 3edc407e87..96a53f9f8b 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -955,7 +955,8 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { const bool isDynamic = p_mass != 0.f; if (isDynamic) { - ERR_FAIL_COND(PhysicsServer::BODY_MODE_RIGID != mode && PhysicsServer::BODY_MODE_CHARACTER != mode); + if (PhysicsServer::BODY_MODE_RIGID != mode && PhysicsServer::BODY_MODE_CHARACTER != mode) + return; m_isStatic = false; compoundShape->calculateLocalInertia(p_mass, localInertia); @@ -975,7 +976,8 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { } } else { - ERR_FAIL_COND(PhysicsServer::BODY_MODE_STATIC != mode && PhysicsServer::BODY_MODE_KINEMATIC != mode); + if (PhysicsServer::BODY_MODE_STATIC != mode && PhysicsServer::BODY_MODE_KINEMATIC != mode) + return; m_isStatic = true; if (PhysicsServer::BODY_MODE_STATIC == mode) { diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index aff6056ad9..c4a9676bdd 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -262,12 +262,12 @@ public: Variant get_state(PhysicsServer::BodyState p_state) const; void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); - void apply_central_impulse(const Vector3 &p_force); + void apply_central_impulse(const Vector3 &p_impulse); void apply_torque_impulse(const Vector3 &p_impulse); void apply_force(const Vector3 &p_force, const Vector3 &p_pos); void apply_central_force(const Vector3 &p_force); - void apply_torque(const Vector3 &p_force); + void apply_torque(const Vector3 &p_torque); void set_applied_force(const Vector3 &p_force); Vector3 get_applied_force() const; diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 4a03c0f014..e04a3c808a 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -99,7 +99,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Plane &p_plane); @@ -116,7 +116,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(real_t p_radius); @@ -133,7 +133,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Vector3 &p_half_extents); @@ -152,7 +152,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(real_t p_height, real_t p_radius); @@ -169,7 +169,7 @@ public: void get_vertices(Vector<Vector3> &out_vertices); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Vector<Vector3> &p_vertices); @@ -187,7 +187,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(PoolVector<Vector3> p_faces); @@ -206,7 +206,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); @@ -222,7 +222,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(real_t p_length); diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index d60d8ba0e2..6f0cda8957 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -116,7 +116,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V } } -int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *p_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) { +int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) { if (p_result_max <= 0) return 0; @@ -138,7 +138,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra collision_object.setCollisionShape(btConvex); collision_object.setWorldTransform(bt_xform); - GodotAllContactResultCallback btQuery(&collision_object, p_results, p_result_max, &p_exclude); + GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude); btQuery.m_collisionFilterGroup = 0; btQuery.m_collisionFilterMask = p_collision_mask; btQuery.m_closestDistanceThreshold = 0; diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 0aeb407dcc..2b97f0b274 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -199,12 +199,12 @@ private: local_shape_most_recovered(0) {} }; - bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); /// This is an API that recover a kinematic object from penetration /// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions - bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); /// This is an API that recover a kinematic object from penetration /// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm - bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); }; #endif diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 3b1e8b73c4..23ee327cc5 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.0-rc1"> +<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.0-stable"> <brief_description> PacketPeer implementation using the ENet library. </brief_description> diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index fbd5201470..bceb4f1f4c 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0-rc1"> +<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0-stable"> <brief_description> GDNative wrapper for an ARVR interface </brief_description> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index b0b50e8378..7e4d956604 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNative" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDNative" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index d30acaf65d..a6874c9ae8 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 7145a16931..f713e4112e 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NativeScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="NativeScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 18857e1939..fbdd8f09e6 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PluginScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="PluginScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h index 94bc2adc7d..04d2911d8b 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.h +++ b/modules/gdnative/gdnative_library_editor_plugin.h @@ -79,7 +79,7 @@ protected: void _on_library_selected(const String &file); void _on_dependencies_selected(const PoolStringArray &files); void _on_filter_selected(int id); - void _on_item_collapsed(Object *item); + void _on_item_collapsed(Object *p_item); void _on_item_activated(); void _on_create_new_entry(); void _set_target_value(const String §ion, const String &target, Variant file); diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 6729eecb32..1be9e907c2 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -112,7 +112,7 @@ public: virtual void update_exports(); virtual void get_script_method_list(List<MethodInfo> *r_methods) const; - virtual void get_script_property_list(List<PropertyInfo> *r_propertieslist) const; + virtual void get_script_property_list(List<PropertyInfo> *r_properties) const; virtual int get_member_line(const StringName &p_member) const; diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index ba7d56a3b2..59cb00e3f6 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="GDScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> A script implemented in the GDScript programming language. </brief_description> @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/scripting/gdscript/index.html + http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html </tutorials> <demos> </demos> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index e670635aa4..8510136f68 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> State of a function call after yielding. </brief_description> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index dfd4703f36..48826ec1e0 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index f380bedf7f..1649fb52f2 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -37,6 +37,9 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN if (!codegen.function_node || codegen.function_node->_static) return false; + if (codegen.stack_identifiers.has(p_name)) + return false; //shadowed + return _is_class_member_property(codegen.script, p_name); } @@ -184,6 +187,14 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: StringName identifier = in->name; + // TRY STACK! + if (!p_initializer && codegen.stack_identifiers.has(identifier)) { + + int pos = codegen.stack_identifiers[identifier]; + return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS); + } + + // TRY CLASS MEMBER if (_is_class_member_property(codegen, identifier)) { //get property codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_MEMBER); // perform operator @@ -194,12 +205,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: return dst_addr; } - // TRY STACK! - if (!p_initializer && codegen.stack_identifiers.has(identifier)) { - - int pos = codegen.stack_identifiers[identifier]; - return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS); - } //TRY MEMBERS! if (!codegen.function_node || !codegen.function_node->_static) { @@ -1336,10 +1341,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(s); - if (_is_class_member_property(codegen, lv->name)) { - _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv); - return ERR_ALREADY_EXISTS; - } + // since we are using properties now for most class access, allow shadowing of class members to make user's life easier. + // + //if (_is_class_member_property(codegen, lv->name)) { + // _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv); + // return ERR_ALREADY_EXISTS; + //} codegen.add_stack_identifier(lv->name, p_stack_level++); codegen.alloc_stack(p_stack_level); @@ -1376,10 +1383,13 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser if (p_func) { for (int i = 0; i < p_func->arguments.size(); i++) { - if (_is_class_member_property(p_script, p_func->arguments[i])) { - _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func); - return ERR_ALREADY_EXISTS; - } + // since we are using properties now for most class access, allow shadowing of class members to make user's life easier. + // + //if (_is_class_member_property(p_script, p_func->arguments[i])) { + // _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func); + // return ERR_ALREADY_EXISTS; + //} + codegen.add_stack_identifier(p_func->arguments[i], i); #ifdef TOOLS_ENABLED argnames.push_back(p_func->arguments[i]); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 75fb7e15c4..1392323d56 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -95,8 +95,6 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { int indent = tokenizer->get_token_line_indent(); int current = tab_level.back()->get(); if (indent <= current) { - print_line("current: " + itos(current) + " indent: " + itos(indent)); - print_line("less than current"); return false; } @@ -460,7 +458,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s //this can be too slow for just validating code if (for_completion && ScriptCodeCompletionCache::get_singleton()) { res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); - } else if (FileAccess::exists(path)) { + } else { // essential; see issue 15902 res = ResourceLoader::load(path); } if (!res.is_valid()) { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 0073631a8b..44685220b3 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridMap" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="GridMap" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Node for 3D tile-based maps. </brief_description> @@ -10,7 +10,7 @@ A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/using_gridmaps.html + http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html </tutorials> <demos> </demos> diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index b17fe43ae8..ed36751fc8 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -247,7 +247,7 @@ public: int get_cell_item(int p_x, int p_y, int p_z) const; int get_cell_item_orientation(int p_x, int p_y, int p_z) const; - Vector3 world_to_map(const Vector3 &p_pos) const; + Vector3 world_to_map(const Vector3 &p_world_pos) const; Vector3 map_to_world(int p_x, int p_y, int p_z) const; void set_clip(bool p_enabled, bool p_clip_above = true, int p_floor = 0, Vector3::Axis p_axis = Vector3::AXIS_X); diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index bc0622f66f..82300e707a 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0-rc1"> +<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0-stable"> <brief_description> Generic mobile VR implementation </brief_description> diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index aee2481366..0dc0018224 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -176,7 +176,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "fixed", "float", "for", - "forech", + "foreach", "goto", "if", "implicit", @@ -222,14 +222,17 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "ushort", "using", "virtual", - "volatile", "void", + "volatile", "while", // Contextual keywords. Not reserved words, but I guess we should include // them because this seems to be used only for syntax highlighting. "add", + "alias", "ascending", + "async", + "await", "by", "descending", "dynamic", @@ -238,10 +241,10 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "get", "global", "group", - "in", "into", "join", "let", + "nameof", "on", "orderby", "partial", @@ -250,6 +253,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "set", "value", "var", + "when", "where", "yield", 0 @@ -447,6 +451,7 @@ String CSharpLanguage::_get_indentation() const { Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { +#ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) @@ -463,8 +468,12 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() si = stack_trace_get_info(stack_trace); return si; +#else + return Vector<StackInfo>(); +#endif } +#ifdef DEBUG_ENABLED Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { // Printing an error here could result in endless recursion, so we must be careful @@ -514,6 +523,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec return si; } +#endif void CSharpLanguage::frame() { @@ -941,19 +951,6 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { #endif } -void CSharpInstance::_ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *p_klass, const StringName &p_method, const Variant **p_args, int p_argcount) { - - GDMonoClass *base = p_klass->get_parent_class(); - if (base && base != script->native) - _ml_call_reversed(p_mono_object, base, p_method, p_args, p_argcount); - - GDMonoMethod *method = p_klass->get_method(p_method, p_argcount); - - if (method) { - method->invoke(p_mono_object, p_args); - } -} - CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance); @@ -1022,6 +1019,8 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true) return true; + + break; } top = top->get_parent_class(); @@ -1082,6 +1081,8 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { r_ret = GDMonoMarshal::mono_object_to_variant(ret); return true; } + + break; } top = top->get_parent_class(); @@ -1133,10 +1134,13 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, Variant()); + if (!mono_object) { + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + ERR_FAIL_V(Variant()); + } if (!script.is_valid()) - return Variant(); + ERR_FAIL_V(Variant()); GDMonoClass *top = script->script_class; @@ -1146,6 +1150,8 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, if (method) { MonoObject *return_value = method->invoke(mono_object, p_args); + r_error.error = Variant::CallError::CALL_OK; + if (return_value) { return GDMonoMarshal::mono_object_to_variant(return_value); } else { @@ -1179,8 +1185,10 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam while (top && top != script->native) { GDMonoMethod *method = top->get_method(p_method, p_argcount); - if (method) + if (method) { method->invoke(p_mono_object, p_args); + return; + } top = top->get_parent_class(); } @@ -1188,13 +1196,9 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - if (script.is_valid()) { - MonoObject *mono_object = get_mono_object(); - - ERR_FAIL_NULL(mono_object); + // Sorry, the method is the one that controls the call order - _ml_call_reversed(mono_object, script->script_class, p_method, p_args, p_argcount); - } + call_multilevel(p_method, p_args, p_argcount); } void CSharpInstance::_reference_owner_unsafe() { @@ -1541,6 +1545,7 @@ bool CSharpScript::_update_exports() { return false; } +#ifdef TOOLS_ENABLED bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { StringName name = p_member->get_name(); @@ -1611,6 +1616,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p return true; } +#endif void CSharpScript::_clear() { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 3ce8a9b64e..f18e339e18 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -105,7 +105,9 @@ class CSharpScript : public Script { void _clear(); bool _update_exports(); +#ifdef TOOLS_ENABLED bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); +#endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error); @@ -170,8 +172,6 @@ class CSharpInstance : public ScriptInstance { bool base_ref; bool ref_dying; - void _ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount); - void _reference_owner_unsafe(); void _unreference_owner_unsafe(); @@ -335,7 +335,9 @@ public: virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); +#ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); +#endif CSharpLanguage(); ~CSharpLanguage(); diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index a9dccea3c1..0f33c76eb2 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@C#" category="Core" version="3.0-rc1"> +<class name="@C#" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 9a3631d2f2..3efe71f1b3 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="CSharpScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 219d041b67..1e5edf2a2a 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" category="Core" version="3.0-rc1"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 9bf81b52bb..62c7a94755 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -250,8 +250,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); - String im_sig = "IntPtr " CS_PARAM_METHODBIND ", IntPtr " CS_PARAM_INSTANCE; - String im_unique_sig = imethod.return_type.operator String() + ",IntPtr,IntPtr"; + String im_sig; + String im_unique_sig; + + if (p_itype.is_object_type) { + im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; + im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr"; + } + + im_sig += "IntPtr " CS_PARAM_INSTANCE; // Get arguments information int i = 0; @@ -263,25 +270,37 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { im_sig += " arg"; im_sig += itos(i + 1); - im_unique_sig += ","; - im_unique_sig += get_unique_sig(*arg_type); + if (p_itype.is_object_type) { + im_unique_sig += ","; + im_unique_sig += get_unique_sig(*arg_type); + } i++; } - // godot_icall_{argc}_{icallcount} - String icall_method = ICALL_PREFIX + itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); + String icall_method = ICALL_PREFIX; + + if (p_itype.is_object_type) { + icall_method += itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); // godot_icall_{argc}_{icallcount} + } else { + icall_method += p_itype.name + "_" + imethod.name; // godot_icall_{Type}_{method} + } InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig); - List<InternalCall>::Element *match = method_icalls.find(im_icall); + if (p_itype.is_object_type) { + List<InternalCall>::Element *match = method_icalls.find(im_icall); - if (match) { - if (p_itype.api_type != ClassDB::API_EDITOR) - match->get().editor_only = false; - method_icalls_map.insert(&E->get(), &match->get()); + if (match) { + if (p_itype.api_type != ClassDB::API_EDITOR) + match->get().editor_only = false; + method_icalls_map.insert(&E->get(), &match->get()); + } else { + List<InternalCall>::Element *added = method_icalls.push_back(im_icall); + method_icalls_map.insert(&E->get(), &added->get()); + } } else { - List<InternalCall>::Element *added = method_icalls.push_back(im_icall); + List<InternalCall>::Element *added = builtin_method_icalls.push_back(im_icall); method_icalls_map.insert(&E->get(), &added->get()); } } @@ -525,6 +544,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); + for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); #undef ADD_INTERNAL_CALL @@ -616,6 +637,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ } + // No need to add builtin_method_icalls. Builtin types are core only + for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) @@ -694,9 +717,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(itype.is_singleton ? "static class " : "class "); output.push_back(itype.proxy_name); - if (itype.is_singleton || !itype.is_object_type) { + if (itype.is_singleton) { output.push_back("\n"); - } else if (!is_derived_type) { + } else if (!is_derived_type || !itype.is_object_type /* assuming only object types inherit */) { output.push_back(" : IDisposable\n"); } else if (obj_types.has(itype.base_name)) { output.push_back(" : "); @@ -838,7 +861,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 "if (disposed) return;\n" INDENT3 "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_"); output.push_back(itype.proxy_name); @@ -929,7 +952,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 "if (disposed) return;\n" INDENT3 "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN @@ -1122,10 +1145,14 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String method_bind_field = "method_bind_" + itos(p_method_bind_count); - String icall_params = method_bind_field + ", " + sformat(p_itype.cs_in, "this"); String arguments_sig; String cs_in_statements; + String icall_params; + if (p_itype.is_object_type) + icall_params += method_bind_field + ", "; + icall_params += sformat(p_itype.cs_in, "this"); + List<String> default_args_doc; // Retrieve information from the arguments @@ -1200,9 +1227,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Generate method { - if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.push_back(MEMBER_BEGIN "private "); - p_output.push_back(p_itype.is_singleton ? "static IntPtr " : "IntPtr "); + if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) { + p_output.push_back(MEMBER_BEGIN "private static IntPtr "); p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.push_back(p_imethod.name); p_output.push_back("\");\n"); @@ -1381,6 +1407,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) + "; }\n#endif // TOOLS_ENABLED\n"); output.push_back("void register_generated_icalls() " OPEN_BLOCK); + output.push_back("\tgodot_register_header_icalls();"); #define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ { \ @@ -1443,6 +1470,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back("#endif\n"); } + for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL_REGISTRATION(E->get()); + #undef ADD_INTERNAL_CALL_REGISTRATION output.push_back(CLOSE_BLOCK "}\n"); @@ -1518,6 +1548,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte i++; } + if (!p_itype.is_object_type) + return OK; // no auto-generated icall functions for builtin types + const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); ERR_FAIL_NULL_V(match, ERR_BUG); @@ -2113,36 +2146,34 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #undef INSERT_STRUCT_TYPE -#define INSERT_PRIMITIVE_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_arg_in = "&%s"; \ - itype.c_type_in = #m_type; \ - itype.c_type_out = #m_type; \ - itype.im_type_in = #m_type; \ - itype.im_type_out = #m_type; \ - builtin_types.insert(itype.cname, itype); \ - } - - INSERT_PRIMITIVE_TYPE(bool) - //INSERT_PRIMITIVE_TYPE(int) + // bool + itype = TypeInterface::create_value_type(String("bool")); + itype.c_arg_in = "&%s"; + // /* MonoBoolean <---> bool + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "bool"; + // */ + itype.c_type_in = "MonoBoolean"; + itype.c_type_out = itype.c_type_in; + itype.im_type_in = itype.name; + itype.im_type_out = itype.name; + builtin_types.insert(itype.cname, itype); // int itype = TypeInterface::create_value_type(String("int")); itype.c_arg_in = "&%s_in"; - //* ptrcall only supports int64_t and uint64_t + // /* ptrcall only supports int64_t and uint64_t itype.c_in = "\t%0 %1_in = (%0)%1;\n"; itype.c_out = "\treturn (%0)%1;\n"; itype.c_type = "int64_t"; - //*/ - itype.c_type_in = itype.name; - itype.c_type_out = itype.name; + // */ + itype.c_type_in = "int32_t"; + itype.c_type_out = itype.c_type_in; itype.im_type_in = itype.name; itype.im_type_out = itype.name; builtin_types.insert(itype.cname, itype); -#undef INSERT_PRIMITIVE_TYPE - // real_t itype = TypeInterface(); #ifdef REAL_T_IS_DOUBLE diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 8929b45cce..9b5a9cea88 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -441,6 +441,7 @@ class BindingsGenerator { Map<StringName, String> extra_members; List<InternalCall> method_icalls; + List<InternalCall> builtin_method_icalls; Map<const MethodInterface *, const InternalCall *> method_icalls_map; List<const InternalCall *> generated_icall_funcs; @@ -503,8 +504,8 @@ class BindingsGenerator { const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname); const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname); - void _default_argument_from_variant(const Variant &p_var, ArgumentInterface &r_iarg); - void _populate_builtin_type(TypeInterface &r_type, Variant::Type vtype); + void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); + void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); void _populate_object_type_interfaces(); void _populate_builtin_type_interfaces(); @@ -513,14 +514,14 @@ class BindingsGenerator { Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); - Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_prop_doc, List<String> &p_output); + Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output); Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output); void _generate_global_constants(List<String> &p_output); Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output); - Error _save_file(const String &path, const List<String> &content); + Error _save_file(const String &p_path, const List<String> &p_content); BindingsGenerator() {} diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 9e48da68c1..0ef3adfdd0 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -112,6 +112,21 @@ void GodotSharpEditor::_remove_create_sln_menu_option() { bottom_panel_btn->show(); } +void GodotSharpEditor::_show_about_dialog() { + + bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); + about_dialog_checkbox->set_pressed(show_on_start); + about_dialog->popup_centered_minsize(); +} + +void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) { + + bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); + if (show_on_start != p_enabled) { + EditorSettings::get_singleton()->set_setting("mono/editor/show_info_on_start", p_enabled); + } +} + void GodotSharpEditor::_menu_option_pressed(int p_id) { switch (p_id) { @@ -119,15 +134,37 @@ void GodotSharpEditor::_menu_option_pressed(int p_id) { _create_project_solution(); } break; + case MENU_ABOUT_CSHARP: { + + _show_about_dialog(); + } break; default: ERR_FAIL(); } } +void GodotSharpEditor::_notification(int p_notification) { + + switch (p_notification) { + + case NOTIFICATION_READY: { + + bool show_info_dialog = EDITOR_GET("mono/editor/show_info_on_start"); + if (show_info_dialog) { + about_dialog->set_exclusive(true); + _show_about_dialog(); + // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive then. + about_dialog->set_exclusive(false); + } + } + } +} + void GodotSharpEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution); ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option); + ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start); ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); } @@ -210,6 +247,55 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { menu_button->set_text(TTR("Mono")); menu_popup = menu_button->get_popup(); + // TODO: Remove or edit this info dialog once Mono support is no longer in alpha + { + menu_popup->add_item(TTR("About C# support"), MENU_ABOUT_CSHARP); + about_dialog = memnew(AcceptDialog); + editor->get_gui_base()->add_child(about_dialog); + about_dialog->set_title("Important: C# support is not feature-complete"); + + // We don't use set_text() as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox + // we'll add. Instead we add containers and a new autowrapped Label inside. + + // Main VBoxContainer (icon + label on top, checkbox at bottom) + VBoxContainer *about_vbc = memnew(VBoxContainer); + about_dialog->add_child(about_vbc); + + // HBoxContainer for icon + label + HBoxContainer *about_hbc = memnew(HBoxContainer); + about_vbc->add_child(about_hbc); + + TextureRect *about_icon = memnew(TextureRect); + about_hbc->add_child(about_icon); + Ref<Texture> about_icon_tex = about_icon->get_icon("NodeWarning", "EditorIcons"); + about_icon->set_texture(about_icon_tex); + + Label *about_label = memnew(Label); + about_hbc->add_child(about_label); + about_label->set_custom_minimum_size(Size2(600, 150) * EDSCALE); + about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); + about_label->set_autowrap(true); + String about_text = + String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + + "It is at the alpha stage and thus not suitable for use in production.\n\n" + + "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " + + "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" + + "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " + + "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" + + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" + + " https://github.com/godotengine/godot/issues\n\n" + + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; + about_label->set_text(about_text); + + EDITOR_DEF("mono/editor/show_info_on_start", true); + + // CheckBox in main container + about_dialog_checkbox = memnew(CheckBox); + about_vbc->add_child(about_dialog_checkbox); + about_dialog_checkbox->set_text("Show this warning when starting the editor"); + about_dialog_checkbox->connect("toggled", this, "_toggle_about_dialog_on_start"); + } + String sln_path = GodotSharpDirs::get_project_sln_path(); String csproj_path = GodotSharpDirs::get_project_csproj_path(); diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 1b83bae1cd..81c49aec30 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -44,6 +44,8 @@ class GodotSharpEditor : public Node { PopupMenu *menu_popup; AcceptDialog *error_dialog; + AcceptDialog *about_dialog; + CheckBox *about_dialog_checkbox; ToolButton *bottom_panel_btn; @@ -54,17 +56,21 @@ class GodotSharpEditor : public Node { bool _create_project_solution(); void _remove_create_sln_menu_option(); + void _show_about_dialog(); + void _toggle_about_dialog_on_start(bool p_enabled); void _menu_option_pressed(int p_id); static GodotSharpEditor *singleton; protected: + void _notification(int p_notification); static void _bind_methods(); public: enum MenuOptions { - MENU_CREATE_SLN + MENU_CREATE_SLN, + MENU_ABOUT_CSHARP, }; enum ExternalEditor { diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h index 7e8a76b595..552c10a61d 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/editor/monodevelop_instance.h @@ -43,7 +43,7 @@ class MonoDevelopInstance { public: void execute(const Vector<String> &p_files); - void execute(const String &p_files); + void execute(const String &p_file); MonoDevelopInstance(const String &p_solution); }; diff --git a/modules/mono/glue/builtin_types_glue.h b/modules/mono/glue/builtin_types_glue.h new file mode 100644 index 0000000000..460de84b65 --- /dev/null +++ b/modules/mono/glue/builtin_types_glue.h @@ -0,0 +1,59 @@ +#ifndef BUILTIN_TYPES_GLUE_H +#define BUILTIN_TYPES_GLUE_H + +#include "core/node_path.h" +#include "core/rid.h" + +#include <mono/metadata/object.h> + +#include "../mono_gd/gd_mono_marshal.h" + +MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { + return (MonoBoolean)p_ptr->is_absolute(); +} + +uint32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) { + return p_ptr->get_name_count(); +} + +MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx)); +} + +uint32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) { + return p_ptr->get_subname_count(); +} + +MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx)); +} + +MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); +} + +NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) { + return memnew(NodePath(p_ptr->get_as_property_path())); +} + +MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { + return (MonoBoolean)p_ptr->is_empty(); +} + +uint32_t godot_icall_RID_get_id(RID *p_ptr) { + return p_ptr->get_id(); +} + +void godot_register_builtin_type_icalls() { + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty); + mono_add_internal_call("Godot.NativeCalls::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id); +} + +#endif // BUILTIN_TYPES_GLUE_H diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 6951ace4fc..476396e9a3 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -71,7 +71,7 @@ namespace Godot public static int Decimals(float step) { - return Decimals(step); + return Decimals((decimal)step); } public static int Decimals(decimal step) diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index 32988c5afa..cedc8e9992 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "builtin_types_glue.h" + #include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_internals.h" @@ -91,12 +93,6 @@ MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); } -MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { - Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); - // TODO Check possible Array/Vector<uint8_t> problem? - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - // -- RID -- RID *godot_icall_RID_Ctor(Object *p_from) { @@ -115,6 +111,12 @@ void godot_icall_RID_Dtor(RID *p_ptr) { // -- String -- +MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); + // TODO Check possible Array/Vector<uint8_t> problem? + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + MonoString *godot_icall_String_md5_text(MonoString *p_str) { String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); return GDMonoMarshal::mono_string_from_godot(ret); @@ -303,3 +305,7 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) { return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); } + +void godot_register_header_icalls() { + godot_register_builtin_type_icalls(); +} diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 6c07c90f79..f5febd415b 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -696,11 +696,13 @@ bool _GodotSharp::is_domain_loaded() { return GDMono::get_singleton()->get_scripts_domain() != NULL; } -#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ - m_queue.push_back(m_inst); \ - if (queue_empty) { \ - queue_empty = false; \ - call_deferred("_dispose_callback"); \ +#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ + m_queue.push_back(m_inst); \ + if (queue_empty) { \ + queue_empty = false; \ + if (!is_finalizing_domain()) { /* call_deferred may not be safe here */ \ + call_deferred("_dispose_callback"); \ + } \ } void _GodotSharp::queue_dispose(MonoObject *p_mono_object, Object *p_object) { diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index ba56ed6ed5..ef39b8549d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -116,6 +116,37 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse } } + String name = mono_assembly_name_get_name(aname); + bool has_extension = name.ends_with(".dll"); + + if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") { + GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); + if (stored_assembly) return (*stored_assembly)->get_assembly(); + + String path; + MonoAssembly *res = NULL; + + for (int i = 0; i < search_dirs.size(); i++) { + const String &search_dir = search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(name); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name.get_basename(), path); + break; + } + } else { + path = search_dir.plus_file(name + ".dll"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name, path); + break; + } + } + } + + if (res) return res; + } + return NULL; } diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 8e7aa701bf..8a5fa19626 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -110,7 +110,7 @@ public: _FORCE_INLINE_ String get_path() const { return path; } _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; } - GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class); + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); GDMonoClass *get_class(MonoClass *p_mono_class); GDMonoClass *get_object_derived_class(const StringName &p_class); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index f5895be144..afccf2fc63 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -125,7 +125,7 @@ public: GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); - GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace); + GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); GDMonoField *get_field(const StringName &p_name); const Vector<GDMonoField *> &get_all_fields(); diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index bc5a1d3a39..0fe527b199 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -139,7 +139,7 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { - MonoMethod *prop_method = mono_property_get_get_method(mono_property); + MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 835a4614c1..a2f0819a72 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -184,7 +184,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); - CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath)); + CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 2666433170..259da46c31 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -43,7 +43,7 @@ namespace GDMonoUtils { typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **); +typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 2671e9a970..b9d8520ac9 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -102,7 +102,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); MonoObject *ex = NULL; - thunk(get_target(), &signal_args, &ex); + thunk(get_target(), signal_args, &ex); if (ex) { mono_print_unhandled_exception(ex); diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index fb7d7e477c..2cf80acd28 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegEx" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="RegEx" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Class for searching text for patterns using regular expressions. </brief_description> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 6870c64246..9eba0f738b 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegExMatch" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="RegExMatch" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Contains the results of a regex search. </brief_description> diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 6a6ee390cc..18ab616826 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -115,7 +115,7 @@ void AudioStreamPlaybackOGGVorbis::seek(float p_time) { if (!active) return; - if (p_time >= get_length()) { + if (p_time >= vorbis_stream->get_length()) { p_time = 0; } frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time); @@ -123,11 +123,6 @@ void AudioStreamPlaybackOGGVorbis::seek(float p_time) { stb_vorbis_seek(ogg_stream, frames_mixed); } -float AudioStreamPlaybackOGGVorbis::get_length() const { - - return vorbis_stream->length; -} - AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { if (ogg_alloc.alloc_buffer) { stb_vorbis_close(ogg_stream); @@ -261,6 +256,11 @@ float AudioStreamOGGVorbis::get_loop_offset() const { return loop_offset; } +float AudioStreamOGGVorbis::get_length() const { + + return length; +} + void AudioStreamOGGVorbis::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamOGGVorbis::set_data); diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index bb01c26902..d7bc7cc0d7 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -71,8 +71,6 @@ public: virtual float get_playback_position() const; virtual void seek(float p_time); - virtual float get_length() const; //if supported, otherwise return 0 - AudioStreamPlaybackOGGVorbis() {} ~AudioStreamPlaybackOGGVorbis(); }; @@ -112,6 +110,8 @@ public: void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; + virtual float get_length() const; //if supported, otherwise return 0 + AudioStreamOGGVorbis(); virtual ~AudioStreamOGGVorbis(); }; diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index 89cded2e91..827e947a79 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.0-rc1"> +<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.0-stable"> <brief_description> OGG Vorbis audio stream driver. </brief_description> diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml index 12598c436e..9a095c3ddd 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml index 266e38021e..a280d767c3 100644 --- a/modules/theora/doc_classes/ResourceImporterTheora.xml +++ b/modules/theora/doc_classes/ResourceImporterTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index b2762707d3..9da3dc0d02 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.0-rc1"> +<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index a037344c70..a6a43f31b8 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="VisualScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> A script implemented in the Visual Script programming environment. </brief_description> @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/scripting/visual_script/index.html + http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html </tutorials> <demos> </demos> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index 0e52d22b1c..d63a6ad524 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node representing a constant from the base types. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 13950ba6f9..da4db29086 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node used to call built-in functions. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index 3b5419871f..189a6f6ad8 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a constant from a given class. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index 321f29ba65..5462c379ad 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node used to annotate the script. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index 398ec40fe0..bb70a17357 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which branches the flow. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index 22e4b89fd6..e2ccb50bfd 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a contant's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index f6365c3e06..da6779b79d 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which calls a base type constructor. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 14f32f5fa6..33d2f1437a 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A scripted Visual Script node. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index b062cf9ed7..09fcba4314 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which deconstructs a base type instance into its parts. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index d39b3432d1..8e26758a31 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.0-rc1"> +<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index f73c0abb94..30f96011d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Emits a specified signal. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 4400889082..0dc0cdf5eb 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node returning a singleton from [@GlobalScope] </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index dfb642d7c4..91f55edb2b 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index f9b43d359a..bd59d739ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index e431e1f15b..e2b732a250 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index 928c715562..614176498a 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index ac3ee0feaa..a36f7809c2 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 1b1aacabea..b2d0a194e0 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index d9e4e858ff..7ad200afa4 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index 9a37617545..45c493887b 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index b42fb01be6..28e8a66182 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Steps through items in a given input. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index 7420912aad..66faf448cb 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index 992ba94bfd..8a816e5dd7 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Changes a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index 41855f312f..45fa471c41 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Commonly used mathematical constants. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index fd52b7fa18..e9d1cd949f 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A node which is part of a [VisualScript]. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index 675fee9cbd..4538bd3c78 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index 4d4a42b8f0..3dae0e4b81 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Creates a new [Resource] or loads one from the filesystem. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index c9ff8e40f4..7555c83960 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index 45032e391f..dc6a9efd83 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index b98502946e..3789626ed0 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index 4b7141e6c7..1172b7555b 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Exits a function and returns an optional value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index c70badffc8..4c6181e040 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Node reference. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index e9d8021652..68cc0d0b55 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index 64fec422d9..017efdb07a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Chooses between two input values. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index c0cd15f04f..e9b480bbae 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Outputs a reference to the current instance. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index 29b77fbd01..be793ae36e 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Executes a series of Sequence ports. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 9a34c5d252..85db63b78a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index c17be9b874..ec7565b31a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Branches program flow based on a given input's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index d2bebff7d4..d414a95657 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 52a3fb76be..ccd2918ec8 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index 12755784c0..e1fc1ba762 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Changes a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index 505dfd00d2..de1ff45746 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Conditional loop. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index aaa45fdb85..f21b53861a 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index e0ae2c0bba..5075fb6ded 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml index 0d04e59a70..20e0e48187 100644 --- a/modules/webm/doc_classes/ResourceImporterWebm.xml +++ b/modules/webm/doc_classes/ResourceImporterWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index ceb2348d10..94aea5c8d2 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.0-rc1"> +<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub new file mode 100644 index 0000000000..067a99ffff --- /dev/null +++ b/modules/websocket/SCsub @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +# Thirdparty source files + +env_lws = env_modules.Clone() + +thirdparty_dir = "#thirdparty/lws/" +helper_dir = "win32helpers/" +openssl_dir = "#thirdparty/openssl/" +thirdparty_sources = [ + "client/client.c", + "client/client-handshake.c", + "client/client-parser.c", + "client/ssl-client.c", + + "ext/extension.c", + "ext/extension-permessage-deflate.c", + + "server/fops-zip.c", + "server/lejp-conf.c", + "server/parsers.c", + "server/ranges.c", + "server/server.c", + "server/server-handshake.c", + "server/ssl-server.c", + + "misc/base64-decode.c", + "misc/lejp.c", + "misc/sha-1.c", + + "alloc.c", + "context.c", + "handshake.c", + "header.c", + "libwebsockets.c", + "minilex.c", + "output.c", + "pollfd.c", + "service.c", + "ssl.c", + +] + +if env_lws["platform"] == "android": # Builtin getifaddrs + thirdparty_sources += ["misc/getifaddrs.c"] + +if env_lws["platform"] == "windows": # Winsock + thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"] +else: # Unix socket + thirdparty_sources += ["plat/lws-plat-unix.c"] + + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +if env_lws["platform"] == "javascript": # No need to add third party libraries at all + pass +else: + env_lws.add_source_files(env.modules_sources, thirdparty_sources) + env_lws.Append(CPPPATH=[thirdparty_dir]) + + if env['builtin_openssl']: + env_lws.Append(CPPPATH=[openssl_dir]) + + if env_lws["platform"] == "windows": + env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) + +env_lws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/config.py b/modules/websocket/config.py new file mode 100644 index 0000000000..fb920482f5 --- /dev/null +++ b/modules/websocket/config.py @@ -0,0 +1,7 @@ + +def can_build(platform): + return True + + +def configure(env): + pass diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp new file mode 100644 index 0000000000..38fe520fc1 --- /dev/null +++ b/modules/websocket/emws_client.cpp @@ -0,0 +1,224 @@ +/*************************************************************************/ +/* emws_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_client.h" +#include "core/io/ip.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_connect(String(proto)); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + + static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); + client->_on_peer_packet(); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_error(); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_disconnect(); +} +} + +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + + String str = "ws://"; + String proto_string = ""; + int i = 0; + + if (p_ssl) + str = "wss://"; + str += p_host + ":" + itos(p_port) + p_path; + for (int i = 0; i < p_protocols.size(); i++) { + proto_string += p_protocols[i]; + proto_string += ","; + } + if (proto_string == "") + proto_string = "binary,"; + + proto_string = proto_string.substr(0, proto_string.length() - 1); + + _is_connecting = true; + /* clang-format off */ + int peer_sock = EM_ASM_INT({ + var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(",")); + var c_ptr = Module.IDHandler.get($0); + socket.binaryType = "arraybuffer"; + + // Connection opened + socket.addEventListener("open", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + ccall("_esws_on_connect", + "void", + ["number", "string"], + [c_ptr, socket.protocol] + ); + }); + + // Listen for messages + socket.addEventListener("message", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + var buffer; + var is_string = 0; + if (event.data instanceof ArrayBuffer) { + + buffer = new Uint8Array(event.data); + + } else if (event.data instanceof Blob) { + + alert("Blob type not supported"); + return; + + } else if (typeof event.data === "string") { + + is_string = 1; + var enc = new TextEncoder("utf-8"); + buffer = new Uint8Array(enc.encode(event.data)); + + } else { + + alert("Unknown message type"); + return; + + } + var len = buffer.length*buffer.BYTES_PER_ELEMENT; + var out = Module._malloc(len); + Module.HEAPU8.set(buffer, out); + ccall("_esws_on_message", + "void", + ["number", "number", "number", "number"], + [c_ptr, out, len, is_string] + ); + Module._free(out); + }); + + socket.addEventListener("error", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + ccall("_esws_on_error", + "void", + ["number"], + [c_ptr] + ); + }); + + socket.addEventListener("close", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + var was_clean = 0; + if (event.was_clean) + was_clean = 1; + ccall("_esws_on_close", + "void", + ["number", "number", "string", "number"], + [c_ptr, event.code, event.reason, was_clean] + ); + }); + + return Module.IDHandler.add(socket); + }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data()); + /* clang-format on */ + + static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock); + + return OK; +}; + +void EMWSClient::poll() { +} + +Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const { + + return _peer; +} + +NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const { + + if (_peer->is_connected_to_host()) + return CONNECTION_CONNECTED; + + if (_is_connecting) + return CONNECTION_CONNECTING; + + return CONNECTION_DISCONNECTED; +}; + +void EMWSClient::disconnect_from_host() { + + _peer->close(); +}; + +IP_Address EMWSClient::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t EMWSClient::get_connected_port() const { + + return 1025; +}; + +EMWSClient::EMWSClient() { + _is_connecting = false; + _peer = Ref<EMWSPeer>(memnew(EMWSPeer)); + /* clang-format off */ + _js_id = EM_ASM_INT({ + return Module.IDHandler.add($0); + }, this); + /* clang-format on */ +}; + +EMWSClient::~EMWSClient() { + + disconnect_from_host(); + _peer = Ref<EMWSPeer>(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h new file mode 100644 index 0000000000..8801f37007 --- /dev/null +++ b/modules/websocket/emws_client.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* emws_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef EMWSCLIENT_H +#define EMWSCLIENT_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "emws_peer.h" +#include "websocket_client.h" + +class EMWSClient : public WebSocketClient { + + GDCIIMPL(EMWSClient, WebSocketClient); + +private: + int _js_id; + +public: + bool _is_connecting; + + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Ref<WebSocketPeer> get_peer(int p_peer_id) const; + void disconnect_from_host(); + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + virtual ConnectionStatus get_connection_status() const; + virtual void poll(); + EMWSClient(); + ~EMWSClient(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // EMWSCLIENT_H diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp new file mode 100644 index 0000000000..93665e6428 --- /dev/null +++ b/modules/websocket/emws_peer.cpp @@ -0,0 +1,173 @@ +/*************************************************************************/ +/* emws_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_peer.h" +#include "core/io/ip.h" + +void EMWSPeer::set_sock(int p_sock) { + + peer_sock = p_sock; + in_buffer.clear(); + queue_count = 0; +} + +void EMWSPeer::set_write_mode(WriteMode p_mode) { + write_mode = p_mode; +} + +EMWSPeer::WriteMode EMWSPeer::get_write_mode() const { + return write_mode; +} + +void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { + + if (in_buffer.space_left() < p_size + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL(); + } + + uint8_t is_string = p_is_string ? 1 : 0; + in_buffer.write((uint8_t *)&p_size, 4); + in_buffer.write((uint8_t *)&is_string, 1); + in_buffer.write(p_data, p_size); + queue_count++; +} + +Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; + + /* clang-format off */ + EM_ASM({ + var sock = Module.IDHandler.get($0); + var bytes_array = new Uint8Array($2); + var i = 0; + + for(i=0; i<$2; i++) { + bytes_array[i] = getValue($1+i, 'i8'); + } + + if ($3) { + sock.send(bytes_array.buffer); + } else { + var string = new TextDecoder("utf-8").decode(bytes_array); + sock.send(string); + } + }, peer_sock, p_buffer, p_buffer_size, is_bin); + /* clang-format on */ + + return OK; +}; + +Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + in_buffer.read((uint8_t *)&to_read, 4); + --queue_count; + left = in_buffer.data_left(); + + if (left < to_read + 1) { + in_buffer.advance_read(left); + return FAILED; + } + + in_buffer.read(&is_string, 1); + _was_string = is_string == 1; + in_buffer.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + + return OK; +}; + +int EMWSPeer::get_available_packet_count() const { + + return queue_count; +}; + +bool EMWSPeer::was_string_packet() const { + + return _was_string; +}; + +bool EMWSPeer::is_connected_to_host() const { + + return peer_sock != -1; +}; + +void EMWSPeer::close() { + + if (peer_sock != -1) { + /* clang-format off */ + EM_ASM({ + var sock = Module.IDHandler.get($0); + sock.close(); + Module.IDHandler.remove($0); + }, peer_sock); + /* clang-format on */ + } + peer_sock = -1; + queue_count = 0; + in_buffer.clear(); +}; + +IP_Address EMWSPeer::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t EMWSPeer::get_connected_port() const { + + return 1025; +}; + +EMWSPeer::EMWSPeer() { + peer_sock = -1; + queue_count = 0; + _was_string = false; + in_buffer.resize(16); + write_mode = WRITE_MODE_BINARY; +}; + +EMWSPeer::~EMWSPeer() { + + in_buffer.resize(0); + close(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h new file mode 100644 index 0000000000..a50d1874ba --- /dev/null +++ b/modules/websocket/emws_peer.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* emws_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef EMWSPEER_H +#define EMWSPEER_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "emscripten.h" +#include "websocket_peer.h" + +class EMWSPeer : public WebSocketPeer { + + GDCIIMPL(EMWSPeer, WebSocketPeer); + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type + }; + + int peer_sock; + WriteMode write_mode; + + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + RingBuffer<uint8_t> in_buffer; + int queue_count; + bool _was_string; + +public: + void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); + void set_sock(int sock); + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + + virtual void close(); + virtual bool is_connected_to_host() const; + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual WriteMode get_write_mode() const; + virtual void set_write_mode(WriteMode p_mode); + virtual bool was_string_packet() const; + + void set_wsi(struct lws *wsi); + Error read_wsi(void *in, size_t len); + Error write_wsi(); + + EMWSPeer(); + ~EMWSPeer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LSWPEER_H diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp new file mode 100644 index 0000000000..60e9133225 --- /dev/null +++ b/modules/websocket/emws_server.cpp @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* emws_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_server.h" +#include "core/os/os.h" + +Error EMWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { + + return FAILED; +} + +bool EMWSServer::is_listening() const { + return false; +} + +void EMWSServer::stop() { +} + +bool EMWSServer::has_peer(int p_id) const { + return false; +} + +Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const { + return NULL; +} + +PoolVector<String> EMWSServer::get_protocols() const { + PoolVector<String> out; + + return out; +} + +EMWSServer::EMWSServer() { +} + +EMWSServer::~EMWSServer() { +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h new file mode 100644 index 0000000000..59f1d76346 --- /dev/null +++ b/modules/websocket/emws_server.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* emws_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef EMWSSERVER_H +#define EMWSSERVER_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/reference.h" +#include "emws_peer.h" +#include "websocket_server.h" + +class EMWSServer : public WebSocketServer { + + GDCIIMPL(EMWSServer, WebSocketServer); + +public: + Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void stop(); + bool is_listening() const; + bool has_peer(int p_id) const; + Ref<WebSocketPeer> get_peer(int p_id) const; + virtual void poll(); + virtual PoolVector<String> get_protocols() const; + + EMWSServer(); + ~EMWSServer(); +}; + +#endif + +#endif // LWSSERVER_H diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp new file mode 100644 index 0000000000..604b1886ad --- /dev/null +++ b/modules/websocket/lws_client.cpp @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* lws_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef JAVASCRIPT_ENABLED + +#include "lws_client.h" +#include "core/io/ip.h" + +Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + + ERR_FAIL_COND_V(context != NULL, FAILED); + + IP_Address addr; + + if (!p_host.is_valid_ip_address()) { + addr = IP::get_singleton()->resolve_hostname(p_host); + } else { + addr = p_host; + } + + ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); + + // prepare protocols + if (p_protocols.size() == 0) // default to binary protocol + p_protocols.append("binary"); + _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref); + + // init lws client + struct lws_context_creation_info info; + struct lws_client_connect_info i; + + memset(&i, 0, sizeof i); + memset(&info, 0, sizeof info); + + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = _lws_ref->lws_structs; + info.gid = -1; + info.uid = -1; + //info.ws_ping_pong_interval = 5; + info.user = _lws_ref; + context = lws_create_context(&info); + + if (context == NULL) { + _lws_free_ref(_lws_ref); + _lws_ref = NULL; + ERR_EXPLAIN("Unable to create lws context"); + ERR_FAIL_V(FAILED); + } + + char abuf[1024]; + char hbuf[1024]; + char pbuf[2048]; + String addr_str = (String)addr; + strncpy(abuf, addr_str.ascii().get_data(), 1024); + strncpy(hbuf, p_host.utf8().get_data(), 1024); + strncpy(pbuf, p_path.utf8().get_data(), 2048); + + i.context = context; + i.protocol = _lws_ref->lws_names; + i.address = abuf; + i.host = hbuf; + i.path = pbuf; + i.port = p_port; + i.ssl_connection = p_ssl; + + lws_client_connect_via_info(&i); + return OK; +}; + +void LWSClient::poll() { + + _lws_poll(); +} + +int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + + Ref<LWSPeer> peer = static_cast<Ref<LWSPeer> >(_peer); + LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; + + switch (reason) { + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + peer->set_wsi(wsi); + peer_data->peer_id = 0; + peer_data->in_size = 0; + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(16); + peer_data->rbr.resize(16); + peer_data->force_close = false; + _on_connect(lws_get_protocol(wsi)->name); + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + _on_error(); + destroy_context(); + return -1; // we should close the connection (would probably happen anyway) + + case LWS_CALLBACK_CLOSED: + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(0); + peer_data->rbr.resize(0); + peer->close(); + destroy_context(); + _on_disconnect(); + return 0; // we can end here + + case LWS_CALLBACK_CLIENT_RECEIVE: + peer->read_wsi(in, len); + if (peer->get_available_packet_count() > 0) + _on_peer_packet(); + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (peer_data->force_close) + return -1; + + peer->write_wsi(); + break; + + default: + break; + } + + return 0; +} + +Ref<WebSocketPeer> LWSClient::get_peer(int p_peer_id) const { + + return _peer; +} + +NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() const { + + if (context == NULL) + return CONNECTION_DISCONNECTED; + + if (_peer->is_connected_to_host()) + return CONNECTION_CONNECTED; + + return CONNECTION_CONNECTING; +} + +void LWSClient::disconnect_from_host() { + + if (context == NULL) + return; + + _peer->close(); + destroy_context(); +}; + +IP_Address LWSClient::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t LWSClient::get_connected_port() const { + + return 1025; +}; + +LWSClient::LWSClient() { + context = NULL; + _lws_ref = NULL; + _peer = Ref<LWSPeer>(memnew(LWSPeer)); +}; + +LWSClient::~LWSClient() { + + invalidate_lws_ref(); // We do not want any more callback + disconnect_from_host(); + _peer = Ref<LWSPeer>(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h new file mode 100644 index 0000000000..2e082175df --- /dev/null +++ b/modules/websocket/lws_client.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* lws_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef LWSCLIENT_H +#define LWSCLIENT_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "lws_helper.h" +#include "lws_peer.h" +#include "websocket_client.h" + +class LWSClient : public WebSocketClient { + + GDCIIMPL(LWSClient, WebSocketClient); + + LWS_HELPER(LWSClient); + +public: + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Ref<WebSocketPeer> get_peer(int p_peer_id) const; + void disconnect_from_host(); + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + virtual ConnectionStatus get_connection_status() const; + virtual void poll(); + + LWSClient(); + ~LWSClient(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LWSCLIENT_H diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h new file mode 100644 index 0000000000..ac0c340aa9 --- /dev/null +++ b/modules/websocket/lws_helper.h @@ -0,0 +1,214 @@ +/*************************************************************************/ +/* lws_helper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef LWS_HELPER_H +#define LWS_HELPER_H + +#include "core/io/stream_peer.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/ring_buffer.h" +#include "lws_peer.h" + +struct _LWSRef { + bool free_context; + bool is_polling; + bool is_valid; + bool is_destroying; + void *obj; + struct lws_protocols *lws_structs; + char *lws_names; +}; + +static _LWSRef *_lws_create_ref(void *obj) { + + _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); + out->is_destroying = false; + out->free_context = false; + out->is_polling = false; + out->obj = obj; + out->is_valid = true; + out->lws_structs = NULL; + out->lws_names = NULL; + return out; +} + +static void _lws_free_ref(_LWSRef *ref) { + // Free strings and structs + memfree(ref->lws_structs); + memfree(ref->lws_names); + // Free ref + memfree(ref); +} + +static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { + if (context == NULL || ref->is_destroying) + return false; + + if (ref->is_polling) { + ref->free_context = true; + return false; + } + + ref->is_destroying = true; + lws_context_destroy(context); + _lws_free_ref(ref); + return true; +} + +static bool _lws_poll(struct lws_context *context, _LWSRef *ref) { + + ERR_FAIL_COND_V(context == NULL, false); + ERR_FAIL_COND_V(ref == NULL, false); + + ref->is_polling = true; + lws_service(context, 0); + ref->is_polling = false; + + if (!ref->free_context) + return false; // Nothing to do + + bool is_valid = ref->is_valid; // Might have been destroyed by poll + + _lws_destroy(context, ref); // Will destroy context and ref + + return is_valid; // If the object should NULL its context and ref +} + +/* + * prepare the protocol_structs to be fed to context + * also prepare the protocol string used by the client + */ +static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { + /* the input strings might go away after this call, + * we need to copy them. Will clear them when + * detroying the context */ + int i; + int len = p_names.size(); + size_t data_size = sizeof(struct LWSPeer::PeerData); + PoolVector<String>::Read pnr = p_names.read(); + + /* + * This is a reference connecting the object with lws + * keep track of status, mallocs, etc. + * Must survive as long the context + * Must be freed manually when context creation fails. + */ + _LWSRef *ref = _lws_create_ref(p_obj); + + /* LWS protocol structs */ + ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); + + CharString strings = p_names.join(",").ascii(); + int str_len = strings.length(); + + /* Joined string of protocols, double the size: comma separated first, NULL separated last */ + ref->lws_names = (char *)memalloc((str_len + 1) * 2); /* plus the terminator */ + + char *names_ptr = ref->lws_names; + struct lws_protocols *structs_ptr = ref->lws_structs; + + copymem(names_ptr, strings.get_data(), str_len); + names_ptr[str_len] = '\0'; /* NULL terminator */ + /* NULL terminated strings to be used in protocol structs */ + copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); + names_ptr[(str_len * 2) + 1] = '\0'; /* NULL terminator */ + int pos = str_len + 1; + + /* the first protocol is always http-only */ + structs_ptr[0].name = "http-only"; + structs_ptr[0].callback = p_callback; + structs_ptr[0].per_session_data_size = data_size; + structs_ptr[0].rx_buffer_size = 0; + /* add user defined protocols */ + for (i = 0; i < len; i++) { + structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; + structs_ptr[i + 1].callback = p_callback; + structs_ptr[i + 1].per_session_data_size = data_size; + structs_ptr[i + 1].rx_buffer_size = 0; + pos += pnr[i].ascii().length() + 1; + names_ptr[pos - 1] = '\0'; + } + /* add protocols terminator */ + structs_ptr[len + 1].name = NULL; + structs_ptr[len + 1].callback = NULL; + structs_ptr[len + 1].per_session_data_size = 0; + structs_ptr[len + 1].rx_buffer_size = 0; + + *r_lws_ref = ref; +} + +/* clang-format off */ +#define LWS_HELPER(CNAME) \ +protected: \ + struct _LWSRef *_lws_ref; \ + struct lws_context *context; \ + \ + static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \ + \ + if (wsi == NULL) { \ + return 0; \ + } \ + \ + struct _LWSRef *ref = (struct _LWSRef *)lws_context_user(lws_get_context(wsi)); \ + if (!ref->is_valid) \ + return 0; \ + CNAME *helper = (CNAME *)ref->obj; \ + return helper->_handle_cb(wsi, reason, user, in, len); \ + } \ + \ + void invalidate_lws_ref() { \ + if (_lws_ref != NULL) \ + _lws_ref->is_valid = false; \ + } \ + \ + void destroy_context() { \ + if (_lws_destroy(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + } \ + } \ + \ +public: \ + virtual int _handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); \ + \ + void _lws_poll() { \ + ERR_FAIL_COND(context == NULL); \ + \ + if (::_lws_poll(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + } \ + } \ + \ +protected: + + /* clang-format on */ + +#endif // LWS_HELPER_H diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp new file mode 100644 index 0000000000..fdaa79f9d4 --- /dev/null +++ b/modules/websocket/lws_peer.cpp @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* lws_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef JAVASCRIPT_ENABLED + +#include "lws_peer.h" +#include "core/io/ip.h" + +void LWSPeer::set_wsi(struct lws *p_wsi) { + wsi = p_wsi; +}; + +void LWSPeer::set_write_mode(WriteMode p_mode) { + write_mode = p_mode; +} + +LWSPeer::WriteMode LWSPeer::get_write_mode() const { + return write_mode; +} + +Error LWSPeer::read_wsi(void *in, size_t len) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); + uint32_t size = peer_data->in_size; + uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + + if (peer_data->rbr.space_left() < len + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL_V(FAILED); + } + + copymem(&(peer_data->input_buffer[size]), in, len); + size += len; + + peer_data->in_size = size; + if (lws_is_final_fragment(wsi)) { + peer_data->rbr.write((uint8_t *)&size, 4); + peer_data->rbr.write((uint8_t *)&is_string, 1); + peer_data->rbr.write(peer_data->input_buffer, size); + peer_data->in_count++; + peer_data->in_size = 0; + } + + return OK; +} + +Error LWSPeer::write_wsi() { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); + PoolVector<uint8_t> tmp; + int left = peer_data->rbw.data_left(); + uint32_t to_write = 0; + + if (left == 0 || peer_data->out_count == 0) + return OK; + + peer_data->rbw.read((uint8_t *)&to_write, 4); + peer_data->out_count--; + + if (left < to_write) { + peer_data->rbw.advance_read(left); + return FAILED; + } + + tmp.resize(LWS_PRE + to_write); + peer_data->rbw.read(&(tmp.write()[LWS_PRE]), to_write); + lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode); + tmp.resize(0); + + if (peer_data->out_count > 0) + lws_callback_on_writable(wsi); // we want to write more! + + return OK; +} + +Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); + peer_data->rbw.write((uint8_t *)&p_buffer_size, 4); + peer_data->rbw.write(p_buffer, MIN(p_buffer_size, peer_data->rbw.space_left())); + peer_data->out_count++; + + lws_callback_on_writable(wsi); // notify that we want to write + return OK; +}; + +Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); + + if (peer_data->in_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + peer_data->rbr.read((uint8_t *)&to_read, 4); + peer_data->in_count--; + left = peer_data->rbr.data_left(); + + if (left < to_read + 1) { + peer_data->rbr.advance_read(left); + return FAILED; + } + + peer_data->rbr.read(&is_string, 1); + peer_data->rbr.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + _was_string = is_string; + + return OK; +}; + +int LWSPeer::get_available_packet_count() const { + + if (!is_connected_to_host()) + return 0; + + return ((PeerData *)lws_wsi_user(wsi))->in_count; +}; + +bool LWSPeer::was_string_packet() const { + + return _was_string; +}; + +bool LWSPeer::is_connected_to_host() const { + + return wsi != NULL; +}; + +void LWSPeer::close() { + if (wsi != NULL) { + struct lws *tmp = wsi; + PeerData *data = ((PeerData *)lws_wsi_user(wsi)); + data->force_close = true; + wsi = NULL; + lws_callback_on_writable(tmp); // notify that we want to disconnect + } +}; + +IP_Address LWSPeer::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t LWSPeer::get_connected_port() const { + + return 1025; +}; + +LWSPeer::LWSPeer() { + wsi = NULL; + _was_string = false; + write_mode = WRITE_MODE_BINARY; +}; + +LWSPeer::~LWSPeer() { + + close(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h new file mode 100644 index 0000000000..0a62b65d24 --- /dev/null +++ b/modules/websocket/lws_peer.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* lws_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef LWSPEER_H +#define LWSPEER_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "libwebsockets.h" +#include "lws_config.h" +#include "websocket_peer.h" + +class LWSPeer : public WebSocketPeer { + + GDCIIMPL(LWSPeer, WebSocketPeer); + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type + }; + + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + struct lws *wsi; + WriteMode write_mode; + bool _was_string; + +public: + struct PeerData { + uint32_t peer_id; + bool force_close; + RingBuffer<uint8_t> rbw; + RingBuffer<uint8_t> rbr; + mutable uint8_t input_buffer[PACKET_BUFFER_SIZE]; + uint32_t in_size; + int in_count; + int out_count; + }; + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + + virtual void close(); + virtual bool is_connected_to_host() const; + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual WriteMode get_write_mode() const; + virtual void set_write_mode(WriteMode p_mode); + virtual bool was_string_packet() const; + + void set_wsi(struct lws *wsi); + Error read_wsi(void *in, size_t len); + Error write_wsi(); + + LWSPeer(); + ~LWSPeer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LSWPEER_H diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp new file mode 100644 index 0000000000..8a47ba557d --- /dev/null +++ b/modules/websocket/lws_server.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* lws_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef JAVASCRIPT_ENABLED + +#include "lws_server.h" +#include "core/os/os.h" + +Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { + + ERR_FAIL_COND_V(context != NULL, FAILED); + + _is_multiplayer = gd_mp_api; + + struct lws_context_creation_info info; + memset(&info, 0, sizeof info); + + if (p_protocols.size() == 0) // default to binary protocol + p_protocols.append(String("binary")); + + // Prepare lws protocol structs + _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref); + + info.port = p_port; + info.user = _lws_ref; + info.protocols = _lws_ref->lws_structs; + info.gid = -1; + info.uid = -1; + //info.ws_ping_pong_interval = 5; + + context = lws_create_context(&info); + + if (context == NULL) { + _lws_free_ref(_lws_ref); + _lws_ref = NULL; + ERR_EXPLAIN("Unable to create LWS context"); + ERR_FAIL_V(FAILED); + } + + return OK; +} + +bool LWSServer::is_listening() const { + return context != NULL; +} + +int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + + LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; + + switch (reason) { + case LWS_CALLBACK_HTTP: + // no http for now + // closing immediately returning -1; + return -1; + + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + // check header here? + break; + + case LWS_CALLBACK_ESTABLISHED: { + int32_t id = _gen_unique_id(); + + Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); + peer->set_wsi(wsi); + _peer_map[id] = peer; + + peer_data->peer_id = id; + peer_data->in_size = 0; + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(16); + peer_data->rbr.resize(16); + peer_data->force_close = false; + + _on_connect(id, lws_get_protocol(wsi)->name); + break; + } + + case LWS_CALLBACK_CLOSED: { + if (peer_data == NULL) + return 0; + int32_t id = peer_data->peer_id; + if (_peer_map.has(id)) { + _peer_map[id]->close(); + _peer_map.erase(id); + } + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbr.resize(0); + peer_data->rbw.resize(0); + _on_disconnect(id); + return 0; // we can end here + } + + case LWS_CALLBACK_RECEIVE: { + int32_t id = peer_data->peer_id; + if (_peer_map.has(id)) { + static_cast<Ref<LWSPeer> >(_peer_map[id])->read_wsi(in, len); + if (_peer_map[id]->get_available_packet_count() > 0) + _on_peer_packet(id); + } + break; + } + + case LWS_CALLBACK_SERVER_WRITEABLE: { + if (peer_data->force_close) + return -1; + + int id = peer_data->peer_id; + if (_peer_map.has(id)) + static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi(); + break; + } + + default: + break; + } + + return 0; +} + +void LWSServer::stop() { + if (context == NULL) + return; + + _peer_map.clear(); + destroy_context(); + context = NULL; +} + +bool LWSServer::has_peer(int p_id) const { + return _peer_map.has(p_id); +} + +Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const { + ERR_FAIL_COND_V(!has_peer(p_id), NULL); + return _peer_map[p_id]; +} + +LWSServer::LWSServer() { + context = NULL; + _lws_ref = NULL; +} + +LWSServer::~LWSServer() { + invalidate_lws_ref(); // we do not want any more callbacks + stop(); +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h new file mode 100644 index 0000000000..5f7ac4850a --- /dev/null +++ b/modules/websocket/lws_server.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* lws_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef LWSSERVER_H +#define LWSSERVER_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/reference.h" +#include "lws_helper.h" +#include "lws_peer.h" +#include "websocket_server.h" + +class LWSServer : public WebSocketServer { + + GDCIIMPL(LWSServer, WebSocketServer); + + LWS_HELPER(LWSServer); + +private: + Map<int, Ref<LWSPeer> > peer_map; + +public: + Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void stop(); + bool is_listening() const; + bool has_peer(int p_id) const; + Ref<WebSocketPeer> get_peer(int p_id) const; + virtual void poll() { _lws_poll(); } + + LWSServer(); + ~LWSServer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LWSSERVER_H diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp new file mode 100644 index 0000000000..39d03ff1f0 --- /dev/null +++ b/modules/websocket/register_types.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "register_types.h" +#include "error_macros.h" +#ifdef JAVASCRIPT_ENABLED +#include "emscripten.h" +#include "emws_client.h" +#include "emws_peer.h" +#include "emws_server.h" +#else +#include "lws_client.h" +#include "lws_peer.h" +#include "lws_server.h" +#endif + +void register_websocket_types() { +#ifdef JAVASCRIPT_ENABLED + EM_ASM({ + var IDHandler = {}; + IDHandler["ids"] = {}; + IDHandler["has"] = function(id) { + return IDHandler.ids.hasOwnProperty(id); + }; + IDHandler["add"] = function(obj) { + var id = crypto.getRandomValues(new Int32Array(32))[0]; + IDHandler.ids[id] = obj; + return id; + }; + IDHandler["get"] = function(id) { + return IDHandler.ids[id]; + }; + IDHandler["remove"] = function(id) { + delete IDHandler.ids[id]; + }; + Module["IDHandler"] = IDHandler; + }); + EMWSPeer::make_default(); + EMWSClient::make_default(); + EMWSServer::make_default(); +#else + LWSPeer::make_default(); + LWSClient::make_default(); + LWSServer::make_default(); +#endif + + ClassDB::register_virtual_class<WebSocketMultiplayerPeer>(); + ClassDB::register_custom_instance_class<WebSocketServer>(); + ClassDB::register_custom_instance_class<WebSocketClient>(); + ClassDB::register_custom_instance_class<WebSocketPeer>(); +} + +void unregister_websocket_types() {} diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h new file mode 100644 index 0000000000..010d88789b --- /dev/null +++ b/modules/websocket/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +void register_websocket_types(); +void unregister_websocket_types(); diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp new file mode 100644 index 0000000000..f92a386988 --- /dev/null +++ b/modules/websocket/websocket_client.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* websocket_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "websocket_client.h" + +GDCINULL(WebSocketClient); + +WebSocketClient::WebSocketClient() { +} + +WebSocketClient::~WebSocketClient() { +} + +Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) { + _is_multiplayer = gd_mp_api; + + String host = p_url; + String path = "/"; + int p_len = -1; + int port = 80; + bool ssl = false; + if (host.begins_with("wss://")) { + ssl = true; // we should implement this + host = host.substr(6, host.length() - 6); + port = 443; + } else { + ssl = false; + if (host.begins_with("ws://")) + host = host.substr(5, host.length() - 5); + } + + // Path + p_len = host.find("/"); + if (p_len != -1) { + path = host.substr(p_len, host.length() - p_len); + host = host.substr(0, p_len); + } + + // Port + p_len = host.find_last(":"); + if (p_len != -1 && p_len == host.find(":")) { + port = host.substr(p_len, host.length() - p_len).to_int(); + host = host.substr(0, p_len); + } + + return connect_to_host(host, path, port, ssl, p_protocols); +} + +bool WebSocketClient::is_server() const { + + return false; +} + +void WebSocketClient::_on_peer_packet() { + + if (_is_multiplayer) { + _process_multiplayer(get_peer(1), 1); + } else { + emit_signal("data_received"); + } +} + +void WebSocketClient::_on_connect(String p_protocol) { + + if (_is_multiplayer) { + // need to wait for ID confirmation... + } else { + emit_signal("connection_established", p_protocol); + } +} + +void WebSocketClient::_on_disconnect() { + + if (_is_multiplayer) { + emit_signal("connection_failed"); + } else { + emit_signal("connection_closed"); + } +} + +void WebSocketClient::_on_error() { + + if (_is_multiplayer) { + emit_signal("connection_failed"); + } else { + emit_signal("connection_error"); + } +} + +void WebSocketClient::_bind_methods() { + ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + + ADD_SIGNAL(MethodInfo("data_received")); + ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); + ADD_SIGNAL(MethodInfo("connection_closed")); + ADD_SIGNAL(MethodInfo("connection_error")); +} diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h new file mode 100644 index 0000000000..0e87825222 --- /dev/null +++ b/modules/websocket/websocket_client.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* websocket_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef WEBSOCKET_CLIENT_H +#define WEBSOCKET_CLIENT_H + +#include "core/error_list.h" +#include "websocket_multiplayer.h" +#include "websocket_peer.h" + +class WebSocketClient : public WebSocketMultiplayerPeer { + + GDCLASS(WebSocketClient, WebSocketMultiplayerPeer); + GDCICLASS(WebSocketClient); + +protected: + Ref<WebSocketPeer> _peer; + + static void _bind_methods(); + +public: + Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + + virtual void poll() = 0; + virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; + virtual void disconnect_from_host() = 0; + virtual IP_Address get_connected_host() const = 0; + virtual uint16_t get_connected_port() const = 0; + + virtual bool is_server() const; + virtual ConnectionStatus get_connection_status() const = 0; + + void _on_peer_packet(); + void _on_connect(String p_protocol); + void _on_disconnect(); + void _on_error(); + + WebSocketClient(); + ~WebSocketClient(); +}; + +#endif // WEBSOCKET_CLIENT_H diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h new file mode 100644 index 0000000000..b5c2159806 --- /dev/null +++ b/modules/websocket/websocket_macros.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* websocket_macros.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef WEBSOCKETMACTOS_H +#define WEBSOCKETMACTOS_H + +/* clang-format off */ +#define GDCICLASS(CNAME) \ +public:\ + static CNAME *(*_create)();\ +\ + static Ref<CNAME > create_ref() {\ +\ + if (!_create)\ + return Ref<CNAME >();\ + return Ref<CNAME >(_create());\ + }\ +\ + static CNAME *create() {\ +\ + if (!_create)\ + return NULL;\ + return _create();\ + }\ +protected:\ + +#define GDCINULL(CNAME) \ +CNAME *(*CNAME::_create)() = NULL; + +#define GDCIIMPL(IMPNAME, CNAME) \ +public:\ + static CNAME *_create() { return memnew(IMPNAME); }\ + static void make_default() { CNAME::_create = IMPNAME::_create; }\ +protected:\ +/* clang-format on */ + +#endif // WEBSOCKETMACTOS_H diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp new file mode 100644 index 0000000000..8cd4dff38b --- /dev/null +++ b/modules/websocket/websocket_multiplayer.cpp @@ -0,0 +1,361 @@ +/*************************************************************************/ +/* websocket_multiplayer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "websocket_multiplayer.h" +#include "core/os/os.h" + +WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() { + + _is_multiplayer = false; + _peer_id = 0; + _target_peer = 0; + _refusing = false; + + _current_packet.source = 0; + _current_packet.destination = 0; + _current_packet.size = 0; + _current_packet.data = NULL; +} + +WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() { + + _clear(); +} + +int WebSocketMultiplayerPeer::_gen_unique_id() const { + + uint32_t hash = 0; + + while (hash == 0 || hash == 1) { + + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_ticks_usec()); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_unix_time(), hash); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_data_path().hash64(), hash); + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)this), hash); //rely on aslr heap + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack + hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion + } + + return hash; +} +void WebSocketMultiplayerPeer::_clear() { + + _peer_map.clear(); + if (_current_packet.data != NULL) + memfree(_current_packet.data); + + for (List<Packet>::Element *E = _incoming_packets.front(); E; E = E->next()) { + memfree(E->get().data); + E->get().data = NULL; + } + + _incoming_packets.clear(); +} + +void WebSocketMultiplayerPeer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer); + + ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source"))); +} + +// +// PacketPeer +// +int WebSocketMultiplayerPeer::get_available_packet_count() const { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + return _incoming_packets.size(); +} + +int WebSocketMultiplayerPeer::get_max_packet_size() const { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + return MAX_PACKET_SIZE; +} + +Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + r_buffer_size = 0; + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + if (_current_packet.data != NULL) { + memfree(_current_packet.data); + _current_packet.data = NULL; + } + + _current_packet = _incoming_packets.front()->get(); + _incoming_packets.pop_front(); + + *r_buffer = _current_packet.data; + r_buffer_size = _current_packet.size; + + return OK; +} + +Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size); + + if (is_server()) { + return _server_relay(1, _target_peer, &(buffer.read()[0]), buffer.size()); + } else { + return get_peer(1)->put_packet(&(buffer.read()[0]), buffer.size()); + } +} + +// +// NetworkedMultiplayerPeer +// +void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { + + // Websocket uses TCP, reliable +} + +NetworkedMultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const { + + // Websocket uses TCP, reliable + return TRANSFER_MODE_RELIABLE; +} + +void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { + + _target_peer = p_target_peer; +} + +int WebSocketMultiplayerPeer::get_packet_peer() const { + + ERR_FAIL_COND_V(!_is_multiplayer, 1); + ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1); + + return _incoming_packets.front()->get().source; +} + +int WebSocketMultiplayerPeer::get_unique_id() const { + + return _peer_id; +} + +void WebSocketMultiplayerPeer::set_refuse_new_connections(bool p_enable) { + + _refusing = p_enable; +} + +bool WebSocketMultiplayerPeer::is_refusing_new_connections() const { + + return _refusing; +} + +void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) { + + ERR_FAIL_COND(!p_peer.is_valid()); + ERR_FAIL_COND(!p_peer->is_connected_to_host()); + + PoolVector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4); + p_peer->put_packet(&(message.read()[0]), message.size()); +} + +PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) { + + PoolVector<uint8_t> out; + out.resize(PROTO_SIZE + p_data_size); + + PoolVector<uint8_t>::Write w = out.write(); + copymem(&w[0], &p_type, 1); + copymem(&w[1], &p_from, 4); + copymem(&w[5], &p_to, 4); + copymem(&w[PROTO_SIZE], p_data, p_data_size); + + return out; +} + +void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) { + + // First of all, confirm the ID! + _send_sys(get_peer(p_peer_id), SYS_ID, p_peer_id); + + // Then send the server peer (which will trigger connection_succeded in client) + _send_sys(get_peer(p_peer_id), SYS_ADD, 1); + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + uint32_t id = E->key(); + if (p_peer_id == id) + continue; // Skip the newwly added peer (already confirmed) + + // Send new peer to others + _send_sys(get_peer(id), SYS_ADD, p_peer_id); + // Send others to new peer + _send_sys(get_peer(p_peer_id), SYS_ADD, id); + } +} + +void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) { + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + uint32_t id = E->key(); + if (p_peer_id != id) + _send_sys(get_peer(id), SYS_DEL, p_peer_id); + } +} + +void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) { + Packet packet; + packet.data = (uint8_t *)memalloc(p_data_size); + packet.size = p_data_size; + packet.source = p_source; + packet.destination = p_dest; + copymem(packet.data, &p_data[PROTO_SIZE], p_data_size); + _incoming_packets.push_back(packet); + emit_signal("peer_packet", p_source); +} + +Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) { + if (p_to == 1) { + + return OK; // Will not send to self + + } else if (p_to == 0) { + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + if (E->key() != p_from) + E->get()->put_packet(p_buffer, p_buffer_size); + } + return OK; // Sent to all but sender + + } else if (p_to < 0) { + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + if (E->key() != p_from && E->key() != -p_to) + E->get()->put_packet(p_buffer, p_buffer_size); + } + return OK; // Sent to all but sender and excluded + + } else { + + ERR_FAIL_COND_V(p_to == p_from, FAILED); + + return get_peer(p_to)->put_packet(p_buffer, p_buffer_size); // Sending to specific peer + } +} + +void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) { + + ERR_FAIL_COND(!p_peer.is_valid()); + + const uint8_t *in_buffer; + int size = 0; + int data_size = 0; + + Error err = p_peer->get_packet(&in_buffer, size); + + ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(size < PROTO_SIZE); + + data_size = size - PROTO_SIZE; + + uint8_t type = 0; + int32_t from = 0; + int32_t to = 0; + copymem(&type, in_buffer, 1); + copymem(&from, &in_buffer[1], 4); + copymem(&to, &in_buffer[5], 4); + + if (is_server()) { // Server can resend + + ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages + ERR_FAIL_COND(from != p_peer_id); // Someone is cheating + + _server_relay(from, to, in_buffer, size); // Relay if needed + + if (to == 1) { // This is for the server + + _store_pkt(from, to, in_buffer, data_size); + + } else if (to == 0) { + + // Broadcast, for us too + _store_pkt(from, to, in_buffer, data_size); + + } else if (to < 0) { + + // All but one, for us if not excluded + if (_peer_id != -p_peer_id) + _store_pkt(from, to, in_buffer, data_size); + + } else { + + // Send to specific peer + ERR_FAIL_COND(!_peer_map.has(to)); + get_peer(to)->put_packet(in_buffer, size); + } + + } else { + + if (type == SYS_NONE) { // Payload message + + _store_pkt(from, to, in_buffer, data_size); + return; + } + + // System message + ERR_FAIL_COND(data_size < 4); + int id = 0; + copymem(&id, &in_buffer[PROTO_SIZE], 4); + + switch (type) { + + case SYS_ADD: // Add peer + _peer_map[id] = Ref<WebSocketPeer>(); + emit_signal("peer_connected", id); + if (id == 1) // We just connected to the server + emit_signal("connection_succeeded"); + break; + + case SYS_DEL: // Remove peer + _peer_map.erase(id); + emit_signal("peer_disconnected", id); + break; + case SYS_ID: // Helo, server assigned ID + _peer_id = id; + break; + default: + ERR_EXPLAIN("Invalid multiplayer message"); + ERR_FAIL(); + break; + } + } +} diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h new file mode 100644 index 0000000000..e8e795e97f --- /dev/null +++ b/modules/websocket/websocket_multiplayer.h @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* websocket_multiplayer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef WEBSOCKET_MULTIPLAYER_PEER_H +#define WEBSOCKET_MULTIPLAYER_PEER_H + +#include "core/error_list.h" +#include "core/io/networked_multiplayer_peer.h" +#include "core/list.h" +#include "websocket_peer.h" + +class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer { + + GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer); + +private: + PoolVector<uint8_t> _make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size); + void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size); + Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size); + +protected: + enum { + SYS_NONE = 0, + SYS_ADD = 1, + SYS_DEL = 2, + SYS_ID = 3, + + PROTO_SIZE = 9, + SYS_PACKET_SIZE = 13, + MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer + }; + + struct Packet { + int source; + int destination; + uint8_t *data; + uint32_t size; + }; + + List<Packet> _incoming_packets; + Map<int, Ref<WebSocketPeer> > _peer_map; + Packet _current_packet; + + bool _is_multiplayer; + int _target_peer; + int _peer_id; + int _refusing; + + static void _bind_methods(); + + void _send_add(int32_t p_peer_id); + void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id); + void _send_del(int32_t p_peer_id); + int _gen_unique_id() const; + +public: + /* NetworkedMultiplayerPeer */ + void set_transfer_mode(TransferMode p_mode); + TransferMode get_transfer_mode() const; + void set_target_peer(int p_peer_id); + int get_packet_peer() const; + int get_unique_id() const; + virtual bool is_server() const = 0; + void set_refuse_new_connections(bool p_enable); + bool is_refusing_new_connections() const; + virtual ConnectionStatus get_connection_status() const = 0; + + /* PacketPeer */ + virtual int get_available_packet_count() const; + virtual int get_max_packet_size() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + /* WebSocketPeer */ + virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0; + + void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id); + void _clear(); + + WebSocketMultiplayerPeer(); + ~WebSocketMultiplayerPeer(); +}; + +#endif // WEBSOCKET_MULTIPLAYER_PEER_H diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp new file mode 100644 index 0000000000..a6fbb4481b --- /dev/null +++ b/modules/websocket/websocket_peer.cpp @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* websocket_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "websocket_peer.h" + +GDCINULL(WebSocketPeer); + +WebSocketPeer::WebSocketPeer() { +} + +WebSocketPeer::~WebSocketPeer() { +} + +void WebSocketPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_write_mode"), &WebSocketPeer::get_write_mode); + ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode); + ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host); + ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet); + ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close); + + BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); + BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); +} diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h new file mode 100644 index 0000000000..f4d8ce3e38 --- /dev/null +++ b/modules/websocket/websocket_peer.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* websocket_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef WEBSOCKETPEER_H +#define WEBSOCKETPEER_H + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "websocket_macros.h" + +class WebSocketPeer : public PacketPeer { + + GDCLASS(WebSocketPeer, PacketPeer); + GDCICLASS(WebSocketPeer); + +public: + enum WriteMode { + WRITE_MODE_TEXT, + WRITE_MODE_BINARY, + }; + +protected: + static void _bind_methods(); + +public: + virtual int get_available_packet_count() const = 0; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; + virtual int get_max_packet_size() const = 0; + + virtual WriteMode get_write_mode() const = 0; + virtual void set_write_mode(WriteMode p_mode) = 0; + + virtual void close() = 0; + + virtual bool is_connected_to_host() const = 0; + virtual IP_Address get_connected_host() const = 0; + virtual uint16_t get_connected_port() const = 0; + virtual bool was_string_packet() const = 0; + + WebSocketPeer(); + ~WebSocketPeer(); +}; + +VARIANT_ENUM_CAST(WebSocketPeer::WriteMode); +#endif // WEBSOCKETPEER_H diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp new file mode 100644 index 0000000000..ba77019f55 --- /dev/null +++ b/modules/websocket/websocket_server.cpp @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* websocket_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "websocket_server.h" + +GDCINULL(WebSocketServer); + +WebSocketServer::WebSocketServer() { + _peer_id = 1; +} + +WebSocketServer::~WebSocketServer() { +} + +void WebSocketServer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); + ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); + ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); + + ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); + ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); +} + +NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { + if (is_listening()) + return CONNECTION_CONNECTED; + + return CONNECTION_DISCONNECTED; +}; + +bool WebSocketServer::is_server() const { + + return true; +} + +void WebSocketServer::_on_peer_packet(int32_t p_peer_id) { + + if (_is_multiplayer) { + _process_multiplayer(get_peer(p_peer_id), p_peer_id); + } else { + emit_signal("data_received", p_peer_id); + } +} + +void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) { + + if (_is_multiplayer) { + // Send add to clients + _send_add(p_peer_id); + emit_signal("peer_connected", p_peer_id); + } else { + emit_signal("client_connected", p_peer_id, p_protocol); + } +} + +void WebSocketServer::_on_disconnect(int32_t p_peer_id) { + + if (_is_multiplayer) { + // Send delete to clients + _send_del(p_peer_id); + emit_signal("peer_disconnected", p_peer_id); + } else { + emit_signal("client_disconnected", p_peer_id); + } +} diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h new file mode 100644 index 0000000000..db188811fd --- /dev/null +++ b/modules/websocket/websocket_server.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* websocket_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef WEBSOCKET_H +#define WEBSOCKET_H + +#include "core/reference.h" +#include "websocket_multiplayer.h" +#include "websocket_peer.h" + +class WebSocketServer : public WebSocketMultiplayerPeer { + + GDCLASS(WebSocketServer, WebSocketMultiplayerPeer); + GDCICLASS(WebSocketServer); + +protected: + static void _bind_methods(); + +public: + virtual void poll() = 0; + virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0; + virtual void stop() = 0; + virtual bool is_listening() const = 0; + virtual bool has_peer(int p_id) const = 0; + virtual Ref<WebSocketPeer> get_peer(int p_id) const = 0; + virtual bool is_server() const; + ConnectionStatus get_connection_status() const; + + void _on_peer_packet(int32_t p_peer_id); + void _on_connect(int32_t p_peer_id, String p_protocol); + void _on_disconnect(int32_t p_peer_id); + + WebSocketServer(); + ~WebSocketServer(); +}; + +#endif // WEBSOCKET_H diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 8c7a904bca..74d6536343 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -28,6 +28,11 @@ def get_flags(): return [ ('tools', False), ('module_theora_enabled', False), + # Disabling the OpenSSL module noticeably reduces file size. + # The module has little use due to the limited networking functionality + # in this platform. For the available networking methods, the browser + # manages TLS. + ('module_openssl_enabled', False), ] diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 3590c30579..e226ab6332 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -430,16 +430,11 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, // can't fulfil fullscreen request due to browser security video_mode.fullscreen = false; /* clang-format off */ - bool resize_canvas_on_start = EM_ASM_INT_V( - return Module.resizeCanvasOnStart; - ); - /* clang-format on */ - if (resize_canvas_on_start) { + if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { + /* clang-format on */ set_window_size(Size2(video_mode.width, video_mode.height)); } else { - Size2 canvas_size = get_window_size(); - video_mode.width = canvas_size.width; - video_mode.height = canvas_size.height; + set_window_size(get_window_size()); } char locale_ptr[16]; diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 029e3d808c..5efe2d0b22 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -10,6 +10,7 @@ def make_debug(target, source, env): os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil %s -o %s.dSYM' % (target[0], target[0])) else: os.system('dsymutil %s -o %s.dSYM' % (target[0], target[0])) + os.system('strip -u -r %s' % (target[0])) files = [ 'crash_handler_osx.mm', @@ -23,6 +24,6 @@ files = [ prog = env.add_program('#bin/godot', files) -if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": +if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: env.AddPostAction(prog, make_debug) diff --git a/platform/osx/detect.py b/platform/osx/detect.py index bb601abd40..5f33100e42 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -19,11 +19,12 @@ def can_build(): def get_opts(): - from SCons.Variables import EnumVariable + from SCons.Variables import BoolVariable, EnumVariable return [ ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), ] diff --git a/platform/server/detect.py b/platform/server/detect.py index bc90a38e24..61b56ddefa 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -87,12 +87,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.87 + # We need at least version 2.88 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if bullet_version < "2.87": + if bullet_version < "2.88": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.87")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 604896b0db..8965b80fb7 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -39,5 +39,5 @@ if env['vsproj']: env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): - if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": + if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: env.AddPostAction(prog, make_debug_mingw) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index bd05d5605d..22d04153c8 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -69,6 +69,7 @@ def get_opts(): # Vista support dropped after EOL due to GH-10243 ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), ] diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 5736ae1585..a2a51f10a7 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -588,7 +588,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mb->set_position(Vector2(old_x, old_y)); } - if (uMsg != WM_MOUSEWHEEL) { + if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { if (mb->is_pressed()) { if (++pressrc > 0) @@ -1655,7 +1655,7 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han PRemoveDllDirectory remove_dll_directory = (PRemoveDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "RemoveDllDirectory"); bool has_dll_directory_api = ((add_dll_directory != NULL) && (remove_dll_directory != NULL)); - DLL_DIRECTORY_COOKIE cookie; + DLL_DIRECTORY_COOKIE cookie = NULL; if (p_also_set_library_path && has_dll_directory_api) { cookie = add_dll_directory(path.get_base_dir().c_str()); @@ -1663,7 +1663,7 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han p_library_handle = (void *)LoadLibraryExW(path.c_str(), NULL, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); - if (p_also_set_library_path && has_dll_directory_api) { + if (cookie) { remove_dll_directory(cookie); } @@ -2211,6 +2211,36 @@ String OS_Windows::get_locale() const { return "en"; } +// We need this because GetSystemInfo() is unreliable on WOW64 +// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724381(v=vs.85).aspx +// Taken from MSDN +typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); +LPFN_ISWOW64PROCESS fnIsWow64Process; + +BOOL is_wow64() { + BOOL wow64 = FALSE; + + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) { + wow64 = FALSE; + } + } + + return wow64; +} + +int OS_Windows::get_processor_count() const { + SYSTEM_INFO sysinfo; + if (is_wow64()) + GetNativeSystemInfo(&sysinfo); + else + GetSystemInfo(&sysinfo); + + return sysinfo.dwNumberOfProcessors; +} + OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const { unsigned long azerty[] = { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c24e35e929..7308650695 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -253,6 +253,9 @@ public: virtual String get_executable_path() const; virtual String get_locale() const; + + virtual int get_processor_count() const; + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; virtual void enable_for_stealing_focus(ProcessID pid); diff --git a/platform/x11/SCsub b/platform/x11/SCsub index 38dd2ddd88..b18757337a 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -19,5 +19,5 @@ common_x11 = [ prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11) -if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": +if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: env.AddPostAction(prog, make_debug) diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 1c6bada815..02bd7232c2 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -55,6 +55,7 @@ def get_opts(): BoolVariable('pulseaudio', 'Detect & use pulseaudio', True), BoolVariable('udev', 'Use udev for gamepad connection callbacks', False), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), BoolVariable('touch', 'Enable touch events', True), ] @@ -173,12 +174,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.87 + # We need at least version 2.88 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if bullet_version < "2.87": + if bullet_version < "2.88": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.87")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index e4165e2fe3..eaf72d4dbf 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -217,7 +217,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a XIFreeDeviceInfo(info); - if (!touch.devices.size()) { + if (is_stdout_verbose() && !touch.devices.size()) { fprintf(stderr, "No touch devices found\n"); } } @@ -1862,8 +1862,12 @@ void OS_X11::process_xevents() { e = event; req = &(e.xselectionrequest); - if (req->target == XA_STRING || req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || - req->target == XInternAtom(x11_display, "UTF8_STRING", 0)) { + if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) || + req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "TEXT", 0) || + req->target == XA_STRING || + req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || + req->target == XInternAtom(x11_display, "text/plain", 0)) { CharString clip = OS::get_clipboard().utf8(); XChangeProperty(x11_display, req->requestor, @@ -1876,26 +1880,40 @@ void OS_X11::process_xevents() { respond.xselection.property = req->property; } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { - Atom data[2]; - data[0] = XInternAtom(x11_display, "UTF8_STRING", 0); - data[1] = XA_STRING; - XChangeProperty(x11_display, req->requestor, req->property, req->target, - 8, PropModeReplace, (unsigned char *)&data, - sizeof(data)); + Atom data[7]; + data[0] = XInternAtom(x11_display, "TARGETS", 0); + data[1] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0); + data[3] = XInternAtom(x11_display, "TEXT", 0); + data[4] = XA_STRING; + data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0); + data[6] = XInternAtom(x11_display, "text/plain", 0); + + XChangeProperty(x11_display, + req->requestor, + req->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char *)&data, + sizeof(data) / sizeof(data[0])); respond.xselection.property = req->property; } else { - printf("No String %x\n", - (int)req->target); + char *targetname = XGetAtomName(x11_display, req->target); + printf("No Target '%s'\n", targetname); + if (targetname) + XFree(targetname); respond.xselection.property = None; } + respond.xselection.type = SelectionNotify; respond.xselection.display = req->display; respond.xselection.requestor = req->requestor; respond.xselection.selection = req->selection; respond.xselection.target = req->target; respond.xselection.time = req->time; - XSendEvent(x11_display, req->requestor, 0, 0, &respond); + XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond); XFlush(x11_display); } break; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index f7bc0b73e0..ee62b89227 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -199,7 +199,7 @@ protected: virtual void set_main_loop(MainLoop *p_main_loop); - void _window_changed(XEvent *xevent); + void _window_changed(XEvent *event); bool is_window_maximize_allowed(); diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 6fff7ac0a4..bb914b90fc 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -399,7 +399,7 @@ void Area2D::set_monitoring(bool p_enable) { if (p_enable == monitoring) return; if (locked) { - ERR_EXPLAIN("Function blocked during in/out signal. Use call_deferred(\"set_enable_monitoring\",true/false)"); + ERR_EXPLAIN("Function blocked during in/out signal. Use call_deferred(\"set_monitoring\",true/false)"); } ERR_FAIL_COND(locked); diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index b11deb08fd..fc67d28a29 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -54,7 +54,7 @@ void AudioStreamPlayer2D::_mix_audio() { int buffer_size = mix_buffer.size(); //mix - stream_playback->mix(buffer, 1.0, buffer_size); + stream_playback->mix(buffer, pitch_scale, buffer_size); //write all outputs for (int i = 0; i < output_count; i++) { @@ -279,6 +279,13 @@ float AudioStreamPlayer2D::get_volume_db() const { return volume_db; } +void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer2D::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer2D::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -419,6 +426,9 @@ void AudioStreamPlayer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer2D::set_volume_db); ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer2D::get_volume_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer2D::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer2D::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer2D::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer2D::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer2D::stop); @@ -448,6 +458,7 @@ void AudioStreamPlayer2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance"); @@ -461,6 +472,7 @@ void AudioStreamPlayer2D::_bind_methods() { AudioStreamPlayer2D::AudioStreamPlayer2D() { volume_db = 0; + pitch_scale = 1.0; autoplay = false; setseek = -1; active = false; diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 39bc985f58..9ae8e3a518 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -70,6 +70,7 @@ private: volatile float setplay; float volume_db; + float pitch_scale; bool autoplay; StringName bus; @@ -98,6 +99,9 @@ public: void set_volume_db(float p_volume); float get_volume_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index e7e62a197c..d172da5bd9 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -91,8 +91,8 @@ Transform2D Camera2D::get_camera_transform() { if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint()) { - camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); - camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); + camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT])); + camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT])); } else { if (h_ofs < 0) { @@ -104,8 +104,8 @@ Transform2D Camera2D::get_camera_transform() { if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint()) { - camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); - camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); + camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP])); + camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM])); } else { diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 2c8f509bd3..052a0ac026 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -39,7 +39,7 @@ Rect2 Path2D::_edit_get_rect() const { - if (curve->get_point_count() == 0) + if (!curve.is_valid() || curve->get_point_count() == 0) return Rect2(0, 0, 0, 0); Rect2 aabb = Rect2(curve->get_point_position(0), Vector2(0, 0)); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index c4c9362387..cc99ce5f49 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -244,6 +244,7 @@ void RigidBody2D::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(E->get().in_scene); @@ -266,6 +267,7 @@ void RigidBody2D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(!E->get().in_scene); @@ -291,6 +293,7 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); /*if (obj) { @@ -763,6 +766,14 @@ void RigidBody2D::set_contact_monitor(bool p_enabled) { for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { //clean up mess + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = Object::cast_to<Node>(obj); + + if (node) { + + node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); + } } memdelete(contact_monitor); diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index bf3bc42e4e..d448ef024d 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -57,18 +57,18 @@ void AudioStreamPlayer3D::_mix_audio() { //mix if (output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) { - float pitch_scale = 0.0; + float output_pitch_scale = 0.0; if (output_count) { //used for doppler, not realistic but good enough for (int i = 0; i < output_count; i++) { - pitch_scale += outputs[i].pitch_scale; + output_pitch_scale += outputs[i].pitch_scale; } - pitch_scale /= float(output_count); + output_pitch_scale /= float(output_count); } else { - pitch_scale = 1.0; + output_pitch_scale = 1.0; } - stream_playback->mix(buffer, pitch_scale, buffer_size); + stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size); } //write all outputs @@ -607,6 +607,13 @@ float AudioStreamPlayer3D::get_max_db() const { return max_db; } +void AudioStreamPlayer3D::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer3D::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer3D::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -832,6 +839,9 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_db", "max_db"), &AudioStreamPlayer3D::set_max_db); ClassDB::bind_method(D_METHOD("get_max_db"), &AudioStreamPlayer3D::get_max_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer3D::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer3D::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer3D::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer3D::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer3D::stop); @@ -885,6 +895,7 @@ void AudioStreamPlayer3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,65536,1"), "set_max_distance", "get_max_distance"); @@ -921,6 +932,7 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { unit_size = 1; attenuation_model = ATTENUATION_INVERSE_DISTANCE; max_db = 3; + pitch_scale = 1.0; autoplay = false; setseek = -1; active = false; diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 9a1f369da2..1fcb83cf21 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -106,6 +106,7 @@ private: float unit_db; float unit_size; float max_db; + float pitch_scale; bool autoplay; StringName bus; @@ -153,6 +154,9 @@ public: void set_max_db(float p_boost); float get_max_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 3fd3303a25..e2679870de 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -97,7 +97,7 @@ protected: void _update_camera_mode(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const; + virtual void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 73770a2dd2..07235b3da4 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -373,7 +373,7 @@ String CollisionObject::get_configuration_warning() const { if (warning == String()) { warning += "\n"; } - warning += TTR("This node has no children shapes, so it can't interact with the space.\nConsider adding CollisionShape or CollisionPolygon children nodes to define it's shape."); + warning += TTR("This node has no children shapes, so it can't interact with the space.\nConsider adding CollisionShape or CollisionPolygon children nodes to define its shape."); } return warning; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index bf7952b2dd..25acd6deb0 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -258,6 +258,7 @@ void RigidBody::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(E->get().in_tree); @@ -281,6 +282,7 @@ void RigidBody::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); ERR_FAIL_COND(!E->get().in_tree); @@ -306,6 +308,7 @@ void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); + ERR_FAIL_COND(!contact_monitor); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); ERR_FAIL_COND(!body_in && !E); @@ -719,6 +722,14 @@ void RigidBody::set_contact_monitor(bool p_enabled) { for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { //clean up mess + Object *obj = ObjectDB::get_instance(E->key()); + Node *node = Object::cast_to<Node>(obj); + + if (node) { + + node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); + node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); + } } memdelete(contact_monitor); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index b20bc64d41..9db4a5fb04 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -78,9 +78,6 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { set_blend_time(from, to, time); } - } else if (p_name == SceneStringNames::get_singleton()->autoplay) { - autoplay = p_value; - } else return false; @@ -123,9 +120,6 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { } r_ret = array; - } else if (name == "autoplay") { - r_ret = autoplay; - } else return false; @@ -172,7 +166,6 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { } p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::STRING, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } void AnimationPlayer::advance(float p_time) { diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 000dbb9cbd..49013b160a 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -219,9 +219,9 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("targeting_property", "object", "property", "initial", "initial_val", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_property, DEFVAL(0)); ClassDB::bind_method(D_METHOD("targeting_method", "object", "method", "initial", "initial_method", "final_val", "duration", "trans_type", "ease_type", "delay"), &Tween::targeting_method, DEFVAL(0)); - ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"))); - ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); - ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::STRING, "key"))); + ADD_SIGNAL(MethodInfo("tween_started", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); + ADD_SIGNAL(MethodInfo("tween_step", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"), PropertyInfo(Variant::REAL, "elapsed"), PropertyInfo(Variant::OBJECT, "value"))); + ADD_SIGNAL(MethodInfo("tween_completed", PropertyInfo(Variant::OBJECT, "object"), PropertyInfo(Variant::NODE_PATH, "key"))); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "repeat"), "set_repeat", "is_repeat"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_tween_process_mode", "get_tween_process_mode"); diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp index cf070c2235..408c00334a 100644 --- a/scene/audio/audio_player.cpp +++ b/scene/audio/audio_player.cpp @@ -45,7 +45,7 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { } //mix - stream_playback->mix(buffer, 1.0, buffer_size); + stream_playback->mix(buffer, pitch_scale, buffer_size); //multiply volume interpolating to avoid clicks if this changes float target_volume = p_fadeout ? -80.0 : volume_db; @@ -126,8 +126,8 @@ void AudioStreamPlayer::_notification(int p_what) { if (!active || (setseek < 0 && !stream_playback->is_playing())) { active = false; - emit_signal("finished"); set_process_internal(false); + emit_signal("finished"); } } @@ -177,6 +177,13 @@ float AudioStreamPlayer::get_volume_db() const { return volume_db; } +void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) { + pitch_scale = p_pitch_scale; +} +float AudioStreamPlayer::get_pitch_scale() const { + return pitch_scale; +} + void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { @@ -297,6 +304,9 @@ void AudioStreamPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer::set_volume_db); ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer::get_volume_db); + ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer::set_pitch_scale); + ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer::get_pitch_scale); + ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek); ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop); @@ -320,6 +330,7 @@ void AudioStreamPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target"); @@ -335,6 +346,7 @@ void AudioStreamPlayer::_bind_methods() { AudioStreamPlayer::AudioStreamPlayer() { mix_volume_db = 0; + pitch_scale = 1.0; volume_db = 0; autoplay = false; setseek = -1; diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h index 4fee30c0c2..21189aea6d 100644 --- a/scene/audio/audio_player.h +++ b/scene/audio/audio_player.h @@ -54,6 +54,7 @@ private: volatile bool active; float mix_volume_db; + float pitch_scale; float volume_db; bool autoplay; StringName bus; @@ -81,6 +82,9 @@ public: void set_volume_db(float p_volume); float get_volume_db() const; + void set_pitch_scale(float p_pitch_scale); + float get_pitch_scale() const; + void play(float p_from_pos = 0.0); void seek(float p_seconds); void stop(); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 8b9469021c..9dfd388c3d 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -311,10 +311,6 @@ void BaseButton::set_disabled(bool p_disabled) { status.disabled = p_disabled; update(); _change_notify("disabled"); - if (p_disabled) - set_focus_mode(FOCUS_NONE); - else - set_focus_mode(enabled_focus_mode); } bool BaseButton::is_disabled() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 1b5014367b..38ce91a4df 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1147,9 +1147,18 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_node_connected", "from", "from_port", "to", "to_port"), &GraphEdit::is_node_connected); ClassDB::bind_method(D_METHOD("disconnect_node", "from", "from_port", "to", "to_port"), &GraphEdit::disconnect_node); ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list); + ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections); ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs); ClassDB::bind_method(D_METHOD("set_scroll_ofs", "ofs"), &GraphEdit::set_scroll_ofs); + ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type); + ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type); + ClassDB::bind_method(D_METHOD("add_valid_left_disconnect_type", "type"), &GraphEdit::add_valid_left_disconnect_type); + ClassDB::bind_method(D_METHOD("remove_valid_left_disconnect_type", "type"), &GraphEdit::remove_valid_left_disconnect_type); + ClassDB::bind_method(D_METHOD("add_valid_connection_type", "from_type", "to_type"), &GraphEdit::add_valid_connection_type); + ClassDB::bind_method(D_METHOD("remove_valid_connection_type", "from_type", "to_type"), &GraphEdit::remove_valid_connection_type); + ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type); + ClassDB::bind_method(D_METHOD("set_zoom", "p_zoom"), &GraphEdit::set_zoom); ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 77d3a34c66..73ac99bfee 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1432,7 +1432,7 @@ void ItemList::_bind_methods() { ADD_GROUP("Icon", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_mode", PROPERTY_HINT_ENUM, "Top,Left"), "set_icon_mode", "get_icon_mode"); ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "icon_scale"), "set_icon_scale", "get_icon_scale"); - ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size"); + ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size"); BIND_ENUM_CONSTANT(ICON_MODE_TOP); BIND_ENUM_CONSTANT(ICON_MODE_LEFT); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 95bd676a08..f728490136 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -890,7 +890,7 @@ void TextEdit::_notification(int p_what) { } else { // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later. if (line == cursor.line && highlight_current_line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color); } } @@ -1122,14 +1122,14 @@ void TextEdit::_notification(int p_what) { // line highlighting handle horizontal clipping if (line == cursor.line && highlight_current_line) { - // char next to margin is skipped - if ((char_ofs + char_margin) > xmargin_beg) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color); - } - // end of line when last char is skipped if (j == str.length() - 1) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); + // end of line when last char is skipped + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color); + + } else if ((char_ofs + char_margin) > xmargin_beg) { + // char next to margin is skipped + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - xmargin_beg, get_row_height()), cache.current_line_color); } } continue; diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 6e58bc88d7..7a1fffaa26 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1603,19 +1603,19 @@ float Animation::get_step() const { return step; } -void Animation::copy_track(int src_track, Ref<Animation> p_to_animation) { +void Animation::copy_track(int p_track, Ref<Animation> p_to_animation) { ERR_FAIL_COND(p_to_animation.is_null()); - ERR_FAIL_INDEX(src_track, get_track_count()); + ERR_FAIL_INDEX(p_track, get_track_count()); int dst_track = p_to_animation->get_track_count(); - p_to_animation->add_track(track_get_type(src_track)); - - p_to_animation->track_set_path(dst_track, track_get_path(src_track)); - p_to_animation->track_set_imported(dst_track, track_is_imported(src_track)); - p_to_animation->track_set_enabled(dst_track, track_is_enabled(src_track)); - p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(src_track)); - p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(src_track)); - for (int i = 0; i < track_get_key_count(src_track); i++) { - p_to_animation->track_insert_key(dst_track, track_get_key_time(src_track, i), track_get_key_value(src_track, i), track_get_key_transition(src_track, i)); + p_to_animation->add_track(track_get_type(p_track)); + + p_to_animation->track_set_path(dst_track, track_get_path(p_track)); + p_to_animation->track_set_imported(dst_track, track_is_imported(p_track)); + p_to_animation->track_set_enabled(dst_track, track_is_enabled(p_track)); + p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(p_track)); + p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(p_track)); + for (int i = 0; i < track_get_key_count(p_track); i++) { + p_to_animation->track_insert_key(dst_track, track_get_key_time(p_track, i), track_get_key_value(p_track, i), track_get_key_transition(p_track, i)); } } diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 93ed700482..b77143cd9d 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -77,7 +77,7 @@ void AudioStreamPlaybackSample::seek(float p_time) { if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) return; //no seeking in ima-adpcm - float max = get_length(); + float max = base->get_length(); if (p_time < 0) { p_time = 0; } else if (p_time >= max) { @@ -390,22 +390,6 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in } } -float AudioStreamPlaybackSample::get_length() const { - - int len = base->data_bytes; - switch (base->format) { - case AudioStreamSample::FORMAT_8_BITS: len /= 1; break; - case AudioStreamSample::FORMAT_16_BITS: len /= 2; break; - case AudioStreamSample::FORMAT_IMA_ADPCM: len *= 2; break; - } - - if (base->stereo) { - len /= 2; - } - - return float(len) / base->mix_rate; -} - AudioStreamPlaybackSample::AudioStreamPlaybackSample() { active = false; @@ -469,6 +453,22 @@ bool AudioStreamSample::is_stereo() const { return stereo; } +float AudioStreamSample::get_length() const { + + int len = data_bytes; + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: len /= 1; break; + case AudioStreamSample::FORMAT_16_BITS: len /= 2; break; + case AudioStreamSample::FORMAT_IMA_ADPCM: len *= 2; break; + } + + if (stereo) { + len /= 2; + } + + return float(len) / mix_rate; +} + void AudioStreamSample::set_data(const PoolVector<uint8_t> &p_data) { AudioServer::get_singleton()->lock(); diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index 41754301eb..5fe65c194e 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -77,8 +77,6 @@ public: virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); - virtual float get_length() const; //if supported, otherwise return 0 - AudioStreamPlaybackSample(); }; @@ -137,6 +135,8 @@ public: void set_stereo(bool p_enable); bool is_stereo() const; + virtual float get_length() const; //if supported, otherwise return 0 + void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index 8c09130873..b2f586d02d 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -71,8 +71,8 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD(COLOR_RAMP_SET_COLORS, "colors"), &Gradient::set_colors); ClassDB::bind_method(D_METHOD(COLOR_RAMP_GET_COLORS), &Gradient::get_colors); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); + ADD_PROPERTY(PropertyInfo(Variant::POOL_REAL_ARRAY, "offsets"), COLOR_RAMP_SET_OFFSETS, COLOR_RAMP_GET_OFFSETS); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "colors"), COLOR_RAMP_SET_COLORS, COLOR_RAMP_GET_COLORS); } Vector<float> Gradient::get_offsets() const { diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index b35a9ae963..e9d5ca969e 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -35,13 +35,7 @@ bool DynamicFontData::CacheID::operator<(CacheID right) const { - if (size < right.size) - return true; - if (mipmaps != right.mipmaps) - return right.mipmaps; - if (filter != right.filter) - return right.filter; - return false; + return key < right.key; } Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(CacheID p_cache_id) { @@ -654,6 +648,7 @@ DynamicFontAtSize::~DynamicFontAtSize() { FT_Done_FreeType(library); } font->size_cache.erase(id); + font.unref(); } ///////////////////////// @@ -983,7 +978,7 @@ void DynamicFont::update_oversampling() { while (E) { if (E->self()->data_at_size.is_valid() && E->self()->data_at_size->update_oversampling()) { - changed.push_back(E->self()); + changed.push_back(Ref<DynamicFont>(E->self())); } E = E->next(); } diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index a949892086..92bb77bed3 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -50,15 +50,17 @@ class DynamicFontData : public Resource { public: struct CacheID { - int size; - bool mipmaps; - bool filter; - + union { + struct { + uint32_t size : 16; + bool mipmaps : 1; + bool filter : 1; + }; + uint32_t key; + }; bool operator<(CacheID right) const; CacheID() { - size = 16; - mipmaps = false; - filter = false; + key = 0; } }; diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 0faa0dc770..91c801c016 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -1312,6 +1312,8 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const return ria->rename_dependencies(f, p_path, p_map); } +ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = NULL; + Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { Error err; diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h index c014b9bfae..c28ded3d77 100644 --- a/scene/resources/scene_format_text.h +++ b/scene/resources/scene_format_text.h @@ -128,7 +128,9 @@ public: }; class ResourceFormatLoaderText : public ResourceFormatLoader { + public: + static ResourceFormatLoaderText *singleton; virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -138,6 +140,8 @@ public: virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); + + ResourceFormatLoaderText() { singleton = this; } }; class ResourceFormatSaverTextInstance { diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 6811517ead..de75bb3976 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -651,8 +651,8 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } //adapt borders (prevent weired overlapping/glitchy drawings) - int width = style_rect.size.width; - int height = style_rect.size.height; + int width = MAX(style_rect.size.width, 0); + int height = MAX(style_rect.size.height, 0); int adapted_border[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; adapt_values(MARGIN_TOP, MARGIN_BOTTOM, adapted_border, border_width, height, height, height); adapt_values(MARGIN_LEFT, MARGIN_RIGHT, adapted_border, border_width, width, width, width); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 067d123b83..c0f6756fd1 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -76,7 +76,7 @@ void Texture::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_data"), &Texture::get_data); - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Ansiotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_flags", "get_flags"); BIND_ENUM_CONSTANT(FLAGS_DEFAULT); BIND_ENUM_CONSTANT(FLAG_MIPMAPS); diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 369dfac042..0ad30987e7 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -46,9 +46,9 @@ void AudioStreamPlaybackResampled::_begin_resample() { void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { - float target_rate = AudioServer::get_singleton()->get_mix_rate() * p_rate_scale; + float target_rate = AudioServer::get_singleton()->get_mix_rate(); - uint64_t mix_increment = uint64_t((get_stream_sampling_rate() / double(target_rate)) * double(FP_LEN)); + uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale) / double(target_rate)) * double(FP_LEN)); for (int i = 0; i < p_frames; i++) { @@ -91,6 +91,13 @@ void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, } //////////////////////////////// +void AudioStream::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length); +} + +//////////////////////////////// + void AudioStreamRandomPitch::set_audio_stream(const Ref<AudioStream> &p_audio_stream) { audio_stream = p_audio_stream; @@ -136,6 +143,14 @@ String AudioStreamRandomPitch::get_stream_name() const { return "RandomPitch"; } +float AudioStreamRandomPitch::get_length() const { + if (audio_stream.is_valid()) { + return audio_stream->get_length(); + } + + return 0; +} + void AudioStreamRandomPitch::_bind_methods() { ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream); @@ -209,14 +224,6 @@ void AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scal } } -float AudioStreamPlaybackRandomPitch::get_length() const { - if (playing.is_valid()) { - return playing->get_length(); - } - - return 0; -} - AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() { random_pitch->playbacks.erase(this); } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index a6fb88364f..fda4fc2ccc 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -50,8 +50,6 @@ public: virtual void seek(float p_time) = 0; virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) = 0; - - virtual float get_length() const = 0; //if supported, otherwise return 0 }; class AudioStreamPlaybackResampled : public AudioStreamPlayback { @@ -85,9 +83,14 @@ class AudioStream : public Resource { GDCLASS(AudioStream, Resource) OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged +protected: + static void _bind_methods(); + public: virtual Ref<AudioStreamPlayback> instance_playback() = 0; virtual String get_stream_name() const = 0; + + virtual float get_length() const = 0; //if supported, otherwise return 0 }; class AudioStreamPlaybackRandomPitch; @@ -114,6 +117,8 @@ public: virtual Ref<AudioStreamPlayback> instance_playback(); virtual String get_stream_name() const; + virtual float get_length() const; //if supported, otherwise return 0 + AudioStreamRandomPitch(); }; @@ -139,8 +144,6 @@ public: virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); - virtual float get_length() const; //if supported, otherwise return 0 - ~AudioStreamPlaybackRandomPitch(); }; diff --git a/servers/audio/effects/audio_effect_reverb.cpp b/servers/audio/effects/audio_effect_reverb.cpp index 204b11746c..162c0a1445 100644 --- a/servers/audio/effects/audio_effect_reverb.cpp +++ b/servers/audio/effects/audio_effect_reverb.cpp @@ -96,7 +96,7 @@ void AudioEffectReverb::set_predelay_msec(float p_msec) { void AudioEffectReverb::set_predelay_feedback(float p_feedback) { - predelay_fb = p_feedback; + predelay_fb = CLAMP(p_feedback, 0, 0.98); } void AudioEffectReverb::set_room_size(float p_size) { @@ -185,7 +185,7 @@ void AudioEffectReverb::_bind_methods() { ADD_GROUP("Predelay", "predelay_"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "predelay_msec", PROPERTY_HINT_RANGE, "20,500,1"), "set_predelay_msec", "get_predelay_msec"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "predelay_feedback", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_predelay_feedback", "get_predelay_feedback"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "predelay_feedback", PROPERTY_HINT_RANGE, "0,0.98,0.01"), "set_predelay_feedback", "get_predelay_feedback"); ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::REAL, "room_size", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_room_size", "get_room_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping", "get_damping"); diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp index 5681ca838a..ea0d372281 100644 --- a/servers/physics/physics_server_sw.cpp +++ b/servers/physics/physics_server_sw.cpp @@ -795,12 +795,12 @@ void PhysicsServerSW::body_set_axis_velocity(RID p_body, const Vector3 &p_axis_v body->wakeup(); }; -void PhysicsServerSW::body_set_axis_lock(RID p_body, BodyAxis p_axis, bool lock) { +void PhysicsServerSW::body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock) { BodySW *body = body_owner.get(p_body); ERR_FAIL_COND(!body); - body->set_axis_lock(p_axis, lock); + body->set_axis_lock(p_axis, p_lock); body->wakeup(); } diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp index 180abc406c..5f08cd9243 100644 --- a/servers/physics_2d_server.cpp +++ b/servers/physics_2d_server.cpp @@ -168,9 +168,9 @@ float Physics2DShapeQueryParameters::get_margin() const { return margin; } -void Physics2DShapeQueryParameters::set_collision_mask(int p_collision_layer) { +void Physics2DShapeQueryParameters::set_collision_mask(int p_collision_mask) { - collision_mask = p_collision_layer; + collision_mask = p_collision_mask; } int Physics2DShapeQueryParameters::get_collision_mask() const { diff --git a/servers/physics_server.h b/servers/physics_server.h index 8b8b8f856d..94fc8d479d 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -118,7 +118,7 @@ public: void set_margin(float p_margin); float get_margin() const; - void set_collision_mask(int p_collision_layer); + void set_collision_mask(int p_collision_mask); int get_collision_mask() const; void set_exclude(const Vector<RID> &p_exclude); diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 206503e643..109cdf711c 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -457,7 +457,7 @@ public: virtual void instance_set_visible(RID p_instance, bool p_visible); virtual void instance_set_use_lightmap(RID p_instance, RID p_lightmap_instance, RID p_lightmap); - virtual void instance_set_custom_aabb(RID p_insatnce, AABB aabb); + virtual void instance_set_custom_aabb(RID p_instance, AABB p_aabb); virtual void instance_attach_skeleton(RID p_instance, RID p_skeleton); virtual void instance_set_exterior(RID p_instance, bool p_enabled); diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index faa8c50837..663fce85e9 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -1656,7 +1656,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("black_bars_set_margins", "left", "top", "right", "bottom"), &VisualServer::black_bars_set_margins); ClassDB::bind_method(D_METHOD("black_bars_set_images", "left", "top", "right", "bottom"), &VisualServer::black_bars_set_images); - ClassDB::bind_method(D_METHOD("free", "rid"), &VisualServer::free); + ClassDB::bind_method(D_METHOD("free_rid", "rid"), &VisualServer::free); // shouldn't conflict with Object::free() ClassDB::bind_method(D_METHOD("request_frame_drawn_callback", "where", "method", "userdata"), &VisualServer::request_frame_drawn_callback); ClassDB::bind_method(D_METHOD("draw", "swap_buffers"), &VisualServer::draw, DEFVAL(true)); diff --git a/thirdparty/README.md b/thirdparty/README.md index 21ff6cebe8..2957842f9c 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -26,9 +26,9 @@ Files extracted from upstream source: ## certs -- Upstream: ? - -TODO. +- Upstream: Mozilla, via https://packages.ubuntu.com/xenial-updates/ca-certificates +- Version: 2016-ish +- License: MPL 2.0 ## enet @@ -217,6 +217,23 @@ changes to ensure they build for Javascript/HTML5. Those changes are marked with `// -- GODOT --` comments. +## libwebsockets + +- Upstream: https://github.com/warmcat/libwebsockets +- Version: 2.4.1 +- License: LGPLv2.1 + static linking exception + +File extracted from upstream source: +- Everything in `lib/` except `mbedtls_wrapper/`, `http2/`, `event-libs/`. + - From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`. + - From `plat/` exclude `lws-plat-{esp*,optee}.c`. + - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`, +`peer-limits.c`, `rewrite.c` +- Also copy `win32helpers/` from `win32port/` + +Important: `lws_config.h` and `lws_config_private.h` contains custom +Godot build configurations, check them out when updating. + ## minizip - Upstream: http://www.zlib.net diff --git a/thirdparty/lws/LICENSE.txt b/thirdparty/lws/LICENSE.txt new file mode 100644 index 0000000000..34a42d5687 --- /dev/null +++ b/thirdparty/lws/LICENSE.txt @@ -0,0 +1,555 @@ +Libwebsockets and included programs are provided under the terms of the GNU +Library General Public License (LGPL) 2.1, with the following exceptions: + +1) Any reference, whether in these modifications or in the GNU +Library General Public License 2.1, to this License, these terms, the +GNU Lesser Public License, GNU Library General Public License, LGPL, or +any similar reference shall refer to the GNU Library General Public +License 2.1 as modified by these paragraphs 1) through 4). + +2) Static linking of programs with the libwebsockets library does not +constitute a derivative work and does not require the author to provide +source code for the program, use the shared libwebsockets libraries, or +link their program against a user-supplied version of libwebsockets. + +If you link the program to a modified version of libwebsockets, then the +changes to libwebsockets must be provided under the terms of the LGPL in +sections 1, 2, and 4. + +3) You do not have to provide a copy of the libwebsockets license with +programs that are linked to the libwebsockets library, nor do you have to +identify the libwebsockets license in your program or documentation as +required by section 6 of the LGPL. + +However, programs must still identify their use of libwebsockets. The +following example statement can be included in user documentation to +satisfy this requirement: + +"[program] is based in part on the work of the libwebsockets project +(https://libwebsockets.org)" + +4) Some sources included have their own, more liberal licenses, or options +to get original sources with the liberal terms. + +Original liberal license retained + + - lib/sha-1.c - 3-clause BSD license retained, link to original + - win32port/zlib - ZLIB license (see zlib.h) + +Relicensed to libwebsocket license + + - lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original + - lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE, + link to original Public Domain version + +Public Domain (CC-zero) to simplify reuse + + - test-server/*.c + - test-server/*.h + - lwsws/* + +------ end of exceptions + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/thirdparty/lws/alloc.c b/thirdparty/lws/alloc.c new file mode 100644 index 0000000000..898db12464 --- /dev/null +++ b/thirdparty/lws/alloc.c @@ -0,0 +1,90 @@ +#include "private-libwebsockets.h" + +#if defined(LWS_PLAT_OPTEE) + +#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000 + +void *__attribute__((weak)) + TEE_Malloc(uint32_t size, uint32_t hint) +{ + return NULL; +} +void *__attribute__((weak)) + TEE_Realloc(void *buffer, uint32_t newSize) +{ + return NULL; +} +void __attribute__((weak)) + TEE_Free(void *buffer) +{ +} + +void *lws_realloc(void *ptr, size_t size, const char *reason) +{ + return TEE_Realloc(ptr, size); +} + +void *lws_malloc(size_t size, const char *reason) +{ + return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); +} + +void lws_free(void *p) +{ + TEE_Free(p); +} + +void *lws_zalloc(size_t size, const char *reason) +{ + void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) +{ + (void)cb; +} +#else + +static void *_realloc(void *ptr, size_t size, const char *reason) +{ + if (size) { +#if defined(LWS_PLAT_ESP32) + lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); +#else + lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason); +#endif +#if defined(LWS_PLAT_OPTEE) + return (void *)TEE_Realloc(ptr, size); +#else + return (void *)realloc(ptr, size); +#endif + } + if (ptr) + free(ptr); + + return NULL; +} + +void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc; + +void *lws_realloc(void *ptr, size_t size, const char *reason) +{ + return _lws_realloc(ptr, size, reason); +} + +void *lws_zalloc(size_t size, const char *reason) +{ + void *ptr = _lws_realloc(NULL, size, reason); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason)) +{ + _lws_realloc = cb; +} +#endif diff --git a/thirdparty/lws/client/client-handshake.c b/thirdparty/lws/client/client-handshake.c new file mode 100644 index 0000000000..c2720d9283 --- /dev/null +++ b/thirdparty/lws/client/client-handshake.c @@ -0,0 +1,1051 @@ +#include "private-libwebsockets.h" + +static int +lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + *result = NULL; + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + +#if !defined(__ANDROID__) + hints.ai_family = AF_INET6; + hints.ai_flags = AI_V4MAPPED; +#endif + } else +#endif + { + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + } + + return getaddrinfo(ads, NULL, &hints, result); +} + +struct lws * +lws_client_connect_2(struct lws *wsi) +{ + sockaddr46 sa46; + struct addrinfo *result; + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollfd pfd; + const char *cce = "", *iface; + int n, port; + ssize_t plen = 0; + const char *ads; +#ifdef LWS_WITH_IPV6 + char ipv6only = lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY | + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); + +#if defined(__ANDROID__) + ipv6only = 0; +#endif +#endif + + lwsl_client("%s\n", __func__); + + if (!wsi->u.hdr.ah) { + cce = "ah was NULL at cc2"; + lwsl_err("%s\n", cce); + goto oom4; + } + + /* + * start off allowing ipv6 on connection if vhost allows it + */ + wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost); + + /* Decide what it is we need to connect to: + * + * Priority 1: connect to http proxy */ + + if (wsi->vhost->http_proxy_port) { + plen = sprintf((char *)pt->serv_buf, + "CONNECT %s:%u HTTP/1.0\x0d\x0a" + "User-agent: libwebsockets\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), + wsi->c_port); + + if (wsi->vhost->proxy_basic_auth_token[0]) + plen += sprintf((char *)pt->serv_buf + plen, + "Proxy-authorization: basic %s\x0d\x0a", + wsi->vhost->proxy_basic_auth_token); + + plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); + ads = wsi->vhost->http_proxy_address; + port = wsi->vhost->http_proxy_port; + +#if defined(LWS_WITH_SOCKS5) + + /* Priority 2: Connect to SOCK5 Proxy */ + + } else if (wsi->vhost->socks_proxy_port) { + socks_generate_msg(wsi, SOCKS_MSG_GREETING, &plen); + lwsl_client("Sending SOCKS Greeting\n"); + ads = wsi->vhost->socks_proxy_address; + port = wsi->vhost->socks_proxy_port; +#endif + } else { + + /* Priority 3: Connect directly */ + + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + } + + /* + * prepare the actual connection + * to whatever we decided to connect to + */ + + lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads); + + n = lws_getaddrinfo46(wsi, ads, &result); + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + + if (n) { + /* lws_getaddrinfo46 failed, there is no usable result */ + lwsl_notice("%s: lws_getaddrinfo46 failed %d\n", + __func__, n); + cce = "ipv6 lws_getaddrinfo46 failed"; + goto oom4; + } + + memset(&sa46, 0, sizeof(sa46)); + + sa46.sa6.sin6_family = AF_INET6; + switch (result->ai_family) { + case AF_INET: + if (ipv6only) + break; + /* map IPv4 to IPv6 */ + bzero((char *)&sa46.sa6.sin6_addr, + sizeof(sa46.sa6.sin6_addr)); + sa46.sa6.sin6_addr.s6_addr[10] = 0xff; + sa46.sa6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&sa46.sa6.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + lwsl_notice("uplevelling AF_INET to AF_INET6\n"); + break; + + case AF_INET6: + memcpy(&sa46.sa6.sin6_addr, + &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id; + sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo; + break; + default: + lwsl_err("Unknown address family\n"); + freeaddrinfo(result); + cce = "unknown address family"; + goto oom4; + } + } else +#endif /* use ipv6 */ + + /* use ipv4 */ + { + void *p = NULL; + + if (!n) { + struct addrinfo *res = result; + + /* pick the first AF_INET (IPv4) result */ + + while (!p && res) { + switch (res->ai_family) { + case AF_INET: + p = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + } + + res = res->ai_next; + } +#if defined(LWS_FALLBACK_GETHOSTBYNAME) + } else if (n == EAI_SYSTEM) { + struct hostent *host; + + lwsl_info("getaddrinfo (ipv4) failed, trying gethostbyname\n"); + host = gethostbyname(ads); + if (host) { + p = host->h_addr; + } else { + lwsl_err("gethostbyname failed\n"); + cce = "gethostbyname (ipv4) failed"; + goto oom4; + } +#endif + } else { + lwsl_err("getaddrinfo failed\n"); + cce = "getaddrinfo failed"; + goto oom4; + } + + if (!p) { + if (result) + freeaddrinfo(result); + lwsl_err("Couldn't identify address\n"); + cce = "unable to lookup address"; + goto oom4; + } + + sa46.sa4.sin_family = AF_INET; + sa46.sa4.sin_addr = *((struct in_addr *)p); + bzero(&sa46.sa4.sin_zero, 8); + } + + if (result) + freeaddrinfo(result); + + /* now we decided on ipv4 or ipv6, set the port */ + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + +#if defined(LWS_WITH_LIBUV) + if (LWS_LIBUV_ENABLED(context)) + if (lws_libuv_check_watcher_active(wsi)) { + lwsl_warn("Waiting for libuv watcher to close\n"); + cce = "waiting for libuv watcher to close"; + goto oom4; + } +#endif + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) + wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + lwsl_warn("Unable to open socket\n"); + cce = "unable to open socket"; + goto oom4; + } + + if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) { + lwsl_err("Failed to set wsi socket options\n"); + compatible_close(wsi->desc.sockfd); + cce = "set socket opts failed"; + goto oom4; + } + + wsi->mode = LWSCM_WSCL_WAITING_CONNECT; + + lws_libev_accept(wsi, wsi->desc); + lws_libuv_accept(wsi, wsi->desc); + lws_libevent_accept(wsi, wsi->desc); + + if (insert_wsi_socket_into_fds(context, wsi)) { + compatible_close(wsi->desc.sockfd); + cce = "insert wsi failed"; + goto oom4; + } + + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + /* + * past here, we can't simply free the structs as error + * handling as oom4 does. We have to run the whole close flow. + */ + + if (!wsi->protocol) + wsi->protocol = &wsi->vhost->protocols[0]; + + wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, + wsi->user_space, NULL, 0); + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); + + if (iface) { + n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, iface); + if (n < 0) { + cce = "unable to bind socket"; + goto failed; + } + } + } + +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + sa46.sa6.sin6_port = htons(port); + n = sizeof(struct sockaddr_in6); + } else +#endif + { + sa46.sa4.sin_port = htons(port); + n = sizeof(struct sockaddr); + } + + if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 || + LWS_ERRNO == LWS_EISCONN) { + if (LWS_ERRNO == LWS_EALREADY || + LWS_ERRNO == LWS_EINPROGRESS || + LWS_ERRNO == LWS_EWOULDBLOCK +#ifdef _WIN32 + || LWS_ERRNO == WSAEINVAL +#endif + ) { + lwsl_client("nonblocking connect retry (errno = %d)\n", + LWS_ERRNO); + + if (lws_plat_check_connection_error(wsi)) { + cce = "socket connect failed"; + goto failed; + } + + /* + * must do specifically a POLLOUT poll to hear + * about the connect completion + */ + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + cce = "POLLOUT set failed"; + goto failed; + } + + return wsi; + } + + if (LWS_ERRNO != LWS_EISCONN) { + lwsl_notice("Connect failed errno=%d\n", LWS_ERRNO); + cce = "connect failed"; + goto failed; + } + } + + lwsl_client("connected\n"); + + /* we are connected to server, or proxy */ + + /* http proxy */ + if (wsi->vhost->http_proxy_port) { + + /* + * OK from now on we talk via the proxy, so connect to that + * + * (will overwrite existing pointer, + * leaving old string/frag there but unreferenced) + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->http_proxy_address)) + goto failed; + wsi->c_port = wsi->vhost->http_proxy_port; + + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to proxy socket\n"); + cce = "proxy write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY; + + return wsi; + } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + else if (wsi->vhost->socks_proxy_port) { + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing socks greeting\n"); + cce = "socks write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY; + + return wsi; + } +#endif + + /* + * provoke service to issue the handshake directly + * we need to do it this way because in the proxy case, this is the + * next state and executed only if and when we get a good proxy + * response inside the state machine... but notice in SSL case this + * may not have sent anything yet with 0 return, and won't until some + * many retries from main loop. To stop that becoming endless, + * cover with a timeout. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE; + pfd.fd = wsi->desc.sockfd; + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLIN; + + n = lws_service_fd(context, &pfd); + if (n < 0) { + cce = "first service failed"; + goto failed; + } + if (n) /* returns 1 on failure after closing wsi */ + return NULL; + + return wsi; + +oom4: + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + + if (wsi->mode == LWSCM_HTTP_CLIENT || + wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; + } + /* take care that we might be inserted in fds already */ + if (wsi->position_in_fds_table != -1) + goto failed1; + lws_remove_from_timeout_list(wsi); + lws_header_table_detach(wsi, 0); + lws_free(wsi); + + return NULL; + +failed: + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; +failed1: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +/** + * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) + * this only works if still in HTTP, ie, not upgraded yet + * wsi: connection to reset + * address: network address of the new server + * port: port to connect to + * path: uri path to connect to on the new server + * host: host header to send to the new server + */ +LWS_VISIBLE struct lws * +lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, + const char *path, const char *host) +{ + char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p; + struct lws *wsi = *pwsi; + + if (wsi->redirects == 3) { + lwsl_err("%s: Too many redirects\n", __func__); + return NULL; + } + wsi->redirects++; + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); + if (p) + strncpy(origin, p, sizeof(origin) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + if (p) + strncpy(protocol, p, sizeof(protocol) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (p) + strncpy(method, p, sizeof(method) - 1); + + p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); + if (p) + strncpy(method, p, sizeof(iface) - 1); + + lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n", + address, port, path, ssl); + + /* close the connection by hand */ + +#ifdef LWS_OPENSSL_SUPPORT + lws_ssl_close(wsi); +#endif + +#ifdef LWS_WITH_LIBUV + if (LWS_LIBUV_ENABLED(wsi->context)) { + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); + /* + * libuv has to do his own close handle processing asynchronously + * but once it starts we can do everything else synchronously, + * including trash wsi->desc.sockfd since it took a copy. + * + * When it completes it will call compatible_close() + */ + lws_libuv_closehandle_manually(wsi); + } else +#else + compatible_close(wsi->desc.sockfd); +#endif + + remove_wsi_socket_from_fds(wsi); + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = ssl; +#else + if (ssl) { + lwsl_err("%s: not configured for ssl\n", __func__); + return NULL; + } +#endif + + wsi->desc.sockfd = LWS_SOCK_INVALID; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->protocol = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->c_port = port; + wsi->hdr_parsing_completed = 0; + _lws_header_table_reset(wsi->u.hdr.ah); + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) + return NULL; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) + return NULL; + + if (origin[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, + origin)) + return NULL; + if (protocol[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + protocol)) + return NULL; + if (method[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, + method)) + return NULL; + + if (iface[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, + iface)) + return NULL; + + origin[0] = '/'; + strncpy(&origin[1], path, sizeof(origin) - 2); + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin)) + return NULL; + + *pwsi = lws_client_connect_2(wsi); + + return *pwsi; +} + +#ifdef LWS_WITH_HTTP_PROXY +static hubbub_error +html_parser_cb(const hubbub_token *token, void *pw) +{ + struct lws_rewrite *r = (struct lws_rewrite *)pw; + char buf[1024], *start = buf + LWS_PRE, *p = start, + *end = &buf[sizeof(buf) - 1]; + size_t i; + + switch (token->type) { + case HUBBUB_TOKEN_DOCTYPE: + + p += lws_snprintf(p, end - p, "<!DOCTYPE %.*s %s ", + (int) token->data.doctype.name.len, + token->data.doctype.name.ptr, + token->data.doctype.force_quirks ? + "(force-quirks) " : ""); + + if (token->data.doctype.public_missing) + lwsl_debug("\tpublic: missing\n"); + else + p += lws_snprintf(p, end - p, "PUBLIC \"%.*s\"\n", + (int) token->data.doctype.public_id.len, + token->data.doctype.public_id.ptr); + + if (token->data.doctype.system_missing) + lwsl_debug("\tsystem: missing\n"); + else + p += lws_snprintf(p, end - p, " \"%.*s\">\n", + (int) token->data.doctype.system_id.len, + token->data.doctype.system_id.ptr); + + break; + case HUBBUB_TOKEN_START_TAG: + p += lws_snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len, + token->data.tag.name.ptr); + +/* (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) || + !hstrcmp(&token->data.tag.attributes[i].name, "action", 6) || + !hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) { + const char *pp = (const char *)token->data.tag.attributes[i].value.ptr; + int plen = (int) token->data.tag.attributes[i].value.len; + + if (strncmp(pp, "http:", 5) && strncmp(pp, "https:", 6)) { + + if (!hstrcmp(&token->data.tag.attributes[i].value, + r->from, r->from_len)) { + pp += r->from_len; + plen -= r->from_len; + } + p += lws_snprintf(p, end - p, " %.*s=\"%s/%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + r->to, plen, pp); + continue; + } + } + + p += lws_snprintf(p, end - p, " %.*s=\"%.*s\"", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">"); + break; + case HUBBUB_TOKEN_END_TAG: + p += lws_snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len, + token->data.tag.name.ptr); +/* + (token->data.tag.self_closing) ? + "(self-closing) " : "", + (token->data.tag.n_attributes > 0) ? + "attributes:" : ""); +*/ + for (i = 0; i < token->data.tag.n_attributes; i++) { + p += lws_snprintf(p, end - p, " %.*s='%.*s'\n", + (int) token->data.tag.attributes[i].name.len, + token->data.tag.attributes[i].name.ptr, + (int) token->data.tag.attributes[i].value.len, + token->data.tag.attributes[i].value.ptr); + } + p += lws_snprintf(p, end - p, ">"); + break; + case HUBBUB_TOKEN_COMMENT: + p += lws_snprintf(p, end - p, "<!-- %.*s -->\n", + (int) token->data.comment.len, + token->data.comment.ptr); + break; + case HUBBUB_TOKEN_CHARACTER: + if (token->data.character.len == 1) { + if (*token->data.character.ptr == '<') { + p += lws_snprintf(p, end - p, "<"); + break; + } + if (*token->data.character.ptr == '>') { + p += lws_snprintf(p, end - p, ">"); + break; + } + if (*token->data.character.ptr == '&') { + p += lws_snprintf(p, end - p, "&"); + break; + } + } + + p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len, + token->data.character.ptr); + break; + case HUBBUB_TOKEN_EOF: + p += lws_snprintf(p, end - p, "\n"); + break; + } + + if (user_callback_handle_rxflow(r->wsi->protocol->callback, + r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + r->wsi->user_space, start, p - start)) + return -1; + + return HUBBUB_OK; +} +#endif + +LWS_VISIBLE struct lws * +lws_client_connect_via_info(struct lws_client_connect_info *i) +{ + struct lws *wsi; + int v = SPEC_LATEST_SUPPORTED; + const struct lws_protocols *p; + + if (i->context->requested_kill) + return NULL; + + if (!i->context->protocol_init_done) + lws_protocol_init(i->context); + + wsi = lws_zalloc(sizeof(struct lws), "client wsi"); + if (wsi == NULL) + goto bail; + + wsi->context = i->context; + /* assert the mode and union status (hdr) clearly */ + lws_union_transition(wsi, LWSCM_HTTP_CLIENT); + wsi->desc.sockfd = LWS_SOCK_INVALID; + + /* 1) fill up the wsi with stuff from the connect_info as far as it + * can go. It's because not only is our connection async, we might + * not even be able to get ahold of an ah at this point. + */ + + /* -1 means just use latest supported */ + if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) + v = i->ietf_version_or_minus_one; + + wsi->ietf_spec_revision = v; + wsi->user_space = NULL; + wsi->state = LWSS_CLIENT_UNCONNECTED; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->position_in_fds_table = -1; + wsi->c_port = i->port; + wsi->vhost = i->vhost; + if (!wsi->vhost) + wsi->vhost = i->context->vhost_list; + + wsi->protocol = &wsi->vhost->protocols[0]; + + /* for http[s] connection, allow protocol selection by name */ + + if (i->method && i->vhost && i->protocol) { + p = lws_vhost_name_to_protocol(i->vhost, i->protocol); + if (p) + wsi->protocol = p; + } + + if (wsi && !wsi->user_space && i->userdata) { + wsi->user_space_externally_allocated = 1; + wsi->user_space = i->userdata; + } else + /* if we stay in http, we can assign the user space now, + * otherwise do it after the protocol negotiated + */ + if (i->method) + if (lws_ensure_user_space(wsi)) + goto bail; + +#ifdef LWS_OPENSSL_SUPPORT + wsi->use_ssl = i->ssl_connection; +#else + if (i->ssl_connection) { + lwsl_err("libwebsockets not configured for ssl\n"); + goto bail; + } +#endif + + /* 2) stash the things from connect_info that we can't process without + * an ah. Because if no ah, we will go on the ah waiting list and + * process those things later (after the connect_info and maybe the + * things pointed to have gone out of scope. + */ + + wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash"); + if (!wsi->u.hdr.stash) { + lwsl_err("%s: OOM\n", __func__); + goto bail; + } + + wsi->u.hdr.stash->origin[0] = '\0'; + wsi->u.hdr.stash->protocol[0] = '\0'; + wsi->u.hdr.stash->method[0] = '\0'; + wsi->u.hdr.stash->iface[0] = '\0'; + + strncpy(wsi->u.hdr.stash->address, i->address, + sizeof(wsi->u.hdr.stash->address) - 1); + strncpy(wsi->u.hdr.stash->path, i->path, + sizeof(wsi->u.hdr.stash->path) - 1); + strncpy(wsi->u.hdr.stash->host, i->host, + sizeof(wsi->u.hdr.stash->host) - 1); + if (i->origin) + strncpy(wsi->u.hdr.stash->origin, i->origin, + sizeof(wsi->u.hdr.stash->origin) - 1); + if (i->protocol) + strncpy(wsi->u.hdr.stash->protocol, i->protocol, + sizeof(wsi->u.hdr.stash->protocol) - 1); + if (i->method) + strncpy(wsi->u.hdr.stash->method, i->method, + sizeof(wsi->u.hdr.stash->method) - 1); + if (i->iface) + strncpy(wsi->u.hdr.stash->iface, i->iface, + sizeof(wsi->u.hdr.stash->iface) - 1); + + wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; + wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; + wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; + wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; + wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; + wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; + wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0'; + + if (i->pwsi) + *i->pwsi = wsi; + + /* if we went on the waiting list, no probs just return the wsi + * when we get the ah, now or later, he will call + * lws_client_connect_via_info2() below. + */ + if (lws_header_table_attach(wsi, 0) < 0) { + /* + * if we failed here, the connection is already closed + * and freed. + */ + goto bail1; + } + + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } +#ifdef LWS_WITH_HTTP_PROXY + if (i->uri_replace_to) + wsi->rw = lws_rewrite_create(wsi, html_parser_cb, + i->uri_replace_from, + i->uri_replace_to); +#endif + + return wsi; + +bail: + lws_free(wsi); + +bail1: + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +} + +struct lws * +lws_client_connect_via_info2(struct lws *wsi) +{ + struct client_info_stash *stash = wsi->u.hdr.stash; + + if (!stash) + return wsi; + + /* + * we're not necessarily in a position to action these right away, + * stash them... we only need during connect phase so u.hdr is fine + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + stash->address)) + goto bail1; + + /* these only need u.hdr lifetime as well */ + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) + goto bail1; + + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) + goto bail1; + + if (stash->origin[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, + stash->origin)) + goto bail1; + /* + * this is a list of protocols we tell the server we're okay with + * stash it for later when we compare server response with it + */ + if (stash->protocol[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + stash->protocol)) + goto bail1; + if (stash->method[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, + stash->method)) + goto bail1; + if (stash->iface[0]) + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, + stash->iface)) + goto bail1; + +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + /* + * Check with each extension if it is able to route and proxy this + * connection for us. For example, an extension like x-google-mux + * can handle this and then we don't need an actual socket for this + * connection. + */ + + if (lws_ext_cb_all_exts(wsi->context, wsi, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, + (void *)stash->address, + wsi->c_port) > 0) { + lwsl_client("lws_client_connect: ext handling conn\n"); + + lws_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, + AWAITING_TIMEOUT); + + wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; + return wsi; + } + lwsl_client("lws_client_connect: direct conn\n"); + wsi->context->count_wsi_allocated++; + + return lws_client_connect_2(wsi); + +bail1: +#if defined(LWS_WITH_SOCKS5) + if (!wsi->vhost->socks_proxy_port) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_client_connect_extended(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one, + void *userdata) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = userdata; + + return lws_client_connect_via_info(&i); +} + +LWS_VISIBLE struct lws * +lws_client_connect(struct lws_context *context, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one) +{ + struct lws_client_connect_info i; + + memset(&i, 0, sizeof(i)); + + i.context = context; + i.address = address; + i.port = port; + i.ssl_connection = ssl_connection; + i.path = path; + i.host = host; + i.origin = origin; + i.protocol = protocol; + i.ietf_version_or_minus_one = ietf_version_or_minus_one; + i.userdata = NULL; + + return lws_client_connect_via_info(&i); +} + +#if defined(LWS_WITH_SOCKS5) +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + ssize_t *msg_len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + ssize_t len = 0, n, passwd_len; + short net_num; + char *p; + + switch (type) { + case SOCKS_MSG_GREETING: + /* socks version, version 5 only */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* number of methods */ + pt->serv_buf[len++] = 2; + /* username password method */ + pt->serv_buf[len++] = SOCKS_AUTH_USERNAME_PASSWORD; + /* no authentication method */ + pt->serv_buf[len++] = SOCKS_AUTH_NO_AUTH; + break; + + case SOCKS_MSG_USERNAME_PASSWORD: + n = strlen(wsi->vhost->socks_user); + passwd_len = strlen(wsi->vhost->socks_password); + + /* the subnegotiation version */ + pt->serv_buf[len++] = SOCKS_SUBNEGOTIATION_VERSION_1; + /* length of the user name */ + pt->serv_buf[len++] = n; + /* user name */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user, + context->pt_serv_buf_size - len); + len += n; + /* length of the password */ + pt->serv_buf[len++] = passwd_len; + /* password */ + strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password, + context->pt_serv_buf_size - len); + len += passwd_len; + break; + + case SOCKS_MSG_CONNECT: + p = (char*)&net_num; + + /* socks version */ + pt->serv_buf[len++] = SOCKS_VERSION_5; + /* socks command */ + pt->serv_buf[len++] = SOCKS_COMMAND_CONNECT; + /* reserved */ + pt->serv_buf[len++] = 0; + /* address type */ + pt->serv_buf[len++] = SOCKS_ATYP_DOMAINNAME; + /* skip length, we fill it in at the end */ + n = len++; + + /* the address we tell SOCKS proxy to connect to */ + strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address, + context->pt_serv_buf_size - len); + len += strlen(wsi->u.hdr.stash->address); + net_num = htons(wsi->c_port); + + /* the port we tell SOCKS proxy to connect to */ + pt->serv_buf[len++] = p[0]; + pt->serv_buf[len++] = p[1]; + + /* the length of the address, excluding port */ + pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address); + break; + + default: + return; + } + + *msg_len = len; +} +#endif diff --git a/thirdparty/lws/client/client-parser.c b/thirdparty/lws/client/client-parser.c new file mode 100644 index 0000000000..0e42dac362 --- /dev/null +++ b/thirdparty/lws/client/client-parser.c @@ -0,0 +1,598 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * parsers.c: lws_rx_sm() needs to be roughly kept in + * sync with changes here, esp related to ext draining + */ + +int lws_client_rx_sm(struct lws *wsi, unsigned char c) +{ + int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; + int handled, n, m, rx_draining_ext = 0; + unsigned short close_code; + struct lws_tokens eff_buf; + unsigned char *pp; + + if (wsi->u.ws.rx_draining_ext) { + assert(!c); + eff_buf.token = NULL; + eff_buf.token_len = 0; + lws_remove_wsi_from_draining_ext_list(wsi); + rx_draining_ext = 1; + lwsl_debug("%s: doing draining flow\n", __func__); + + goto drain_extension; + } + + if (wsi->socket_is_permanently_unusable) + return -1; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + /* control frames (PING) may interrupt checkable sequences */ + wsi->u.ws.defeat_check_utf8 = 0; + + switch (wsi->ietf_spec_revision) { + case 13: + wsi->u.ws.opcode = c & 0xf; + /* revisit if an extension wants them... */ + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.continuation_possible = 1; + wsi->u.ws.check_utf8 = lws_check_opt( + wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8); + wsi->u.ws.utf8 = 0; + break; + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.continuation_possible = 1; + break; + case LWSWSOPC_CONTINUATION: + if (!wsi->u.ws.continuation_possible) { + lwsl_info("disordered continuation\n"); + return -1; + } + break; + case LWSWSOPC_CLOSE: + wsi->u.ws.check_utf8 = 0; + wsi->u.ws.utf8 = 0; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + default: + wsi->u.ws.defeat_check_utf8 = 1; + break; + } + wsi->u.ws.rsv = (c & 0x70); + /* revisit if an extension wants them... */ + if ( +#ifndef LWS_NO_EXTENSIONS + !wsi->count_act_ext && +#endif + wsi->u.ws.rsv) { + lwsl_info("illegal rsv bits set\n"); + return -1; + } + wsi->u.ws.final = !!((c >> 7) & 1); + lwsl_ext("%s: This RX frame Final %d\n", __func__, + wsi->u.ws.final); + + if (wsi->u.ws.owed_a_fin && + (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME || + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) { + lwsl_info("hey you owed us a FIN\n"); + return -1; + } + if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) { + wsi->u.ws.continuation_possible = 0; + wsi->u.ws.owed_a_fin = 0; + } + + if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) { + lwsl_info("control msg can't be fragmented\n"); + return -1; + } + if (!wsi->u.ws.final) + wsi->u.ws.owed_a_fin = 1; + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == + LWSWSOPC_BINARY_FRAME; + break; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + default: + lwsl_err("unknown spec version %02d\n", + wsi->ietf_spec_revision); + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; + break; + case 127: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (c) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.ws.rx_packet_length = 0; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_7: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_6: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_5: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_4: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_3: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_2: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_1: + wsi->u.ws.rx_packet_length |= (size_t)c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else { + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + } + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_2: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_3: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_4: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.rx_draining_ext) + goto drain_extension; + + if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce) + c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3]; + + wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) + break; + + /* spill because we filled our rx buffer */ +spill: + + handled = 0; + + /* + * is this frame a control packet we should take care of at this + * layer? If so service it and hide it from the user callback + */ + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_CLOSE: + pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE]; + if (lws_check_opt(wsi->context->options, + LWS_SERVER_OPTION_VALIDATE_UTF8) && + wsi->u.ws.rx_ubuf_head > 2 && + lws_check_utf8(&wsi->u.ws.utf8, pp + 2, + wsi->u.ws.rx_ubuf_head - 2)) + goto utf8_fail; + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen server's close ack\n"); + return -1; + } + + lwsl_parser("client sees server close len = %d\n", + wsi->u.ws.rx_ubuf_head); + if (wsi->u.ws.rx_ubuf_head >= 2) { + close_code = (pp[0] << 8) | pp[1]; + if (close_code < 1000 || + close_code == 1004 || + close_code == 1005 || + close_code == 1006 || + close_code == 1012 || + close_code == 1013 || + close_code == 1014 || + close_code == 1015 || + (close_code >= 1016 && close_code < 3000) + ) { + pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff; + pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff; + } + } + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, pp, + wsi->u.ws.rx_ubuf_head)) + return -1; + + if (lws_partial_buffered(wsi)) + /* + * if we're in the middle of something, + * we can't do a normal close response and + * have to just close our end. + */ + wsi->socket_is_permanently_unusable = 1; + else + /* + * parrot the close packet payload back + * we do not care about how it went, we are closing + * immediately afterwards + */ + lws_write(wsi, (unsigned char *) + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head, + LWS_WRITE_CLOSE); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* close the connection */ + return -1; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + /* he set a close reason on this guy, ignore PING */ + if (wsi->u.ws.close_in_ping_buffer_len) + goto ping_drop; + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } + + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + handled = 1; + break; + + case LWSWSOPC_PONG: + lwsl_info("client receied pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == + PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("%p: received expected PONG\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG; + break; + + case LWSWSOPC_CONTINUATION: + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + break; + + default: + + lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); + + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) { + /* not handled or failed */ + lwsl_ext("Unhandled ext opc 0x%x\n", + wsi->u.ws.opcode); + wsi->u.ws.rx_ubuf_head = 0; + + return 0; + } + handled = 1; + break; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + if (handled) + goto already_done; + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) + goto already_done; + +drain_extension: + lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); + + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + lwsl_ext("Ext RX returned %d\n", n); + if (n < 0) { + wsi->socket_is_permanently_unusable = 1; + return -1; + } + + lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len); + + if (rx_draining_ext && !eff_buf.token_len) { + lwsl_debug(" --- ending drain on 0 read result\n"); + goto already_done; + } + + if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) { + if (lws_check_utf8(&wsi->u.ws.utf8, + (unsigned char *)eff_buf.token, + eff_buf.token_len)) + goto utf8_fail; + + /* we are ending partway through utf-8 character? */ + if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && + wsi->u.ws.utf8 && !n) { + lwsl_info("FINAL utf8 error\n"); +utf8_fail: + lwsl_info("utf8 error\n"); + return -1; + } + } + + if (eff_buf.token_len < 0 && + callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) + goto already_done; + + if (!eff_buf.token) + goto already_done; + + eff_buf.token[eff_buf.token_len] = '\0'; + + if (!wsi->protocol->callback) + goto already_done; + + if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) + lwsl_info("Client doing pong callback\n"); + + if (n && eff_buf.token_len) + /* extension had more... main loop will come back + * we want callback to be done with this set, if so, + * because lws_is_final() hides it was final until the + * last chunk + */ + lws_add_wsi_to_draining_ext_list(wsi); + else + lws_remove_wsi_from_draining_ext_list(wsi); + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + m = wsi->protocol->callback(wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, eff_buf.token, eff_buf.token_len); + + /* if user code wants to close, let caller know */ + if (m) + return 1; + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + default: + lwsl_err("client rx illegal state\n"); + return 1; + } + + return 0; + +illegal_ctl_length: + lwsl_warn("Control frame asking for extended length is illegal\n"); + + /* kill the connection */ + return -1; +} + + diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c new file mode 100644 index 0000000000..20450aa923 --- /dev/null +++ b/thirdparty/lws/client/client.c @@ -0,0 +1,1296 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +int +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + switch (wsi->mode) { + case LWSCM_WSCL_WAITING_PROXY_REPLY: + case LWSCM_WSCL_ISSUE_HANDSHAKE: + case LWSCM_WSCL_WAITING_SERVER_REPLY: + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + case LWSCM_WS_CLIENT: + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (lws_is_flowcontrolled(wsi)) { + lwsl_debug("%s: caching %ld\n", __func__, (long)len); + lws_rxflow_cache(wsi, *buf, 0, len); + return 0; + } + if (wsi->u.ws.rx_draining_ext) { +#if !defined(LWS_NO_CLIENT) + if (wsi->mode == LWSCM_WS_CLIENT) + m = lws_client_rx_sm(wsi, 0); + else +#endif + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) + wsi->rxflow_pos++; + + if (lws_client_rx_sm(wsi, *(*buf)++)) { + lwsl_debug("client_rx_sm exited\n"); + return -1; + } + len--; + } + lwsl_debug("%s: finished with %ld\n", __func__, (long)len); + return 0; + default: + break; + } + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) +{ + wsi->client_http_body_pending = !!something_left_to_send; +} + +int +lws_client_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + const char *cce = NULL; + unsigned char c; + char *sb = p; + int n = 0; + ssize_t len = 0; +#if defined(LWS_WITH_SOCKS5) + char conn_mode = 0, pending_timeout = 0; +#endif + + switch (wsi->mode) { + + case LWSCM_WSCL_WAITING_CONNECT: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + */ + + if (!lws_client_connect_2(wsi)) { + /* closed */ + lwsl_client("closed\n"); + return -1; + } + + /* either still pending connection, or changed mode */ + return 0; + +#if defined(LWS_WITH_SOCKS5) + /* SOCKS Greeting Reply */ + case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: + case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: + case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read EAGAIN, retrying\n"); + return 0; + } + lwsl_err("ERROR reading from SOCKS socket\n"); + goto bail3; + } + + switch (wsi->mode) { + + case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5) + goto socks_reply_fail; + + if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { + lwsl_client("SOCKS greeting reply: No Auth Method\n"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; + goto socks_send; + } + + if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { + lwsl_client("SOCKS greeting reply: User/Pw Method\n"); + socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; + goto socks_send; + } + goto socks_reply_fail; + + case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: + if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || + pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) + goto socks_reply_fail; + + lwsl_client("SOCKS password OK, sending connect\n"); + socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); + conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; +socks_send: + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to socks proxy\n"); + goto bail3; + } + + lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); + wsi->mode = conn_mode; + break; + +socks_reply_fail: + lwsl_notice("socks reply: v%d, err %d\n", + pt->serv_buf[0], pt->serv_buf[1]); + goto bail3; + + case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5 || + pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) + goto socks_reply_fail; + + lwsl_client("socks connect OK\n"); + + /* free stash since we are done with it */ + lws_free_set_NULL(wsi->u.hdr.stash); + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->socks_proxy_address)) + goto bail3; + + wsi->c_port = wsi->vhost->socks_proxy_port; + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + goto start_ws_handshake; + } + break; +#endif + + case LWSCM_WSCL_WAITING_PROXY_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("Proxy connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("Proxy read returned EAGAIN... retrying\n"); + return 0; + } + lwsl_err("ERROR reading from proxy socket\n"); + goto bail3; + } + + pt->serv_buf[13] = '\0'; + if (strcmp(sb, "HTTP/1.0 200 ") && + strcmp(sb, "HTTP/1.1 200 ")) { + lwsl_err("ERROR proxy: %s\n", sb); + goto bail3; + } + + /* clear his proxy connection timeout */ + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + * + * take care of our lws_callback_on_writable + * happening at a time when there's no real connection yet + */ +#if defined(LWS_WITH_SOCKS5) +start_ws_handshake: +#endif + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return -1; + +#ifdef LWS_OPENSSL_SUPPORT + /* we can retry this... just cook the SSL BIO the first time */ + + if (wsi->use_ssl && !wsi->ssl) { + if (lws_ssl_client_bio_create(wsi)) + return -1; + } + + if (wsi->use_ssl) { + n = lws_ssl_client_connect1(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect1 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; + + /* fallthru */ + + case LWSCM_WSCL_WAITING_SSL: + + if (wsi->use_ssl) { + n = lws_ssl_client_connect2(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect2 failed"; + goto bail3; + } + } else + wsi->ssl = NULL; +#endif + + wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + context->timeout_secs); + + /* fallthru */ + + case LWSCM_WSCL_ISSUE_HANDSHAKE2: + p = lws_generate_client_handshake(wsi, p); + if (p == NULL) { + if (wsi->mode == LWSCM_RAW) + return 0; + + lwsl_err("Failed to generate handshake for client\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + /* send our request to the server */ + lws_latency_pre(context, wsi); + + n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == p - sb); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + lwsl_debug("ERROR writing to client socket\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lws_callback_on_writable(wsi); + break; + } + + if (wsi->client_http_body_pending) { + wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } + + goto client_http_body_sent; + + case LWSCM_WSCL_ISSUE_HTTP_BODY: + if (wsi->client_http_body_pending) { + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); + /* user code must ask for writable callback */ + break; + } +client_http_body_sent: + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + context->timeout_secs); + break; + + case LWSCM_WSCL_WAITING_SERVER_REPLY: + /* + * handle server hanging up on us... + * but if there is POLLIN waiting, handle that first + */ + if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == + LWS_POLLHUP) { + + lwsl_debug("Server connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + cce = "Peer hung up"; + goto bail3; + } + + if (!(pollfd->revents & LWS_POLLIN)) + break; + + /* interpret the server response + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + * + * we have to take some care here to only take from the + * socket bytewise. The browser may (and has been seen to + * in the case that onopen() performs websocket traffic) + * coalesce both handshake response and websocket traffic + * in one packet, since at that point the connection is + * definitively ready from browser pov. + */ + len = 1; + while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && + len > 0) { + n = lws_ssl_capable_read(wsi, &c, 1); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == 1); + switch (n) { + case 0: + case LWS_SSL_CAPABLE_ERROR: + cce = "read failed"; + goto bail3; + case LWS_SSL_CAPABLE_MORE_SERVICE: + return 0; + } + + if (lws_parse(wsi, c)) { + lwsl_warn("problems parsing header\n"); + goto bail3; + } + } + + /* + * hs may also be coming in multiple packets, there is a 5-sec + * libwebsocket timeout still active here too, so if parsing did + * not complete just wait for next packet coming in this state + */ + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + break; + + /* + * otherwise deal with the handshake. If there's any + * packet traffic already arrived we'll trigger poll() again + * right away and deal with it that way + */ + return lws_client_interpret_server_handshake(wsi); + +bail3: + lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); + if (cce) + lwsl_info("reason: %s\n", cce); + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); + wsi->already_did_cce = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return -1; + + case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: + lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); + break; + + case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: + lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); + break; + default: + break; + } + + return 0; +} + +/* + * In-place str to lower case + */ + +static void +strtolower(char *s) +{ + while (*s) { +#ifdef LWS_PLAT_OPTEE + int tolower_optee(int c); + *s = tolower_optee((int)*s); +#else + *s = tolower((int)*s); +#endif + s++; + } +} + +int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi) +{ + lwsl_debug("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + /* we don't support chained client connections yet */ + return 1; +#if 0 + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED; + wsi->u.http.rx_content_length = 0; + wsi->hdr_parsing_completed = 0; + + /* He asked for it to stay alive indefinitely */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* + * As client, nothing new is going to come until we ask for it + * we can drop the ah, if any + */ + if (wsi->u.hdr.ah) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +#endif +} + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *wsi) +{ + if (!wsi->u.http.ah) + return 0; + + return wsi->u.http.ah->http_response; +} + +int +lws_client_interpret_server_handshake(struct lws *wsi) +{ + int n, len, okay = 0, port = 0, ssl = 0; + int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; + struct lws_context *context = wsi->context; + const char *pc, *prot, *ads = NULL, *path, *cce = NULL; + struct allocated_headers *ah = NULL; + char *p, *q; + char new_path[300]; +#ifndef LWS_NO_EXTENSIONS + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *sb = (char *)&pt->serv_buf[0]; + const struct lws_ext_options *opts; + const struct lws_extension *ext; + char ext_name[128]; + const char *c, *a; + char ignore; + int more = 1; + void *v; +#endif + if (wsi->u.hdr.stash) + lws_free_set_NULL(wsi->u.hdr.stash); + + ah = wsi->u.hdr.ah; + if (!wsi->do_ws) { + /* we are being an http client... + */ + lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED); + wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; + wsi->u.http.ah = ah; + ah->http_response = 0; + } + + /* + * well, what the server sent looked reasonable for syntax. + * Now let's confirm it sent all the necessary headers + * + * http (non-ws) client will expect something like this + * + * HTTP/1.0.200 + * server:.libwebsockets + * content-type:.text/html + * content-length:.17703 + * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 + * + * + * + */ + + wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE; + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); + if (wsi->do_ws && !p) { + lwsl_info("no URI\n"); + cce = "HS: URI missing"; + goto bail3; + } + if (!p) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + } + if (!p) { + cce = "HS: URI missing"; + lwsl_info("no URI\n"); + goto bail3; + } + n = atoi(p); + if (ah) + ah->http_response = n; + + if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); + if (!p) { + cce = "HS: Redirect code but no Location"; + goto bail3; + } + + /* Relative reference absolute path */ + if (p[0] == '/') + { +#ifdef LWS_OPENSSL_SUPPORT + ssl = wsi->use_ssl; +#endif + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + path = p + 1; /* +1 as lws_client_reset expects leading / to be omitted */ + } + /* Absolute (Full) URI */ + else if (strchr(p, ':')) + { + if (lws_parse_uri(p, &prot, &ads, &port, &path)) { + cce = "HS: URI did not parse"; + goto bail3; + } + + if (!strcmp(prot, "wss") || !strcmp(prot, "https")) + ssl = 1; + } + /* Relative reference relative path */ + else + { + /* This doesn't try to calculate an absolute path, that will be left to the server */ +#ifdef LWS_OPENSSL_SUPPORT + ssl = wsi->use_ssl; +#endif + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + path = new_path + 1; /* +1 as lws_client_reset expects leading / to be omitted */ + strncpy(new_path, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), sizeof(new_path)); + new_path[sizeof(new_path) - 1] = '\0'; + q = strrchr(new_path, '/'); + if (q) + { + strncpy(q + 1, p, sizeof(new_path) - (q - new_path) - 1); + new_path[sizeof(new_path) - 1] = '\0'; + } + else + { + path = p; + } + } + +#ifdef LWS_OPENSSL_SUPPORT + if (wsi->use_ssl && !ssl) { + cce = "HS: Redirect attempted SSL downgrade"; + goto bail3; + } +#endif + + if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) { + /* there are two ways to fail out with NULL return... + * simple, early problem where the wsi is intact, or + * we went through with the reconnect attempt and the + * wsi is already closed. In the latter case, the wsi + * has beet set to NULL additionally. + */ + lwsl_err("Redirect failed\n"); + cce = "HS: Redirect failed"; + if (wsi) + goto bail3; + + return 1; + } + return 0; + } + + if (!wsi->do_ws) { + +#ifdef LWS_WITH_HTTP_PROXY + wsi->perform_rewrite = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE), + "text/html", 9)) + wsi->perform_rewrite = 1; + } +#endif + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* he may choose to send us stuff in chunked transfer-coding */ + wsi->chunked = 0; + wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { + wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING), + "chunked"); + /* first thing is hex, after payload there is crlf */ + wsi->chunk_parser = ELCP_HEX; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + wsi->u.http.rx_content_length = + atoll(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + lwsl_notice("%s: incoming content length %llu\n", __func__, + (unsigned long long)wsi->u.http.rx_content_length); + wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length; + } else /* can't do 1.1 without a content length or chunked */ + if (!wsi->chunked) + wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + + cce = "HS: disallowed by client filter"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* call him back to inform him he is up */ + if (wsi->protocol->callback(wsi, + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + cce = "HS: disallowed at ESTABLISHED"; + goto bail3; + } + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lwsl_notice("%s: client connection up\n", __func__); + + return 0; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { + lwsl_info("no ACCEPT\n"); + cce = "HS: ACCEPT missing"; + goto bail3; + } + + if (p && strncmp(p, "101", 3)) { + lwsl_warn( + "lws_client_handshake: got bad HTTP response '%s'\n", p); + cce = "HS: ws upgrade response not 101"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); + if (!p) { + lwsl_info("no UPGRADE\n"); + cce = "HS: UPGRADE missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "websocket")) { + lwsl_warn( + "lws_client_handshake: got bad Upgrade header '%s'\n", p); + cce = "HS: Upgrade to something other than websocket"; + goto bail3; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION); + if (!p) { + lwsl_info("no Connection hdr\n"); + cce = "HS: CONNECTION missing"; + goto bail3; + } + strtolower(p); + if (strcmp(p, "upgrade")) { + lwsl_warn("lws_client_int_s_hs: bad header %s\n", p); + cce = "HS: UPGRADE malformed"; + goto bail3; + } + + pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + if (!pc) { + lwsl_parser("lws_client_int_s_hs: no protocol list\n"); + } else + lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc); + + /* + * confirm the protocol the server wants to talk was in the list + * of protocols we offered + */ + + len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + if (!len) { + lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n"); + /* + * no protocol name to work from, + * default to first protocol + */ + n = 0; + wsi->protocol = &wsi->vhost->protocols[0]; + goto check_extensions; + } + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); + len = strlen(p); + + while (pc && *pc && !okay) { + if (!strncmp(pc, p, len) && + (pc[len] == ',' || pc[len] == '\0')) { + okay = 1; + continue; + } + while (*pc && *pc++ != ',') + ; + while (*pc && *pc == ' ') + pc++; + } + + if (!okay) { + lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p); + cce = "HS: PROTOCOL malformed"; + goto bail2; + } + + /* + * identify the selected protocol struct and set it + */ + n = 0; + wsi->protocol = NULL; + while (wsi->vhost->protocols[n].callback && !wsi->protocol) { + if (strcmp(p, wsi->vhost->protocols[n].name) == 0) { + wsi->protocol = &wsi->vhost->protocols[n]; + break; + } + n++; + } + + if (wsi->protocol == NULL) { + lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p); + cce = "HS: Cannot match protocol"; + goto bail2; + } + +check_extensions: + /* + * stitch protocol choice into the vh protocol linked list + * We always insert ourselves at the start of the list + * + * X <-> B + * X <-> pAn <-> pB + */ + //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n", + // __func__, + // wsi->vhost->same_vh_protocol_list[n], + // wsi->same_vh_protocol_prev); + wsi->same_vh_protocol_prev = /* guy who points to us */ + &wsi->vhost->same_vh_protocol_list[n]; + wsi->same_vh_protocol_next = /* old first guy is our next */ + wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; + +#ifndef LWS_NO_EXTENSIONS + /* instantiate the accepted extensions */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { + lwsl_ext("no client extensions allowed by server\n"); + goto check_accept; + } + + /* + * break down the list of server accepted extensions + * and go through matching them or identifying bogons + */ + + if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) { + lwsl_warn("ext list from server failed to copy\n"); + cce = "HS: EXT: list too big"; + goto bail2; + } + + c = sb; + n = 0; + ignore = 0; + a = NULL; + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') { + ignore = 1; + if (!a) + a = c + 1; + } + if (ignore || *c == ' ') { + c++; + continue; + } + + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check we actually support it */ + + lwsl_notice("checking client ext %s\n", ext_name); + + n = 0; + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + n = 1; + lwsl_notice("instantiating client ext %s\n", ext_name); + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->count_act_ext] = ext; + + /* allow him to construct his ext instance */ + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_CLIENT_CONSTRUCT, + (void *)&wsi->act_ext_user[wsi->count_act_ext], + (void *)&opts, 0)) { + lwsl_info(" ext %s failed construction\n", ext_name); + ext++; + continue; + } + + /* + * allow the user code to override ext defaults if it + * wants to + */ + ext_name[0] = '\0'; + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, + (char *)ext->name, ext_name, + sizeof(ext_name))) { + cce = "HS: EXT: failed setting defaults"; + goto bail2; + } + + if (ext_name[0] && + lws_ext_parse_options(ext, wsi, wsi->act_ext_user[ + wsi->count_act_ext], opts, ext_name, + strlen(ext_name))) { + lwsl_err("%s: unable to parse user defaults '%s'", + __func__, ext_name); + cce = "HS: EXT: failed parsing defaults"; + goto bail2; + } + + /* + * give the extension the server options + */ + if (a && lws_ext_parse_options(ext, wsi, + wsi->act_ext_user[wsi->count_act_ext], + opts, a, c - a)) { + lwsl_err("%s: unable to parse remote def '%s'", + __func__, a); + cce = "HS: EXT: failed parsing options"; + goto bail2; + } + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_OPTION_CONFIRM, + wsi->act_ext_user[wsi->count_act_ext], + NULL, 0)) { + lwsl_err("%s: ext %s rejects server options %s", + __func__, ext->name, a); + cce = "HS: EXT: Rejects server options"; + goto bail2; + } + + wsi->count_act_ext++; + + ext++; + } + + if (n == 0) { + lwsl_warn("Unknown ext '%s'!\n", ext_name); + cce = "HS: EXT: unknown ext"; + goto bail2; + } + + a = NULL; + n = 0; + } + +check_accept: +#endif + + /* + * Confirm his accept token is the one we precomputed + */ + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); + if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) { + lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, + wsi->u.hdr.ah->initial_handshake_hash_base64); + cce = "HS: Accept hash wrong"; + goto bail2; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected by filter cb"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* free up his parsing allocations */ + lws_header_table_detach(wsi, 0); + + lws_union_transition(wsi, LWSCM_WS_CLIENT); + wsi->state = LWSS_ESTABLISHED; + lws_restart_ws_ping_pong_timer(wsi); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, then + * use a big default for compatibility + */ + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "client frame buffer"); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + cce = "HS: OOM"; + goto bail2; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_info("Allocating client RX buffer %d\n", n); + +#if !defined(LWS_WITH_ESP32) + if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n, + sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + cce = "HS: SO_SNDBUF failed"; + goto bail3; + } +#endif + + lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); + + /* call him back to inform him he is up */ + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, + wsi->user_space, NULL, 0)) { + cce = "HS: Rejected at CLIENT_ESTABLISHED"; + goto bail3; + } +#ifndef LWS_NO_EXTENSIONS + /* + * inform all extensions, not just active ones since they + * already know + */ + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + v = NULL; + for (n = 0; n < wsi->count_act_ext; n++) + if (wsi->active_extensions[n] == ext) + v = wsi->act_ext_user[n]; + + ext->callback(context, ext, wsi, + LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0); + ext++; + } +#endif + + return 0; + +bail3: + close_reason = LWS_CLOSE_STATUS_NOSTATUS; + +bail2: + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, + (unsigned int)strlen(cce)); + wsi->already_did_cce = 1; + + lwsl_info("closing connection due to bail2 connection error\n"); + + /* closing will free up his parsing allocations */ + lws_close_free_wsi(wsi, close_reason); + + return 1; +} + + +char * +lws_generate_client_handshake(struct lws *wsi, char *pkt) +{ + char buf[128], hash[20], key_b64[40], *p = pkt; + struct lws_context *context = wsi->context; + const char *meth; + int n; +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *ext; + int ext_count = 0; +#endif + const char *pp = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (!meth) { + meth = "GET"; + wsi->do_ws = 1; + } else { + wsi->do_ws = 0; + } + + if (!strcmp(meth, "RAW")) { + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lwsl_notice("client transition to raw\n"); + + if (pp) { + const struct lws_protocols *pr; + + pr = lws_vhost_name_to_protocol(wsi->vhost, pp); + + if (!pr) { + lwsl_err("protocol %s not enabled on vhost\n", + pp); + return NULL; + } + + lws_bind_protocol(wsi, pr); + } + + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_ADOPT, + wsi->user_space, NULL, 0)) + return NULL; + + lws_header_table_force_to_detachable_state(wsi); + lws_union_transition(wsi, LWSCM_RAW); + lws_header_table_detach(wsi, 1); + + return NULL; + } + + if (wsi->do_ws) { + /* + * create the random key + */ + n = lws_get_random(context, hash, 16); + if (n != 16) { + lwsl_err("Unable to read from random dev %s\n", + SYSTEM_RANDOM_FILEPATH); + return NULL; + } + + lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); + } + + /* + * 04 example client handshake + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + */ + + p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth, + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); + + p += sprintf(p, "Pragma: no-cache\x0d\x0a" + "Cache-Control: no-cache\x0d\x0a"); + + p += sprintf(p, "Host: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); + + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { + if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) + p += sprintf(p, "Origin: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); + else + p += sprintf(p, "Origin: http://%s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); + } + + if (wsi->do_ws) { + p += sprintf(p, "Upgrade: websocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Key: "); + strcpy(p, key_b64); + p += strlen(key_b64); + p += sprintf(p, "\x0d\x0a"); + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) + p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); + + /* tell the server what extensions we could support */ + +#ifndef LWS_NO_EXTENSIONS + ext = wsi->vhost->extensions; + while (ext && ext->callback) { + n = lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, + (char *)ext->name, 0); + if (n) { /* an extension vetos us */ + lwsl_ext("ext %s vetoed\n", (char *)ext->name); + ext++; + continue; + } + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, + wsi->user_space, (char *)ext->name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *p++ = ','; + else + p += sprintf(p, "Sec-WebSocket-Extensions: "); + p += sprintf(p, "%s", ext->client_offer); + ext_count++; + + ext++; + } + if (ext_count) + p += sprintf(p, "\x0d\x0a"); +#endif + + if (wsi->ietf_spec_revision) + p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", + wsi->ietf_spec_revision); + + /* prepare the expected server accept response */ + + key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ + n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); + + lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); + + lws_b64_encode_string(hash, 20, + wsi->u.hdr.ah->initial_handshake_hash_base64, + sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); + } + + /* give userland a chance to append, eg, cookies */ + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12)) + return NULL; + + p += sprintf(p, "\x0d\x0a"); + + return p; +} + diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c new file mode 100644 index 0000000000..b69fd2da30 --- /dev/null +++ b/thirdparty/lws/client/ssl-client.c @@ -0,0 +1,625 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +extern int lws_ssl_get_error(struct lws *wsi, int n); + +#if defined(USE_WOLFSSL) +#else + +static int +OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ +#if defined(LWS_WITH_MBEDTLS) + lwsl_notice("%s\n", __func__); + + return 0; +#else + SSL *ssl; + int n; + struct lws *wsi; + + /* keep old behaviour accepting self-signed server certs */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) { + lwsl_notice("accepting self-signed certificate (verify_callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID || + err == X509_V_ERR_CERT_HAS_EXPIRED) && + wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) { + if (err == X509_V_ERR_CERT_NOT_YET_VALID) + lwsl_notice("accepting not yet valid certificate (verify_callback)\n"); + else if (err == X509_V_ERR_CERT_HAS_EXPIRED) + lwsl_notice("accepting expired certificate (verify_callback)\n"); + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + return 1; // ok + } + } + } + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = lws_get_context_protocol(wsi->context, 0).callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* keep old behaviour if something wrong with server certs */ + /* if ssl error is overruled in callback and cert is ok, + * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and + * return value is 0 from callback */ + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(x509_ctx); + + if (err != X509_V_OK) { /* cert validation error was not handled in callback */ + int depth = X509_STORE_CTX_get_error_depth(x509_ctx); + const char* msg = X509_verify_cert_error_string(err); + lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth); + return preverify_ok; // not ok + } + } + /* convert callback return code from 0 = OK to verify callback return value 1 = OK */ + return !n; +#endif +} +#endif + +int +lws_ssl_client_bio_create(struct lws *wsi) +{ + char hostname[128], *p; + + if (lws_hdr_copy(wsi, hostname, sizeof(hostname), + _WSI_TOKEN_CLIENT_HOST) <= 0) { + lwsl_err("%s: Unable to get hostname\n", __func__); + + return -1; + } + + /* + * remove any :port part on the hostname... necessary for network + * connection but typical certificates do not contain it + */ + p = hostname; + while (*p) { + if (*p == ':') { + *p = '\0'; + break; + } + p++; + } + + wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); + if (!wsi->ssl) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); + lws_ssl_elaborate_error(); + return -1; + } + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); +#endif + +#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host + X509_VERIFY_PARAM *param; + (void)param; + + if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { + param = SSL_get0_param(wsi->ssl); + /* Enable automatic hostname checks */ + X509_VERIFY_PARAM_set_hostflags(param, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + X509_VERIFY_PARAM_set1_host(param, hostname, 0); + } + +#endif + +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) +#ifndef USE_OLD_CYASSL + /* OpenSSL_client_verify_callback will be called @ SSL_connect() */ + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); +#endif +#endif + +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#else +#ifdef WOLFSSL_SNI_HOST_NAME + wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname)); +#endif +#endif +#else +#if defined(LWS_WITH_MBEDTLS) + if (wsi->vhost->x509_client_CA) + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); + else + SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback); + +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +#endif +#endif +#endif + +#ifdef USE_WOLFSSL + /* + * wolfSSL/CyaSSL does certificate verification differently + * from OpenSSL. + * If we should ignore the certificate, we need to set + * this before SSL_new and SSL_connect is called. + * Otherwise the connect will simply fail with error code -155 + */ +#ifdef USE_OLD_CYASSL + if (wsi->use_ssl == 2) + CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#else + if (wsi->use_ssl == 2) + wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); +#endif +#endif /* USE_WOLFSSL */ + +#if !defined(LWS_WITH_MBEDTLS) + wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE); + SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); +#else + SSL_set_fd(wsi->ssl, wsi->desc.sockfd); +#endif + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else +#if !defined(LWS_WITH_MBEDTLS) + BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ +#endif +#endif + +#if !defined(LWS_WITH_MBEDTLS) + SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, + wsi); +#endif + + return 0; +} + +#if defined(LWS_WITH_MBEDTLS) +int ERR_get_error(void) +{ + return 0; +} +#endif + +int +lws_ssl_client_connect1(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + int n = 0; + + lws_latency_pre(context, wsi); + + n = SSL_connect(wsi->ssl); + + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) + goto some_wait; + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("%s: WANT_WRITE... retrying\n", __func__); + lws_callback_on_writable(wsi); +some_wait: + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + { + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + + lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lws_ssl_elaborate_error(); + } + + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + + unsigned long error = ERR_get_error(); + + if (error != SSL_ERROR_NONE) { + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + lwsl_err("SSL connect error %lu: %s\n", + error, ERR_error_string(error, sb)); + return -1; + } + + return 0; + } + + return 1; +} + +int +lws_ssl_client_connect2(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + char *sb = p; + int n = 0; + + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { + lws_latency_pre(context, wsi); + n = SSL_connect(wsi->ssl); + lwsl_debug("%s: SSL_connect says %d\n", __func__, n); + + lws_latency(context, wsi, + "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); + + if (n < 0) { + n = lws_ssl_get_error(wsi, n); + + if (n == SSL_ERROR_WANT_READ) { + lwsl_info("SSL_connect WANT_READ... retrying\n"); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + if (n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + lwsl_info("SSL_connect WANT_WRITE... retrying\n"); + lws_callback_on_writable(wsi); + + wsi->mode = LWSCM_WSCL_WAITING_SSL; + + return 0; /* no error */ + } + + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + unsigned long error = ERR_get_error(); + if (error != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + error, ERR_error_string(error, sb)); + return -1; + } + } + } + +#if defined(LWS_WITH_MBEDTLS) + { + X509 *peer = SSL_get_peer_certificate(wsi->ssl); + + if (!peer) { + lwsl_notice("peer did not provide cert\n"); + + return -1; + } + lwsl_notice("peer provided cert\n"); + } +#endif + +#ifndef USE_WOLFSSL + /* + * See comment above about wolfSSL certificate + * verification + */ + lws_latency_pre(context, wsi); + n = SSL_get_verify_result(wsi->ssl); + lws_latency(context, wsi, + "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); + + lwsl_debug("get_verify says %d\n", n); + + if (n != X509_V_OK) { + if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || + n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && + (wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { + lwsl_notice("accepting self-signed certificate\n"); + } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID || + n == X509_V_ERR_CERT_HAS_EXPIRED) && + (wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) { + lwsl_notice("accepting expired certificate\n"); + } else if (n == X509_V_ERR_CERT_NOT_YET_VALID) { + lwsl_notice("Cert is from the future... " + "probably our clock... accepting...\n"); + } else { + lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n", + n, ERR_error_string(n, sb)); + lws_ssl_elaborate_error(); + return -1; + } + } + +#endif /* USE_WOLFSSL */ + + return 1; +} + + +int lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + SSL_METHOD *method = NULL; + struct lws wsi; + unsigned long error; + const char *ca_filepath = info->ssl_ca_filepath; +#if !defined(LWS_WITH_MBEDTLS) + const char *cipher_list = info->ssl_cipher_list; + const char *private_key_filepath = info->ssl_private_key_filepath; + const char *cert_filepath = info->ssl_cert_filepath; + int n; + + if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW) + return 0; + + /* + * for backwards-compatibility default to using ssl_... members, but + * if the newer client-specific ones are given, use those + */ + if (info->client_ssl_cipher_list) + cipher_list = info->client_ssl_cipher_list; + if (info->client_ssl_cert_filepath) + cert_filepath = info->client_ssl_cert_filepath; + if (info->client_ssl_private_key_filepath) + private_key_filepath = info->client_ssl_private_key_filepath; +#endif + if (info->client_ssl_ca_filepath) + ca_filepath = info->client_ssl_ca_filepath; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return 0; + + if (vhost->ssl_client_ctx) + return 0; + + if (info->provided_client_ssl_ctx) { + /* use the provided OpenSSL context if given one */ + vhost->ssl_client_ctx = info->provided_client_ssl_ctx; + /* nothing for lib to delete */ + vhost->user_supplied_ssl_ctx = 1; + + return 0; + } + + /* basic openssl init already happened in context init */ + + /* choose the most recent spin of the api */ +#if defined(LWS_HAVE_TLS_CLIENT_METHOD) + method = (SSL_METHOD *)TLS_client_method(); +#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD) + method = (SSL_METHOD *)TLSv1_2_client_method(); +#else + method = (SSL_METHOD *)SSLv23_client_method(); +#endif + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + /* create context */ + vhost->ssl_client_ctx = SSL_CTX_new(method); + if (!vhost->ssl_client_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)vhost->context->pt[0].serv_buf)); + return 1; + } + + lwsl_notice("created client ssl context for %s\n", vhost->name); + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION); +#endif + +#if defined(LWS_WITH_MBEDTLS) + if (ca_filepath) { + lws_filepos_t len; + uint8_t *buf; + /* + * prototype this here, the shim does not export it in the + * header, and we need to use the shim unchanged for ESP32 case + */ + X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len); + + if (alloc_file(vhost->context, ca_filepath, &buf, &len)) { + lwsl_err("Load CA cert file %s failed\n", ca_filepath); + return 1; + } + + vhost->x509_client_CA = d2i_X509(NULL, buf, len); + free(buf); + if (!vhost->x509_client_CA) { + lwsl_err("client CA: x509 parse failed\n"); + return 1; + } + + SSL_CTX_add_client_CA(vhost->ssl_client_ctx, + vhost->x509_client_CA); + + lwsl_notice("client loaded CA for verification %s\n", ca_filepath); + } +#else + SSL_CTX_set_options(vhost->ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + + if (cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list); + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!ca_filepath) { + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err("Unable to load SSL Client certs from %s " + "(set by LWS_OPENSSL_CLIENT_CERTS) -- " + "client ssl isn't going to work\n", + LWS_OPENSSL_CLIENT_CERTS); + } else + if (!SSL_CTX_load_verify_locations( + vhost->ssl_client_ctx, ca_filepath, NULL)) { + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work\n", info->client_ssl_ca_filepath); + lws_ssl_elaborate_error(); + } + else + lwsl_info("loaded ssl_ca_filepath\n"); + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (cert_filepath) { + lwsl_notice("%s: doing cert filepath\n", __func__); + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx, + cert_filepath); + if (n < 1) { + lwsl_err("problem %d getting cert '%s'\n", n, + cert_filepath); + lws_ssl_elaborate_error(); + return 1; + } + lwsl_notice("Loaded client cert %s\n", cert_filepath); + } + if (private_key_filepath) { + lwsl_notice("%s: doing private key filepath\n", __func__); + lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info); + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx, + private_key_filepath, SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s'\n", + private_key_filepath); + lws_ssl_elaborate_error(); + return 1; + } + lwsl_notice("Loaded client cert private key %s\n", + private_key_filepath); + + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + } +#endif + /* + * give him a fake wsi with context set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = vhost->context; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, + vhost->ssl_client_ctx, NULL, 0); + + return 0; +} diff --git a/thirdparty/lws/context.c b/thirdparty/lws/context.c new file mode 100644 index 0000000000..f67476b1e3 --- /dev/null +++ b/thirdparty/lws/context.c @@ -0,0 +1,1628 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#ifndef LWS_BUILD_HASH +#define LWS_BUILD_HASH "unknown-build-hash" +#endif + +static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; + +/** + * lws_get_library_version: get version and git hash library built from + * + * returns a const char * to a string like "1.1 178d78c" + * representing the library version followed by the git head hash it + * was built from + */ +LWS_VISIBLE const char * +lws_get_library_version(void) +{ + return library_version; +} + +static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" +}; + +#if defined(LWS_WITH_HTTP2) +/* + * These are the standardized defaults. + * Override what actually goes in the vhost settings in platform or user code. + * Leave these alone because they are used to determine "what is different + * from the protocol defaults". + */ +const struct http2_settings lws_h2_defaults = { { + 1, + /* H2SET_HEADER_TABLE_SIZE */ 4096, + /* *** This controls how many entries in the dynamic table *** + * Allows the sender to inform the remote endpoint of the maximum + * size of the header compression table used to decode header + * blocks, in octets. The encoder can select any size equal to or + * less than this value by using signaling specific to the header + * compression format inside a header block (see [COMPRESSION]). + * The initial value is 4,096 octets. + */ + /* H2SET_ENABLE_PUSH */ 1, + /* H2SET_MAX_CONCURRENT_STREAMS */ 0x7fffffff, + /* H2SET_INITIAL_WINDOW_SIZE */ 65535, + /* H2SET_MAX_FRAME_SIZE */ 16384, + /* H2SET_MAX_HEADER_LIST_SIZE */ 0x7fffffff, + /*< This advisory setting informs a peer of the maximum size of + * header list that the sender is prepared to accept, in octets. + * The value is based on the uncompressed size of header fields, + * including the length of the name and value in octets plus an + * overhead of 32 octets for each header field. + */ + +}}; + +const struct http2_settings lws_h2_stock_settings = { { + 1, + /* H2SET_HEADER_TABLE_SIZE */ 4096, + /* *** This controls how many entries in the dynamic table *** + * Allows the sender to inform the remote endpoint of the maximum + * size of the header compression table used to decode header + * blocks, in octets. The encoder can select any size equal to or + * less than this value by using signaling specific to the header + * compression format inside a header block (see [COMPRESSION]). + * The initial value is 4,096 octets. + * + * Can't pass h2spec with less than 4096 here... + */ + /* H2SET_ENABLE_PUSH */ 1, + /* H2SET_MAX_CONCURRENT_STREAMS */ 24, + /* H2SET_INITIAL_WINDOW_SIZE */ 65535, + /* H2SET_MAX_FRAME_SIZE */ 16384, + /* H2SET_MAX_HEADER_LIST_SIZE */ 4096, + /*< This advisory setting informs a peer of the maximum size of + * header list that the sender is prepared to accept, in octets. + * The value is based on the uncompressed size of header fields, + * including the length of the name and value in octets plus an + * overhead of 32 octets for each header field. + */ + +}}; +#endif + +LWS_VISIBLE void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, + const struct lws_protocols *prot, int size) +{ + int n = 0; + + /* allocate the vh priv array only on demand */ + if (!vhost->protocol_vh_privs) { + vhost->protocol_vh_privs = (void **)lws_zalloc( + vhost->count_protocols * sizeof(void *), "protocol_vh_privs"); + if (!vhost->protocol_vh_privs) + return NULL; + } + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) + return NULL; + } + + vhost->protocol_vh_privs[n] = lws_zalloc(size, "vh priv"); + return vhost->protocol_vh_privs[n]; +} + +LWS_VISIBLE void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, + const struct lws_protocols *prot) +{ + int n = 0; + + if (!vhost || !vhost->protocol_vh_privs) + return NULL; + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols && + strcmp(vhost->protocols[n].name, prot->name)) + n++; + + if (n == vhost->count_protocols) { + lwsl_err("%s: unknown protocol %p\n", __func__, prot); + return NULL; + } + } + + return vhost->protocol_vh_privs[n]; +} + +static const struct lws_protocol_vhost_options * +lws_vhost_protocol_options(struct lws_vhost *vh, const char *name) +{ + const struct lws_protocol_vhost_options *pvo = vh->pvo; + + if (!name) + return NULL; + + while (pvo) { + if (!strcmp(pvo->name, name)) + return pvo; + pvo = pvo->next; + } + + return NULL; +} + +/* + * inform every vhost that hasn't already done it, that + * his protocols are initializing + */ +LWS_VISIBLE int +lws_protocol_init(struct lws_context *context) +{ + struct lws_vhost *vh = context->vhost_list; + const struct lws_protocol_vhost_options *pvo, *pvo1; + struct lws wsi; + int n; + + if (context->doing_protocol_init) + return 0; + + context->doing_protocol_init = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + + lwsl_info("%s\n", __func__); + + while (vh) { + wsi.vhost = vh; + + /* only do the protocol init once for a given vhost */ + if (vh->created_vhost_protocols) + goto next; + + /* initialize supported protocols on this vhost */ + + for (n = 0; n < vh->count_protocols; n++) { + wsi.protocol = &vh->protocols[n]; + if (!vh->protocols[n].name) + continue; + pvo = lws_vhost_protocol_options(vh, + vh->protocols[n].name); + if (pvo) { + /* + * linked list of options specific to + * vh + protocol + */ + pvo1 = pvo; + pvo = pvo1->options; + + while (pvo) { + lwsl_notice( + " vhost \"%s\", protocol \"%s\", option \"%s\"\n", + vh->name, + vh->protocols[n].name, + pvo->name); + + if (!strcmp(pvo->name, "default")) { + lwsl_notice("Setting default " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->default_protocol_index = n; + } + if (!strcmp(pvo->name, "raw")) { + lwsl_notice("Setting raw " + "protocol for vh %s to %s\n", + vh->name, + vh->protocols[n].name); + vh->raw_protocol_index = n; + } + pvo = pvo->next; + } + + pvo = pvo1->options; + } + + /* + * inform all the protocols that they are doing their + * one-time initialization if they want to. + * + * NOTE the wsi is all zeros except for the context, vh + * + protocol ptrs so lws_get_context(wsi) etc can work + */ + if (vh->protocols[n].callback(&wsi, + LWS_CALLBACK_PROTOCOL_INIT, NULL, + (void *)pvo, 0)) { + lwsl_err("%s: vhost %s failed init\n", __func__, + vh->protocols[n].name); + context->doing_protocol_init = 0; + return 1; + } + } + + vh->created_vhost_protocols = 1; +next: + vh = vh->vhost_next; + } + + context->doing_protocol_init = 0; + + if (!context->protocol_init_done) + lws_finalize_startup(context); + + context->protocol_init_done = 1; + + return 0; +} + +LWS_VISIBLE int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_ssl_info *si; +#ifdef LWS_WITH_CGI + struct lws_cgi_args *args; +#endif +#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) + char buf[512]; + int n; +#endif + + switch (reason) { + case LWS_CALLBACK_HTTP: +#ifndef LWS_NO_SERVER + if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) + return -1; + + if (lws_http_transaction_completed(wsi)) +#endif + return -1; + break; +#if !defined(LWS_NO_SERVER) + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + if (lws_http_transaction_completed(wsi)) + return -1; + break; +#endif + + case LWS_CALLBACK_HTTP_WRITEABLE: +#ifdef LWS_WITH_CGI + if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | + LWS_CB_REASON_AUX_BF__CGI)) { + n = lws_cgi_write_split_stdout_headers(wsi); + if (n < 0) { + lwsl_debug("LWS_CB_REASON_AUX_BF__CGI forcing close\n"); + return -1; + } + if (!n) + lws_rx_flow_control(wsi->cgi->stdwsi[LWS_STDOUT], 1); + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; + else + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { + if (!wsi->http2_substream) { + memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); + lwsl_debug("writing chunk terminator and exiting\n"); + n = lws_write(wsi, (unsigned char *)buf + LWS_PRE, + 5, LWS_WRITE_HTTP); + } else + n = lws_write(wsi, (unsigned char *)buf + LWS_PRE, + 0, LWS_WRITE_HTTP_FINAL); + + /* always close after sending it */ + return -1; + } +#endif +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { + char *px = buf + LWS_PRE; + int lenx = sizeof(buf) - LWS_PRE; + + /* + * our sink is writeable and our source has something + * to read. So read a lump of source material of + * suitable size to send or what's available, whichever + * is the smaller. + */ + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; + if (!lws_get_child(wsi)) + break; + if (lws_http_client_read(lws_get_child(wsi), &px, &lenx) < 0) + return -1; + break; + } +#endif + break; + +#if defined(LWS_WITH_HTTP_PROXY) + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + assert(lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; + lws_callback_on_writable(lws_get_parent(wsi)); + break; + + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: + assert(lws_get_parent(wsi)); + n = lws_write(lws_get_parent(wsi), (unsigned char *)in, + len, LWS_WRITE_HTTP); + if (n < 0) + return -1; + break; + + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { + unsigned char *p, *end; + char ctype[64], ctlen = 0; + + p = (unsigned char *)buf + LWS_PRE; + end = p + sizeof(buf) - LWS_PRE; + + if (lws_add_http_header_status(lws_get_parent(wsi), + HTTP_STATUS_OK, &p, end)) + return 1; + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_SERVER, + (unsigned char *)"libwebsockets", + 13, &p, end)) + return 1; + + ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), + WSI_TOKEN_HTTP_CONTENT_TYPE); + if (ctlen > 0) { + if (lws_add_http_header_by_token(lws_get_parent(wsi), + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)ctype, ctlen, &p, end)) + return 1; + } + + if (lws_finalize_http_header(lws_get_parent(wsi), &p, end)) + return 1; + + *p = '\0'; + n = lws_write(lws_get_parent(wsi), + (unsigned char *)buf + LWS_PRE, + p - ((unsigned char *)buf + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + if (n < 0) + return -1; + + break; } + +#endif + +#ifdef LWS_WITH_CGI + /* CGI IO events (POLLIN/OUT) appear here, our default policy is: + * + * - POST data goes on subprocess stdin + * - subprocess stdout goes on http via writeable callback + * - subprocess stderr goes to the logs + */ + case LWS_CALLBACK_CGI: + args = (struct lws_cgi_args *)in; + switch (args->ch) { /* which of stdin/out/err ? */ + case LWS_STDIN: + /* TBD stdin rx flow control */ + break; + case LWS_STDOUT: + /* quench POLLIN on STDOUT until MASTER got writeable */ + lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; + /* when writing to MASTER would not block */ + lws_callback_on_writable(wsi); + break; + case LWS_STDERR: + n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]); + if (n < 0) + break; + n = read(n, buf, sizeof(buf) - 2); + if (n > 0) { + if (buf[n - 1] != '\n') + buf[n++] = '\n'; + buf[n] = '\0'; + lwsl_notice("CGI-stderr: %s\n", buf); + } + break; + } + break; + + case LWS_CALLBACK_CGI_TERMINATED: + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n", + wsi->cgi->explicitly_chunked, + (uint64_t)wsi->cgi->content_length); + if (!wsi->cgi->explicitly_chunked && + !wsi->cgi->content_length) { + /* send terminating chunk */ + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n"); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; + lws_callback_on_writable(wsi); + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); + break; + } + return -1; + + case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ + args = (struct lws_cgi_args *)in; + args->data[args->len] = '\0'; + n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]); + if (n < 0) + return -1; + n = write(n, args->data, args->len); + if (n < args->len) + lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + return n; +#endif + + case LWS_CALLBACK_SSL_INFO: + si = in; + + (void)si; + lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", + si->where, si->ret); + break; + + default: + break; + } + + return 0; +} + +/* list of supported protocols and callbacks */ + +static const struct lws_protocols protocols_dummy[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + lws_callback_http_dummy, /* callback */ + 0, /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + 0, NULL, 0 + }, + /* + * the other protocols are provided by lws plugins + */ + { NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */ +}; + +#ifdef LWS_PLAT_OPTEE +#undef LWS_HAVE_GETENV +#endif + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_vhost *vh = lws_zalloc(sizeof(*vh), "create vhost"), + **vh1 = &context->vhost_list; + const struct lws_http_mount *mounts; + const struct lws_protocol_vhost_options *pvo; +#ifdef LWS_WITH_PLUGINS + struct lws_plugin *plugin = context->plugin_list; +#endif + struct lws_protocols *lwsp; + int m, f = !info->pvo; +#ifdef LWS_HAVE_GETENV + char *p; +#endif + int n; + + if (!vh) + return NULL; + + if (!info->protocols) + info->protocols = &protocols_dummy[0]; + + vh->context = context; + if (!info->vhost_name) + vh->name = "default"; + else + vh->name = info->vhost_name; + + if (info->options & LWS_SERVER_OPTION_ONLY_RAW) + lwsl_info("%s set to only support RAW\n", vh->name); + +#if defined(LWS_WITH_HTTP2) + vh->set = context->set; + if (info->http2_settings[0]) + for (n = 1; n < LWS_H2_SETTINGS_LEN; n++) + vh->set.s[n] = info->http2_settings[n]; +#endif + + vh->iface = info->iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + vh->bind_iface = info->bind_iface; +#endif + + for (vh->count_protocols = 0; + info->protocols[vh->count_protocols].callback; + vh->count_protocols++) + ; + + vh->options = info->options; + vh->pvo = info->pvo; + vh->headers = info->headers; + vh->user = info->user; + vh->ssl_info_event_mask = info->ssl_info_event_mask; + if (info->keepalive_timeout) + vh->keepalive_timeout = info->keepalive_timeout; + else + vh->keepalive_timeout = 5; + + if (info->timeout_secs_ah_idle) + vh->timeout_secs_ah_idle = info->timeout_secs_ah_idle; + else + vh->timeout_secs_ah_idle = 10; + + /* + * give the vhost a unified list of protocols including the + * ones that came from plugins + */ + lwsp = lws_zalloc(sizeof(struct lws_protocols) * + (vh->count_protocols + + context->plugin_protocol_count + 1), "vhost-specific plugin table"); + if (!lwsp) { + lwsl_err("OOM\n"); + return NULL; + } + + m = vh->count_protocols; + memcpy(lwsp, info->protocols, sizeof(struct lws_protocols) * m); + + /* for compatibility, all protocols enabled on vhost if only + * the default vhost exists. Otherwise only vhosts who ask + * for a protocol get it enabled. + */ + + if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + f = 0; + (void)f; +#ifdef LWS_WITH_PLUGINS + if (plugin) { + + while (plugin) { + for (n = 0; n < plugin->caps.count_protocols; n++) { + /* + * for compatibility's sake, no pvo implies + * allow all protocols + */ + if (f || lws_vhost_protocol_options(vh, + plugin->caps.protocols[n].name)) { + memcpy(&lwsp[m], + &plugin->caps.protocols[n], + sizeof(struct lws_protocols)); + m++; + vh->count_protocols++; + } + } + plugin = plugin->list; + } + } +#endif + + if ( +#ifdef LWS_WITH_PLUGINS + (context->plugin_list) || +#endif + context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + vh->protocols = lwsp; + else { + vh->protocols = info->protocols; + lws_free(lwsp); + } + + vh->same_vh_protocol_list = (struct lws **) + lws_zalloc(sizeof(struct lws *) * vh->count_protocols, "same vh list"); + + vh->mount_list = info->mounts; + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(context)) { + lwsl_notice("Creating Vhost '%s' path \"%s\", %d protocols\n", + vh->name, info->iface, vh->count_protocols); + } else +#endif + lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n", + vh->name, info->port, vh->count_protocols, + LWS_IPV6_ENABLED(vh) ? "on" : "off"); + + mounts = info->mounts; + while (mounts) { + (void)mount_protocols[0]; + lwsl_notice(" mounting %s%s to %s\n", + mount_protocols[mounts->origin_protocol], + mounts->origin, mounts->mountpoint); + + /* convert interpreter protocol names to pointers */ + pvo = mounts->interpret; + while (pvo) { + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(pvo->value, vh->protocols[n].name)) { + ((struct lws_protocol_vhost_options *)pvo)->value = + (const char *)(lws_intptr_t)n; + break; + } + if (n == vh->count_protocols) + lwsl_err("ignoring unknown interpret protocol %s\n", + pvo->value); + pvo = pvo->next; + } + + mounts = mounts->mount_next; + } + +#ifndef LWS_NO_EXTENSIONS +#ifdef LWS_WITH_PLUGINS + if (context->plugin_extension_count) { + + m = 0; + while (info->extensions && info->extensions[m].callback) + m++; + + /* + * give the vhost a unified list of extensions including the + * ones that came from plugins + */ + vh->extensions = lws_zalloc(sizeof(struct lws_extension) * + (m + + context->plugin_extension_count + 1), "extensions"); + if (!vh->extensions) + return NULL; + + memcpy((struct lws_extension *)vh->extensions, info->extensions, + sizeof(struct lws_extension) * m); + plugin = context->plugin_list; + while (plugin) { + memcpy((struct lws_extension *)&vh->extensions[m], + plugin->caps.extensions, + sizeof(struct lws_extension) * + plugin->caps.count_extensions); + m += plugin->caps.count_extensions; + plugin = plugin->list; + } + } else +#endif + vh->extensions = info->extensions; +#endif + + vh->listen_port = info->port; +#if !defined(LWS_WITH_ESP8266) + vh->http_proxy_port = 0; + vh->http_proxy_address[0] = '\0'; +#if defined(LWS_WITH_SOCKS5) + vh->socks_proxy_port = 0; + vh->socks_proxy_address[0] = '\0'; +#endif + + /* either use proxy from info, or try get it from env var */ + + /* http proxy */ + if (info->http_proxy_address) { + /* override for backwards compatibility */ + if (info->http_proxy_port) + vh->http_proxy_port = info->http_proxy_port; + lws_set_proxy(vh, info->http_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("http_proxy"); + if (p) + lws_set_proxy(vh, p); +#endif + } +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + if (info->socks_proxy_address) { + /* override for backwards compatibility */ + if (info->socks_proxy_port) + vh->socks_proxy_port = info->socks_proxy_port; + lws_set_socks(vh, info->socks_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("socks_proxy"); + if (p) + lws_set_socks(vh, p); +#endif + } +#endif +#endif + + vh->ka_time = info->ka_time; + vh->ka_interval = info->ka_interval; + vh->ka_probes = info->ka_probes; + + if (vh->options & LWS_SERVER_OPTION_STS) + lwsl_notice(" STS enabled\n"); + +#ifdef LWS_WITH_ACCESS_LOG + if (info->log_filepath) { + vh->log_fd = open(info->log_filepath, + O_CREAT | O_APPEND | O_RDWR, 0600); + if (vh->log_fd == (int)LWS_INVALID_FILE) { + lwsl_err("unable to open log filepath %s\n", + info->log_filepath); + goto bail; + } +#ifndef WIN32 + if (context->uid != -1) + if (chown(info->log_filepath, context->uid, + context->gid) == -1) + lwsl_err("unable to chown log file %s\n", + info->log_filepath); +#endif + } else + vh->log_fd = (int)LWS_INVALID_FILE; +#endif + if (lws_context_init_server_ssl(info, vh)) + goto bail; + if (lws_context_init_client_ssl(info, vh)) + goto bail; + if (lws_context_init_server(info, vh)) { + lwsl_err("init server failed\n"); + goto bail; + } + + while (1) { + if (!(*vh1)) { + *vh1 = vh; + break; + } + vh1 = &(*vh1)->vhost_next; + }; + /* for the case we are adding a vhost much later, after server init */ + + if (context->protocol_init_done) + lws_protocol_init(context); + + return vh; + +bail: + lws_free(vh); + + return NULL; +} + +LWS_VISIBLE int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context_creation_info i; + + memcpy(&i, info, sizeof(i)); + i.port = CONTEXT_PORT_NO_LISTEN; + + return lws_context_init_client_ssl(&i, vhost); +} + +LWS_VISIBLE struct lws_context * +lws_create_context(struct lws_context_creation_info *info) +{ + struct lws_context *context = NULL; + struct lws_plat_file_ops *prev; +#ifndef LWS_NO_DAEMONIZE + int pid_daemon = get_daemonize_pid(); +#endif + int n; +#if defined(__ANDROID__) + struct rlimit rt; +#endif + + lwsl_info("Initial logging level %d\n", log_level); + lwsl_info("Libwebsockets version: %s\n", library_version); +#if defined(GCC_VER) + lwsl_info("Compiled with %s\n", GCC_VER); +#endif +#if LWS_POSIX +#ifdef LWS_WITH_IPV6 + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_info("IPV6 compiled in and enabled\n"); + else + lwsl_info("IPV6 compiled in but disabled\n"); +#else + lwsl_info("IPV6 not compiled in\n"); +#endif +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_ESP32) + lws_feature_status_libev(info); + lws_feature_status_libuv(info); +#endif +#endif + lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN); + lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS); + lwsl_info(" LWS_MAX_SMP : %u\n", LWS_MAX_SMP); + lwsl_info(" sizeof (*info) : %ld\n", (long)sizeof(*info)); +#if defined(LWS_WITH_STATS) + lwsl_info(" LWS_WITH_STATS : on\n"); +#endif +#if LWS_POSIX + lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); +#endif +#if defined(LWS_WITH_HTTP2) + lwsl_info(" HTTP2 support : available\n"); +#else + lwsl_info(" HTTP2 support : not configured"); +#endif + if (lws_plat_context_early_init()) + return NULL; + + context = lws_zalloc(sizeof(struct lws_context), "context"); + if (!context) { + lwsl_err("No memory for websocket context\n"); + return NULL; + } + if (info->pt_serv_buf_size) + context->pt_serv_buf_size = info->pt_serv_buf_size; + else + context->pt_serv_buf_size = 4096; + +#if defined(LWS_WITH_HTTP2) + context->set = lws_h2_stock_settings; +#endif + +#if LWS_MAX_SMP > 1 + pthread_mutex_init(&context->lock, NULL); +#endif + +#if defined(LWS_WITH_ESP32) + context->last_free_heap = esp_get_free_heap_size(); +#endif + + /* default to just the platform fops implementation */ + + context->fops_platform.LWS_FOP_OPEN = _lws_plat_file_open; + context->fops_platform.LWS_FOP_CLOSE = _lws_plat_file_close; + context->fops_platform.LWS_FOP_SEEK_CUR = _lws_plat_file_seek_cur; + context->fops_platform.LWS_FOP_READ = _lws_plat_file_read; + context->fops_platform.LWS_FOP_WRITE = _lws_plat_file_write; + context->fops_platform.fi[0].sig = NULL; + + /* + * arrange a linear linked-list of fops starting from context->fops + * + * platform fops + * [ -> fops_zip (copied into context so .next settable) ] + * [ -> info->fops ] + */ + + context->fops = &context->fops_platform; + prev = (struct lws_plat_file_ops *)context->fops; + +#if defined(LWS_WITH_ZIP_FOPS) + /* make a soft copy so we can set .next */ + context->fops_zip = fops_zip; + prev->next = &context->fops_zip; + prev = (struct lws_plat_file_ops *)prev->next; +#endif + + /* if user provided fops, tack them on the end of the list */ + if (info->fops) + prev->next = info->fops; + + context->reject_service_keywords = info->reject_service_keywords; + if (info->external_baggage_free_on_destroy) + context->external_baggage_free_on_destroy = + info->external_baggage_free_on_destroy; + + context->time_up = time(NULL); + + context->simultaneous_ssl_restriction = info->simultaneous_ssl_restriction; + +#ifndef LWS_NO_DAEMONIZE + if (pid_daemon) { + context->started_with_parent = pid_daemon; + lwsl_info(" Started with daemon pid %d\n", pid_daemon); + } +#endif +#if defined(__ANDROID__) + n = getrlimit ( RLIMIT_NOFILE,&rt); + if (-1 == n) { + lwsl_err("Get RLIMIT_NOFILE failed!\n"); + return NULL; + } + context->max_fds = rt.rlim_cur; +#else + context->max_fds = getdtablesize(); +#endif + + if (info->count_threads) + context->count_threads = info->count_threads; + else + context->count_threads = 1; + + if (context->count_threads > LWS_MAX_SMP) + context->count_threads = LWS_MAX_SMP; + + context->token_limits = info->token_limits; + + context->options = info->options; + + if (info->timeout_secs) + context->timeout_secs = info->timeout_secs; + else + context->timeout_secs = AWAITING_TIMEOUT; + + context->ws_ping_pong_interval = info->ws_ping_pong_interval; + + lwsl_info(" default timeout (secs): %u\n", context->timeout_secs); + + if (info->max_http_header_data) + context->max_http_header_data = info->max_http_header_data; + else + if (info->max_http_header_data2) + context->max_http_header_data = + info->max_http_header_data2; + else + context->max_http_header_data = LWS_DEF_HEADER_LEN; + if (info->max_http_header_pool) + context->max_http_header_pool = info->max_http_header_pool; + else + context->max_http_header_pool = LWS_DEF_HEADER_POOL; + + /* + * Allocate the per-thread storage for scratchpad buffers, + * and header data pool + */ + for (n = 0; n < context->count_threads; n++) { + context->pt[n].serv_buf = lws_malloc(context->pt_serv_buf_size, + "pt_serv_buf"); + if (!context->pt[n].serv_buf) { + lwsl_err("OOM\n"); + return NULL; + } + +#ifdef LWS_WITH_LIBUV + context->pt[n].context = context; +#endif + context->pt[n].tid = n; + context->pt[n].ah_list = NULL; + context->pt[n].ah_pool_length = 0; + + lws_pt_mutex_init(&context->pt[n]); + } + + if (info->fd_limit_per_thread) + context->fd_limit_per_thread = info->fd_limit_per_thread; + else + context->fd_limit_per_thread = context->max_fds / + context->count_threads; + + lwsl_info(" Threads: %d each %d fds\n", context->count_threads, + context->fd_limit_per_thread); + + if (!info->ka_interval && info->ka_time > 0) { + lwsl_err("info->ka_interval can't be 0 if ka_time used\n"); + return NULL; + } + +#ifdef LWS_WITH_LIBEV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; +#endif /* LWS_WITH_LIBEV */ +#ifdef LWS_WITH_LIBUV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; +#endif +#ifdef LWS_WITH_LIBEVENT + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_event_sigint_cb = &lws_event_sigint_cb; +#endif /* LWS_WITH_LIBEVENT */ + +#if defined(LWS_WITH_PEER_LIMITS) + /* scale the peer hash table according to the max fds for the process, + * so that the max list depth averages 16. Eg, 1024 fd -> 64, + * 102400 fd -> 6400 + */ + context->pl_hash_elements = + (context->count_threads * context->fd_limit_per_thread) / 16; + context->pl_hash_table = lws_zalloc(sizeof(struct lws_peer *) * + context->pl_hash_elements, "peer limits hash table"); + context->ip_limit_ah = info->ip_limit_ah; + context->ip_limit_wsi = info->ip_limit_wsi; +#endif + + lwsl_info(" mem: context: %5lu bytes (%ld ctx + (%ld thr x %d))\n", + (long)sizeof(struct lws_context) + + (context->count_threads * context->pt_serv_buf_size), + (long)sizeof(struct lws_context), + (long)context->count_threads, + context->pt_serv_buf_size); + + lwsl_info(" mem: http hdr rsvd: %5lu bytes (%u thr x (%u + %lu) x %u))\n", + (long)(context->max_http_header_data + + sizeof(struct allocated_headers)) * + context->max_http_header_pool * context->count_threads, + context->count_threads, + context->max_http_header_data, + (long)sizeof(struct allocated_headers), + context->max_http_header_pool); + n = sizeof(struct lws_pollfd) * context->count_threads * + context->fd_limit_per_thread; + context->pt[0].fds = lws_zalloc(n, "fds table"); + if (context->pt[0].fds == NULL) { + lwsl_err("OOM allocating %d fds\n", context->max_fds); + goto bail; + } + lwsl_info(" mem: pollfd map: %5u\n", n); + + if (info->server_string) { + context->server_string = info->server_string; + context->server_string_len = (short) + strlen(context->server_string); + } + +#if LWS_MAX_SMP > 1 + /* each thread serves his own chunk of fds */ + for (n = 1; n < (int)info->count_threads; n++) + context->pt[n].fds = context->pt[n - 1].fds + + context->fd_limit_per_thread; +#endif + + if (lws_plat_init(context, info)) + goto bail; + +#if defined(LWS_WITH_HTTP2) + /* + * let the user code see what the platform default SETTINGS were, he + * can modify them when he creates the vhosts. + */ + for (n = 1; n < LWS_H2_SETTINGS_LEN; n++) + info->http2_settings[n] = context->set.s[n]; +#endif + + lws_context_init_ssl_library(info); + + context->user_space = info->user; + + /* + * if he's not saying he'll make his own vhosts later then act + * compatibly and make a default vhost using the data in the info + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + if (!lws_create_vhost(context, info)) { + lwsl_err("Failed to create default vhost\n"); + return NULL; + } + + lws_context_init_extensions(info, context); + + lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n", + (unsigned long)sizeof(struct lws)); + + strcpy(context->canonical_hostname, "unknown"); + lws_server_get_canonical_hostname(context, info); + + context->uid = info->uid; + context->gid = info->gid; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + memcpy(context->caps, info->caps, sizeof(context->caps)); + context->count_caps = info->count_caps; +#endif + + /* + * drop any root privs for this process + * to listen on port < 1023 we would have needed root, but now we are + * listening, we don't want the power for anything else + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(info); + + /* + * give all extensions a chance to create any per-context + * allocations they need + */ + if (info->port != CONTEXT_PORT_NO_LISTEN) { + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + } else + if (lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0) + goto bail; + + return context; + +bail: + lws_context_destroy(context); + return NULL; +} + +LWS_VISIBLE LWS_EXTERN void +lws_context_deprecate(struct lws_context *context, lws_reload_func cb) +{ + struct lws_vhost *vh = context->vhost_list, *vh1; + struct lws *wsi; + + /* + * "deprecation" means disable the context from accepting any new + * connections and free up listen sockets to be used by a replacement + * context. + * + * Otherwise the deprecated context remains operational, until its + * number of connected sockets falls to zero, when it is deleted. + */ + + /* for each vhost, close his listen socket */ + + while (vh) { + wsi = vh->lserv_wsi; + if (wsi) { + wsi->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + wsi->context->deprecation_pending_listen_close_count++; + /* + * other vhosts can share the listen port, they + * point to the same wsi. So zap those too. + */ + vh1 = context->vhost_list; + while (vh1) { + if (vh1->lserv_wsi == wsi) + vh1->lserv_wsi = NULL; + vh1 = vh1->vhost_next; + } + } + vh = vh->vhost_next; + } + + context->deprecated = 1; + context->deprecation_cb = cb; +} + +LWS_VISIBLE LWS_EXTERN int +lws_context_is_deprecated(struct lws_context *context) +{ + return context->deprecated; +} + +LWS_VISIBLE void +lws_context_destroy2(struct lws_context *context); + + +static void +lws_vhost_destroy1(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context_per_thread *pt; + int n, m = vh->context->count_threads; + struct lws_context *context = vh->context; + struct lws wsi; + + lwsl_info("%s\n", __func__); + + if (vh->being_destroyed) + return; + + vh->being_destroyed = 1; + + /* + * Are there other vhosts that are piggybacking on our listen socket? + * If so we need to hand the listen socket off to one of the others + * so it will remain open. If not, leave it attached to the closing + * vhost and it will get closed. + */ + + if (vh->lserv_wsi) + lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { + if (v != vh && + !v->being_destroyed && + v->listen_port == vh->listen_port && + ((!v->iface && !vh->iface) || + (v->iface && vh->iface && + !strcmp(v->iface, vh->iface)))) { + /* + * this can only be a listen wsi, which is + * restricted... it has no protocol or other + * bindings or states. So we can simply + * swap it to a vhost that has the same + * iface + port, but is not closing. + */ + assert(v->lserv_wsi == NULL); + v->lserv_wsi = vh->lserv_wsi; + vh->lserv_wsi = NULL; + if (v->lserv_wsi) + v->lserv_wsi->vhost = v; + + lwsl_notice("%s: listen skt from %s to %s\n", + __func__, vh->name, v->name); + break; + } + } lws_end_foreach_ll(v, vhost_next); + + /* + * Forcibly close every wsi assoicated with this vhost. That will + * include the listen socket if it is still associated with the closing + * vhost. + */ + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost != vh) + continue; + + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + } + + /* + * let the protocols destroy the per-vhost protocol objects + */ + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = vh->context; + wsi.vhost = vh; + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + wsi.protocol = protocol; + protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + n++; + } + } + + /* + * remove vhost from context list of vhosts + */ + + lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) { + if (*pv == vh) { + *pv = vh->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* add ourselves to the pending destruction list */ + + vh->vhost_next = vh->context->vhost_pending_destruction_list; + vh->context->vhost_pending_destruction_list = vh; +} + +static void +lws_vhost_destroy2(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context *context = vh->context; + struct lws_deferred_free *df; + int n; + + lwsl_info("%s: %p\n", __func__, vh); + + /* if we are still on deferred free list, remove ourselves */ + + lws_start_foreach_llp(struct lws_deferred_free **, pdf, + context->deferred_free_list) { + if ((*pdf)->payload == vh) { + df = *pdf; + *pdf = df->next; + lws_free(df); + break; + } + } lws_end_foreach_llp(pdf, next); + + /* remove ourselves from the pending destruction list */ + + lws_start_foreach_llp(struct lws_vhost **, pv, + context->vhost_pending_destruction_list) { + if ((*pv) == vh) { + *pv = (*pv)->vhost_next; + break; + } + } lws_end_foreach_llp(pv, vhost_next); + + /* + * Free all the allocations associated with the vhost + */ + + protocol = vh->protocols; + if (protocol) { + n = 0; + while (n < vh->count_protocols) { + if (vh->protocol_vh_privs && + vh->protocol_vh_privs[n]) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + } + protocol++; + n++; + } + } + if (vh->protocol_vh_privs) + lws_free(vh->protocol_vh_privs); + lws_ssl_SSL_CTX_destroy(vh); + lws_free(vh->same_vh_protocol_list); +#ifdef LWS_WITH_PLUGINS + if (LWS_LIBUV_ENABLED(context)) { + if (context->plugin_list) + lws_free((void *)vh->protocols); + } else +#endif + { + if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) + lws_free((void *)vh->protocols); + } + +#ifdef LWS_WITH_PLUGINS +#ifndef LWS_NO_EXTENSIONS + if (context->plugin_extension_count) + lws_free((void *)vh->extensions); +#endif +#endif +#ifdef LWS_WITH_ACCESS_LOG + if (vh->log_fd != (int)LWS_INVALID_FILE) + close(vh->log_fd); +#endif + + /* + * although async event callbacks may still come for wsi handles with + * pending close in the case of asycn event library like libuv, + * they do not refer to the vhost. So it's safe to free. + */ + + lwsl_info(" %s: Freeing vhost %p\n", __func__, vh); + + memset(vh, 0, sizeof(*vh)); + lws_free(vh); +} + +int +lws_check_deferred_free(struct lws_context *context, int force) +{ + struct lws_deferred_free *df; + time_t now = lws_now_secs(); + + lws_start_foreach_llp(struct lws_deferred_free **, pdf, + context->deferred_free_list) { + if (now > (*pdf)->deadline || force) { + df = *pdf; + *pdf = df->next; + /* finalize vh destruction */ + lwsl_notice("deferred vh %p destroy\n", df->payload); + lws_vhost_destroy2(df->payload); + lws_free(df); + continue; /* after deletion we already point to next */ + } + } lws_end_foreach_llp(pdf, next); + + return 0; +} + +LWS_VISIBLE void +lws_vhost_destroy(struct lws_vhost *vh) +{ + struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free"); + + if (!df) + return; + + lws_vhost_destroy1(vh); + + /* part 2 is deferred to allow all the handle closes to complete */ + + df->next = vh->context->deferred_free_list; + df->deadline = lws_now_secs() + 5; + df->payload = vh; + vh->context->deferred_free_list = df; +} + +LWS_VISIBLE void +lws_context_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + struct lws_vhost *vh = NULL; + struct lws wsi; + int n, m; + + if (!context) { + lwsl_notice("%s: ctx %p\n", __func__, context); + return; + } + if (context->being_destroyed1) { + lwsl_notice("%s: ctx %p: already being destroyed\n", + __func__, context); + return; + } + + lwsl_info("%s: ctx %p\n", __func__, context); + + m = context->count_threads; + context->being_destroyed = 1; + context->being_destroyed1 = 1; + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + +#ifdef LWS_LATENCY + if (context->worst_latency_info[0]) + lwsl_notice("Worst latency: %s\n", context->worst_latency_info); +#endif + + while (m--) { + pt = &context->pt[m]; + + for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { + struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY + /* no protocol close */); + n--; + } + lws_pt_mutex_destroy(pt); + } + + /* + * give all extensions a chance to clean up any per-context + * allocations they might have made + */ + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0); + + n = lws_ext_cb_all_exts(context, NULL, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0); + + /* + * inform all the protocols that they are done and will have no more + * callbacks. + * + * We can't free things until after the event loop shuts down. + */ + if (context->protocol_init_done) + vh = context->vhost_list; + while (vh) { + struct lws_vhost *vhn = vh->vhost_next; + lws_vhost_destroy1(vh); + vh = vhn; + } + + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + + lws_libev_destroyloop(context, n); + lws_libuv_destroyloop(context, n); + lws_libevent_destroyloop(context, n); + + lws_free_set_NULL(context->pt[n].serv_buf); + + while (pt->ah_list) + _lws_destroy_ah(pt, pt->ah_list); + } + lws_plat_context_early_destroy(context); + + if (context->pt[0].fds) + lws_free_set_NULL(context->pt[0].fds); + + if (!LWS_LIBUV_ENABLED(context)) + lws_context_destroy2(context); +} + +/* + * call the second one after the event loop has been shut down cleanly + */ + +LWS_VISIBLE void +lws_context_destroy2(struct lws_context *context) +{ + struct lws_vhost *vh = NULL, *vh1; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t n; +#endif + + lwsl_info("%s: ctx %p\n", __func__, context); + + /* + * free all the per-vhost allocations + */ + + vh = context->vhost_list; + while (vh) { + vh1 = vh->vhost_next; + lws_vhost_destroy2(vh); + vh = vh1; + } + + /* remove ourselves from the pending destruction list */ + + while (context->vhost_pending_destruction_list) + /* removes itself from list */ + lws_vhost_destroy2(context->vhost_pending_destruction_list); + + + lws_stats_log_dump(context); + + lws_ssl_context_destroy(context); + lws_plat_context_late_destroy(context); + +#if defined(LWS_WITH_PEER_LIMITS) + for (n = 0; n < context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + *peer = df->next; + lws_free(df); + continue; + } lws_end_foreach_llp(peer, next); + } + lws_free(context->pl_hash_table); +#endif + + if (context->external_baggage_free_on_destroy) + free(context->external_baggage_free_on_destroy); + + lws_check_deferred_free(context, 1); + +#if LWS_MAX_SMP > 1 + pthread_mutex_destroy(&context->lock, NULL); +#endif + + lws_free(context); +} diff --git a/thirdparty/lws/ext/extension-permessage-deflate.c b/thirdparty/lws/ext/extension-permessage-deflate.c new file mode 100644 index 0000000000..e2be2ae615 --- /dev/null +++ b/thirdparty/lws/ext/extension-permessage-deflate.c @@ -0,0 +1,473 @@ +/* + * ./lib/extension-permessage-deflate.c + * + * Copyright (C) 2016 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "extension-permessage-deflate.h" +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#define LWS_ZLIB_MEMLEVEL 8 + +const struct lws_ext_options lws_ext_pm_deflate_options[] = { + /* public RFC7692 settings */ + { "server_no_context_takeover", EXTARG_NONE }, + { "client_no_context_takeover", EXTARG_NONE }, + { "server_max_window_bits", EXTARG_OPT_DEC }, + { "client_max_window_bits", EXTARG_OPT_DEC }, + /* ones only user code can set */ + { "rx_buf_size", EXTARG_DEC }, + { "tx_buf_size", EXTARG_DEC }, + { "compression_level", EXTARG_DEC }, + { "mem_level", EXTARG_DEC }, + { NULL, 0 }, /* sentinel */ +}; + +static void +lws_extension_pmdeflate_restrict_args(struct lws *wsi, + struct lws_ext_pm_deflate_priv *priv) +{ + int n, extra; + + /* cap the RX buf at the nearest power of 2 to protocol rx buf */ + + n = wsi->context->pt_serv_buf_size; + if (wsi->protocol->rx_buffer_size) + n = wsi->protocol->rx_buffer_size; + + extra = 7; + while (n >= 1 << (extra + 1)) + extra++; + + if (extra < priv->args[PMD_RX_BUF_PWR2]) { + priv->args[PMD_RX_BUF_PWR2] = extra; + lwsl_info(" Capping pmd rx to %d\n", 1 << extra); + } +} + +LWS_VISIBLE int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_ext_pm_deflate_priv *priv = + (struct lws_ext_pm_deflate_priv *)user; + struct lws_tokens *eff_buf = (struct lws_tokens *)in; + static unsigned char trail[] = { 0, 0, 0xff, 0xff }; + int n, ret = 0, was_fin = 0, extra; + struct lws_ext_option_arg *oa; + + switch (reason) { + case LWS_EXT_CB_NAMED_OPTION_SET: + oa = in; + if (!oa->option_name) + break; + for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++) + if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name)) + break; + + if (n == ARRAY_SIZE(lws_ext_pm_deflate_options)) + break; + oa->option_index = n; + + /* fallthru */ + + case LWS_EXT_CB_OPTION_SET: + oa = in; + lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__, + oa->option_index, oa->start, oa->len); + if (oa->start) + priv->args[oa->option_index] = atoi(oa->start); + else + priv->args[oa->option_index] = 1; + + if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8) + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9; + + lws_extension_pmdeflate_restrict_args(wsi, priv); + break; + + case LWS_EXT_CB_OPTION_CONFIRM: + if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 || + priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 || + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 || + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15) + return -1; + break; + + case LWS_EXT_CB_CLIENT_CONSTRUCT: + case LWS_EXT_CB_CONSTRUCT: + + n = context->pt_serv_buf_size; + if (wsi->protocol->rx_buffer_size) + n = wsi->protocol->rx_buffer_size; + + if (n < 128) { + lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n", + wsi->protocol->name); + return -1; + } + + /* fill in **user */ + priv = lws_zalloc(sizeof(*priv), "pmd priv"); + *((void **)user) = priv; + lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__); + memset(priv, 0, sizeof(*priv)); + + /* fill in pointer to options list */ + if (in) + *((const struct lws_ext_options **)in) = + lws_ext_pm_deflate_options; + + /* fallthru */ + + case LWS_EXT_CB_OPTION_DEFAULT: + + /* set the public, RFC7692 defaults... */ + + priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0, + priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0; + priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15; + priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15; + + /* ...and the ones the user code can override */ + + priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */ + priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */ + priv->args[PMD_COMP_LEVEL] = 1; + priv->args[PMD_MEM_LEVEL] = 8; + + lws_extension_pmdeflate_restrict_args(wsi, priv); + break; + + case LWS_EXT_CB_DESTROY: + lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__); + lws_free(priv->buf_rx_inflated); + lws_free(priv->buf_tx_deflated); + if (priv->rx_init) + (void)inflateEnd(&priv->rx); + if (priv->tx_init) + (void)deflateEnd(&priv->tx); + lws_free(priv); + return ret; + + case LWS_EXT_CB_PAYLOAD_RX: + lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n", + __func__, eff_buf->token_len, priv->rx.avail_in); + if (!(wsi->u.ws.rsv_first_msg & 0x40)) + return 0; + +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + printf("\n"); + } + printf("\n"); +#endif + if (!priv->rx_init) + if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) { + lwsl_err("%s: iniflateInit failed\n", __func__); + return -1; + } + priv->rx_init = 1; + if (!priv->buf_rx_inflated) + priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 + + (1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf"); + if (!priv->buf_rx_inflated) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + /* + * We have to leave the input stream alone if we didn't + * finish with it yet. The input stream is held in the wsi + * rx buffer by the caller, so this assumption is safe while + * we block new rx while draining the existing rx + */ + if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) { + priv->rx.next_in = (unsigned char *)eff_buf->token; + priv->rx.avail_in = eff_buf->token_len; + } + priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE; + eff_buf->token = (char *)priv->rx.next_out; + priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2]; + + if (priv->rx_held_valid) { + lwsl_ext("-- RX piling on held byte --\n"); + *(priv->rx.next_out++) = priv->rx_held; + priv->rx.avail_out--; + priv->rx_held_valid = 0; + } + + /* if... + * + * - he has no remaining input content for this message, and + * - and this is the final fragment, and + * - we used everything that could be drained on the input side + * + * ...then put back the 00 00 FF FF the sender stripped as our + * input to zlib + */ + if (!priv->rx.avail_in && wsi->u.ws.final && + !wsi->u.ws.rx_packet_length) { + lwsl_ext("RX APPEND_TRAILER-DO\n"); + was_fin = 1; + priv->rx.next_in = trail; + priv->rx.avail_in = sizeof(trail); + } + + n = inflate(&priv->rx, Z_NO_FLUSH); + lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n, + priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final); + switch (n) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + lwsl_info("zlib error inflate %d: %s\n", + n, priv->rx.msg); + return -1; + } + /* + * If we did not already send in the 00 00 FF FF, and he's + * out of input, he did not EXACTLY fill the output buffer + * (which is ambiguous and we will force it to go around + * again by withholding a byte), and he's otherwise working on + * being a FIN fragment, then do the FIN message processing + * of faking up the 00 00 FF FF that the sender stripped. + */ + if (!priv->rx.avail_in && wsi->u.ws.final && + !wsi->u.ws.rx_packet_length && !was_fin && + priv->rx.avail_out /* ambiguous as to if it is the end */ + ) { + lwsl_ext("RX APPEND_TRAILER-DO\n"); + was_fin = 1; + priv->rx.next_in = trail; + priv->rx.avail_in = sizeof(trail); + n = inflate(&priv->rx, Z_SYNC_FLUSH); + lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n, + priv->rx.avail_in, priv->rx.avail_out); + switch (n) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + lwsl_info("zlib error inflate %d: %s\n", + n, priv->rx.msg); + return -1; + } + } + /* + * we must announce in our returncode now if there is more + * output to be expected from inflate, so we can decide to + * set the FIN bit on this bufferload or not. However zlib + * is ambiguous when we exactly filled the inflate buffer. It + * does not give us a clue as to whether we should understand + * that to mean he ended on a buffer boundary, or if there is + * more in the pipeline. + * + * So to work around that safely, if it used all output space + * exactly, we ALWAYS say there is more coming and we withhold + * the last byte of the buffer to guarantee that is true. + * + * That still leaves us at least one byte to finish with a FIN + * on, even if actually nothing more is coming from the next + * inflate action itself. + */ + if (!priv->rx.avail_out) { /* he used all available out buf */ + lwsl_ext("-- rx grabbing held --\n"); + /* snip the last byte and hold it for next time */ + priv->rx_held = *(--priv->rx.next_out); + priv->rx_held_valid = 1; + } + + eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token; + priv->count_rx_between_fin += eff_buf->token_len; + + lwsl_ext(" %s: RX leaving with new effbuff len %d, " + "ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n", + __func__, eff_buf->token_len, priv->rx_held_valid, + priv->rx.avail_in, + (unsigned long)priv->count_rx_between_fin); + + if (was_fin) { + priv->count_rx_between_fin = 0; + if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) { + (void)inflateEnd(&priv->rx); + priv->rx_init = 0; + } + } +#if 0 + for (n = 0; n < eff_buf->token_len; n++) + putchar(eff_buf->token[n]); + puts("\n"); +#endif + + return priv->rx_held_valid; + + case LWS_EXT_CB_PAYLOAD_TX: + + if (!priv->tx_init) { + n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL], + Z_DEFLATED, + -priv->args[PMD_SERVER_MAX_WINDOW_BITS + + (wsi->vhost->listen_port <= 0)], + priv->args[PMD_MEM_LEVEL], + Z_DEFAULT_STRATEGY); + if (n != Z_OK) { + lwsl_ext("inflateInit2 failed %d\n", n); + return 1; + } + } + priv->tx_init = 1; + if (!priv->buf_tx_deflated) + priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 + + (1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf"); + if (!priv->buf_tx_deflated) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + if (eff_buf->token) { + lwsl_ext("%s: TX: eff_buf length %d\n", __func__, + eff_buf->token_len); + priv->tx.next_in = (unsigned char *)eff_buf->token; + priv->tx.avail_in = eff_buf->token_len; + } + +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + printf("\n"); + } + printf("\n"); +#endif + + priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5; + eff_buf->token = (char *)priv->tx.next_out; + priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2]; + + n = deflate(&priv->tx, Z_SYNC_FLUSH); + if (n == Z_STREAM_ERROR) { + lwsl_ext("%s: Z_STREAM_ERROR\n", __func__); + return -1; + } + + if (priv->tx_held_valid) { + priv->tx_held_valid = 0; + if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2]) + /* + * we can get a situation he took something in + * but did not generate anything out, at the end + * of a message (eg, next thing he sends is 80 + * 00, a zero length FIN, like Authobahn can + * send). + * If we have come back as a FIN, we must not + * place the pending trailer 00 00 FF FF, just + * the 1 byte of live data + */ + *(--eff_buf->token) = priv->tx_held[0]; + else { + /* he generated data, prepend whole pending */ + eff_buf->token -= 5; + for (n = 0; n < 5; n++) + eff_buf->token[n] = priv->tx_held[n]; + + } + } + priv->compressed_out = 1; + eff_buf->token_len = (int)(priv->tx.next_out - + (unsigned char *)eff_buf->token); + + /* + * we must announce in our returncode now if there is more + * output to be expected from inflate, so we can decide to + * set the FIN bit on this bufferload or not. However zlib + * is ambiguous when we exactly filled the inflate buffer. It + * does not give us a clue as to whether we should understand + * that to mean he ended on a buffer boundary, or if there is + * more in the pipeline. + * + * Worse, the guy providing the stuff we are sending may not + * know until after that this was, actually, the last chunk, + * that can happen even if we did not fill the output buf, ie + * he may send after this a zero-length FIN fragment. + * + * This is super difficult because we must snip the last 4 + * bytes in the case this is the last compressed output of the + * message. The only way to deal with it is defer sending the + * last 5 bytes of each frame until the next one, when we will + * be in a position to understand if that has a FIN or not. + */ + + extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out; + + if (eff_buf->token_len >= 4 + extra) { + lwsl_ext("tx held %d\n", 4 + extra); + priv->tx_held_valid = extra; + for (n = 3 + extra; n >= 0; n--) + priv->tx_held[n] = *(--priv->tx.next_out); + eff_buf->token_len -= 4 + extra; + } + lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n", + eff_buf->token_len, !priv->tx.avail_out); + + return !priv->tx.avail_out; /* 1 == have more tx pending */ + + case LWS_EXT_CB_PACKET_TX_PRESEND: + if (!priv->compressed_out) + break; + priv->compressed_out = 0; + + if ((*(eff_buf->token) & 0x80) && + priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) { + lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n"); + (void)deflateEnd(&priv->tx); + priv->tx_init = 0; + } + + n = *(eff_buf->token) & 15; + /* set RSV1, but not on CONTINUATION */ + if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME) + *eff_buf->token |= 0x40; +#if 0 + for (n = 0; n < eff_buf->token_len; n++) { + printf("%02X ", (unsigned char)eff_buf->token[n]); + if ((n & 15) == 15) + puts("\n"); + } + puts("\n"); +#endif + lwsl_ext("%s: tx opcode 0x%02X\n", __func__, + (unsigned char)*eff_buf->token); + break; + + default: + break; + } + + return 0; +} + diff --git a/thirdparty/lws/ext/extension-permessage-deflate.h b/thirdparty/lws/ext/extension-permessage-deflate.h new file mode 100644 index 0000000000..8737736897 --- /dev/null +++ b/thirdparty/lws/ext/extension-permessage-deflate.h @@ -0,0 +1,41 @@ + +#include <zlib.h> + +#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1 +#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION + +enum arg_indexes { + PMD_SERVER_NO_CONTEXT_TAKEOVER, + PMD_CLIENT_NO_CONTEXT_TAKEOVER, + PMD_SERVER_MAX_WINDOW_BITS, + PMD_CLIENT_MAX_WINDOW_BITS, + PMD_RX_BUF_PWR2, + PMD_TX_BUF_PWR2, + PMD_COMP_LEVEL, + PMD_MEM_LEVEL, + + PMD_ARG_COUNT +}; + +struct lws_ext_pm_deflate_priv { + z_stream rx; + z_stream tx; + + unsigned char *buf_rx_inflated; /* RX inflated output buffer */ + unsigned char *buf_tx_deflated; /* TX deflated output buffer */ + + size_t count_rx_between_fin; + + unsigned char args[PMD_ARG_COUNT]; + unsigned char tx_held[5]; + unsigned char rx_held; + + unsigned char tx_init:1; + unsigned char rx_init:1; + unsigned char compressed_out:1; + unsigned char rx_held_valid:1; + unsigned char tx_held_valid:1; + unsigned char rx_append_trailer:1; + unsigned char pending_tx_trailer:1; +}; + diff --git a/thirdparty/lws/ext/extension.c b/thirdparty/lws/ext/extension.c new file mode 100644 index 0000000000..ac28204034 --- /dev/null +++ b/thirdparty/lws/ext/extension.c @@ -0,0 +1,344 @@ +#include "private-libwebsockets.h" + +#include "extension-permessage-deflate.h" + +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct lws_context *context) +{ + lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE); +} + +enum lws_ext_option_parser_states { + LEAPS_SEEK_NAME, + LEAPS_EAT_NAME, + LEAPS_SEEK_VAL, + LEAPS_EAT_DEC, + LEAPS_SEEK_ARG_TERM +}; + +LWS_VISIBLE int +lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, + void *ext_user, const struct lws_ext_options *opts, + const char *in, int len) +{ + enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME; + unsigned int match_map = 0, n, m, w = 0, count_options = 0, + pending_close_quote = 0; + struct lws_ext_option_arg oa; + + oa.option_name = NULL; + + while (opts[count_options].name) + count_options++; + while (len) { + lwsl_ext("'%c' %d", *in, leap); + switch (leap) { + case LEAPS_SEEK_NAME: + if (*in == ' ') + break; + if (*in == ',') { + len = 1; + break; + } + match_map = (1 << count_options) - 1; + leap = LEAPS_EAT_NAME; + w = 0; + + /* fallthru */ + + case LEAPS_EAT_NAME: + oa.start = NULL; + oa.len = 0; + m = match_map; + n = 0; + pending_close_quote = 0; + while (m) { + if (m & 1) { + lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w); + + if (*in == opts[n].name[w]) { + if (!opts[n].name[w + 1]) { + oa.option_index = n; + lwsl_ext("hit %d\n", oa.option_index); + leap = LEAPS_SEEK_VAL; + if (len == 1) + goto set_arg; + break; + } + } else { + match_map &= ~(1 << n); + if (!match_map) { + lwsl_ext("empty match map\n"); + return -1; + } + } + } + m >>= 1; + n++; + } + w++; + break; + case LEAPS_SEEK_VAL: + if (*in == ' ') + break; + if (*in == ',') { + len = 1; + break; + } + if (*in == ';' || len == 1) { /* ie,nonoptional */ + if (opts[oa.option_index].type == EXTARG_DEC) + return -1; + leap = LEAPS_SEEK_NAME; + goto set_arg; + } + if (*in == '=') { + w = 0; + pending_close_quote = 0; + if (opts[oa.option_index].type == EXTARG_NONE) + return -1; + + leap = LEAPS_EAT_DEC; + break; + } + return -1; + + case LEAPS_EAT_DEC: + if (*in >= '0' && *in <= '9') { + if (!w) + oa.start = in; + w++; + if (len != 1) + break; + } + if (!w && *in =='"') { + pending_close_quote = 1; + break; + } + if (!w) + return -1; + if (pending_close_quote && *in != '"' && len != 1) + return -1; + leap = LEAPS_SEEK_ARG_TERM; + if (oa.start) + oa.len = in - oa.start; + if (len == 1) + oa.len++; + +set_arg: + ext->callback(lws_get_context(wsi), + ext, wsi, LWS_EXT_CB_OPTION_SET, + ext_user, (char *)&oa, 0); + if (len == 1) + break; + if (pending_close_quote && *in == '"') + break; + + /* fallthru */ + + case LEAPS_SEEK_ARG_TERM: + if (*in == ' ') + break; + if (*in == ';') { + leap = LEAPS_SEEK_NAME; + break; + } + if (*in == ',') { + len = 1; + break; + } + return -1; + } + len--; + in++; + } + + return 0; +} + + +/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */ + +int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len) +{ + int n, m, handled = 0; + + for (n = 0; n < wsi->count_act_ext; n++) { + m = wsi->active_extensions[n]->callback(lws_get_context(wsi), + wsi->active_extensions[n], wsi, reason, + wsi->act_ext_user[n], arg, len); + if (m < 0) { + lwsl_ext("Ext '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + /* valgrind... */ + if (reason == LWS_EXT_CB_DESTROY) + wsi->act_ext_user[n] = NULL; + if (m > handled) + handled = m; + } + + return handled; +} + +int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, + int reason, void *arg, int len) +{ + int n = 0, m, handled = 0; + const struct lws_extension *ext; + + if (!wsi || !wsi->vhost) + return 0; + + ext = wsi->vhost->extensions; + + while (ext && ext->callback && !handled) { + m = ext->callback(context, ext, wsi, reason, + (void *)(lws_intptr_t)n, arg, len); + if (m < 0) { + lwsl_ext("Ext '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + if (m) + handled = 1; + + ext++; + n++; + } + + return 0; +} + +int +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len) +{ + struct lws_tokens eff_buf; + int ret, m, n = 0; + + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + /* + * while we have original buf to spill ourselves, or extensions report + * more in their pipeline + */ + + ret = 1; + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + + /* show every extension the new incoming data */ + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0); + if (m < 0) + return -1; + if (m) /* handled */ + ret = 1; + + if ((char *)buf != eff_buf.token) + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from ext access\n"); + return -1; + } + + /* always either sent it all or privately buffered */ + if (wsi->u.ws.clean_buffer) + len = n; + } + + lwsl_parser("written %d bytes to client\n", n); + + /* no extension has more to spill? Then we can go */ + + if (!ret) + break; + + /* we used up what we had */ + + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* + * Did that leave the pipe choked? + * Or we had to hold on to some of it? + */ + + if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len) + /* no we could add more, lets's do that */ + continue; + + lwsl_debug("choked\n"); + + /* + * Yes, he's choked. Don't spill the rest now get a callback + * when he is ready to send and take care of it there + */ + lws_callback_on_writable(wsi); + wsi->extension_data_pending = 1; + ret = 0; + } + + return len; +} + +int +lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r, + void *v, size_t len) +{ + struct lws_context *context = wsi->context; + int n, handled = 0; + + /* maybe an extension will take care of it for us */ + + for (n = 0; n < wsi->count_act_ext && !handled; n++) { + if (!wsi->active_extensions[n]->callback) + continue; + + handled |= wsi->active_extensions[n]->callback(context, + wsi->active_extensions[n], wsi, + r, wsi->act_ext_user[n], v, len); + } + + return handled; +} + +int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + struct lws_ext_option_arg oa; + int idx = 0; + + /* first identify if the ext is active on this wsi */ + while (idx < wsi->count_act_ext && + strcmp(wsi->active_extensions[idx]->name, ext_name)) + idx++; + + if (idx == wsi->count_act_ext) + return -1; /* request ext not active on this wsi */ + + oa.option_name = opt_name; + oa.option_index = 0; + oa.start = opt_val; + oa.len = 0; + + return wsi->active_extensions[idx]->callback( + wsi->context, wsi->active_extensions[idx], wsi, + LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0); +} diff --git a/thirdparty/lws/handshake.c b/thirdparty/lws/handshake.c new file mode 100644 index 0000000000..bc7609d920 --- /dev/null +++ b/thirdparty/lws/handshake.c @@ -0,0 +1,280 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * -04 of the protocol (actually the 80th version) has a radically different + * handshake. The 04 spec gives the following idea + * + * The handshake from the client looks as follows: + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + * + * The handshake from the server looks as follows: + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + */ + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * We have to take care about parsing because the headers may be split + * into multiple fragments. They may contain unknown headers with arbitrary + * argument lengths. So, we parse using a single-character at a time state + * machine that is completely independent of packet size. + * + * Returns <0 for error or length of chars consumed from buf (up to len) + */ + +LWS_VISIBLE int +lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len) +{ + unsigned char *last_char, *oldbuf = buf; + lws_filepos_t body_chunk_len; + size_t n; + + switch (wsi->state) { +#ifdef LWS_WITH_HTTP2 + case LWSS_HTTP2_AWAIT_CLIENT_PREFACE: + case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS: + case LWSS_HTTP2_ESTABLISHED: + n = 0; + //lwsl_debug("%s: starting new block of %d\n", __func__, (int)len); + /* + * wsi here is always the network connection wsi, not a stream + * wsi. + */ + while (n < len) { + /* + * we were accepting input but now we stopped doing so + */ + if (lws_is_flowcontrolled(wsi)) { + lws_rxflow_cache(wsi, buf, n, len); + + return 1; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) { + wsi->rxflow_pos++; + assert(wsi->rxflow_pos <= wsi->rxflow_len); + } + + if (lws_h2_parser(wsi, buf[n++])) { + lwsl_debug("%s: http2_parser bailed\n", __func__); + goto bail; + } + } + lwsl_debug("%s: used up block of %d\n", __func__, (int)len); + break; +#endif + + case LWSS_HTTP_ISSUING_FILE: + return 0; + + case LWSS_CLIENT_HTTP_ESTABLISHED: + break; + + case LWSS_HTTP: + wsi->hdr_parsing_completed = 0; + + /* fallthru */ + + case LWSS_HTTP_HEADERS: + if (!wsi->u.hdr.ah) { + lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__); + assert(0); + } + lwsl_parser("issuing %d bytes to parser\n", (int)len); + + lwsl_hexdump(buf, (size_t)len); + + if (lws_handshake_client(wsi, &buf, (size_t)len)) + goto bail; + + last_char = buf; + if (lws_handshake_server(wsi, &buf, (size_t)len)) + /* Handshake indicates this session is done. */ + goto bail; + + /* we might have transitioned to RAW */ + if (wsi->mode == LWSCM_RAW) + /* we gave the read buffer to RAW handler already */ + goto read_ok; + + /* + * It's possible that we've exhausted our data already, or + * rx flow control has stopped us dealing with this early, + * but lws_handshake_server doesn't update len for us. + * Figure out how much was read, so that we can proceed + * appropriately: + */ + len -= (buf - last_char); + lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len); + + if (!wsi->hdr_parsing_completed) + /* More header content on the way */ + goto read_ok; + + switch (wsi->state) { + case LWSS_HTTP: + case LWSS_HTTP_HEADERS: + goto read_ok; + case LWSS_HTTP_ISSUING_FILE: + goto read_ok; + case LWSS_HTTP_BODY: + wsi->u.http.rx_content_remain = + wsi->u.http.rx_content_length; + if (wsi->u.http.rx_content_remain) + goto http_postbody; + + /* there is no POST content */ + goto postbody_completion; + default: + break; + } + break; + + case LWSS_HTTP_BODY: +http_postbody: + //lwsl_notice("http post body\n"); + while (len && wsi->u.http.rx_content_remain) { + /* Copy as much as possible, up to the limit of: + * what we have in the read buffer (len) + * remaining portion of the POST body (content_remain) + */ + body_chunk_len = min(wsi->u.http.rx_content_remain, len); + wsi->u.http.rx_content_remain -= body_chunk_len; + len -= body_chunk_len; +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + struct lws_cgi_args args; + + args.ch = LWS_STDIN; + args.stdwsi = &wsi->cgi->stdwsi[0]; + args.data = buf; + args.len = body_chunk_len; + + /* returns how much used */ + n = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_CGI_STDIN_DATA, + wsi->user_space, + (void *)&args, 0); + if ((int)n < 0) + goto bail; + } else { +#endif + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY, wsi->user_space, + buf, (size_t)body_chunk_len); + if (n) + goto bail; + n = (size_t)body_chunk_len; +#ifdef LWS_WITH_CGI + } +#endif + buf += n; + + if (wsi->u.http.rx_content_remain) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); + break; + } + /* he sent all the content in time */ +postbody_completion: +#ifdef LWS_WITH_CGI + /* + * If we're running a cgi, we can't let him off the + * hook just because he sent his POST data + */ + if (wsi->cgi) + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, + wsi->context->timeout_secs); + else +#endif + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); +#ifdef LWS_WITH_CGI + if (!wsi->cgi) +#endif + { + lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + n = wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_BODY_COMPLETION, + wsi->user_space, NULL, 0); + if (n) + goto bail; + + if (wsi->http2_substream) + wsi->state = LWSS_HTTP2_ESTABLISHED; + } + + break; + } + break; + + case LWSS_ESTABLISHED: + case LWSS_AWAITING_CLOSE_ACK: + case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION: + case LWSS_SHUTDOWN: + if (lws_handshake_client(wsi, &buf, (size_t)len)) + goto bail; + switch (wsi->mode) { + case LWSCM_WS_SERVING: + + if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) { + lwsl_info("interpret_incoming_packet has bailed\n"); + goto bail; + } + break; + } + break; + default: + lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state); + break; + } + +read_ok: + /* Nothing more to do for now */ + lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf)); + + return buf - oldbuf; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return -1; +} diff --git a/thirdparty/lws/header.c b/thirdparty/lws/header.c new file mode 100644 index 0000000000..e2562cd6ea --- /dev/null +++ b/thirdparty/lws/header.c @@ -0,0 +1,355 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include "lextable-strings.h" + + +const unsigned char *lws_token_to_string(enum lws_token_indexes token) +{ + if ((unsigned int)token >= ARRAY_SIZE(set)) + return NULL; + + return (unsigned char *)set[token]; +} + +int +lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end) +{ +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_by_name(wsi, name, + value, length, p, end); +#else + (void)wsi; +#endif + if (name) { + while (*p < end && *name) + *((*p)++) = *name++; + if (*p == end) + return 1; + *((*p)++) = ' '; + } + if (*p + length + 3 >= end) + return 1; + + memcpy(*p, value, length); + *p += length; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + + return 0; +} + +int lws_finalize_http_header(struct lws *wsi, unsigned char **p, + unsigned char *end) +{ +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return 0; +#else + (void)wsi; +#endif + if ((lws_intptr_t)(end - *p) < 3) + return 1; + *((*p)++) = '\x0d'; + *((*p)++) = '\x0a'; + + return 0; +} + +int +lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end) +{ + const unsigned char *name; +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_by_token(wsi, token, value, + length, p, end); +#endif + name = lws_token_to_string(token); + if (!name) + return 1; + return lws_add_http_header_by_name(wsi, name, value, length, p, end); +} + +int lws_add_http_header_content_length(struct lws *wsi, + lws_filepos_t content_length, + unsigned char **p, unsigned char *end) +{ + char b[24]; + int n; + + n = sprintf(b, "%llu", (unsigned long long)content_length); + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)b, n, p, end)) + return 1; + wsi->u.http.tx_content_length = content_length; + wsi->u.http.tx_content_remain = content_length; + + return 0; +} + +STORE_IN_ROM static const char * const err400[] = { + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Auth Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request URI too Long", + "Unsupported Media Type", + "Requested Range Not Satisfiable", + "Expectation Failed" +}; + +STORE_IN_ROM static const char * const err500[] = { + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported" +}; + +int +lws_add_http_header_status(struct lws *wsi, unsigned int _code, + unsigned char **p, unsigned char *end) +{ + STORE_IN_ROM static const char * const hver[] = { + "HTTP/1.0", "HTTP/1.1", "HTTP/2" + }; + const struct lws_protocol_vhost_options *headers; + unsigned int code = _code & LWSAHH_CODE_MASK; + const char *description = "", *p1; + unsigned char code_and_desc[60]; + int n; + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.response = code; +#endif + +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) + return lws_add_http2_header_status(wsi, code, p, end); +#endif + if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + ARRAY_SIZE(err500))) + description = err500[code - 500]; + + if (code == 100) + description = "Continue"; + + if (code == 200) + description = "OK"; + + if (code == 304) + description = "Not Modified"; + else + if (code >= 300 && code < 400) + description = "Redirect"; + + if (wsi->u.http.request_version < ARRAY_SIZE(hver)) + p1 = hver[wsi->u.http.request_version]; + else + p1 = hver[0]; + + n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description); + + if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end)) + return 1; + + headers = wsi->vhost->headers; + while (headers) { + if (lws_add_http_header_by_name(wsi, + (const unsigned char *)headers->name, + (unsigned char *)headers->value, + strlen(headers->value), p, end)) + return 1; + + headers = headers->next; + } + + if (wsi->context->server_string && + !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, + (unsigned char *)wsi->context->server_string, + wsi->context->server_string_len, p, end)) + return 1; + + if (wsi->vhost->options & LWS_SERVER_OPTION_STS) + if (lws_add_http_header_by_name(wsi, (unsigned char *) + "Strict-Transport-Security:", + (unsigned char *)"max-age=15768000 ; " + "includeSubDomains", 36, p, end)) + return 1; + + return 0; +} + +LWS_VISIBLE int +lws_return_http_status(struct lws *wsi, unsigned int code, + const char *html_body) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + unsigned char *p = pt->serv_buf + LWS_PRE; + unsigned char *start = p; + unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; + int n = 0, m = 0, len; + char slen[20]; + + if (!html_body) + html_body = ""; + + if (lws_add_http_header_status(wsi, code, &p, end)) + return 1; + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"text/html", 9, + &p, end)) + return 1; + + len = 35 + strlen(html_body) + sprintf(slen, "%d", code); + n = sprintf(slen, "%d", len); + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)slen, n, + &p, end)) + return 1; + + if (lws_finalize_http_header(wsi, &p, end)) + return 1; + +#if defined(LWS_WITH_HTTP2) + if (wsi->http2_substream) { + unsigned char *body = p + 512; + + /* + * for HTTP/2, the headers must be sent separately, since they + * go out in their own frame. That puts us in a bind that + * we won't always be able to get away with two lws_write()s in + * sequence, since the first may use up the writability due to + * the pipe being choked or SSL_WANT_. + * + * However we do need to send the human-readable body, and the + * END_STREAM. + * + * Solve it by writing the headers now... + */ + m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + if (m != (int)(p - start)) + return 1; + + /* + * ... but stash the body and send it as a priority next + * handle_POLLOUT + */ + + len = sprintf((char *)body, + "<html><body><h1>%u</h1>%s</body></html>", + code, html_body); + wsi->u.http.tx_content_length = len; + wsi->u.http.tx_content_remain = len; + + wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1, + "pending status body"); + if (!wsi->u.h2.pending_status_body) + return -1; + + strcpy(wsi->u.h2.pending_status_body + LWS_PRE, + (const char *)body); + lws_callback_on_writable(wsi); + + return 0; + } else +#endif + { + /* + * for http/1, we can just append the body after the finalized + * headers and send it all in one go. + */ + p += lws_snprintf((char *)p, end - p - 1, + "<html><body><h1>%u</h1>%s</body></html>", + code, html_body); + + n = (int)(p - start); + + m = lws_write(wsi, start, n, LWS_WRITE_HTTP); + if (m != n) + return 1; + } + + lwsl_notice("%s: return\n", __func__); + + return m != n; +} + +LWS_VISIBLE int +lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, + unsigned char **p, unsigned char *end) +{ + unsigned char *start = *p; + int n; + + if (lws_add_http_header_status(wsi, code, p, end)) + return -1; + + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_LOCATION, + loc, len, p, end)) + return -1; + /* + * if we're going with http/1.1 and keepalive, we have to give fake + * content metadata so the client knows we completed the transaction and + * it can do the redirect... + */ + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"text/html", 9, + p, end)) + return -1; + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH, + (unsigned char *)"0", 1, p, end)) + return -1; + + if (lws_finalize_http_header(wsi, p, end)) + return -1; + + n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END); + + return n; +} diff --git a/thirdparty/lws/lextable-strings.h b/thirdparty/lws/lextable-strings.h new file mode 100644 index 0000000000..ab42c3e476 --- /dev/null +++ b/thirdparty/lws/lextable-strings.h @@ -0,0 +1,104 @@ +/* set of parsable strings -- ALL LOWER CASE */ + +#if !defined(STORE_IN_ROM) +#define STORE_IN_ROM +#endif + +STORE_IN_ROM static const char * const set[] = { + "get ", + "post ", + "options ", + "host:", + "connection:", + "upgrade:", + "origin:", + "sec-websocket-draft:", + "\x0d\x0a", + + "sec-websocket-extensions:", + "sec-websocket-key1:", + "sec-websocket-key2:", + "sec-websocket-protocol:", + + "sec-websocket-accept:", + "sec-websocket-nonce:", + "http/1.1 ", + "http2-settings:", + + "accept:", + "access-control-request-headers:", + "if-modified-since:", + "if-none-match:", + "accept-encoding:", + "accept-language:", + "pragma:", + "cache-control:", + "authorization:", + "cookie:", + "content-length:", + "content-type:", + "date:", + "range:", + "referer:", + "sec-websocket-key:", + "sec-websocket-version:", + "sec-websocket-origin:", + + ":authority", + ":method", + ":path", + ":scheme", + ":status", + + "accept-charset:", + "accept-ranges:", + "access-control-allow-origin:", + "age:", + "allow:", + "content-disposition:", + "content-encoding:", + "content-language:", + "content-location:", + "content-range:", + "etag:", + "expect:", + "expires:", + "from:", + "if-match:", + "if-range:", + "if-unmodified-since:", + "last-modified:", + "link:", + "location:", + "max-forwards:", + "proxy-authenticate:", + "proxy-authorization:", + "refresh:", + "retry-after:", + "server:", + "set-cookie:", + "strict-transport-security:", + "transfer-encoding:", + "user-agent:", + "vary:", + "via:", + "www-authenticate:", + + "patch", + "put", + "delete", + + "uri-args", /* fake header used for uri-only storage */ + + "proxy ", + "x-real-ip:", + "http/1.0 ", + + "x-forwarded-for", + "connect ", + "head ", + "te:", /* http/2 wants it to reject it */ + + "", /* not matchable */ + +}; diff --git a/thirdparty/lws/lextable.h b/thirdparty/lws/lextable.h new file mode 100644 index 0000000000..f940afd25b --- /dev/null +++ b/thirdparty/lws/lextable.h @@ -0,0 +1,805 @@ +/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */, + 0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */, + 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */, + 0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */, + 0x63 /* 'c' */, 0x69, 0x00 /* (to 0x0075 state 23) */, + 0x75 /* 'u' */, 0x8A, 0x00 /* (to 0x0099 state 34) */, + 0x73 /* 's' */, 0xA0, 0x00 /* (to 0x00B2 state 48) */, + 0x0D /* '.' */, 0xD9, 0x00 /* (to 0x00EE state 68) */, + 0x61 /* 'a' */, 0x31, 0x01 /* (to 0x0149 state 129) */, + 0x69 /* 'i' */, 0x70, 0x01 /* (to 0x018B state 163) */, + 0x64 /* 'd' */, 0x19, 0x02 /* (to 0x0237 state 265) */, + 0x72 /* 'r' */, 0x22, 0x02 /* (to 0x0243 state 270) */, + 0x3A /* ':' */, 0x53, 0x02 /* (to 0x0277 state 299) */, + 0x65 /* 'e' */, 0xDF, 0x02 /* (to 0x0306 state 409) */, + 0x66 /* 'f' */, 0xFB, 0x02 /* (to 0x0325 state 425) */, + 0x6C /* 'l' */, 0x1D, 0x03 /* (to 0x034A state 458) */, + 0x6D /* 'm' */, 0x40, 0x03 /* (to 0x0370 state 484) */, + 0x74 /* 't' */, 0xAF, 0x03 /* (to 0x03E2 state 578) */, + 0x76 /* 'v' */, 0xD0, 0x03 /* (to 0x0406 state 606) */, + 0x77 /* 'w' */, 0xDD, 0x03 /* (to 0x0416 state 614) */, + 0x78 /* 'x' */, 0x04, 0x04 /* (to 0x0440 state 650) */, + 0x08, /* fail */ +/* pos 0040: 1 */ 0xE5 /* 'e' -> */, +/* pos 0041: 2 */ 0xF4 /* 't' -> */, +/* pos 0042: 3 */ 0xA0 /* ' ' -> */, +/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */, + 0x72 /* 'r' */, 0x95, 0x01 /* (to 0x01DD state 211) */, + 0x61 /* 'a' */, 0xDD, 0x03 /* (to 0x0428 state 631) */, + 0x75 /* 'u' */, 0xDF, 0x03 /* (to 0x042D state 635) */, + 0x08, /* fail */ +/* pos 0052: 6 */ 0xF3 /* 's' -> */, +/* pos 0053: 7 */ 0xF4 /* 't' -> */, +/* pos 0054: 8 */ 0xA0 /* ' ' -> */, +/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */, + 0x72 /* 'r' */, 0x51, 0x00 /* (to 0x00AB state 42) */, + 0x08, /* fail */ +/* pos 005e: 11 */ 0xF4 /* 't' -> */, +/* pos 005f: 12 */ 0xE9 /* 'i' -> */, +/* pos 0060: 13 */ 0xEF /* 'o' -> */, +/* pos 0061: 14 */ 0xEE /* 'n' -> */, +/* pos 0062: 15 */ 0xF3 /* 's' -> */, +/* pos 0063: 16 */ 0xA0 /* ' ' -> */, +/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 0066: 18 */ 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x0070 state 19) */, + 0x74 /* 't' */, 0xBF, 0x00 /* (to 0x0128 state 110) */, + 0x65 /* 'e' */, 0xF8, 0x03 /* (to 0x0464 state 676) */, + 0x08, /* fail */ +/* pos 0070: 19 */ 0xF3 /* 's' -> */, +/* pos 0071: 20 */ 0xF4 /* 't' -> */, +/* pos 0072: 21 */ 0xBA /* ':' -> */, +/* pos 0073: 22 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0075: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x007C state 24) */, + 0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01EA state 217) */, + 0x08, /* fail */ +/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */, + 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */, + 0x08, /* fail */ +/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */, + 0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C state 248) */, + 0x08, /* fail */ +/* pos 008a: 26 */ 0xE5 /* 'e' -> */, +/* pos 008b: 27 */ 0xE3 /* 'c' -> */, +/* pos 008c: 28 */ 0xF4 /* 't' -> */, +/* pos 008d: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0094 state 30) */, + 0x20 /* ' ' */, 0xD2, 0x03 /* (to 0x0462 state 675) */, + 0x08, /* fail */ +/* pos 0094: 30 */ 0xEF /* 'o' -> */, +/* pos 0095: 31 */ 0xEE /* 'n' -> */, +/* pos 0096: 32 */ 0xBA /* ':' -> */, +/* pos 0097: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 0099: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A3 state 35) */, + 0x73 /* 's' */, 0x5F, 0x03 /* (to 0x03FB state 596) */, + 0x72 /* 'r' */, 0x97, 0x03 /* (to 0x0436 state 642) */, + 0x08, /* fail */ +/* pos 00a3: 35 */ 0xE7 /* 'g' -> */, +/* pos 00a4: 36 */ 0xF2 /* 'r' -> */, +/* pos 00a5: 37 */ 0xE1 /* 'a' -> */, +/* pos 00a6: 38 */ 0xE4 /* 'd' -> */, +/* pos 00a7: 39 */ 0xE5 /* 'e' -> */, +/* pos 00a8: 40 */ 0xBA /* ':' -> */, +/* pos 00a9: 41 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 00ab: 42 */ 0xE9 /* 'i' -> */, +/* pos 00ac: 43 */ 0xE7 /* 'g' -> */, +/* pos 00ad: 44 */ 0xE9 /* 'i' -> */, +/* pos 00ae: 45 */ 0xEE /* 'n' -> */, +/* pos 00af: 46 */ 0xBA /* ':' -> */, +/* pos 00b0: 47 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 00b2: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B9 state 49) */, + 0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C8 state 553) */, + 0x08, /* fail */ +/* pos 00b9: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C3 state 50) */, + 0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B8 state 539) */, + 0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BE state 544) */, + 0x08, /* fail */ +/* pos 00c3: 50 */ 0xAD /* '-' -> */, +/* pos 00c4: 51 */ 0xF7 /* 'w' -> */, +/* pos 00c5: 52 */ 0xE5 /* 'e' -> */, +/* pos 00c6: 53 */ 0xE2 /* 'b' -> */, +/* pos 00c7: 54 */ 0xF3 /* 's' -> */, +/* pos 00c8: 55 */ 0xEF /* 'o' -> */, +/* pos 00c9: 56 */ 0xE3 /* 'c' -> */, +/* pos 00ca: 57 */ 0xEB /* 'k' -> */, +/* pos 00cb: 58 */ 0xE5 /* 'e' -> */, +/* pos 00cc: 59 */ 0xF4 /* 't' -> */, +/* pos 00cd: 60 */ 0xAD /* '-' -> */, +/* pos 00ce: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E7 state 62) */, + 0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00F1 state 70) */, + 0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FD state 81) */, + 0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010F state 88) */, + 0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0119 state 97) */, + 0x6E /* 'n' */, 0x44, 0x00 /* (to 0x0121 state 104) */, + 0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0266 state 284) */, + 0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026F state 292) */, + 0x08, /* fail */ +/* pos 00e7: 62 */ 0xF2 /* 'r' -> */, +/* pos 00e8: 63 */ 0xE1 /* 'a' -> */, +/* pos 00e9: 64 */ 0xE6 /* 'f' -> */, +/* pos 00ea: 65 */ 0xF4 /* 't' -> */, +/* pos 00eb: 66 */ 0xBA /* ':' -> */, +/* pos 00ec: 67 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 00ee: 68 */ 0x8A /* '.' -> */, +/* pos 00ef: 69 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00f1: 70 */ 0xF8 /* 'x' -> */, +/* pos 00f2: 71 */ 0xF4 /* 't' -> */, +/* pos 00f3: 72 */ 0xE5 /* 'e' -> */, +/* pos 00f4: 73 */ 0xEE /* 'n' -> */, +/* pos 00f5: 74 */ 0xF3 /* 's' -> */, +/* pos 00f6: 75 */ 0xE9 /* 'i' -> */, +/* pos 00f7: 76 */ 0xEF /* 'o' -> */, +/* pos 00f8: 77 */ 0xEE /* 'n' -> */, +/* pos 00f9: 78 */ 0xF3 /* 's' -> */, +/* pos 00fa: 79 */ 0xBA /* ':' -> */, +/* pos 00fb: 80 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00fd: 81 */ 0xE5 /* 'e' -> */, +/* pos 00fe: 82 */ 0xF9 /* 'y' -> */, +/* pos 00ff: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0109 state 84) */, + 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x010C state 86) */, + 0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0264 state 283) */, + 0x08, /* fail */ +/* pos 0109: 84 */ 0xBA /* ':' -> */, +/* pos 010a: 85 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 010c: 86 */ 0xBA /* ':' -> */, +/* pos 010d: 87 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 010f: 88 */ 0xF2 /* 'r' -> */, +/* pos 0110: 89 */ 0xEF /* 'o' -> */, +/* pos 0111: 90 */ 0xF4 /* 't' -> */, +/* pos 0112: 91 */ 0xEF /* 'o' -> */, +/* pos 0113: 92 */ 0xE3 /* 'c' -> */, +/* pos 0114: 93 */ 0xEF /* 'o' -> */, +/* pos 0115: 94 */ 0xEC /* 'l' -> */, +/* pos 0116: 95 */ 0xBA /* ':' -> */, +/* pos 0117: 96 */ 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 0119: 97 */ 0xE3 /* 'c' -> */, +/* pos 011a: 98 */ 0xE3 /* 'c' -> */, +/* pos 011b: 99 */ 0xE5 /* 'e' -> */, +/* pos 011c: 100 */ 0xF0 /* 'p' -> */, +/* pos 011d: 101 */ 0xF4 /* 't' -> */, +/* pos 011e: 102 */ 0xBA /* ':' -> */, +/* pos 011f: 103 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 0121: 104 */ 0xEF /* 'o' -> */, +/* pos 0122: 105 */ 0xEE /* 'n' -> */, +/* pos 0123: 106 */ 0xE3 /* 'c' -> */, +/* pos 0124: 107 */ 0xE5 /* 'e' -> */, +/* pos 0125: 108 */ 0xBA /* ':' -> */, +/* pos 0126: 109 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 0128: 110 */ 0xF4 /* 't' -> */, +/* pos 0129: 111 */ 0xF0 /* 'p' -> */, +/* pos 012a: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x0131 state 113) */, + 0x32 /* '2' */, 0x10, 0x00 /* (to 0x013D state 118) */, + 0x08, /* fail */ +/* pos 0131: 113 */ 0xB1 /* '1' -> */, +/* pos 0132: 114 */ 0xAE /* '.' -> */, +/* pos 0133: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x013A state 116) */, + 0x30 /* '0' */, 0x1B, 0x03 /* (to 0x0451 state 660) */, + 0x08, /* fail */ +/* pos 013a: 116 */ 0xA0 /* ' ' -> */, +/* pos 013b: 117 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 013d: 118 */ 0xAD /* '-' -> */, +/* pos 013e: 119 */ 0xF3 /* 's' -> */, +/* pos 013f: 120 */ 0xE5 /* 'e' -> */, +/* pos 0140: 121 */ 0xF4 /* 't' -> */, +/* pos 0141: 122 */ 0xF4 /* 't' -> */, +/* pos 0142: 123 */ 0xE9 /* 'i' -> */, +/* pos 0143: 124 */ 0xEE /* 'n' -> */, +/* pos 0144: 125 */ 0xE7 /* 'g' -> */, +/* pos 0145: 126 */ 0xF3 /* 's' -> */, +/* pos 0146: 127 */ 0xBA /* ':' -> */, +/* pos 0147: 128 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 0149: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0156 state 130) */, + 0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F8 state 230) */, + 0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02CC state 358) */, + 0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02D0 state 361) */, + 0x08, /* fail */ +/* pos 0156: 130 */ 0xE3 /* 'c' -> */, +/* pos 0157: 131 */ 0xE5 /* 'e' -> */, +/* pos 0158: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015F state 133) */, + 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0169 state 136) */, + 0x08, /* fail */ +/* pos 015f: 133 */ 0xF4 /* 't' -> */, +/* pos 0160: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0167 state 135) */, + 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01BC state 192) */, + 0x08, /* fail */ +/* pos 0167: 135 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 0169: 136 */ 0xF3 /* 's' -> */, +/* pos 016a: 137 */ 0xAD /* '-' -> */, +/* pos 016b: 138 */ 0xE3 /* 'c' -> */, +/* pos 016c: 139 */ 0xEF /* 'o' -> */, +/* pos 016d: 140 */ 0xEE /* 'n' -> */, +/* pos 016e: 141 */ 0xF4 /* 't' -> */, +/* pos 016f: 142 */ 0xF2 /* 'r' -> */, +/* pos 0170: 143 */ 0xEF /* 'o' -> */, +/* pos 0171: 144 */ 0xEC /* 'l' -> */, +/* pos 0172: 145 */ 0xAD /* '-' -> */, +/* pos 0173: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x017A state 147) */, + 0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BE state 345) */, + 0x08, /* fail */ +/* pos 017a: 147 */ 0xE5 /* 'e' -> */, +/* pos 017b: 148 */ 0xF1 /* 'q' -> */, +/* pos 017c: 149 */ 0xF5 /* 'u' -> */, +/* pos 017d: 150 */ 0xE5 /* 'e' -> */, +/* pos 017e: 151 */ 0xF3 /* 's' -> */, +/* pos 017f: 152 */ 0xF4 /* 't' -> */, +/* pos 0180: 153 */ 0xAD /* '-' -> */, +/* pos 0181: 154 */ 0xE8 /* 'h' -> */, +/* pos 0182: 155 */ 0xE5 /* 'e' -> */, +/* pos 0183: 156 */ 0xE1 /* 'a' -> */, +/* pos 0184: 157 */ 0xE4 /* 'd' -> */, +/* pos 0185: 158 */ 0xE5 /* 'e' -> */, +/* pos 0186: 159 */ 0xF2 /* 'r' -> */, +/* pos 0187: 160 */ 0xF3 /* 's' -> */, +/* pos 0188: 161 */ 0xBA /* ':' -> */, +/* pos 0189: 162 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 018b: 163 */ 0xE6 /* 'f' -> */, +/* pos 018c: 164 */ 0xAD /* '-' -> */, +/* pos 018d: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x019A state 166) */, + 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01B0 state 181) */, + 0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x0331 state 435) */, + 0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0338 state 441) */, + 0x08, /* fail */ +/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */, + 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B state 430) */, + 0x08, /* fail */ +/* pos 01a1: 167 */ 0xE4 /* 'd' -> */, +/* pos 01a2: 168 */ 0xE9 /* 'i' -> */, +/* pos 01a3: 169 */ 0xE6 /* 'f' -> */, +/* pos 01a4: 170 */ 0xE9 /* 'i' -> */, +/* pos 01a5: 171 */ 0xE5 /* 'e' -> */, +/* pos 01a6: 172 */ 0xE4 /* 'd' -> */, +/* pos 01a7: 173 */ 0xAD /* '-' -> */, +/* pos 01a8: 174 */ 0xF3 /* 's' -> */, +/* pos 01a9: 175 */ 0xE9 /* 'i' -> */, +/* pos 01aa: 176 */ 0xEE /* 'n' -> */, +/* pos 01ab: 177 */ 0xE3 /* 'c' -> */, +/* pos 01ac: 178 */ 0xE5 /* 'e' -> */, +/* pos 01ad: 179 */ 0xBA /* ':' -> */, +/* pos 01ae: 180 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 01b0: 181 */ 0xEF /* 'o' -> */, +/* pos 01b1: 182 */ 0xEE /* 'n' -> */, +/* pos 01b2: 183 */ 0xE5 /* 'e' -> */, +/* pos 01b3: 184 */ 0xAD /* '-' -> */, +/* pos 01b4: 185 */ 0xED /* 'm' -> */, +/* pos 01b5: 186 */ 0xE1 /* 'a' -> */, +/* pos 01b6: 187 */ 0xF4 /* 't' -> */, +/* pos 01b7: 188 */ 0xE3 /* 'c' -> */, +/* pos 01b8: 189 */ 0xE8 /* 'h' -> */, +/* pos 01b9: 190 */ 0xBA /* ':' -> */, +/* pos 01ba: 191 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 01bc: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C9 state 193) */, + 0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D3 state 202) */, + 0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AD state 330) */, + 0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B6 state 338) */, + 0x08, /* fail */ +/* pos 01c9: 193 */ 0xEE /* 'n' -> */, +/* pos 01ca: 194 */ 0xE3 /* 'c' -> */, +/* pos 01cb: 195 */ 0xEF /* 'o' -> */, +/* pos 01cc: 196 */ 0xE4 /* 'd' -> */, +/* pos 01cd: 197 */ 0xE9 /* 'i' -> */, +/* pos 01ce: 198 */ 0xEE /* 'n' -> */, +/* pos 01cf: 199 */ 0xE7 /* 'g' -> */, +/* pos 01d0: 200 */ 0xBA /* ':' -> */, +/* pos 01d1: 201 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 01d3: 202 */ 0xE1 /* 'a' -> */, +/* pos 01d4: 203 */ 0xEE /* 'n' -> */, +/* pos 01d5: 204 */ 0xE7 /* 'g' -> */, +/* pos 01d6: 205 */ 0xF5 /* 'u' -> */, +/* pos 01d7: 206 */ 0xE1 /* 'a' -> */, +/* pos 01d8: 207 */ 0xE7 /* 'g' -> */, +/* pos 01d9: 208 */ 0xE5 /* 'e' -> */, +/* pos 01da: 209 */ 0xBA /* ':' -> */, +/* pos 01db: 210 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 01dd: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E4 state 212) */, + 0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037E state 497) */, + 0x08, /* fail */ +/* pos 01e4: 212 */ 0xE7 /* 'g' -> */, +/* pos 01e5: 213 */ 0xED /* 'm' -> */, +/* pos 01e6: 214 */ 0xE1 /* 'a' -> */, +/* pos 01e7: 215 */ 0xBA /* ':' -> */, +/* pos 01e8: 216 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 01ea: 217 */ 0xE3 /* 'c' -> */, +/* pos 01eb: 218 */ 0xE8 /* 'h' -> */, +/* pos 01ec: 219 */ 0xE5 /* 'e' -> */, +/* pos 01ed: 220 */ 0xAD /* '-' -> */, +/* pos 01ee: 221 */ 0xE3 /* 'c' -> */, +/* pos 01ef: 222 */ 0xEF /* 'o' -> */, +/* pos 01f0: 223 */ 0xEE /* 'n' -> */, +/* pos 01f1: 224 */ 0xF4 /* 't' -> */, +/* pos 01f2: 225 */ 0xF2 /* 'r' -> */, +/* pos 01f3: 226 */ 0xEF /* 'o' -> */, +/* pos 01f4: 227 */ 0xEC /* 'l' -> */, +/* pos 01f5: 228 */ 0xBA /* ':' -> */, +/* pos 01f6: 229 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 01f8: 230 */ 0xF4 /* 't' -> */, +/* pos 01f9: 231 */ 0xE8 /* 'h' -> */, +/* pos 01fa: 232 */ 0xEF /* 'o' -> */, +/* pos 01fb: 233 */ 0xF2 /* 'r' -> */, +/* pos 01fc: 234 */ 0xE9 /* 'i' -> */, +/* pos 01fd: 235 */ 0xFA /* 'z' -> */, +/* pos 01fe: 236 */ 0xE1 /* 'a' -> */, +/* pos 01ff: 237 */ 0xF4 /* 't' -> */, +/* pos 0200: 238 */ 0xE9 /* 'i' -> */, +/* pos 0201: 239 */ 0xEF /* 'o' -> */, +/* pos 0202: 240 */ 0xEE /* 'n' -> */, +/* pos 0203: 241 */ 0xBA /* ':' -> */, +/* pos 0204: 242 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 0206: 243 */ 0xEB /* 'k' -> */, +/* pos 0207: 244 */ 0xE9 /* 'i' -> */, +/* pos 0208: 245 */ 0xE5 /* 'e' -> */, +/* pos 0209: 246 */ 0xBA /* ':' -> */, +/* pos 020a: 247 */ 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 020c: 248 */ 0xE5 /* 'e' -> */, +/* pos 020d: 249 */ 0xEE /* 'n' -> */, +/* pos 020e: 250 */ 0xF4 /* 't' -> */, +/* pos 020f: 251 */ 0xAD /* '-' -> */, +/* pos 0210: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x0220 state 253) */, + 0x74 /* 't' */, 0x1E, 0x00 /* (to 0x0231 state 260) */, + 0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D6 state 366) */, + 0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E3 state 378) */, + 0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FF state 403) */, + 0x08, /* fail */ +/* pos 0220: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x022A state 254) */, + 0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02ED state 387) */, + 0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F6 state 395) */, + 0x08, /* fail */ +/* pos 022a: 254 */ 0xEE /* 'n' -> */, +/* pos 022b: 255 */ 0xE7 /* 'g' -> */, +/* pos 022c: 256 */ 0xF4 /* 't' -> */, +/* pos 022d: 257 */ 0xE8 /* 'h' -> */, +/* pos 022e: 258 */ 0xBA /* ':' -> */, +/* pos 022f: 259 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 0231: 260 */ 0xF9 /* 'y' -> */, +/* pos 0232: 261 */ 0xF0 /* 'p' -> */, +/* pos 0233: 262 */ 0xE5 /* 'e' -> */, +/* pos 0234: 263 */ 0xBA /* ':' -> */, +/* pos 0235: 264 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 0237: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023E state 266) */, + 0x65 /* 'e' */, 0xF6, 0x01 /* (to 0x0430 state 637) */, + 0x08, /* fail */ +/* pos 023e: 266 */ 0xF4 /* 't' -> */, +/* pos 023f: 267 */ 0xE5 /* 'e' -> */, +/* pos 0240: 268 */ 0xBA /* ':' -> */, +/* pos 0241: 269 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 0243: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x024A state 271) */, + 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0250 state 276) */, + 0x08, /* fail */ +/* pos 024a: 271 */ 0xEE /* 'n' -> */, +/* pos 024b: 272 */ 0xE7 /* 'g' -> */, +/* pos 024c: 273 */ 0xE5 /* 'e' -> */, +/* pos 024d: 274 */ 0xBA /* ':' -> */, +/* pos 024e: 275 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 0250: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0257 state 277) */, + 0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AD state 529) */, + 0x08, /* fail */ +/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */, + 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 state 524) */, + 0x08, /* fail */ +/* pos 025e: 278 */ 0xF2 /* 'r' -> */, +/* pos 025f: 279 */ 0xE5 /* 'e' -> */, +/* pos 0260: 280 */ 0xF2 /* 'r' -> */, +/* pos 0261: 281 */ 0xBA /* ':' -> */, +/* pos 0262: 282 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 0264: 283 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 0266: 284 */ 0xE5 /* 'e' -> */, +/* pos 0267: 285 */ 0xF2 /* 'r' -> */, +/* pos 0268: 286 */ 0xF3 /* 's' -> */, +/* pos 0269: 287 */ 0xE9 /* 'i' -> */, +/* pos 026a: 288 */ 0xEF /* 'o' -> */, +/* pos 026b: 289 */ 0xEE /* 'n' -> */, +/* pos 026c: 290 */ 0xBA /* ':' -> */, +/* pos 026d: 291 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* pos 026f: 292 */ 0xF2 /* 'r' -> */, +/* pos 0270: 293 */ 0xE9 /* 'i' -> */, +/* pos 0271: 294 */ 0xE7 /* 'g' -> */, +/* pos 0272: 295 */ 0xE9 /* 'i' -> */, +/* pos 0273: 296 */ 0xEE /* 'n' -> */, +/* pos 0274: 297 */ 0xBA /* ':' -> */, +/* pos 0275: 298 */ 0x00, 0x22 /* - terminal marker 34 - */, +/* pos 0277: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0284 state 300) */, + 0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028E state 309) */, + 0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0295 state 315) */, + 0x73 /* 's' */, 0x1A, 0x00 /* (to 0x029A state 319) */, + 0x08, /* fail */ +/* pos 0284: 300 */ 0xF5 /* 'u' -> */, +/* pos 0285: 301 */ 0xF4 /* 't' -> */, +/* pos 0286: 302 */ 0xE8 /* 'h' -> */, +/* pos 0287: 303 */ 0xEF /* 'o' -> */, +/* pos 0288: 304 */ 0xF2 /* 'r' -> */, +/* pos 0289: 305 */ 0xE9 /* 'i' -> */, +/* pos 028a: 306 */ 0xF4 /* 't' -> */, +/* pos 028b: 307 */ 0xF9 /* 'y' -> */, +/* pos 028c: 308 */ 0x00, 0x23 /* - terminal marker 35 - */, +/* pos 028e: 309 */ 0xE5 /* 'e' -> */, +/* pos 028f: 310 */ 0xF4 /* 't' -> */, +/* pos 0290: 311 */ 0xE8 /* 'h' -> */, +/* pos 0291: 312 */ 0xEF /* 'o' -> */, +/* pos 0292: 313 */ 0xE4 /* 'd' -> */, +/* pos 0293: 314 */ 0x00, 0x24 /* - terminal marker 36 - */, +/* pos 0295: 315 */ 0xE1 /* 'a' -> */, +/* pos 0296: 316 */ 0xF4 /* 't' -> */, +/* pos 0297: 317 */ 0xE8 /* 'h' -> */, +/* pos 0298: 318 */ 0x00, 0x25 /* - terminal marker 37 - */, +/* pos 029a: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x02A1 state 320) */, + 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A7 state 325) */, + 0x08, /* fail */ +/* pos 02a1: 320 */ 0xE8 /* 'h' -> */, +/* pos 02a2: 321 */ 0xE5 /* 'e' -> */, +/* pos 02a3: 322 */ 0xED /* 'm' -> */, +/* pos 02a4: 323 */ 0xE5 /* 'e' -> */, +/* pos 02a5: 324 */ 0x00, 0x26 /* - terminal marker 38 - */, +/* pos 02a7: 325 */ 0xE1 /* 'a' -> */, +/* pos 02a8: 326 */ 0xF4 /* 't' -> */, +/* pos 02a9: 327 */ 0xF5 /* 'u' -> */, +/* pos 02aa: 328 */ 0xF3 /* 's' -> */, +/* pos 02ab: 329 */ 0x00, 0x27 /* - terminal marker 39 - */, +/* pos 02ad: 330 */ 0xE8 /* 'h' -> */, +/* pos 02ae: 331 */ 0xE1 /* 'a' -> */, +/* pos 02af: 332 */ 0xF2 /* 'r' -> */, +/* pos 02b0: 333 */ 0xF3 /* 's' -> */, +/* pos 02b1: 334 */ 0xE5 /* 'e' -> */, +/* pos 02b2: 335 */ 0xF4 /* 't' -> */, +/* pos 02b3: 336 */ 0xBA /* ':' -> */, +/* pos 02b4: 337 */ 0x00, 0x28 /* - terminal marker 40 - */, +/* pos 02b6: 338 */ 0xE1 /* 'a' -> */, +/* pos 02b7: 339 */ 0xEE /* 'n' -> */, +/* pos 02b8: 340 */ 0xE7 /* 'g' -> */, +/* pos 02b9: 341 */ 0xE5 /* 'e' -> */, +/* pos 02ba: 342 */ 0xF3 /* 's' -> */, +/* pos 02bb: 343 */ 0xBA /* ':' -> */, +/* pos 02bc: 344 */ 0x00, 0x29 /* - terminal marker 41 - */, +/* pos 02be: 345 */ 0xEC /* 'l' -> */, +/* pos 02bf: 346 */ 0xEC /* 'l' -> */, +/* pos 02c0: 347 */ 0xEF /* 'o' -> */, +/* pos 02c1: 348 */ 0xF7 /* 'w' -> */, +/* pos 02c2: 349 */ 0xAD /* '-' -> */, +/* pos 02c3: 350 */ 0xEF /* 'o' -> */, +/* pos 02c4: 351 */ 0xF2 /* 'r' -> */, +/* pos 02c5: 352 */ 0xE9 /* 'i' -> */, +/* pos 02c6: 353 */ 0xE7 /* 'g' -> */, +/* pos 02c7: 354 */ 0xE9 /* 'i' -> */, +/* pos 02c8: 355 */ 0xEE /* 'n' -> */, +/* pos 02c9: 356 */ 0xBA /* ':' -> */, +/* pos 02ca: 357 */ 0x00, 0x2A /* - terminal marker 42 - */, +/* pos 02cc: 358 */ 0xE5 /* 'e' -> */, +/* pos 02cd: 359 */ 0xBA /* ':' -> */, +/* pos 02ce: 360 */ 0x00, 0x2B /* - terminal marker 43 - */, +/* pos 02d0: 361 */ 0xEC /* 'l' -> */, +/* pos 02d1: 362 */ 0xEF /* 'o' -> */, +/* pos 02d2: 363 */ 0xF7 /* 'w' -> */, +/* pos 02d3: 364 */ 0xBA /* ':' -> */, +/* pos 02d4: 365 */ 0x00, 0x2C /* - terminal marker 44 - */, +/* pos 02d6: 366 */ 0xE9 /* 'i' -> */, +/* pos 02d7: 367 */ 0xF3 /* 's' -> */, +/* pos 02d8: 368 */ 0xF0 /* 'p' -> */, +/* pos 02d9: 369 */ 0xEF /* 'o' -> */, +/* pos 02da: 370 */ 0xF3 /* 's' -> */, +/* pos 02db: 371 */ 0xE9 /* 'i' -> */, +/* pos 02dc: 372 */ 0xF4 /* 't' -> */, +/* pos 02dd: 373 */ 0xE9 /* 'i' -> */, +/* pos 02de: 374 */ 0xEF /* 'o' -> */, +/* pos 02df: 375 */ 0xEE /* 'n' -> */, +/* pos 02e0: 376 */ 0xBA /* ':' -> */, +/* pos 02e1: 377 */ 0x00, 0x2D /* - terminal marker 45 - */, +/* pos 02e3: 378 */ 0xEE /* 'n' -> */, +/* pos 02e4: 379 */ 0xE3 /* 'c' -> */, +/* pos 02e5: 380 */ 0xEF /* 'o' -> */, +/* pos 02e6: 381 */ 0xE4 /* 'd' -> */, +/* pos 02e7: 382 */ 0xE9 /* 'i' -> */, +/* pos 02e8: 383 */ 0xEE /* 'n' -> */, +/* pos 02e9: 384 */ 0xE7 /* 'g' -> */, +/* pos 02ea: 385 */ 0xBA /* ':' -> */, +/* pos 02eb: 386 */ 0x00, 0x2E /* - terminal marker 46 - */, +/* pos 02ed: 387 */ 0xEE /* 'n' -> */, +/* pos 02ee: 388 */ 0xE7 /* 'g' -> */, +/* pos 02ef: 389 */ 0xF5 /* 'u' -> */, +/* pos 02f0: 390 */ 0xE1 /* 'a' -> */, +/* pos 02f1: 391 */ 0xE7 /* 'g' -> */, +/* pos 02f2: 392 */ 0xE5 /* 'e' -> */, +/* pos 02f3: 393 */ 0xBA /* ':' -> */, +/* pos 02f4: 394 */ 0x00, 0x2F /* - terminal marker 47 - */, +/* pos 02f6: 395 */ 0xE3 /* 'c' -> */, +/* pos 02f7: 396 */ 0xE1 /* 'a' -> */, +/* pos 02f8: 397 */ 0xF4 /* 't' -> */, +/* pos 02f9: 398 */ 0xE9 /* 'i' -> */, +/* pos 02fa: 399 */ 0xEF /* 'o' -> */, +/* pos 02fb: 400 */ 0xEE /* 'n' -> */, +/* pos 02fc: 401 */ 0xBA /* ':' -> */, +/* pos 02fd: 402 */ 0x00, 0x30 /* - terminal marker 48 - */, +/* pos 02ff: 403 */ 0xE1 /* 'a' -> */, +/* pos 0300: 404 */ 0xEE /* 'n' -> */, +/* pos 0301: 405 */ 0xE7 /* 'g' -> */, +/* pos 0302: 406 */ 0xE5 /* 'e' -> */, +/* pos 0303: 407 */ 0xBA /* ':' -> */, +/* pos 0304: 408 */ 0x00, 0x31 /* - terminal marker 49 - */, +/* pos 0306: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030D state 410) */, + 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x0312 state 414) */, + 0x08, /* fail */ +/* pos 030d: 410 */ 0xE1 /* 'a' -> */, +/* pos 030e: 411 */ 0xE7 /* 'g' -> */, +/* pos 030f: 412 */ 0xBA /* ':' -> */, +/* pos 0310: 413 */ 0x00, 0x32 /* - terminal marker 50 - */, +/* pos 0312: 414 */ 0xF0 /* 'p' -> */, +/* pos 0313: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x031A state 416) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031F state 420) */, + 0x08, /* fail */ +/* pos 031a: 416 */ 0xE3 /* 'c' -> */, +/* pos 031b: 417 */ 0xF4 /* 't' -> */, +/* pos 031c: 418 */ 0xBA /* ':' -> */, +/* pos 031d: 419 */ 0x00, 0x33 /* - terminal marker 51 - */, +/* pos 031f: 420 */ 0xF2 /* 'r' -> */, +/* pos 0320: 421 */ 0xE5 /* 'e' -> */, +/* pos 0321: 422 */ 0xF3 /* 's' -> */, +/* pos 0322: 423 */ 0xBA /* ':' -> */, +/* pos 0323: 424 */ 0x00, 0x34 /* - terminal marker 52 - */, +/* pos 0325: 425 */ 0xF2 /* 'r' -> */, +/* pos 0326: 426 */ 0xEF /* 'o' -> */, +/* pos 0327: 427 */ 0xED /* 'm' -> */, +/* pos 0328: 428 */ 0xBA /* ':' -> */, +/* pos 0329: 429 */ 0x00, 0x35 /* - terminal marker 53 - */, +/* pos 032b: 430 */ 0xF4 /* 't' -> */, +/* pos 032c: 431 */ 0xE3 /* 'c' -> */, +/* pos 032d: 432 */ 0xE8 /* 'h' -> */, +/* pos 032e: 433 */ 0xBA /* ':' -> */, +/* pos 032f: 434 */ 0x00, 0x36 /* - terminal marker 54 - */, +/* pos 0331: 435 */ 0xE1 /* 'a' -> */, +/* pos 0332: 436 */ 0xEE /* 'n' -> */, +/* pos 0333: 437 */ 0xE7 /* 'g' -> */, +/* pos 0334: 438 */ 0xE5 /* 'e' -> */, +/* pos 0335: 439 */ 0xBA /* ':' -> */, +/* pos 0336: 440 */ 0x00, 0x37 /* - terminal marker 55 - */, +/* pos 0338: 441 */ 0xEE /* 'n' -> */, +/* pos 0339: 442 */ 0xED /* 'm' -> */, +/* pos 033a: 443 */ 0xEF /* 'o' -> */, +/* pos 033b: 444 */ 0xE4 /* 'd' -> */, +/* pos 033c: 445 */ 0xE9 /* 'i' -> */, +/* pos 033d: 446 */ 0xE6 /* 'f' -> */, +/* pos 033e: 447 */ 0xE9 /* 'i' -> */, +/* pos 033f: 448 */ 0xE5 /* 'e' -> */, +/* pos 0340: 449 */ 0xE4 /* 'd' -> */, +/* pos 0341: 450 */ 0xAD /* '-' -> */, +/* pos 0342: 451 */ 0xF3 /* 's' -> */, +/* pos 0343: 452 */ 0xE9 /* 'i' -> */, +/* pos 0344: 453 */ 0xEE /* 'n' -> */, +/* pos 0345: 454 */ 0xE3 /* 'c' -> */, +/* pos 0346: 455 */ 0xE5 /* 'e' -> */, +/* pos 0347: 456 */ 0xBA /* ':' -> */, +/* pos 0348: 457 */ 0x00, 0x38 /* - terminal marker 56 - */, +/* pos 034a: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0354 state 459) */, + 0x69 /* 'i' */, 0x15, 0x00 /* (to 0x0362 state 472) */, + 0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0367 state 476) */, + 0x08, /* fail */ +/* pos 0354: 459 */ 0xF3 /* 's' -> */, +/* pos 0355: 460 */ 0xF4 /* 't' -> */, +/* pos 0356: 461 */ 0xAD /* '-' -> */, +/* pos 0357: 462 */ 0xED /* 'm' -> */, +/* pos 0358: 463 */ 0xEF /* 'o' -> */, +/* pos 0359: 464 */ 0xE4 /* 'd' -> */, +/* pos 035a: 465 */ 0xE9 /* 'i' -> */, +/* pos 035b: 466 */ 0xE6 /* 'f' -> */, +/* pos 035c: 467 */ 0xE9 /* 'i' -> */, +/* pos 035d: 468 */ 0xE5 /* 'e' -> */, +/* pos 035e: 469 */ 0xE4 /* 'd' -> */, +/* pos 035f: 470 */ 0xBA /* ':' -> */, +/* pos 0360: 471 */ 0x00, 0x39 /* - terminal marker 57 - */, +/* pos 0362: 472 */ 0xEE /* 'n' -> */, +/* pos 0363: 473 */ 0xEB /* 'k' -> */, +/* pos 0364: 474 */ 0xBA /* ':' -> */, +/* pos 0365: 475 */ 0x00, 0x3A /* - terminal marker 58 - */, +/* pos 0367: 476 */ 0xE3 /* 'c' -> */, +/* pos 0368: 477 */ 0xE1 /* 'a' -> */, +/* pos 0369: 478 */ 0xF4 /* 't' -> */, +/* pos 036a: 479 */ 0xE9 /* 'i' -> */, +/* pos 036b: 480 */ 0xEF /* 'o' -> */, +/* pos 036c: 481 */ 0xEE /* 'n' -> */, +/* pos 036d: 482 */ 0xBA /* ':' -> */, +/* pos 036e: 483 */ 0x00, 0x3B /* - terminal marker 59 - */, +/* pos 0370: 484 */ 0xE1 /* 'a' -> */, +/* pos 0371: 485 */ 0xF8 /* 'x' -> */, +/* pos 0372: 486 */ 0xAD /* '-' -> */, +/* pos 0373: 487 */ 0xE6 /* 'f' -> */, +/* pos 0374: 488 */ 0xEF /* 'o' -> */, +/* pos 0375: 489 */ 0xF2 /* 'r' -> */, +/* pos 0376: 490 */ 0xF7 /* 'w' -> */, +/* pos 0377: 491 */ 0xE1 /* 'a' -> */, +/* pos 0378: 492 */ 0xF2 /* 'r' -> */, +/* pos 0379: 493 */ 0xE4 /* 'd' -> */, +/* pos 037a: 494 */ 0xF3 /* 's' -> */, +/* pos 037b: 495 */ 0xBA /* ':' -> */, +/* pos 037c: 496 */ 0x00, 0x3C /* - terminal marker 60 - */, +/* pos 037e: 497 */ 0xF8 /* 'x' -> */, +/* pos 037f: 498 */ 0xF9 /* 'y' -> */, +/* pos 0380: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0387 state 500) */, + 0x20 /* ' ' */, 0xBB, 0x00 /* (to 0x043E state 649) */, + 0x08, /* fail */ +/* pos 0387: 500 */ 0xE1 /* 'a' -> */, +/* pos 0388: 501 */ 0xF5 /* 'u' -> */, +/* pos 0389: 502 */ 0xF4 /* 't' -> */, +/* pos 038a: 503 */ 0xE8 /* 'h' -> */, +/* pos 038b: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0392 state 505) */, + 0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x039C state 514) */, + 0x08, /* fail */ +/* pos 0392: 505 */ 0xEE /* 'n' -> */, +/* pos 0393: 506 */ 0xF4 /* 't' -> */, +/* pos 0394: 507 */ 0xE9 /* 'i' -> */, +/* pos 0395: 508 */ 0xE3 /* 'c' -> */, +/* pos 0396: 509 */ 0xE1 /* 'a' -> */, +/* pos 0397: 510 */ 0xF4 /* 't' -> */, +/* pos 0398: 511 */ 0xE5 /* 'e' -> */, +/* pos 0399: 512 */ 0xBA /* ':' -> */, +/* pos 039a: 513 */ 0x00, 0x3D /* - terminal marker 61 - */, +/* pos 039c: 514 */ 0xF2 /* 'r' -> */, +/* pos 039d: 515 */ 0xE9 /* 'i' -> */, +/* pos 039e: 516 */ 0xFA /* 'z' -> */, +/* pos 039f: 517 */ 0xE1 /* 'a' -> */, +/* pos 03a0: 518 */ 0xF4 /* 't' -> */, +/* pos 03a1: 519 */ 0xE9 /* 'i' -> */, +/* pos 03a2: 520 */ 0xEF /* 'o' -> */, +/* pos 03a3: 521 */ 0xEE /* 'n' -> */, +/* pos 03a4: 522 */ 0xBA /* ':' -> */, +/* pos 03a5: 523 */ 0x00, 0x3E /* - terminal marker 62 - */, +/* pos 03a7: 524 */ 0xE5 /* 'e' -> */, +/* pos 03a8: 525 */ 0xF3 /* 's' -> */, +/* pos 03a9: 526 */ 0xE8 /* 'h' -> */, +/* pos 03aa: 527 */ 0xBA /* ':' -> */, +/* pos 03ab: 528 */ 0x00, 0x3F /* - terminal marker 63 - */, +/* pos 03ad: 529 */ 0xF2 /* 'r' -> */, +/* pos 03ae: 530 */ 0xF9 /* 'y' -> */, +/* pos 03af: 531 */ 0xAD /* '-' -> */, +/* pos 03b0: 532 */ 0xE1 /* 'a' -> */, +/* pos 03b1: 533 */ 0xE6 /* 'f' -> */, +/* pos 03b2: 534 */ 0xF4 /* 't' -> */, +/* pos 03b3: 535 */ 0xE5 /* 'e' -> */, +/* pos 03b4: 536 */ 0xF2 /* 'r' -> */, +/* pos 03b5: 537 */ 0xBA /* ':' -> */, +/* pos 03b6: 538 */ 0x00, 0x40 /* - terminal marker 64 - */, +/* pos 03b8: 539 */ 0xF6 /* 'v' -> */, +/* pos 03b9: 540 */ 0xE5 /* 'e' -> */, +/* pos 03ba: 541 */ 0xF2 /* 'r' -> */, +/* pos 03bb: 542 */ 0xBA /* ':' -> */, +/* pos 03bc: 543 */ 0x00, 0x41 /* - terminal marker 65 - */, +/* pos 03be: 544 */ 0xAD /* '-' -> */, +/* pos 03bf: 545 */ 0xE3 /* 'c' -> */, +/* pos 03c0: 546 */ 0xEF /* 'o' -> */, +/* pos 03c1: 547 */ 0xEF /* 'o' -> */, +/* pos 03c2: 548 */ 0xEB /* 'k' -> */, +/* pos 03c3: 549 */ 0xE9 /* 'i' -> */, +/* pos 03c4: 550 */ 0xE5 /* 'e' -> */, +/* pos 03c5: 551 */ 0xBA /* ':' -> */, +/* pos 03c6: 552 */ 0x00, 0x42 /* - terminal marker 66 - */, +/* pos 03c8: 553 */ 0xF2 /* 'r' -> */, +/* pos 03c9: 554 */ 0xE9 /* 'i' -> */, +/* pos 03ca: 555 */ 0xE3 /* 'c' -> */, +/* pos 03cb: 556 */ 0xF4 /* 't' -> */, +/* pos 03cc: 557 */ 0xAD /* '-' -> */, +/* pos 03cd: 558 */ 0xF4 /* 't' -> */, +/* pos 03ce: 559 */ 0xF2 /* 'r' -> */, +/* pos 03cf: 560 */ 0xE1 /* 'a' -> */, +/* pos 03d0: 561 */ 0xEE /* 'n' -> */, +/* pos 03d1: 562 */ 0xF3 /* 's' -> */, +/* pos 03d2: 563 */ 0xF0 /* 'p' -> */, +/* pos 03d3: 564 */ 0xEF /* 'o' -> */, +/* pos 03d4: 565 */ 0xF2 /* 'r' -> */, +/* pos 03d5: 566 */ 0xF4 /* 't' -> */, +/* pos 03d6: 567 */ 0xAD /* '-' -> */, +/* pos 03d7: 568 */ 0xF3 /* 's' -> */, +/* pos 03d8: 569 */ 0xE5 /* 'e' -> */, +/* pos 03d9: 570 */ 0xE3 /* 'c' -> */, +/* pos 03da: 571 */ 0xF5 /* 'u' -> */, +/* pos 03db: 572 */ 0xF2 /* 'r' -> */, +/* pos 03dc: 573 */ 0xE9 /* 'i' -> */, +/* pos 03dd: 574 */ 0xF4 /* 't' -> */, +/* pos 03de: 575 */ 0xF9 /* 'y' -> */, +/* pos 03df: 576 */ 0xBA /* ':' -> */, +/* pos 03e0: 577 */ 0x00, 0x43 /* - terminal marker 67 - */, +/* pos 03e2: 578 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x03E9 state 579) */, + 0x65 /* 'e' */, 0x84, 0x00 /* (to 0x0469 state 680) */, + 0x08, /* fail */ +/* pos 03e9: 579 */ 0xE1 /* 'a' -> */, +/* pos 03ea: 580 */ 0xEE /* 'n' -> */, +/* pos 03eb: 581 */ 0xF3 /* 's' -> */, +/* pos 03ec: 582 */ 0xE6 /* 'f' -> */, +/* pos 03ed: 583 */ 0xE5 /* 'e' -> */, +/* pos 03ee: 584 */ 0xF2 /* 'r' -> */, +/* pos 03ef: 585 */ 0xAD /* '-' -> */, +/* pos 03f0: 586 */ 0xE5 /* 'e' -> */, +/* pos 03f1: 587 */ 0xEE /* 'n' -> */, +/* pos 03f2: 588 */ 0xE3 /* 'c' -> */, +/* pos 03f3: 589 */ 0xEF /* 'o' -> */, +/* pos 03f4: 590 */ 0xE4 /* 'd' -> */, +/* pos 03f5: 591 */ 0xE9 /* 'i' -> */, +/* pos 03f6: 592 */ 0xEE /* 'n' -> */, +/* pos 03f7: 593 */ 0xE7 /* 'g' -> */, +/* pos 03f8: 594 */ 0xBA /* ':' -> */, +/* pos 03f9: 595 */ 0x00, 0x44 /* - terminal marker 68 - */, +/* pos 03fb: 596 */ 0xE5 /* 'e' -> */, +/* pos 03fc: 597 */ 0xF2 /* 'r' -> */, +/* pos 03fd: 598 */ 0xAD /* '-' -> */, +/* pos 03fe: 599 */ 0xE1 /* 'a' -> */, +/* pos 03ff: 600 */ 0xE7 /* 'g' -> */, +/* pos 0400: 601 */ 0xE5 /* 'e' -> */, +/* pos 0401: 602 */ 0xEE /* 'n' -> */, +/* pos 0402: 603 */ 0xF4 /* 't' -> */, +/* pos 0403: 604 */ 0xBA /* ':' -> */, +/* pos 0404: 605 */ 0x00, 0x45 /* - terminal marker 69 - */, +/* pos 0406: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x040D state 607) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0412 state 611) */, + 0x08, /* fail */ +/* pos 040d: 607 */ 0xF2 /* 'r' -> */, +/* pos 040e: 608 */ 0xF9 /* 'y' -> */, +/* pos 040f: 609 */ 0xBA /* ':' -> */, +/* pos 0410: 610 */ 0x00, 0x46 /* - terminal marker 70 - */, +/* pos 0412: 611 */ 0xE1 /* 'a' -> */, +/* pos 0413: 612 */ 0xBA /* ':' -> */, +/* pos 0414: 613 */ 0x00, 0x47 /* - terminal marker 71 - */, +/* pos 0416: 614 */ 0xF7 /* 'w' -> */, +/* pos 0417: 615 */ 0xF7 /* 'w' -> */, +/* pos 0418: 616 */ 0xAD /* '-' -> */, +/* pos 0419: 617 */ 0xE1 /* 'a' -> */, +/* pos 041a: 618 */ 0xF5 /* 'u' -> */, +/* pos 041b: 619 */ 0xF4 /* 't' -> */, +/* pos 041c: 620 */ 0xE8 /* 'h' -> */, +/* pos 041d: 621 */ 0xE5 /* 'e' -> */, +/* pos 041e: 622 */ 0xEE /* 'n' -> */, +/* pos 041f: 623 */ 0xF4 /* 't' -> */, +/* pos 0420: 624 */ 0xE9 /* 'i' -> */, +/* pos 0421: 625 */ 0xE3 /* 'c' -> */, +/* pos 0422: 626 */ 0xE1 /* 'a' -> */, +/* pos 0423: 627 */ 0xF4 /* 't' -> */, +/* pos 0424: 628 */ 0xE5 /* 'e' -> */, +/* pos 0425: 629 */ 0xBA /* ':' -> */, +/* pos 0426: 630 */ 0x00, 0x48 /* - terminal marker 72 - */, +/* pos 0428: 631 */ 0xF4 /* 't' -> */, +/* pos 0429: 632 */ 0xE3 /* 'c' -> */, +/* pos 042a: 633 */ 0xE8 /* 'h' -> */, +/* pos 042b: 634 */ 0x00, 0x49 /* - terminal marker 73 - */, +/* pos 042d: 635 */ 0xF4 /* 't' -> */, +/* pos 042e: 636 */ 0x00, 0x4A /* - terminal marker 74 - */, +/* pos 0430: 637 */ 0xEC /* 'l' -> */, +/* pos 0431: 638 */ 0xE5 /* 'e' -> */, +/* pos 0432: 639 */ 0xF4 /* 't' -> */, +/* pos 0433: 640 */ 0xE5 /* 'e' -> */, +/* pos 0434: 641 */ 0x00, 0x4B /* - terminal marker 75 - */, +/* pos 0436: 642 */ 0xE9 /* 'i' -> */, +/* pos 0437: 643 */ 0xAD /* '-' -> */, +/* pos 0438: 644 */ 0xE1 /* 'a' -> */, +/* pos 0439: 645 */ 0xF2 /* 'r' -> */, +/* pos 043a: 646 */ 0xE7 /* 'g' -> */, +/* pos 043b: 647 */ 0xF3 /* 's' -> */, +/* pos 043c: 648 */ 0x00, 0x4C /* - terminal marker 76 - */, +/* pos 043e: 649 */ 0x00, 0x4D /* - terminal marker 77 - */, +/* pos 0440: 650 */ 0xAD /* '-' -> */, +/* pos 0441: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0448 state 652) */, + 0x66 /* 'f' */, 0x10, 0x00 /* (to 0x0454 state 662) */, + 0x08, /* fail */ +/* pos 0448: 652 */ 0xE5 /* 'e' -> */, +/* pos 0449: 653 */ 0xE1 /* 'a' -> */, +/* pos 044a: 654 */ 0xEC /* 'l' -> */, +/* pos 044b: 655 */ 0xAD /* '-' -> */, +/* pos 044c: 656 */ 0xE9 /* 'i' -> */, +/* pos 044d: 657 */ 0xF0 /* 'p' -> */, +/* pos 044e: 658 */ 0xBA /* ':' -> */, +/* pos 044f: 659 */ 0x00, 0x4E /* - terminal marker 78 - */, +/* pos 0451: 660 */ 0xA0 /* ' ' -> */, +/* pos 0452: 661 */ 0x00, 0x4F /* - terminal marker 79 - */, +/* pos 0454: 662 */ 0xEF /* 'o' -> */, +/* pos 0455: 663 */ 0xF2 /* 'r' -> */, +/* pos 0456: 664 */ 0xF7 /* 'w' -> */, +/* pos 0457: 665 */ 0xE1 /* 'a' -> */, +/* pos 0458: 666 */ 0xF2 /* 'r' -> */, +/* pos 0459: 667 */ 0xE4 /* 'd' -> */, +/* pos 045a: 668 */ 0xE5 /* 'e' -> */, +/* pos 045b: 669 */ 0xE4 /* 'd' -> */, +/* pos 045c: 670 */ 0xAD /* '-' -> */, +/* pos 045d: 671 */ 0xE6 /* 'f' -> */, +/* pos 045e: 672 */ 0xEF /* 'o' -> */, +/* pos 045f: 673 */ 0xF2 /* 'r' -> */, +/* pos 0460: 674 */ 0x00, 0x50 /* - terminal marker 80 - */, +/* pos 0462: 675 */ 0x00, 0x51 /* - terminal marker 81 - */, +/* pos 0464: 676 */ 0xE1 /* 'a' -> */, +/* pos 0465: 677 */ 0xE4 /* 'd' -> */, +/* pos 0466: 678 */ 0xA0 /* ' ' -> */, +/* pos 0467: 679 */ 0x00, 0x52 /* - terminal marker 82 - */, +/* pos 0469: 680 */ 0xBA /* ':' -> */, +/* pos 046a: 681 */ 0x00, 0x53 /* - terminal marker 83 - */, +/* total size 1132 bytes */ diff --git a/thirdparty/lws/libwebsockets.c b/thirdparty/lws/libwebsockets.c new file mode 100644 index 0000000000..50f975d21e --- /dev/null +++ b/thirdparty/lws/libwebsockets.c @@ -0,0 +1,3048 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef LWS_WITH_IPV6 +#if defined(WIN32) || defined(_WIN32) +#include <Iphlpapi.h> +#else +#include <net/if.h> +#endif +#endif + +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +static void (*lwsl_emit)(int level, const char *line) +#ifndef LWS_PLAT_OPTEE + = lwsl_emit_stderr +#endif + ; +#ifndef LWS_PLAT_OPTEE +static const char * const log_level_names[] = { + "ERR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "PARSER", + "HEADER", + "EXTENSION", + "CLIENT", + "LATENCY", + "USER", + "?", + "?" +}; +#endif + +void +lws_free_wsi(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + struct allocated_headers *ah; + + if (!wsi) + return; + + pt = &wsi->context->pt[(int)wsi->tsi]; + + /* + * Protocol user data may be allocated either internally by lws + * or by specified the user. We should only free what we allocated. + */ + if (wsi->protocol && wsi->protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) + lws_free(wsi->user_space); + + lws_free_set_NULL(wsi->rxflow_buffer); + lws_free_set_NULL(wsi->trunc_alloc); + + /* we may not have an ah, but may be on the waiting list... */ + lwsl_info("ah det due to close\n"); + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + + if (wsi->vhost->lserv_wsi == wsi) + wsi->vhost->lserv_wsi = NULL; + + lws_pt_lock(pt); + ah = pt->ah_list; + while (ah) { + if (ah->in_use && ah->wsi == wsi) { + lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi); + ah->in_use = 0; + ah->wsi = NULL; + pt->ah_count_in_use--; + break; + } + ah = ah->next; + } + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_track_wsi_close(wsi->context, wsi->peer); + wsi->peer = NULL; +#endif + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2 || wsi->http2_substream) { + lws_hpack_destroy_dynamic_header(wsi); + + if (wsi->u.h2.h2n) + lws_free_set_NULL(wsi->u.h2.h2n); + } +#endif + + lws_pt_unlock(pt); + + /* since we will destroy the wsi, make absolutely sure now */ + + lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_remove_from_timeout_list(wsi); + + wsi->context->count_wsi_allocated--; + lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi, + wsi->context->count_wsi_allocated); + + lws_free(wsi); +} + +void +lws_remove_from_timeout_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (!wsi->timeout_list_prev) /* ie, not part of the list */ + return; + + lws_pt_lock(pt); + /* if we have a next guy, set his prev to our prev */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev; + /* set our prev guy to our next guy instead of us */ + *wsi->timeout_list_prev = wsi->timeout_list; + + /* we're out of the list, we should not point anywhere any more */ + wsi->timeout_list_prev = NULL; + wsi->timeout_list = NULL; + lws_pt_unlock(pt); +} + +LWS_VISIBLE void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + time_t now; + + if (secs == LWS_TO_KILL_SYNC) { + lws_remove_from_timeout_list(wsi); + lwsl_debug("synchronously killing %p\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + return; + } + + lws_pt_lock(pt); + + time(&now); + + if (reason && !wsi->timeout_list_prev) { + /* our next guy is current first guy */ + wsi->timeout_list = pt->timeout_list; + /* if there is a next guy, set his prev ptr to our next ptr */ + if (wsi->timeout_list) + wsi->timeout_list->timeout_list_prev = &wsi->timeout_list; + /* our prev ptr is first ptr */ + wsi->timeout_list_prev = &pt->timeout_list; + /* set the first guy to be us */ + *wsi->timeout_list_prev = wsi; + } + + lwsl_debug("%s: %p: %d secs\n", __func__, wsi, secs); + wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout = reason; + + lws_pt_unlock(pt); + + if (!reason) + lws_remove_from_timeout_list(wsi); +} + +static void +lws_remove_child_from_any_parent(struct lws *wsi) +{ + struct lws **pwsi; + int seen = 0; + + if (!wsi->parent) + return; + + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: detach %p from parent %p\n", __func__, + wsi, wsi->parent); + + if (wsi->parent->protocol) + wsi->parent->protocol->callback(wsi, + LWS_CALLBACK_CHILD_CLOSING, + wsi->parent->user_space, wsi, 0); + + *pwsi = wsi->sibling_list; + seen = 1; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (!seen) + lwsl_err("%s: failed to detach from parent\n", __func__); + + wsi->parent = NULL; +} + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p) +{ +// if (wsi->protocol == p) +// return 0; + const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; + + if (wsi->protocol) + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + if (!wsi->user_space_externally_allocated) + lws_free_set_NULL(wsi->user_space); + + lws_same_vh_protocol_remove(wsi); + + wsi->protocol = p; + if (!p) + return 0; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (p > vp && p < &vp[wsi->vhost->count_protocols]) + lws_same_vh_protocol_insert(wsi, p - vp); + else { + int n = wsi->vhost->count_protocols; + int hit = 0; + + vpo = vp; + + while (n--) { + if (p->name && vp->name && !strcmp(p->name, vp->name)) { + hit = 1; + lws_same_vh_protocol_insert(wsi, vp - vpo); + break; + } + vp++; + } + if (!hit) + lwsl_err("%s: %p is not in vhost '%s' protocols list\n", + __func__, p, wsi->vhost->name); + } + + if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BIND_PROTOCOL, + wsi->user_space, NULL, 0)) + return 1; + + return 0; +} + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) +{ + struct lws_context_per_thread *pt; + struct lws *wsi1, *wsi2; + struct lws_context *context; + struct lws_tokens eff_buf; + int n, m, ret; + + lwsl_debug("%s: %p\n", __func__, wsi); + + if (!wsi) + return; + + lws_access_log(wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->premature_rx) + lws_free(wsi->premature_rx); + + if (wsi->pending_send_completion && !wsi->close_is_pending_send_completion) { + lwsl_notice("delaying close\n"); + wsi->close_is_pending_send_completion = 1; + return; + } +#endif + + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1); + + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + wsi1 = wsi2->sibling_list; + wsi2->parent = NULL; + /* stop it doing shutdown processing */ + wsi2->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi2, reason); + wsi2 = wsi1; + } + wsi->child_list = NULL; + } + +#if defined(LWS_WITH_HTTP2) + + if (wsi->u.h2.parent_wsi) { + lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi, wsi->u.h2.parent_wsi); + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.parent_wsi->u.h2.child_list) { + lwsl_info(" \\---- child %p\n", *w); + } lws_end_foreach_llp(w, u.h2.sibling_list); + } + + if (wsi->upgraded_to_http2 || wsi->http2_substream) { + lwsl_info("closing %p: parent %p\n", wsi, wsi->u.h2.parent_wsi); + + if (wsi->u.h2.child_list) { + lwsl_info(" parent %p: closing children: list:\n", wsi); + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) { + lwsl_info(" \\---- child %p\n", *w); + } lws_end_foreach_llp(w, u.h2.sibling_list); + /* trigger closing of all of our http2 children first */ + lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) { + lwsl_info(" closing child %p\n", *w); + /* disconnect from siblings */ + wsi2 = (*w)->u.h2.sibling_list; + (*w)->u.h2.sibling_list = NULL; + (*w)->socket_is_permanently_unusable = 1; + lws_close_free_wsi(*w, reason); + *w = wsi2; + continue; + } lws_end_foreach_llp(w, u.h2.sibling_list); + } + } + + if (wsi->upgraded_to_http2) { + /* remove pps */ + struct lws_h2_protocol_send *w = wsi->u.h2.h2n->pps, *w1; + while (w) { + w1 = wsi->u.h2.h2n->pps->next; + free(w); + w = w1; + } + wsi->u.h2.h2n->pps = NULL; + } + + if (wsi->http2_substream && wsi->u.h2.parent_wsi) { + lwsl_info(" %p: disentangling from siblings\n", wsi); + lws_start_foreach_llp(struct lws **, w, + wsi->u.h2.parent_wsi->u.h2.child_list) { + /* disconnect from siblings */ + if (*w == wsi) { + wsi2 = (*w)->u.h2.sibling_list; + (*w)->u.h2.sibling_list = NULL; + *w = wsi2; + lwsl_info(" %p disentangled from sibling %p\n", wsi, wsi2); + break; + } + } lws_end_foreach_llp(w, u.h2.sibling_list); + wsi->u.h2.parent_wsi->u.h2.child_count--; + wsi->u.h2.parent_wsi = NULL; + if (wsi->u.h2.pending_status_body) + lws_free_set_NULL(wsi->u.h2.pending_status_body); + } + + if (wsi->upgraded_to_http2 && wsi->u.h2.h2n && + wsi->u.h2.h2n->rx_scratch) + lws_free_set_NULL(wsi->u.h2.h2n->rx_scratch); +#endif + + if (wsi->mode == LWSCM_RAW_FILEDESC) { + lws_remove_child_from_any_parent(wsi); + remove_wsi_socket_from_fds(wsi); + wsi->protocol->callback(wsi, + LWS_CALLBACK_RAW_CLOSE_FILE, + wsi->user_space, NULL, 0); + goto async_close; + } + +#ifdef LWS_WITH_CGI + if (wsi->mode == LWSCM_CGI) { + /* we are not a network connection, but a handler for CGI io */ + if (wsi->parent && wsi->parent->cgi) { + + if (wsi->cgi_channel == LWS_STDOUT) + lws_cgi_remove_and_kill(wsi->parent); + + /* end the binding between us and master */ + wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; + } + wsi->socket_is_permanently_unusable = 1; + + goto just_kill_connection; + } + + if (wsi->cgi) + lws_cgi_remove_and_kill(wsi); +#endif + +#if !defined(LWS_NO_CLIENT) + if (wsi->mode == LWSCM_HTTP_CLIENT || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_WAITING_PROXY_REPLY || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2 || + wsi->mode == LWSCM_WSCL_WAITING_SSL || + wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_EXTENSION_CONNECT || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY) + if (wsi->u.hdr.stash) + lws_free_set_NULL(wsi->u.hdr.stash); +#endif + + if (wsi->mode == LWSCM_RAW) { + wsi->protocol->callback(wsi, + LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0); + wsi->socket_is_permanently_unusable = 1; + goto just_kill_connection; + } + + if ((wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED || + wsi->mode == LWSCM_HTTP2_SERVING) && + wsi->u.http.fop_fd != NULL) { + lws_vfs_file_close(&wsi->u.http.fop_fd); + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + if (wsi->socket_is_permanently_unusable || + reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || + wsi->state == LWSS_SHUTDOWN) + goto just_kill_connection; + + wsi->state_pre_close = wsi->state; + + switch (wsi->state_pre_close) { + case LWSS_DEAD_SOCKET: + return; + + /* we tried the polite way... */ + case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION: + case LWSS_AWAITING_CLOSE_ACK: + goto just_kill_connection; + + case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE: + if (wsi->trunc_len) { + lws_callback_on_writable(wsi); + return; + } + lwsl_info("%p: end FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + goto just_kill_connection; + default: + if (wsi->trunc_len) { + lwsl_info("%p: start FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5); + return; + } + break; + } + + if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT || + wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) + goto just_kill_connection; + + if (wsi->mode == LWSCM_HTTP_SERVING || + wsi->mode == LWSCM_HTTP2_SERVING) { + if (wsi->user_space) + wsi->vhost->protocols->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + /* + * are his extensions okay with him closing? Eg he might be a mux + * parent and just his ch1 aspect is closing? + */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { + lwsl_ext("extension vetoed close\n"); + return; + } + + /* + * flush any tx pending from extensions, since we may send close packet + * if there are problems with send, just nuke the connection + */ + do { + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* show every extension the new incoming data */ + + m = lws_ext_cb_active(wsi, + LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0); + if (m < 0) { + lwsl_ext("Extension reports fatal error\n"); + goto just_kill_connection; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) + if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len) != + eff_buf.token_len) { + lwsl_debug("close: ext spill failed\n"); + goto just_kill_connection; + } + } while (ret); + + /* + * signal we are closing, lws_write will + * add any necessary version-specific stuff. If the write fails, + * no worries we are closing anyway. If we didn't initiate this + * close, then our state has been changed to + * LWSS_RETURNED_CLOSE_ALREADY and we will skip this. + * + * Likewise if it's a second call to close this connection after we + * sent the close indication to the peer already, we are in state + * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time. + */ + + if (wsi->state_pre_close == LWSS_ESTABLISHED && + (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */ + (reason != LWS_CLOSE_STATUS_NOSTATUS && + (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) { + lwsl_debug("sending close indication...\n"); + + /* if no prepared close reason, use 1000 and no aux data */ + if (!wsi->u.ws.close_in_ping_buffer_len) { + wsi->u.ws.close_in_ping_buffer_len = 2; + wsi->u.ws.ping_payload_buf[LWS_PRE] = + (reason >> 8) & 0xff; + wsi->u.ws.ping_payload_buf[LWS_PRE + 1] = + reason & 0xff; + } + +#if defined (LWS_WITH_ESP8266) + wsi->close_is_pending_send_completion = 1; +#endif + + lwsl_debug("waiting for chance to send close\n"); + wsi->waiting_to_send_close_frame = 1; + wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 2); + lws_callback_on_writable(wsi); + + return; + } + +just_kill_connection: + + lws_remove_child_from_any_parent(wsi); + n = 0; + + if (wsi->user_space) { + lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, + wsi->protocol->name); + wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_DROP_PROTOCOL, + wsi->user_space, NULL, 0); + } + + if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || + wsi->mode == LWSCM_WSCL_WAITING_CONNECT) && + !wsi->already_did_cce) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, NULL, 0); + } + + if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) { + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLOSED_CLIENT_HTTP, + wsi->user_space, NULL, 0); + wsi->told_user_closed = 1; + } + + +#if LWS_POSIX + /* + * Testing with ab shows that we have to stage the socket close when + * the system is under stress... shutdown any further TX, change the + * state to one that won't emit anything more, and wait with a timeout + * for the POLLIN to show a zero-size rx before coming back and doing + * the actual close. + */ + if (wsi->mode != LWSCM_RAW && + !(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) && + wsi->state != LWSS_SHUTDOWN && + wsi->state != LWSS_CLIENT_UNCONNECTED && + reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY && + !wsi->socket_is_permanently_unusable) { +#ifdef LWS_OPENSSL_SUPPORT + if (lws_is_ssl(wsi) && wsi->ssl) { + n = SSL_shutdown(wsi->ssl); + /* + * If finished the SSL shutdown, then do socket + * shutdown, else need to retry SSL shutdown + */ + switch (n) { + case 0: + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + break; + case 1: + n = shutdown(wsi->desc.sockfd, SHUT_WR); + break; + default: + if (SSL_want_read(wsi->ssl)) { + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + break; + } + if (SSL_want_write(wsi->ssl)) { + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + n = 0; + break; + } + n = shutdown(wsi->desc.sockfd, SHUT_WR); + break; + } + } else +#endif + { + lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n", + __func__, wsi, (int)(long)wsi->desc.sockfd, + wsi->state); + if (!wsi->socket_is_permanently_unusable && + lws_sockfd_valid(wsi->desc.sockfd)) + n = shutdown(wsi->desc.sockfd, SHUT_WR); + } + if (n) + lwsl_debug("closing: shutdown (state %d) ret %d\n", + wsi->state, LWS_ERRNO); + + /* + * This causes problems on WINCE / ESP32 with disconnection + * when the events are half closing connection + */ +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32) + /* libuv: no event available to guarantee completion */ + if (!wsi->socket_is_permanently_unusable && + lws_sockfd_valid(wsi->desc.sockfd) && + !LWS_LIBUV_ENABLED(context)) { + lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); + wsi->state = LWSS_SHUTDOWN; + lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, + context->timeout_secs); + + return; + } +#endif + } +#endif + + lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, + wsi, wsi->desc.sockfd); + +#ifdef LWS_WITH_HTTP_PROXY + if (wsi->rw) { + lws_rewrite_destroy(wsi->rw); + wsi->rw = NULL; + } +#endif + /* + * we won't be servicing or receiving anything further from this guy + * delete socket from the internal poll list if still present + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + lws_remove_from_timeout_list(wsi); + + /* checking return redundant since we anyway close */ + if (wsi->desc.sockfd != LWS_SOCK_INVALID) + remove_wsi_socket_from_fds(wsi); + else + lws_same_vh_protocol_remove(wsi); + +#if defined(LWS_WITH_ESP8266) + espconn_disconnect(wsi->desc.sockfd); +#endif + + wsi->state = LWSS_DEAD_SOCKET; + + lws_free_set_NULL(wsi->rxflow_buffer); + if (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->mode == LWSCM_WS_SERVING || + wsi->mode == LWSCM_WS_CLIENT) { + + if (wsi->u.ws.rx_draining_ext) { + struct lws **w = &pt->rx_draining_ext_list; + + wsi->u.ws.rx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; + } + + if (wsi->u.ws.tx_draining_ext) { + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + } + lws_free_set_NULL(wsi->u.ws.rx_ubuf); + + if (wsi->trunc_alloc) + /* not going to be completed... nuke it */ + lws_free_set_NULL(wsi->trunc_alloc); + + wsi->u.ws.ping_payload_len = 0; + wsi->u.ws.ping_pending_flag = 0; + } + + /* tell the user it's all over for this guy */ + + if (!wsi->told_user_closed && + wsi->mode != LWSCM_RAW && wsi->protocol && + wsi->protocol->callback && + (wsi->state_pre_close == LWSS_ESTABLISHED || + wsi->state_pre_close == LWSS_HTTP2_ESTABLISHED || + wsi->state_pre_close == LWSS_HTTP_BODY || + wsi->state_pre_close == LWSS_HTTP || + wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK || + wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE || + (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) || + (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) { + lwsl_debug("calling back CLOSED %d %d\n", wsi->mode, wsi->state); + wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED, + wsi->user_space, NULL, 0); + } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_debug("calling back CLOSED_HTTP\n"); + wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->user_space, NULL, 0 ); + } else + lwsl_debug("not calling back closed mode=%d state=%d\n", + wsi->mode, wsi->state_pre_close); + + /* deallocate any active extension contexts */ + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0) + lwsl_warn("extension destruction failed\n"); + /* + * inform all extensions in case they tracked this guy out of band + * even though not active on him specifically + */ + if (lws_ext_cb_all_exts(context, wsi, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0) + lwsl_warn("ext destroy wsi failed\n"); + +async_close: + wsi->socket_is_permanently_unusable = 1; + +#ifdef LWS_WITH_LIBUV + if (!wsi->parent_carries_io && + lws_sockfd_valid(wsi->desc.sockfd)) + if (LWS_LIBUV_ENABLED(context)) { + if (wsi->listener) { + lwsl_debug("%s: stop listener poll\n", __func__); + uv_poll_stop(&wsi->w_read.uv_watcher); + } + lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", + __func__, wsi); + /* + * libuv has to do his own close handle processing + * asynchronously + */ + lws_libuv_closehandle(wsi); + + return; + } +#endif + + lws_close_free_wsi_final(wsi); +} + +void +lws_close_free_wsi_final(struct lws *wsi) +{ + int n; + + if (lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) { +#if LWS_POSIX + n = compatible_close(wsi->desc.sockfd); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); + +#else + compatible_close(wsi->desc.sockfd); + (void)n; +#endif + wsi->desc.sockfd = LWS_SOCK_INVALID; + } + + /* outermost destroy notification for wsi (user_space still intact) */ + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->user_space, NULL, 0); + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + + for (n = 0; n < 3; n++) { + if (wsi->cgi->pipe_fds[n][!!(n == 0)] == 0) + lwsl_err("ZERO FD IN CGI CLOSE"); + + if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) + close(wsi->cgi->pipe_fds[n][!!(n == 0)]); + } + + lws_free(wsi->cgi); + } +#endif + + lws_free_wsi(wsi); +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, sl = strlen(name); + + while (lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { + + if (!strncmp(buf, name, sl)) + return buf + sl; + + n++; + } + + return NULL; +} + +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +LWS_VISIBLE int +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen) +{ + int ipv6 = 0; +#ifdef LWS_WITH_IPV6 + ipv6 = LWS_IPV6_ENABLED(vh); +#endif + (void)vh; + + return lws_interface_to_sa(ipv6, ifname, addr, addrlen); +} +#endif + +#ifndef LWS_PLAT_OPTEE +#if LWS_POSIX +static int +lws_get_addresses(struct lws_vhost *vh, void *ads, char *name, + int name_len, char *rip, int rip_len) +{ +#if LWS_POSIX + struct addrinfo ai, *res; + struct sockaddr_in addr4; + + rip[0] = '\0'; + name[0] = '\0'; + addr4.sin_family = AF_UNSPEC; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vh)) { + if (!lws_plat_inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)ads)->sin6_addr, + rip, rip_len)) { + lwsl_err("inet_ntop: %s", strerror(LWS_ERRNO)); + return -1; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in6), name, name_len, NULL, 0, 0); + + return 0; + } else +#endif + { + struct addrinfo *result; + + memset(&ai, 0, sizeof ai); + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_CANONNAME; +#if !defined(LWS_WITH_ESP32) + if (getnameinfo((struct sockaddr *)ads, + sizeof(struct sockaddr_in), + name, name_len, NULL, 0, 0)) + return -1; +#endif + + if (getaddrinfo(name, NULL, &ai, &result)) + return -1; + + res = result; + while (addr4.sin_family == AF_UNSPEC && res) { + switch (res->ai_family) { + case AF_INET: + addr4.sin_addr = + ((struct sockaddr_in *)res->ai_addr)->sin_addr; + addr4.sin_family = AF_INET; + break; + } + + res = res->ai_next; + } + freeaddrinfo(result); + } + + if (addr4.sin_family == AF_UNSPEC) + return -1; + + if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL) + return -1; + + return 0; +#else + (void)vh; + (void)ads; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + + return -1; +#endif +} +#endif + + +LWS_VISIBLE const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen) +{ +#if LWS_POSIX + socklen_t len, olen; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + int af = AF_INET; + void *p, *q; + +#if defined(LWS_WITH_HTTP2) + if (wsi->http2_substream) + wsi = wsi->u.h2.parent_wsi; +#endif + + if (wsi->parent_carries_io) + wsi = wsi->parent; + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + af = AF_INET6; + q = &sin6.sin6_addr; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + q = &sin4.sin_addr; + } + + olen = len; + if (getpeername(wsi->desc.sockfd, p, &len) < 0 || len > olen) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + return NULL; + } + + return lws_plat_inet_ntop(af, q, name, namelen); +#else +#if defined(LWS_WITH_ESP8266) + return lws_plat_get_peer_simple(wsi, name, namelen); +#else + return NULL; +#endif +#endif +} +#endif + +LWS_VISIBLE void +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len) +{ +#ifndef LWS_PLAT_OPTEE +#if LWS_POSIX + socklen_t len; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; + struct lws_context *context = wsi->context; + int ret = -1; + void *p; + + rip[0] = '\0'; + name[0] = '\0'; + + lws_latency_pre(context, wsi); + +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(wsi->vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + ret = lws_get_addresses(wsi->vhost, p, name, name_len, rip, rip_len); + +bail: + lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1); +#endif +#endif + (void)wsi; + (void)fd; + (void)name; + (void)name_len; + (void)rip; + (void)rip_len; + +} + +LWS_EXTERN void * +lws_vhost_user(struct lws_vhost *vhost) +{ + return vhost->user; +} + +LWS_EXTERN void * +lws_context_user(struct lws_context *context) +{ + return context->user_space; +} + +LWS_VISIBLE struct lws_vhost * +lws_vhost_get(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->vhost; +} + +LWS_VISIBLE const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE struct lws * +lws_get_network_wsi(struct lws *wsi) +{ + if (!wsi) + return NULL; + +#if defined(LWS_WITH_HTTP2) + if (!wsi->http2_substream) + return wsi; + + while (wsi->u.h2.parent_wsi) + wsi = wsi->u.h2.parent_wsi; +#endif + + return wsi; +} + +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) +{ + int n; + + for (n = 0; n < vh->count_protocols; n++) + if (!strcmp(name, vh->protocols[n].name)) + return &vh->protocols[n]; + + return NULL; +} + +LWS_VISIBLE int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(wsi, reason, wsi->user_space, + NULL, 0); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason, + void *argp, size_t len) +{ + struct lws_context *context = vh->context; + struct lws_context_per_thread *pt = &context->pt[0]; + unsigned int n, m = context->count_threads; + struct lws *wsi; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->vhost == vh && (wsi->protocol == protocol || + !protocol)) + wsi->protocol->callback(wsi, reason, + wsi->user_space, argp, len); + } + pt++; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +{ + return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); +} + +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) +{ + int n; + + for (n = 0; n < wsi->vhost->count_protocols; n++) + if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) + return 1; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops) +{ + context->fops = fops; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_tell(lws_fop_fd_t fop_fd) +{ + return fop_fd->pos; +} + +LWS_VISIBLE LWS_EXTERN lws_filepos_t +lws_vfs_get_length(lws_fop_fd_t fop_fd) +{ + return fop_fd->len; +} + +LWS_VISIBLE LWS_EXTERN uint32_t +lws_vfs_get_mod_time(lws_fop_fd_t fop_fd) +{ + return fop_fd->mod_time; +} + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + lws_fileofs_t ofs; + + ofs = fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset - fop_fd->pos); + + return ofs; +} + + +LWS_VISIBLE lws_fileofs_t +lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, fop_fd->len + + fop_fd->pos + offset); +} + + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath) +{ + const struct lws_plat_file_ops *pf; + const char *p = vfs_path; + int n; + + *vpath = NULL; + + /* no non-platform fops, just use that */ + + if (!fops->next) + return fops; + + /* + * scan the vfs path looking for indications we are to be + * handled by a specific fops + */ + + while (p && *p) { + if (*p != '/') { + p++; + continue; + } + /* the first one is always platform fops, so skip */ + pf = fops->next; + while (pf) { + n = 0; + while (n < ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + if (p >= vfs_path + pf->fi[n].len) + if (!strncmp(p - (pf->fi[n].len - 1), + pf->fi[n].sig, + pf->fi[n].len - 1)) { + *vpath = p + 1; + return pf; + } + + n++; + } + pf = pf->next; + } + p++; + } + + return fops; +} + +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT +lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + lws_fop_flags_t *flags) +{ + const char *vpath = ""; + const struct lws_plat_file_ops *selected; + + selected = lws_vfs_select_fops(fops, vfs_path, &vpath); + + return selected->LWS_FOP_OPEN(fops, vfs_path, vpath, flags); +} + + +/** + * lws_now_secs() - seconds since 1970-1-1 + * + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec; +} + + +#if LWS_POSIX + +LWS_VISIBLE int +lws_get_socket_fd(struct lws *wsi) +{ + if (!wsi) + return -1; + return wsi->desc.sockfd; +} + +#endif + +#ifdef LWS_LATENCY +void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completed) +{ + unsigned long long u; + char buf[256]; + + u = time_in_microseconds(); + + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; + } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); + + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif + +LWS_VISIBLE int +lws_rx_flow_control(struct lws *wsi, int _enable) +{ + int en = _enable; + + lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); + + if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { + /* + * convert user bool style to bitmap style... in user simple + * bool style _enable = 0 = flow control it, = 1 = allow rx + */ + en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; + if (_enable & 1) + en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; + } + + /* any bit set in rxflow_bitmap DISABLEs rxflow control */ + if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) + wsi->rxflow_bitmap &= ~(en & 0xff); + else + wsi->rxflow_bitmap |= en & 0xff; + + if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == + wsi->rxflow_change_to) + return 0; + + wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !wsi->rxflow_bitmap; + + lwsl_info("%s: 0x%p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, + wsi->rxflow_bitmap, en, wsi->rxflow_change_to); + + if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || + !wsi->rxflow_will_be_applied) + return _lws_rx_flow_control(wsi); + + return 0; +} + +LWS_VISIBLE void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + const struct lws_context_per_thread *pt = &context->pt[0]; + struct lws *wsi; + unsigned int n, m = context->count_threads; + + while (m--) { + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(context, pt->fds[n].fd); + if (!wsi) + continue; + if (wsi->protocol == protocol) + lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); + } + pt++; + } +} + +LWS_VISIBLE extern const char * +lws_canonical_hostname(struct lws_context *context) +{ + return (const char *)context->canonical_hostname; +} + +int user_callback_handle_rxflow(lws_callback_function callback_function, + struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + int n; + + wsi->rxflow_will_be_applied = 1; + n = callback_function(wsi, reason, user, in, len); + wsi->rxflow_will_be_applied = 0; + if (!n) + n = _lws_rx_flow_control(wsi); + + return n; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +LWS_VISIBLE int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) +{ +#if !defined(LWS_WITH_ESP8266) + char *p; + char authstring[96]; + + if (!proxy) + return -1; + + /* we have to deal with a possible redundant leading http:// */ + if (!strncmp(proxy, "http://", 7)) + proxy += 7; + + p = strchr(proxy, '@'); + if (p) { /* auth is around */ + + if ((unsigned int)(p - proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + strncpy(authstring, proxy, p - proxy); + // null termination not needed on input + if (lws_b64_encode_string(authstring, (p - proxy), + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) + goto auth_too_long; + + lwsl_info(" Proxy auth in use\n"); + + proxy = p + 1; + } else + vhost->proxy_basic_auth_token[0] = '\0'; + + strncpy(vhost->http_proxy_address, proxy, + sizeof(vhost->http_proxy_address) - 1); + vhost->http_proxy_address[ + sizeof(vhost->http_proxy_address) - 1] = '\0'; + + p = strchr(vhost->http_proxy_address, ':'); + if (!p && !vhost->http_proxy_port) { + lwsl_err("http_proxy needs to be ads:port\n"); + + return -1; + } else { + if (p) { + *p = '\0'; + vhost->http_proxy_port = atoi(p + 1); + } + } + + lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address, + vhost->http_proxy_port); + + return 0; + +auth_too_long: + lwsl_err("proxy auth too long\n"); +#endif + return -1; +} + +#if defined(LWS_WITH_SOCKS5) +LWS_VISIBLE int +lws_set_socks(struct lws_vhost *vhost, const char *socks) +{ +#if !defined(LWS_WITH_ESP8266) + char *p_at, *p_colon; + char user[96]; + char password[96]; + + if (!socks) + return -1; + + vhost->socks_user[0] = '\0'; + vhost->socks_password[0] = '\0'; + + p_at = strchr(socks, '@'); + if (p_at) { /* auth is around */ + if ((unsigned int)(p_at - socks) > (sizeof(user) + + sizeof(password) - 2)) { + lwsl_err("Socks auth too long\n"); + goto bail; + } + + p_colon = strchr(socks, ':'); + if (p_colon) { + if ((unsigned int)(p_colon - socks) > (sizeof(user) + - 1) ) { + lwsl_err("Socks user too long\n"); + goto bail; + } + if ((unsigned int)(p_at - p_colon) > (sizeof(password) + - 1) ) { + lwsl_err("Socks password too long\n"); + goto bail; + } + + strncpy(vhost->socks_user, socks, p_colon - socks); + strncpy(vhost->socks_password, p_colon + 1, + p_at - (p_colon + 1)); + } + + lwsl_info(" Socks auth, user: %s, password: %s\n", + vhost->socks_user, vhost->socks_password ); + + socks = p_at + 1; + } + + strncpy(vhost->socks_proxy_address, socks, + sizeof(vhost->socks_proxy_address) - 1); + vhost->socks_proxy_address[sizeof(vhost->socks_proxy_address) - 1] + = '\0'; + + p_colon = strchr(vhost->socks_proxy_address, ':'); + if (!p_colon && !vhost->socks_proxy_port) { + lwsl_err("socks_proxy needs to be address:port\n"); + return -1; + } else { + if (p_colon) { + *p_colon = '\0'; + vhost->socks_proxy_port = atoi(p_colon + 1); + } + } + + lwsl_info(" Socks %s:%u\n", vhost->socks_proxy_address, + vhost->socks_proxy_port); + + return 0; + +bail: +#endif + return -1; +} +#endif + +LWS_VISIBLE const struct lws_protocols * +lws_get_protocol(struct lws *wsi) +{ + return wsi->protocol; +} + +LWS_VISIBLE int +lws_is_final_fragment(struct lws *wsi) +{ + lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__, + wsi->u.ws.final, (long)wsi->u.ws.rx_packet_length, + (long)wsi->u.ws.rx_draining_ext); + return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && + !wsi->u.ws.rx_draining_ext; +} + +LWS_VISIBLE int +lws_is_first_fragment(struct lws *wsi) +{ + return wsi->u.ws.first_fragment; +} + +LWS_VISIBLE unsigned char +lws_get_reserved_bits(struct lws *wsi) +{ + return wsi->u.ws.rsv; +} + +int +lws_ensure_user_space(struct lws *wsi) +{ + if (!wsi->protocol) + return 0; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size, "user space"); + if (wsi->user_space == NULL) { + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } else + lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, + wsi, (long)wsi->protocol->per_session_data_size, + wsi->user_space); + return 0; +} + +LWS_VISIBLE void * +lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) +{ + ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = + new_size; + + if (lws_ensure_user_space(wsi)) + return NULL; + + return wsi->user_space; +} + +LWS_VISIBLE int +lwsl_timestamp(int level, char *p, int len) +{ +#ifndef LWS_PLAT_OPTEE + time_t o_now = time(NULL); + unsigned long long now; + struct tm *ptm = NULL; +#ifndef WIN32 + struct tm tm; +#endif + int n; + +#ifndef _WIN32_WCE +#ifdef WIN32 + ptm = localtime(&o_now); +#else + if (localtime_r(&o_now, &tm)) + ptm = &tm; +#endif +#endif + p[0] = '\0'; + for (n = 0; n < LLL_COUNT; n++) { + if (level != (1 << n)) + continue; + now = time_in_microseconds() / 100; + if (ptm) + n = lws_snprintf(p, len, + "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + (int)(now % 10000), log_level_names[n]); + else + n = lws_snprintf(p, len, "[%llu:%04d] %s: ", + (unsigned long long) now / 10000, + (int)(now % 10000), log_level_names[n]); + return n; + } +#endif + return 0; +} + +static const char * const colours[] = { + "[31;1m", /* LLL_ERR */ + "[36;1m", /* LLL_WARN */ + "[35;1m", /* LLL_NOTICE */ + "[32;1m", /* LLL_INFO */ + "[34;1m", /* LLL_DEBUG */ + "[33;1m", /* LLL_PARSER */ + "[33;1m", /* LLL_HEADER */ + "[33;1m", /* LLL_EXT */ + "[33;1m", /* LLL_CLIENT */ + "[33;1m", /* LLL_LATENCY */ + "[30;1m", /* LLL_USER */ +}; + +#ifndef LWS_PLAT_OPTEE +LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) +{ +#if !defined(LWS_WITH_ESP8266) + char buf[50]; + static char tty; + int n, m = ARRAY_SIZE(colours) - 1; + + if (!tty) + tty = isatty(2) | 2; + + lwsl_timestamp(level, buf, sizeof(buf)); + + if (tty == 3) { + n = 1 << (ARRAY_SIZE(colours) - 1); + while (n) { + if (level & n) + break; + m--; + n >>= 1; + } + fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27); + } else + fprintf(stderr, "%s%s", buf, line); +#endif +} +#endif + +LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl) +{ +#if defined(LWS_WITH_ESP8266) + char buf[128]; +#else + char buf[256]; +#endif + int n; + + if (!(log_level & filter)) + return; + + n = vsnprintf(buf, sizeof(buf) - 1, format, vl); + (void)n; +#if defined(LWS_WITH_ESP8266) + buf[sizeof(buf) - 1] = '\0'; +#else + /* vnsprintf returns what it would have written, even if truncated */ + if (n > sizeof(buf) - 1) + n = sizeof(buf) - 1; + if (n > 0) + buf[n] = '\0'; +#endif + + lwsl_emit(filter, buf); +} + +LWS_VISIBLE void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _lws_logv(filter, format, ap); + va_end(ap); +} + +LWS_VISIBLE void lws_set_log_level(int level, + void (*func)(int level, const char *line)) +{ + log_level = level; + if (func) + lwsl_emit = func; +} + +LWS_VISIBLE int lwsl_visible(int level) +{ + return log_level & level; +} + +LWS_VISIBLE void +lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) +{ + unsigned char *buf = (unsigned char *)vbuf; + unsigned int n, m, start; + char line[80]; + char *p; + + if (!lwsl_visible(hexdump_level)) + return; + + _lws_log(hexdump_level, "\n"); + + for (n = 0; n < len;) { + start = n; + p = line; + + p += sprintf(p, "%04X: ", start); + + for (m = 0; m < 16 && n < len; m++) + p += sprintf(p, "%02X ", buf[n++]); + while (m++ < 16) + p += sprintf(p, " "); + + p += sprintf(p, " "); + + for (m = 0; m < 16 && (start + m) < len; m++) { + if (buf[start + m] >= ' ' && buf[start + m] < 127) + *p++ = buf[start + m]; + else + *p++ = '.'; + } + while (m++ < 16) + *p++ = ' '; + + *p++ = '\n'; + *p = '\0'; + _lws_log(hexdump_level, "%s", line); + (void)line; + } + + _lws_log(hexdump_level, "\n"); +} + +LWS_VISIBLE void +lwsl_hexdump(const void *vbuf, size_t len) +{ + lwsl_hexdump_level(LLL_DEBUG, vbuf, len); +} + +LWS_VISIBLE int +lws_is_ssl(struct lws *wsi) +{ +#ifdef LWS_OPENSSL_SUPPORT + return wsi->use_ssl; +#else + (void)wsi; + return 0; +#endif +} + +#ifdef LWS_OPENSSL_SUPPORT +LWS_VISIBLE SSL* +lws_get_ssl(struct lws *wsi) +{ + return wsi->ssl; +} +#endif + +LWS_VISIBLE int +lws_partial_buffered(struct lws *wsi) +{ + return !!wsi->trunc_len; +} + +LWS_VISIBLE size_t +lws_get_peer_write_allowance(struct lws *wsi) +{ +#ifdef LWS_WITH_HTTP2 + /* only if we are using HTTP2 on this connection */ + if (wsi->mode != LWSCM_HTTP2_SERVING) + return -1; + + return lws_h2_tx_cr_get(wsi); +#else + (void)wsi; + return -1; +#endif +} + +LWS_VISIBLE void +lws_union_transition(struct lws *wsi, enum connection_mode mode) +{ + lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode); + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->mode = mode; +} + +LWS_VISIBLE struct lws_plat_file_ops * +lws_get_fops(struct lws_context *context) +{ + return (struct lws_plat_file_ops *)context->fops; +} + +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_get_context(const struct lws *wsi) +{ + return wsi->context; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_count_threads(struct lws_context *context) +{ + return context->count_threads; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_wsi_user(struct lws *wsi, void *data) +{ + if (wsi->user_space_externally_allocated) + wsi->user_space = data; + else + lwsl_err("%s: Cannot set internally-allocated user_space\n", + __func__); +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_parent_carries_io(struct lws *wsi) +{ + wsi->parent_carries_io = 1; +} + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_parent_data(const struct lws *wsi) +{ + return wsi->opaque_parent_data; +} + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_parent_data(struct lws *wsi, void *data) +{ + wsi->opaque_parent_data = data; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_child_pending_on_writable(const struct lws *wsi) +{ + return wsi->parent_pending_cb_on_writable; +} + +LWS_VISIBLE LWS_EXTERN void +lws_clear_child_pending_on_writable(struct lws *wsi) +{ + wsi->parent_pending_cb_on_writable = 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_get_close_length(struct lws *wsi) +{ + return wsi->u.ws.close_in_ping_buffer_len; +} + +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_get_close_payload(struct lws *wsi) +{ + return &wsi->u.ws.ping_payload_buf[LWS_PRE]; +} + +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len) +{ + unsigned char *p, *start; + int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE; + + assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT); + + start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE]; + + *p++ = (((int)status) >> 8) & 0xff; + *p++ = ((int)status) & 0xff; + + if (buf) + while (len-- && p < start + budget) + *p++ = *buf++; + + wsi->u.ws.close_in_ping_buffer_len = p - start; +} + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi) +{ + struct lws *wsic = wsi->child_list; + + /* if he has children, do those if they were changed */ + while (wsic) { + if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) + _lws_rx_flow_control(wsic); + + wsic = wsic->sibling_list; + } + + /* there is no pending change */ + if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) + return 0; + + /* stuff is still buffered, not ready to really accept new input */ + if (wsi->rxflow_buffer) { + /* get ourselves called back to deal with stashed buffer */ + lws_callback_on_writable(wsi); + return 0; + } + + /* pending is cleared, we can change rxflow state */ + + wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + +LWS_EXTERN int +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len) +{ + static const unsigned char e0f4[] = { + 0xa0 | ((2 - 1) << 2) | 1, /* e0 */ + 0x80 | ((4 - 1) << 2) | 1, /* e1 */ + 0x80 | ((4 - 1) << 2) | 1, /* e2 */ + 0x80 | ((4 - 1) << 2) | 1, /* e3 */ + 0x80 | ((4 - 1) << 2) | 1, /* e4 */ + 0x80 | ((4 - 1) << 2) | 1, /* e5 */ + 0x80 | ((4 - 1) << 2) | 1, /* e6 */ + 0x80 | ((4 - 1) << 2) | 1, /* e7 */ + 0x80 | ((4 - 1) << 2) | 1, /* e8 */ + 0x80 | ((4 - 1) << 2) | 1, /* e9 */ + 0x80 | ((4 - 1) << 2) | 1, /* ea */ + 0x80 | ((4 - 1) << 2) | 1, /* eb */ + 0x80 | ((4 - 1) << 2) | 1, /* ec */ + 0x80 | ((2 - 1) << 2) | 1, /* ed */ + 0x80 | ((4 - 1) << 2) | 1, /* ee */ + 0x80 | ((4 - 1) << 2) | 1, /* ef */ + 0x90 | ((3 - 1) << 2) | 2, /* f0 */ + 0x80 | ((4 - 1) << 2) | 2, /* f1 */ + 0x80 | ((4 - 1) << 2) | 2, /* f2 */ + 0x80 | ((4 - 1) << 2) | 2, /* f3 */ + 0x80 | ((1 - 1) << 2) | 2, /* f4 */ + + 0, /* s0 */ + 0x80 | ((4 - 1) << 2) | 0, /* s2 */ + 0x80 | ((4 - 1) << 2) | 1, /* s3 */ + }; + unsigned char s = *state; + + while (len--) { + unsigned char c = *buf++; + + if (!s) { + if (c >= 0x80) { + if (c < 0xc2 || c > 0xf4) + return 1; + if (c < 0xe0) + s = 0x80 | ((4 - 1) << 2); + else + s = e0f4[c - 0xe0]; + } + } else { + if (c < (s & 0xf0) || + c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30)) + return 1; + s = e0f4[21 + (s & 3)]; + } + } + + *state = s; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path) +{ + const char *end; + static const char *slash = "/"; + + /* cut up the location into address, port and path */ + *prot = p; + while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) + p++; + if (!*p) { + end = p; + p = (char *)*prot; + *prot = end; + } else { + *p = '\0'; + p += 3; + } + *ads = p; + if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) + *port = 80; + else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) + *port = 443; + + if (*p == '[') + { + ++(*ads); + while (*p && *p != ']') + p++; + if (*p) + *p++ = '\0'; + } + else + { + while (*p && *p != ':' && *p != '/') + p++; + } + if (*p == ':') { + *p++ = '\0'; + *port = atoi(p); + while (*p && *p != '/') + p++; + } + *path = slash; + if (*p) { + *p++ = '\0'; + if (*p) + *path = p; + } + + return 0; +} + +#ifdef LWS_NO_EXTENSIONS + +/* we need to provide dummy callbacks for internal exts + * so user code runs when faced with a lib compiled with + * extensions disabled. + */ + +int +lws_extension_callback_pm_deflate(struct lws_context *context, + const struct lws_extension *ext, + struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len) +{ + (void)context; + (void)ext; + (void)wsi; + (void)reason; + (void)user; + (void)in; + (void)len; + + return 0; +} +#endif + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface) +{ +#if LWS_POSIX +#ifdef LWS_WITH_UNIX_SOCK + struct sockaddr_un serv_unix; +#endif +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; +#ifndef LWS_PLAT_OPTEE + socklen_t len = sizeof(struct sockaddr_storage); +#endif + int n; + struct sockaddr_storage sin; + struct sockaddr *v; + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_unix; + n = sizeof(struct sockaddr_un); + bzero((char *) &serv_unix, sizeof(serv_unix)); + serv_unix.sun_family = AF_UNIX; + if (sizeof(serv_unix.sun_path) <= strlen(iface)) { + lwsl_err("\"%s\" too long for UNIX domain socket\n", + iface); + return -1; + } + strcpy(serv_unix.sun_path, iface); + if (serv_unix.sun_path[0] == '@') + serv_unix.sun_path[0] = '\0'; + + } else +#endif +#if defined(LWS_WITH_IPV6) && !defined(LWS_WITH_ESP32) + if (LWS_IPV6_ENABLED(vhost)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + if (iface) { + if (interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find if %s\n", iface); + return -1; + } + serv_addr6.sin6_scope_id = lws_get_addr_scope(iface); + } + + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; +#if !defined(LWS_WITH_ESP32) + + if (iface && + interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", iface); + return -1; + } +#endif + serv_addr4.sin_port = htons(port); + } /* ipv4 */ + + n = bind(sockfd, v, n); +#ifdef LWS_WITH_UNIX_SOCK + if (n < 0 && LWS_UNIX_SOCK_ENABLED(vhost)) { + lwsl_err("ERROR on binding fd %d to \"%s\" (%d %d)\n", + sockfd, iface, n, LWS_ERRNO); + return -1; + } else +#endif + if (n < 0) { + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); + return -1; + } + +#ifndef LWS_PLAT_OPTEE + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else +#endif +#if defined(LWS_WITH_IPV6) + port = (sin.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *) &sin)->sin6_port) : + ntohs(((struct sockaddr_in *) &sin)->sin_port); +#else + { + struct sockaddr_in sain; + memcpy(&sain, &sin, sizeof(sain)); + port = ntohs(sain.sin_port); + } +#endif +#endif + + return port; +} + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr) +{ + unsigned long scope = 0; + +#ifndef WIN32 + struct ifaddrs *addrs, *addr; + char ip[NI_MAXHOST]; + unsigned int i; + + getifaddrs(&addrs); + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr || + addr->ifa_addr->sa_family != AF_INET6) + continue; + + getnameinfo(addr->ifa_addr, + sizeof(struct sockaddr_in6), + ip, sizeof(ip), + NULL, 0, NI_NUMERICHOST); + + i = 0; + while (ip[i]) + if (ip[i++] == '%') { + ip[i - 1] = '\0'; + break; + } + + if (!strcmp(ip, ipaddr)) { + scope = if_nametoindex(addr->ifa_name); + break; + } + } + freeifaddrs(addrs); +#else + PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; + PIP_ADAPTER_UNICAST_ADDRESS addr; + ULONG size = 0; + DWORD ret; + struct sockaddr_in6 *sockaddr; + char ip[NI_MAXHOST]; + unsigned int i; + int found = 0; + + for (i = 0; i < 5; i++) + { + ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, + NULL, addrs, &size); + if ((ret == NO_ERROR) || (ret == ERROR_NO_DATA)) { + break; + } else if (ret == ERROR_BUFFER_OVERFLOW) + { + if (addrs) + free(addrs); + addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); + } else + { + if (addrs) + { + free(addrs); + addrs = NULL; + } + lwsl_err("Failed to get IPv6 address table (%d)", ret); + break; + } + } + + if ((ret == NO_ERROR) && (addrs)) { + adapter = addrs; + while (adapter && !found) { + addr = adapter->FirstUnicastAddress; + while (addr && !found) { + if (addr->Address.lpSockaddr->sa_family == AF_INET6) { + sockaddr = (struct sockaddr_in6 *) + (addr->Address.lpSockaddr); + + lws_plat_inet_ntop(sockaddr->sin6_family, + &sockaddr->sin6_addr, + ip, sizeof(ip)); + + if (!strcmp(ip, ipaddr)) { + scope = sockaddr->sin6_scope_id; + found = 1; + break; + } + } + addr = addr->Next; + } + adapter = adapter->Next; + } + } + if (addrs) + free(addrs); +#endif + + return scope; +} +#endif + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi) +{ + if (!wsi->context->ws_ping_pong_interval) + return; + if (wsi->state != LWSS_ESTABLISHED) + return; + + wsi->u.ws.time_next_ping_check = (time_t)lws_now_secs() + + wsi->context->ws_ping_pong_interval; +} + +static const char *hex = "0123456789ABCDEF"; + +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 2) { + if (*p == '\'') { + *q++ = '\''; + *q++ = '\''; + len --; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + if (!p) { + escaped[0] = '\0'; + return escaped; + } + + while (*p && len-- > 6) { + if (*p == '\"' || *p == '\\' || *p < 0x20) { + *q++ = '\\'; + *q++ = 'u'; + *q++ = '0'; + *q++ = '0'; + *q++ = hex[((*p) >> 4) & 15]; + *q++ = hex[(*p) & 15]; + len -= 5; + p++; + } else + *q++ = *p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len) +{ + const char *p = string; + char *q = escaped; + + while (*p && len-- > 3) { + if (*p == ' ') { + *q++ = '+'; + p++; + continue; + } + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z')) { + *q++ = *p++; + continue; + } + *q++ = '%'; + *q++ = hex[(*p >> 4) & 0xf]; + *q++ = hex[*p & 0xf]; + + len -= 2; + p++; + } + *q = '\0'; + + return escaped; +} + +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len) +{ + int state = 0, n; + char sum = 0; + + while (*escaped && len) { + switch (state) { + case 0: + if (*escaped == '%') { + state++; + escaped++; + continue; + } + if (*escaped == '+') { + escaped++; + *string++ = ' '; + len--; + continue; + } + *string++ = *escaped++; + len--; + break; + case 1: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + sum = n << 4; + state++; + break; + + case 2: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + *string++ = sum | n; + len--; + state = 0; + break; + } + + } + *string = '\0'; + + return 0; +} + +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context) +{ + struct lws_context_creation_info info; + + info.uid = context->uid; + info.gid = context->gid; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + memcpy(info.caps, context->caps, sizeof(info.caps)); + info.count_caps = context->count_caps; +#endif + + if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(&info); + + return 0; +} + +int +lws_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int n; + + if (!size) + return 0; + + va_start(ap, format); + n = vsnprintf(str, size, format, ap); + va_end(ap); + + if (n >= (int)size) + return size; + + return n; +} + + +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi) { +#ifdef LWS_WITH_CGI + return !!wsi->cgi; +#else + return 0; +#endif +} + + + +#ifdef LWS_NO_EXTENSIONS +LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val) +{ + return -1; +} +#endif + + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs) +{ + const struct lws_vhost *vh = ctx->vhost_list; + + while (vh) { + + cs->rx += vh->conn_stats.rx; + cs->tx += vh->conn_stats.tx; + cs->h1_conn += vh->conn_stats.h1_conn; + cs->h1_trans += vh->conn_stats.h1_trans; + cs->h2_trans += vh->conn_stats.h2_trans; + cs->ws_upg += vh->conn_stats.ws_upg; + cs->h2_upg += vh->conn_stats.h2_upg; + cs->h2_alpn += vh->conn_stats.h2_alpn; + cs->h2_subs += vh->conn_stats.h2_subs; + cs->rejected += vh->conn_stats.rejected; + + vh = vh->vhost_next; + } +} + +#ifdef LWS_WITH_SERVER_STATUS + +LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len) +{ + static const char * const prots[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://" + }; + char *orig = buf, *end = buf + len - 1, first = 1; + int n = 0; + + if (len < 100) + return 0; + + buf += lws_snprintf(buf, end - buf, + "{\n \"name\":\"%s\",\n" + " \"port\":\"%d\",\n" + " \"use_ssl\":\"%d\",\n" + " \"sts\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_upg\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\"" + , + vh->name, vh->listen_port, +#ifdef LWS_OPENSSL_SUPPORT + vh->use_ssl, +#else + 0, +#endif + !!(vh->options & LWS_SERVER_OPTION_STS), + vh->conn_stats.rx, vh->conn_stats.tx, + vh->conn_stats.h1_conn, + vh->conn_stats.h1_trans, + vh->conn_stats.h2_trans, + vh->conn_stats.ws_upg, + vh->conn_stats.rejected, + vh->conn_stats.h2_upg, + vh->conn_stats.h2_alpn, + vh->conn_stats.h2_subs + ); + + if (vh->mount_list) { + const struct lws_http_mount *m = vh->mount_list; + + buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":["); + while (m) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"mountpoint\":\"%s\",\n" + " \"origin\":\"%s%s\",\n" + " \"cache_max_age\":\"%d\",\n" + " \"cache_reuse\":\"%d\",\n" + " \"cache_revalidate\":\"%d\",\n" + " \"cache_intermediaries\":\"%d\"\n" + , + m->mountpoint, + prots[m->origin_protocol], + m->origin, + m->cache_max_age, + m->cache_reusable, + m->cache_revalidate, + m->cache_intermediaries); + if (m->def) + buf += lws_snprintf(buf, end - buf, + ",\n \"default\":\"%s\"", + m->def); + buf += lws_snprintf(buf, end - buf, "\n }"); + first = 0; + m = m->mount_next; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + if (vh->protocols) { + n = 0; + first = 1; + + buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":["); + while (n < vh->count_protocols) { + if (!first) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n \"%s\":{\n" + " \"status\":\"ok\"\n }\n }" + , + vh->protocols[n].name); + first = 0; + n++; + } + buf += lws_snprintf(buf, end - buf, "\n ]"); + } + + buf += lws_snprintf(buf, end - buf, "\n}"); + + return buf - orig; +} + + +LWS_EXTERN LWS_VISIBLE int +lws_json_dump_context(const struct lws_context *context, char *buf, int len, + int hide_vhosts) +{ + char *orig = buf, *end = buf + len - 1, first = 1; + const struct lws_vhost *vh = context->vhost_list; + const struct lws_context_per_thread *pt; + time_t t = time(NULL); + int n, listening = 0, cgi_count = 0; + struct lws_conn_stats cs; + double d = 0; +#ifdef LWS_WITH_CGI + struct lws_cgi * const *pcgi; +#endif + +#ifdef LWS_WITH_LIBUV + uv_uptime(&d); +#endif + + buf += lws_snprintf(buf, end - buf, "{ " + "\"version\":\"%s\",\n" + "\"uptime\":\"%ld\",\n", + lws_get_library_version(), + (long)d); + +#ifdef LWS_HAVE_GETLOADAVG + { + double d[3]; + int m; + + m = getloadavg(d, 3); + for (n = 0; n < m; n++) { + buf += lws_snprintf(buf, end - buf, + "\"l%d\":\"%.2f\",\n", + n + 1, d[n]); + } + } +#endif + + buf += lws_snprintf(buf, end - buf, "\"contexts\":[\n"); + + buf += lws_snprintf(buf, end - buf, "{ " + "\"context_uptime\":\"%ld\",\n" + "\"cgi_spawned\":\"%d\",\n" + "\"pt_fd_max\":\"%d\",\n" + "\"ah_pool_max\":\"%d\",\n" + "\"deprecated\":\"%d\",\n" + "\"wsi_alive\":\"%d\",\n", + (unsigned long)(t - context->time_up), + context->count_cgi_spawned, + context->fd_limit_per_thread, + context->max_http_header_pool, + context->deprecated, + context->count_wsi_allocated); + + buf += lws_snprintf(buf, end - buf, "\"pt\":[\n "); + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + if (n) + buf += lws_snprintf(buf, end - buf, ","); + buf += lws_snprintf(buf, end - buf, + "\n {\n" + " \"fds_count\":\"%d\",\n" + " \"ah_pool_inuse\":\"%d\",\n" + " \"ah_wait_list\":\"%d\"\n" + " }", + pt->fds_count, + pt->ah_count_in_use, + pt->ah_wait_list_length); + } + + buf += lws_snprintf(buf, end - buf, "]"); + + buf += lws_snprintf(buf, end - buf, ", \"vhosts\":[\n "); + + first = 1; + vh = context->vhost_list; + listening = 0; + cs = context->conn_stats; + lws_sum_stats(context, &cs); + while (vh) { + + if (!hide_vhosts) { + if (!first) + if(buf != end) + *buf++ = ','; + buf += lws_json_dump_vhost(vh, buf, end - buf); + first = 0; + } + if (vh->lserv_wsi) + listening++; + vh = vh->vhost_next; + } + + buf += lws_snprintf(buf, end - buf, + "],\n\"listen_wsi\":\"%d\",\n" + " \"rx\":\"%llu\",\n" + " \"tx\":\"%llu\",\n" + " \"h1_conn\":\"%lu\",\n" + " \"h1_trans\":\"%lu\",\n" + " \"h2_trans\":\"%lu\",\n" + " \"ws_upg\":\"%lu\",\n" + " \"rejected\":\"%lu\",\n" + " \"h2_alpn\":\"%lu\",\n" + " \"h2_subs\":\"%lu\",\n" + " \"h2_upg\":\"%lu\"", + listening, cs.rx, cs.tx, + cs.h1_conn, + cs.h1_trans, + cs.h2_trans, + cs.ws_upg, + cs.rejected, + cs.h2_alpn, + cs.h2_subs, + cs.h2_upg); + +#ifdef LWS_WITH_CGI + for (n = 0; n < context->count_threads; n++) { + pt = &context->pt[n]; + pcgi = &pt->cgi_list; + + while (*pcgi) { + pcgi = &(*pcgi)->cgi_list; + + cgi_count++; + } + } +#endif + buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ", + cgi_count); + + buf += lws_snprintf(buf, end - buf, "}"); + + + buf += lws_snprintf(buf, end - buf, "]}\n "); + + return buf - orig; +} + +#endif + +#if defined(LWS_WITH_STATS) + +LWS_VISIBLE LWS_EXTERN uint64_t +lws_stats_get(struct lws_context *context, int index) +{ + if (index >= LWSSTATS_SIZE) + return 0; + + return context->lws_stats[index]; +} + +LWS_VISIBLE LWS_EXTERN void +lws_stats_log_dump(struct lws_context *context) +{ + struct lws_vhost *v = context->vhost_list; + int n, m; + + (void)m; + + if (!context->updated) + return; + + context->updated = 0; + + lwsl_notice("\n"); + lwsl_notice("LWS internal statistics dump ----->\n"); + lwsl_notice("LWSSTATS_C_CONNECTIONS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_CONNECTIONS)); + lwsl_notice("LWSSTATS_C_API_CLOSE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_CLOSE)); + lwsl_notice("LWSSTATS_C_API_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_READ)); + lwsl_notice("LWSSTATS_C_API_LWS_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_LWS_WRITE)); + lwsl_notice("LWSSTATS_C_API_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_API_WRITE)); + lwsl_notice("LWSSTATS_C_WRITE_PARTIALS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITE_PARTIALS)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB_EFF_REQ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB_EFF_REQ)); + lwsl_notice("LWSSTATS_C_WRITEABLE_CB: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_FAILED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_FAILED)); + lwsl_notice("LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)); + lwsl_notice("LWSSTATS_C_SSL_CONNS_HAD_RX: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_AH_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_AH_DENIED)); + lwsl_notice("LWSSTATS_C_PEER_LIMIT_WSI_DENIED: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_PEER_LIMIT_WSI_DENIED)); + + lwsl_notice("LWSSTATS_C_TIMEOUTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_TIMEOUTS)); + lwsl_notice("LWSSTATS_C_SERVICE_ENTRY: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_C_SERVICE_ENTRY)); + lwsl_notice("LWSSTATS_B_READ: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_READ)); + lwsl_notice("LWSSTATS_B_WRITE: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_WRITE)); + lwsl_notice("LWSSTATS_B_PARTIALS_ACCEPTED_PARTS: %8llu\n", (unsigned long long)lws_stats_get(context, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS)); + lwsl_notice("LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) + lwsl_notice(" Avg accept delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY) / + lws_stats_get(context, LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED)) / 1000); + lwsl_notice("LWSSTATS_MS_SSL_RX_DELAY: %8llums\n", (unsigned long long)lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) / 1000); + if (lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) + lwsl_notice(" Avg accept-rx delay: %8llums\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_SSL_RX_DELAY) / + lws_stats_get(context, LWSSTATS_C_SSL_CONNS_HAD_RX)) / 1000); + + lwsl_notice("LWSSTATS_MS_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY)); + lwsl_notice("LWSSTATS_MS_WORST_WRITABLE_DELAY: %8lluus\n", + (unsigned long long)lws_stats_get(context, LWSSTATS_MS_WORST_WRITABLE_DELAY)); + if (lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB)) + lwsl_notice(" Avg writable delay: %8lluus\n", + (unsigned long long)(lws_stats_get(context, LWSSTATS_MS_WRITABLE_DELAY) / + lws_stats_get(context, LWSSTATS_C_WRITEABLE_CB))); + lwsl_notice("Simultaneous SSL restriction: %8d/%d/%d\n", context->simultaneous_ssl, + context->simultaneous_ssl_restriction, context->ssl_gate_accepts); + + lwsl_notice("Live wsi: %8d\n", context->count_wsi_allocated); + + context->updated = 1; + + while (v) { + if (v->lserv_wsi) { + + struct lws_context_per_thread *pt = &context->pt[(int)v->lserv_wsi->tsi]; + struct lws_pollfd *pfd; + + pfd = &pt->fds[v->lserv_wsi->position_in_fds_table]; + + lwsl_notice(" Listen port %d actual POLLIN: %d\n", + v->listen_port, (int)pfd->events & LWS_POLLIN); + } + + v = v->vhost_next; + } + + for (n = 0; n < context->count_threads; n++) { + struct lws_context_per_thread *pt = &context->pt[n]; + struct lws *wl; + int m = 0; + + lwsl_notice("PT %d\n", n + 1); + + lws_pt_lock(pt); + + lwsl_notice(" AH in use / max: %d / %d\n", + pt->ah_count_in_use, + context->max_http_header_pool); + + wl = pt->ah_wait_list; + while (wl) { + m++; + wl = wl->u.hdr.ah_wait_list; + } + + lwsl_notice(" AH wait list count / actual: %d / %d\n", + pt->ah_wait_list_length, m); + + lws_pt_unlock(pt); + } + +#if defined(LWS_WITH_PEER_LIMITS) + m = 0; + for (n = 0; n < (int)context->pl_hash_elements; n++) { + lws_start_foreach_llp(struct lws_peer **, peer, + context->pl_hash_table[n]) { + m++; + } lws_end_foreach_llp(peer, next); + } + + lwsl_notice(" Peers: total active %d\n", m); + if (m > 10) { + m = 10; + lwsl_notice(" (showing 10 peers only)\n"); + } + + if (m) { + for (n = 0; n < (int)context->pl_hash_elements; n++) { + char buf[72]; + + lws_start_foreach_llp(struct lws_peer **, peer, context->pl_hash_table[n]) { + struct lws_peer *df = *peer; + + if (!lws_plat_inet_ntop(df->af, df->addr, buf, + sizeof(buf) - 1)) + strcpy(buf, "unknown"); + + lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", + buf, df->count_wsi, df->count_ah); + + if (!--m) + break; + } lws_end_foreach_llp(peer, next); + } + } +#endif + + lwsl_notice("\n"); +} + +void +lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) +{ + lws_pt_lock(pt); + context->lws_stats[index] += bump; + if (index != LWSSTATS_C_SERVICE_ENTRY) + context->updated = 1; + lws_pt_unlock(pt); +} + +void +lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) +{ + lws_pt_lock(pt); + if (val > context->lws_stats[index]) { + context->lws_stats[index] = val; + context->updated = 1; + } + lws_pt_unlock(pt); +} + +#endif + diff --git a/thirdparty/lws/libwebsockets.h b/thirdparty/lws/libwebsockets.h new file mode 100644 index 0000000000..ef996c5d78 --- /dev/null +++ b/thirdparty/lws/libwebsockets.h @@ -0,0 +1,5790 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/** @file */ + +#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C +#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C + +#ifdef __cplusplus +#include <cstddef> +#include <cstdarg> +# +extern "C" { +#else +#include <stdarg.h> +#endif + +#include "lws_config.h" + +/* + * CARE: everything using cmake defines needs to be below here + */ + +#if defined(LWS_WITH_ESP8266) +struct sockaddr_in; +#define LWS_POSIX 0 +#else +#define LWS_POSIX 1 +#endif + +#if defined(LWS_HAS_INTPTR_T) +#include <stdint.h> +#define lws_intptr_t intptr_t +#else +typedef unsigned long long lws_intptr_t; +#endif + +#if defined(WIN32) || defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include <winsock2.h> +#include <ws2tcpip.h> +#include <stddef.h> +#include <basetsd.h> +#ifndef _WIN32_WCE +#include <fcntl.h> +#else +#define _O_RDONLY 0x0000 +#define O_RDONLY _O_RDONLY +#endif + +// Visual studio older than 2015 and WIN_CE has only _stricmp +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) +#define strcasecmp _stricmp +#elif !defined(__MINGW32__) +#define strcasecmp stricmp +#endif +#define getdtablesize() 30000 + +#define LWS_INLINE __inline +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#define LWS_FORMAT(string_index) + +#ifdef LWS_DLL +#ifdef LWS_INTERNAL +#define LWS_EXTERN extern __declspec(dllexport) +#else +#define LWS_EXTERN extern __declspec(dllimport) +#endif +#else +#define LWS_EXTERN +#endif + +#define LWS_INVALID_FILE INVALID_HANDLE_VALUE +#define LWS_O_RDONLY _O_RDONLY +#define LWS_O_WRONLY _O_WRONLY +#define LWS_O_CREAT _O_CREAT +#define LWS_O_TRUNC _O_TRUNC + +#if !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1900) /* Visual Studio 2015 already defines this in <stdio.h> */ +#define lws_snprintf _snprintf +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#if !defined(__MINGW32__) &&(!defined(_MSC_VER) || _MSC_VER < 1900) && !defined(snprintf) +#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) +#endif + +#else /* NOT WIN32 */ +#include <unistd.h> +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) +#include <sys/capability.h> +#endif + +#if defined(__NetBSD__) || defined(__FreeBSD__) +#include <netinet/in.h> +#endif + +#define LWS_INLINE inline +#define LWS_O_RDONLY O_RDONLY +#define LWS_O_WRONLY O_WRONLY +#define LWS_O_CREAT O_CREAT +#define LWS_O_TRUNC O_TRUNC + +#if !defined(LWS_WITH_ESP8266) && !defined(OPTEE_TA) && !defined(LWS_WITH_ESP32) +#include <poll.h> +#include <netdb.h> +#define LWS_INVALID_FILE -1 +#else +#define getdtablesize() (30) +#if defined(LWS_WITH_ESP32) +#define LWS_INVALID_FILE NULL +#else +#define LWS_INVALID_FILE NULL +#endif +#endif + +#if defined(__GNUC__) + +/* warn_unused_result attribute only supported by GCC 3.4 or later */ +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define LWS_WARN_UNUSED_RESULT +#endif + +#define LWS_VISIBLE __attribute__((visibility("default"))) +#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) +#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1))) +#else +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#define LWS_FORMAT(string_index) +#endif + +#if defined(__ANDROID__) +#include <unistd.h> +#define getdtablesize() sysconf(_SC_OPEN_MAX) +#endif + +#endif + +#ifdef LWS_WITH_LIBEV +#include <ev.h> +#endif /* LWS_WITH_LIBEV */ +#ifdef LWS_WITH_LIBUV +#include <uv.h> +#ifdef LWS_HAVE_UV_VERSION_H +#include <uv-version.h> +#endif +#endif /* LWS_WITH_LIBUV */ +#ifdef LWS_WITH_LIBEVENT +#include <event2/event.h> +#endif /* LWS_WITH_LIBEVENT */ + +#ifndef LWS_EXTERN +#define LWS_EXTERN extern +#endif + +#ifdef _WIN32 +#define random rand +#else +#if !defined(OPTEE_TA) +#include <sys/time.h> +#include <unistd.h> +#endif +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include <cyassl/openssl/ssl.h> +#include <cyassl/error-ssl.h> +#else +#include <wolfssl/openssl/ssl.h> +#include <wolfssl/error-ssl.h> +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_WITH_MBEDTLS) +#if defined(LWS_WITH_ESP32) +/* this filepath is passed to us but without quotes or <> */ +#undef MBEDTLS_CONFIG_FILE +#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h> +#endif +#include <mbedtls/ssl.h> +#endif +#include <openssl/ssl.h> +#if !defined(LWS_WITH_MBEDTLS) +#include <openssl/err.h> +#endif +#endif /* not USE_WOLFSSL */ +#endif + + +#define CONTEXT_PORT_NO_LISTEN -1 +#define CONTEXT_PORT_NO_LISTEN_SERVER -2 + +/** \defgroup log Logging + * + * ##Logging + * + * Lws provides flexible and filterable logging facilities, which can be + * used inside lws and in user code. + * + * Log categories may be individually filtered bitwise, and directed to built-in + * sinks for syslog-compatible logging, or a user-defined function. + */ +///@{ + +enum lws_log_levels { + LLL_ERR = 1 << 0, + LLL_WARN = 1 << 1, + LLL_NOTICE = 1 << 2, + LLL_INFO = 1 << 3, + LLL_DEBUG = 1 << 4, + LLL_PARSER = 1 << 5, + LLL_HEADER = 1 << 6, + LLL_EXT = 1 << 7, + LLL_CLIENT = 1 << 8, + LLL_LATENCY = 1 << 9, + LLL_USER = 1 << 10, + + LLL_COUNT = 11 /* set to count of valid flags */ +}; + +LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2); +LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl); +/** + * lwsl_timestamp: generate logging timestamp string + * + * \param level: logging level + * \param p: char * buffer to take timestamp + * \param len: length of p + * + * returns length written in p + */ +LWS_VISIBLE LWS_EXTERN int +lwsl_timestamp(int level, char *p, int len); + +/* these guys are unconditionally included */ + +#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) +#define lwsl_user(...) _lws_log(LLL_USER, __VA_ARGS__) + +#if !defined(LWS_WITH_NO_LOGS) +/* notice and warn are usually included by being compiled in */ +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +/* + * weaker logging can be deselected by telling CMake to build in RELEASE mode + * that gets rid of the overhead of checking while keeping _warn and _err + * active + */ + +#if defined(LWS_WITH_ESP8266) +#undef _DEBUG +#endif + +#ifdef _DEBUG +#if defined(LWS_WITH_NO_LOGS) +/* notice, warn and log are always compiled in */ +#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) +#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) +#endif +#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__) +#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__) +#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__) +#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__) +#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) +#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) +#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) + +#else /* no debug */ +#if defined(LWS_WITH_NO_LOGS) +#define lwsl_warn(...) do {} while(0) +#define lwsl_notice(...) do {} while(0) +#endif +#define lwsl_info(...) do {} while(0) +#define lwsl_debug(...) do {} while(0) +#define lwsl_parser(...) do {} while(0) +#define lwsl_header(...) do {} while(0) +#define lwsl_ext(...) do {} while(0) +#define lwsl_client(...) do {} while(0) +#define lwsl_latency(...) do {} while(0) + +#endif + +/** + * lwsl_hexdump() - helper to hexdump a buffer + * + * \param level: one of LLL_ constants + * \param buf: buffer start to dump + * \param len: length of buffer to dump + * + * If \p level is visible, does a nice hexdump -C style dump of \p buf for + * \p len bytes. This can be extremely convenient while debugging. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_hexdump_level(int level, const void *vbuf, size_t len); + +/** + * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only) + * + * \param buf: buffer start to dump + * \param len: length of buffer to dump + * + * Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability. + * It's better to use lwsl_hexdump_level(level, ... directly so you can control + * the visibility. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_hexdump(const void *buf, size_t len); + +/** + * lws_is_be() - returns nonzero if the platform is Big Endian + */ +static LWS_INLINE int lws_is_be(void) { + const int probe = ~0xff; + + return *(const char *)&probe; +} + +/** + * lws_set_log_level() - Set the logging bitfield + * \param level: OR together the LLL_ debug contexts you want output from + * \param log_emit_function: NULL to leave it as it is, or a user-supplied + * function to perform log string emission instead of + * the default stderr one. + * + * log level defaults to "err", "warn" and "notice" contexts enabled and + * emission on stderr. If stderr is a tty (according to isatty()) then + * the output is coloured according to the log level using ANSI escapes. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_log_level(int level, + void (*log_emit_function)(int level, const char *line)); + +/** + * lwsl_emit_syslog() - helper log emit function writes to system log + * + * \param level: one of LLL_ log level indexes + * \param line: log string + * + * You use this by passing the function pointer to lws_set_log_level(), to set + * it as the log emit function, it is not called directly. + */ +LWS_VISIBLE LWS_EXTERN void +lwsl_emit_syslog(int level, const char *line); + +/** + * lwsl_visible() - returns true if the log level should be printed + * + * \param level: one of LLL_ log level indexes + * + * This is useful if you have to do work to generate the log content, you + * can skip the work if the log level used to print it is not actually + * enabled at runtime. + */ +LWS_VISIBLE LWS_EXTERN int +lwsl_visible(int level); + +///@} + + +#include <stddef.h> + +#ifndef lws_container_of +#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) +#endif + + +struct lws; +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +/* api change list for user code to test against */ + +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG + +/* the struct lws_protocols has the id field present */ +#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD + +/* you can call lws_get_peer_write_allowance */ +#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE + +/* extra parameter introduced in 917f43ab821 */ +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN + +/* File operations stuff exists */ +#define LWS_FEATURE_FOPS + + +#if defined(_WIN32) +typedef SOCKET lws_sockfd_type; +typedef HANDLE lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct lws_pollfd { + lws_sockfd_type fd; /**< file descriptor */ + SHORT events; /**< which events to respond to */ + SHORT revents; /**< which events happened */ +}; +#define LWS_POLLHUP (FD_CLOSE) +#define LWS_POLLIN (FD_READ | FD_ACCEPT) +#define LWS_POLLOUT (FD_WRITE) +#else + + +#if defined(LWS_WITH_ESP8266) + +#include <user_interface.h> +#include <espconn.h> + +typedef struct espconn * lws_sockfd_type; +typedef void * lws_filefd_type; +#define lws_sockfd_valid(sfd) (!!sfd) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +struct lws_vhost; + +lws_sockfd_type esp8266_create_tcp_listen_socket(struct lws_vhost *vh); +void esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi); + +#include <os_type.h> +#include <osapi.h> +#include "ets_sys.h" + +int ets_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); +#define snprintf ets_snprintf + +typedef os_timer_t uv_timer_t; +typedef void uv_cb_t(uv_timer_t *); + +void os_timer_disarm(void *); +void os_timer_setfn(os_timer_t *, os_timer_func_t *, void *); + +void ets_timer_arm_new(os_timer_t *, int, int, int); + +//void os_timer_arm(os_timer_t *, int, int); + +#define UV_VERSION_MAJOR 1 + +#define lws_uv_getloop(a, b) (NULL) + +static inline void uv_timer_init(void *l, uv_timer_t *t) +{ + (void)l; + memset(t, 0, sizeof(*t)); + os_timer_disarm(t); +} + +static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +{ + os_timer_setfn(t, (os_timer_func_t *)cb, t); + /* ms, repeat */ + os_timer_arm(t, first, !!rep); +} + +static inline void uv_timer_stop(uv_timer_t *t) +{ + os_timer_disarm(t); +} + +#else +#if defined(LWS_WITH_ESP32) + +typedef int lws_sockfd_type; +typedef int lws_filefd_type; +#define lws_sockfd_valid(sfd) (sfd >= 0) +struct pollfd { + lws_sockfd_type fd; /**< fd related to */ + short events; /**< which POLL... events to respond to */ + short revents; /**< which POLL... events occurred */ +}; +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +#include <freertos/FreeRTOS.h> +#include <freertos/event_groups.h> +#include <string.h> +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_event_loop.h" +#include "nvs.h" +#include "driver/gpio.h" +#include "esp_spi_flash.h" +#include "freertos/timers.h" + +#if !defined(CONFIG_FREERTOS_HZ) +#define CONFIG_FREERTOS_HZ 100 +#endif + +typedef TimerHandle_t uv_timer_t; +typedef void uv_cb_t(uv_timer_t *); +typedef void * uv_handle_t; + +struct timer_mapping { + uv_cb_t *cb; + uv_timer_t *t; +}; + +#define UV_VERSION_MAJOR 1 + +#define lws_uv_getloop(a, b) (NULL) + +static inline void uv_timer_init(void *l, uv_timer_t *t) +{ + (void)l; + *t = NULL; +} + +extern void esp32_uvtimer_cb(TimerHandle_t t); + +static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +{ + struct timer_mapping *tm = (struct timer_mapping *)malloc(sizeof(*tm)); + + if (!tm) + return; + + tm->t = t; + tm->cb = cb; + + *t = xTimerCreate("x", pdMS_TO_TICKS(first), !!rep, tm, + (TimerCallbackFunction_t)esp32_uvtimer_cb); + xTimerStart(*t, 0); +} + +static inline void uv_timer_stop(uv_timer_t *t) +{ + xTimerStop(*t, 0); +} + +static inline void uv_close(uv_handle_t *h, void *v) +{ + free(pvTimerGetTimerID((uv_timer_t)h)); + xTimerDelete(*(uv_timer_t *)h, 0); +} + +/* ESP32 helper declarations */ + +#include <mdns.h> +#include <esp_partition.h> + +#define LWS_PLUGIN_STATIC +#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc +#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe +#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b +#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac + + +/* user code provides these */ + +extern void +lws_esp32_identify_physical_device(void); + +/* lws-plat-esp32 provides these */ + +typedef void (*lws_cb_scan_done)(uint16_t count, wifi_ap_record_t *recs, void *arg); + +enum genled_state { + LWSESP32_GENLED__INIT, + LWSESP32_GENLED__LOST_NETWORK, + LWSESP32_GENLED__NO_NETWORK, + LWSESP32_GENLED__CONN_AP, + LWSESP32_GENLED__GOT_IP, + LWSESP32_GENLED__OK, +}; + +struct lws_group_member { + struct lws_group_member *next; + uint64_t last_seen; + char model[16]; + char role[16]; + char host[32]; + char mac[20]; + int width, height; + struct ip4_addr addr; + struct ip6_addr addrv6; + uint8_t flags; +}; + +#define LWS_SYSTEM_GROUP_MEMBER_ADD 1 +#define LWS_SYSTEM_GROUP_MEMBER_CHANGE 2 +#define LWS_SYSTEM_GROUP_MEMBER_REMOVE 3 + +#define LWS_GROUP_FLAG_SELF 1 + +struct lws_esp32 { + char sta_ip[16]; + char sta_mask[16]; + char sta_gw[16]; + char serial[16]; + char opts[16]; + char model[16]; + char group[16]; + char role[16]; + char ssid[4][16]; + char password[4][32]; + char active_ssid[32]; + char access_pw[16]; + char hostname[32]; + char mac[20]; + mdns_server_t *mdns; + char region; + char inet; + char conn_ap; + + enum genled_state genled; + uint64_t genled_t; + + lws_cb_scan_done scan_consumer; + void *scan_consumer_arg; + struct lws_group_member *first; + int extant_group_members; +}; + +struct lws_esp32_image { + uint32_t romfs; + uint32_t romfs_len; + uint32_t json; + uint32_t json_len; +}; + +extern struct lws_esp32 lws_esp32; +struct lws_vhost; + +extern esp_err_t +lws_esp32_event_passthru(void *ctx, system_event_t *event); +extern void +lws_esp32_wlan_config(void); +extern void +lws_esp32_wlan_start_ap(void); +extern void +lws_esp32_wlan_start_station(void); +struct lws_context_creation_info; +extern void +lws_esp32_set_creation_defaults(struct lws_context_creation_info *info); +extern struct lws_context * +lws_esp32_init(struct lws_context_creation_info *, struct lws_vhost **pvh); +extern int +lws_esp32_wlan_nvs_get(int retry); +extern esp_err_t +lws_nvs_set_str(nvs_handle handle, const char* key, const char* value); +extern void +lws_esp32_restart_guided(uint32_t type); +extern const esp_partition_t * +lws_esp_ota_get_boot_partition(void); +extern int +lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, char *json, int json_len); +extern int +lws_esp32_leds_network_indication(void); + +extern uint32_t lws_esp32_get_reboot_type(void); +extern uint16_t lws_esp32_sine_interp(int n); + +/* required in external code by esp32 plat (may just return if no leds) */ +extern void lws_esp32_leds_timer_cb(TimerHandle_t th); +#else +typedef int lws_sockfd_type; +typedef int lws_filefd_type; +#define lws_sockfd_valid(sfd) (sfd >= 0) +#endif +#endif + +#define lws_pollfd pollfd +#define LWS_POLLHUP (POLLHUP|POLLERR) +#define LWS_POLLIN (POLLIN) +#define LWS_POLLOUT (POLLOUT) +#endif + + +#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__) +/* ... */ +#define ssize_t SSIZE_T +#endif + +#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) +#include <sys/types.h> +#include <sys/stat.h> +#endif + +#if defined(LWS_HAVE_STDINT_H) +#include <stdint.h> +#else +#if defined(WIN32) || defined(_WIN32) +/* !!! >:-[ */ +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; +#else +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +#endif +#endif + +typedef unsigned long long lws_filepos_t; +typedef long long lws_fileofs_t; +typedef uint32_t lws_fop_flags_t; + +/** struct lws_pollargs - argument structure for all external poll related calls + * passed in via 'in' */ +struct lws_pollargs { + lws_sockfd_type fd; /**< applicable socket descriptor */ + int events; /**< the new event mask */ + int prev_events; /**< the previous event mask */ +}; + +struct lws_tokens; +struct lws_token_limits; + +/*! \defgroup wsclose Websocket Close + * + * ##Websocket close frame control + * + * When we close a ws connection, we can send a reason code and a short + * UTF-8 description back with the close packet. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_close_status - RFC6455 close status codes */ +enum lws_close_status { + LWS_CLOSE_STATUS_NOSTATUS = 0, + LWS_CLOSE_STATUS_NORMAL = 1000, + /**< 1000 indicates a normal closure, meaning that the purpose for + which the connection was established has been fulfilled. */ + LWS_CLOSE_STATUS_GOINGAWAY = 1001, + /**< 1001 indicates that an endpoint is "going away", such as a server + going down or a browser having navigated away from a page. */ + LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002, + /**< 1002 indicates that an endpoint is terminating the connection due + to a protocol error. */ + LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, + /**< 1003 indicates that an endpoint is terminating the connection + because it has received a type of data it cannot accept (e.g., an + endpoint that understands only text data MAY send this if it + receives a binary message). */ + LWS_CLOSE_STATUS_RESERVED = 1004, + /**< Reserved. The specific meaning might be defined in the future. */ + LWS_CLOSE_STATUS_NO_STATUS = 1005, + /**< 1005 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that no status + code was actually present. */ + LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, + /**< 1006 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed abnormally, e.g., without sending or + receiving a Close control frame. */ + LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007, + /**< 1007 indicates that an endpoint is terminating the connection + because it has received data within a message that was not + consistent with the type of the message (e.g., non-UTF-8 [RFC3629] + data within a text message). */ + LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008, + /**< 1008 indicates that an endpoint is terminating the connection + because it has received a message that violates its policy. This + is a generic status code that can be returned when there is no + other more suitable status code (e.g., 1003 or 1009) or if there + is a need to hide specific details about the policy. */ + LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, + /**< 1009 indicates that an endpoint is terminating the connection + because it has received a message that is too big for it to + process. */ + LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, + /**< 1010 indicates that an endpoint (client) is terminating the + connection because it has expected the server to negotiate one or + more extension, but the server didn't return them in the response + message of the WebSocket handshake. The list of extensions that + are needed SHOULD appear in the /reason/ part of the Close frame. + Note that this status code is not used by the server, because it + can fail the WebSocket handshake instead */ + LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, + /**< 1011 indicates that a server is terminating the connection because + it encountered an unexpected condition that prevented it from + fulfilling the request. */ + LWS_CLOSE_STATUS_TLS_FAILURE = 1015, + /**< 1015 is a reserved value and MUST NOT be set as a status code in a + Close control frame by an endpoint. It is designated for use in + applications expecting a status code to indicate that the + connection was closed due to a failure to perform a TLS handshake + (e.g., the server certificate can't be verified). */ + + /****** add new things just above ---^ ******/ + + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999, +}; + +/** + * lws_close_reason - Set reason and aux data to send with Close packet + * If you are going to return nonzero from the callback + * requesting the connection to close, you can optionally + * call this to set the reason the peer will be told if + * possible. + * + * \param wsi: The websocket connection to set the close reason on + * \param status: A valid close status from websocket standard + * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data + * \param len: Length of data in \param buf to send + */ +LWS_VISIBLE LWS_EXTERN void +lws_close_reason(struct lws *wsi, enum lws_close_status status, + unsigned char *buf, size_t len); + +///@} + +struct lws; +struct lws_context; +/* needed even with extensions disabled for create context */ +struct lws_extension; + +/*! \defgroup lwsmeta lws-meta + * + * ##lws-meta protocol + * + * The protocol wraps other muxed connections inside one tcp connection. + * + * Commands are assigned from 0x41 up (so they are valid unicode) + */ +///@{ + +enum lws_meta_commands { + LWS_META_CMD_OPEN_SUBCHANNEL = 'A', + /**< Client requests to open new subchannel + */ + LWS_META_CMD_OPEN_RESULT, + /**< Result of client request to open new subchannel */ + LWS_META_CMD_CLOSE_NOTIFY, + /**< Notification of subchannel closure */ + LWS_META_CMD_CLOSE_RQ, + /**< client requests to close a subchannel */ + LWS_META_CMD_WRITE, + /**< connection writes something to specific channel index */ + + /****** add new things just above ---^ ******/ +}; + +/* channel numbers are transported offset by 0x20 so they are valid unicode */ + +#define LWS_META_TRANSPORT_OFFSET 0x20 + +///@} + +/*! \defgroup usercb User Callback + * + * ##User protocol callback + * + * The protocol callback is the primary way lws interacts with + * user code. For one of a list of a few dozen reasons the callback gets + * called at some event to be handled. + * + * All of the events can be ignored, returning 0 is taken as "OK" and returning + * nonzero in most cases indicates that the connection should be closed. + */ +///@{ + +struct lws_ssl_info { + int where; + int ret; +}; + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +/** enum lws_callback_reasons - reason you're getting a protocol callback */ +enum lws_callback_reasons { + LWS_CALLBACK_ESTABLISHED = 0, + /**< (VH) after the server completes a handshake with an incoming + * client. If you built the library with ssl support, in is a + * pointer to the ssl struct associated with the connection or NULL.*/ + LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1, + /**< the request client connection has been unable to complete a + * handshake with the remote server. If in is non-NULL, you can + * find an error string of length len where it points to + * + * Diagnostic strings that may be returned include + * + * "getaddrinfo (ipv6) failed" + * "unknown address family" + * "getaddrinfo (ipv4) failed" + * "set socket opts failed" + * "insert wsi failed" + * "lws_ssl_client_connect1 failed" + * "lws_ssl_client_connect2 failed" + * "Peer hung up" + * "read failed" + * "HS: URI missing" + * "HS: Redirect code but no Location" + * "HS: URI did not parse" + * "HS: Redirect failed" + * "HS: Server did not return 200" + * "HS: OOM" + * "HS: disallowed by client filter" + * "HS: disallowed at ESTABLISHED" + * "HS: ACCEPT missing" + * "HS: ws upgrade response not 101" + * "HS: UPGRADE missing" + * "HS: Upgrade to something other than websocket" + * "HS: CONNECTION missing" + * "HS: UPGRADE malformed" + * "HS: PROTOCOL malformed" + * "HS: Cannot match protocol" + * "HS: EXT: list too big" + * "HS: EXT: failed setting defaults" + * "HS: EXT: failed parsing defaults" + * "HS: EXT: failed parsing options" + * "HS: EXT: Rejects server options" + * "HS: EXT: unknown ext" + * "HS: Accept hash wrong" + * "HS: Rejected by filter cb" + * "HS: OOM" + * "HS: SO_SNDBUF failed" + * "HS: Rejected at CLIENT_ESTABLISHED" + */ + LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2, + /**< this is the last chance for the client user code to examine the + * http headers and decide to reject the connection. If the + * content in the headers is interesting to the + * client (url, etc) it needs to copy it out at + * this point since it will be destroyed before + * the CLIENT_ESTABLISHED call */ + LWS_CALLBACK_CLIENT_ESTABLISHED = 3, + /**< after your client connection completed + * a handshake with the remote server */ + LWS_CALLBACK_CLOSED = 4, + /**< when the websocket session ends */ + LWS_CALLBACK_CLOSED_HTTP = 5, + /**< when a HTTP (non-websocket) session ends */ + LWS_CALLBACK_RECEIVE = 6, + /**< data has appeared for this server endpoint from a + * remote client, it can be found at *in and is + * len bytes long */ + LWS_CALLBACK_RECEIVE_PONG = 7, + /**< servers receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_RECEIVE = 8, + /**< data has appeared from the server for the client connection, it + * can be found at *in and is len bytes long */ + LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9, + /**< clients receive PONG packets with this callback reason */ + LWS_CALLBACK_CLIENT_WRITEABLE = 10, + /**< If you call lws_callback_on_writable() on a connection, you will + * get one of these callbacks coming when the connection socket + * is able to accept another write packet without blocking. + * If it already was able to take another packet without blocking, + * you'll get this callback at the next call to the service loop + * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE + * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */ + LWS_CALLBACK_SERVER_WRITEABLE = 11, + /**< See LWS_CALLBACK_CLIENT_WRITEABLE */ + LWS_CALLBACK_HTTP = 12, + /**< an http request has come from a client that is not + * asking to upgrade the connection to a websocket + * one. This is a chance to serve http content, + * for example, to send a script to the client + * which will then open the websockets connection. + * in points to the URI path requested and + * lws_serve_http_file() makes it very + * simple to send back a file to the client. + * Normally after sending the file you are done + * with the http connection, since the rest of the + * activity will come by websockets from the script + * that was delivered by http, so you will want to + * return 1; to close and free up the connection. */ + LWS_CALLBACK_HTTP_BODY = 13, + /**< the next len bytes data from the http + * request body HTTP connection is now available in in. */ + LWS_CALLBACK_HTTP_BODY_COMPLETION = 14, + /**< the expected amount of http request body has been delivered */ + LWS_CALLBACK_HTTP_FILE_COMPLETION = 15, + /**< a file requested to be sent down http link has completed. */ + LWS_CALLBACK_HTTP_WRITEABLE = 16, + /**< you can write more down the http protocol link now. */ + LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17, + /**< called when a client connects to + * the server at network level; the connection is accepted but then + * passed to this callback to decide whether to hang up immediately + * or not, based on the client IP. in contains the connection + * socket's descriptor. Since the client connection information is + * not available yet, wsi still pointing to the main server socket. + * Return non-zero to terminate the connection before sending or + * receiving anything. Because this happens immediately after the + * network connection from the client, there's no websocket protocol + * selected yet so this callback is issued only to protocol 0. */ + LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18, + /**< called when the request has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the URI, eg, "/" + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the http + * connection to proceed or to kill the connection. */ + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19, + /**< A new client just had + * been connected, accepted, and instantiated into the pool. This + * callback allows setting any relevant property to it. Because this + * happens immediately after the instantiation of a new client, + * there's no websocket protocol selected yet so this callback is + * issued only to protocol 0. Only wsi is defined, pointing to the + * new client, and the return value is ignored. */ + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20, + /**< called when the handshake has + * been received and parsed from the client, but the response is + * not sent yet. Return non-zero to disallow the connection. + * user is a pointer to the connection user space allocation, + * in is the requested protocol name + * In your handler you can use the public APIs + * lws_hdr_total_length() / lws_hdr_copy() to access all of the + * headers using the header enums lws_token_indexes from + * libwebsockets.h to check for and read the supported header + * presence and content before deciding to allow the handshake + * to proceed or to kill the connection. */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to perform extra SSL_CTX_load_verify_locations() or similar + * calls to direct OpenSSL where to find certificates the client + * can use to confirm the remote server identity. user is the + * OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, + /**< if configured for + * including OpenSSL support, this callback allows your user code + * to load extra certifcates into the server which allow it to + * verify the validity of certificates returned by clients. user + * is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, + /**< if the libwebsockets vhost was created with the option + * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this + * callback is generated during OpenSSL verification of the cert + * sent from the client. It is sent to protocol[0] callback as + * no protocol has been negotiated on the connection yet. + * Notice that the libwebsockets context and wsi are both NULL + * during this callback. See + * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html + * to understand more detail about the OpenSSL callback that + * generates this libwebsockets callback and the meanings of the + * arguments passed. In this callback, user is the x509_ctx, + * in is the ssl pointer and len is preverify_ok + * Notice that this callback maintains libwebsocket return + * conventions, return 0 to mean the cert is OK or 1 to fail it. + * This also means that if you don't handle this callback then + * the default callback action of returning 0 allows the client + * certificates. */ + LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, + /**< this callback happens + * when a client handshake is being compiled. user is NULL, + * in is a char **, it's pointing to a char * which holds the + * next location in the header buffer where you can add + * headers, and len is the remaining space in the header buffer, + * which is typically some hundreds of bytes. So, to add a canned + * cookie, your handler code might look similar to: + * + * char **p = (char **)in; + * + * if (len < 100) + * return 1; + * + * *p += sprintf(*p, "Cookie: a=b\x0d\x0a"); + * + * return 0; + * + * Notice if you add anything, you just have to take care about + * the CRLF on the line you added. Obviously this callback is + * optional, if you don't handle it everything is fine. + * + * Notice the callback is coming to protocols[0] all the time, + * because there is no specific protocol negotiated yet. */ + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25, + /**< When the server handshake code + * sees that it does support a requested extension, before + * accepting the extension by additing to the list sent back to + * the client it gives this callback just to check that it's okay + * to use that extension. It calls back to the requested protocol + * and with in being the extension name, len is 0 and user is + * valid. Note though at this time the ESTABLISHED callback hasn't + * happened yet so if you initialize user content there, user + * content during this callback might not be useful for anything. */ + LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26, + /**< When a client + * connection is being prepared to start a handshake to a server, + * each supported extension is checked with protocols[0] callback + * with this reason, giving the user code a chance to suppress the + * claim to support that extension by returning non-zero. If + * unhandled, by default 0 will be returned and the extension + * support included in the header to the server. Notice this + * callback comes to protocols[0]. */ + LWS_CALLBACK_PROTOCOL_INIT = 27, + /**< One-time call per protocol, per-vhost using it, so it can + * do initial setup / allocations etc */ + LWS_CALLBACK_PROTOCOL_DESTROY = 28, + /**< One-time call per protocol, per-vhost using it, indicating + * this protocol won't get used at all after this callback, the + * vhost is getting destroyed. Take the opportunity to + * deallocate everything that was allocated by the protocol. */ + LWS_CALLBACK_WSI_CREATE = 29, + /**< outermost (earliest) wsi create notification to protocols[0] */ + LWS_CALLBACK_WSI_DESTROY = 30, + /**< outermost (latest) wsi destroy notification to protocols[0] */ + LWS_CALLBACK_GET_THREAD_ID = 31, + /**< lws can accept callback when writable requests from other + * threads, if you implement this callback and return an opaque + * current thread ID integer. */ + + /* external poll() management support */ + LWS_CALLBACK_ADD_POLL_FD = 32, + /**< lws normally deals with its poll() or other event loop + * internally, but in the case you are integrating with another + * server you will need to have lws sockets share a + * polling array with the other server. This and the other + * POLL_FD related callbacks let you put your specialized + * poll array interface code in the callback for protocol 0, the + * first protocol you support, usually the HTTP protocol in the + * serving case. + * This callback happens when a socket needs to be + * added to the polling loop: in points to a struct + * lws_pollargs; the fd member of the struct is the file + * descriptor, and events contains the active events + * + * If you are using the internal lws polling / event loop + * you can just ignore these callbacks. */ + LWS_CALLBACK_DEL_POLL_FD = 33, + /**< This callback happens when a socket descriptor + * needs to be removed from an external polling array. in is + * again the struct lws_pollargs containing the fd member + * to be removed. If you are using the internal polling + * loop, you can just ignore it. */ + LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34, + /**< This callback happens when lws wants to modify the events for + * a connection. + * in is the struct lws_pollargs with the fd to change. + * The new event mask is in events member and the old mask is in + * the prev_events member. + * If you are using the internal polling loop, you can just ignore + * it. */ + LWS_CALLBACK_LOCK_POLL = 35, + /**< These allow the external poll changes driven + * by lws to participate in an external thread locking + * scheme around the changes, so the whole thing is threadsafe. + * These are called around three activities in the library, + * - inserting a new wsi in the wsi / fd table (len=1) + * - deleting a wsi from the wsi / fd table (len=1) + * - changing a wsi's POLLIN/OUT state (len=0) + * Locking and unlocking external synchronization objects when + * len == 1 allows external threads to be synchronized against + * wsi lifecycle changes if it acquires the same lock for the + * duration of wsi dereference from the other thread context. */ + LWS_CALLBACK_UNLOCK_POLL = 36, + /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */ + + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37, + /**< if configured for including OpenSSL support but no private key + * file has been specified (ssl_private_key_filepath is NULL), this is + * called to allow the user to set the private key directly via + * libopenssl and perform further operations if required; this might be + * useful in situations where the private key is not directly accessible + * by the OS, for example if it is stored on a smartcard. + * user is the server's OpenSSL SSL_CTX* */ + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38, + /**< The peer has sent an unsolicited Close WS packet. in and + * len are the optional close code (first 2 bytes, network + * order) and the optional additional information which is not + * defined in the standard, and may be a string or non-human- readable data. + * If you return 0 lws will echo the close and then close the + * connection. If you return nonzero lws will just close the + * connection. */ + + LWS_CALLBACK_WS_EXT_DEFAULTS = 39, + /**< Gives client connections an opportunity to adjust negotiated + * extension defaults. `user` is the extension name that was + * negotiated (eg, "permessage-deflate"). `in` points to a + * buffer and `len` is the buffer size. The user callback can + * set the buffer to a string describing options the extension + * should parse. Or just ignore for defaults. */ + + LWS_CALLBACK_CGI = 40, + /**< CGI: CGI IO events on stdin / out / err are sent here on + * protocols[0]. The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. */ + LWS_CALLBACK_CGI_TERMINATED = 41, + /**< CGI: The related CGI process ended, this is called before + * the wsi is closed. Used to, eg, terminate chunking. + * The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. The child PID that terminated is in len. */ + LWS_CALLBACK_CGI_STDIN_DATA = 42, + /**< CGI: Data is, to be sent to the CGI process stdin, eg from + * a POST body. The provided `lws_callback_http_dummy()` + * handles this and the callback should be directed there if + * you use CGI. */ + LWS_CALLBACK_CGI_STDIN_COMPLETED = 43, + /**< CGI: no more stdin is coming. The provided + * `lws_callback_http_dummy()` handles this and the callback + * should be directed there if you use CGI. */ + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, + /**< The HTTP client connection has succeeded, and is now + * connected to the server */ + LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, + /**< The HTTP client connection is closing */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, + /**< This simply indicates data was received on the HTTP client + * connection. It does NOT drain or provide the data. + * This exists to neatly allow a proxying type situation, + * where this incoming data will go out on another connection. + * If the outgoing connection stalls, we should stall processing + * the incoming data. So a handler for this in that case should + * simply set a flag to indicate there is incoming data ready + * and ask for a writeable callback on the outgoing connection. + * In the writable callback he can check the flag and then get + * and drain the waiting incoming data using lws_http_client_read(). + * This will use callbacks to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ + * to get and drain the incoming data, where it should be sent + * back out on the outgoing connection. */ + LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, + /**< The client transaction completed... at the moment this + * is the same as closing since transaction pipelining on + * client side is not yet supported. */ + LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48, + /**< This is generated by lws_http_client_read() used to drain + * incoming data. In the case the incoming data was chunked, + * it will be split into multiple smaller callbacks for each + * chunk block, removing the chunk headers. If not chunked, + * it will appear all in one callback. */ + LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49, + /**< By default, all HTTP handling is done in protocols[0]. + * However you can bind different protocols (by name) to + * different parts of the URL space using callback mounts. This + * callback occurs in the new protocol when a wsi is bound + * to that protocol. Any protocol allocation related to the + * http transaction processing should be created then. + * These specific callbacks are necessary because with HTTP/1.1, + * a single connection may perform at series of different + * transactions at different URLs, thus the lifetime of the + * protocol bind is just for one transaction, not connection. */ + LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50, + /**< This is called when a transaction is unbound from a protocol. + * It indicates the connection completed its transaction and may + * do something different now. Any protocol allocation related + * to the http transaction processing should be destroyed. */ + LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51, + /**< This gives the user code a chance to forbid an http access. + * `in` points to a `struct lws_process_html_args`, which + * describes the URL, and a bit mask describing the type of + * authentication required. If the callback returns nonzero, + * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */ + LWS_CALLBACK_PROCESS_HTML = 52, + /**< This gives your user code a chance to mangle outgoing + * HTML. `in` points to a `struct lws_process_html_args` + * which describes the buffer containing outgoing HTML. + * The buffer may grow up to `.max_len` (currently +128 + * bytes per buffer). + * */ + LWS_CALLBACK_ADD_HEADERS = 53, + /**< This gives your user code a chance to add headers to a + * transaction bound to your protocol. `in` points to a + * `struct lws_process_html_args` describing a buffer and length + * you can add headers into using the normal lws apis. + * + * Only `args->p` and `args->len` are valid, and `args->p` should + * be moved on by the amount of bytes written, if any. Eg + * + * case LWS_CALLBACK_ADD_HEADERS: + * + * struct lws_process_html_args *args = + * (struct lws_process_html_args *)in; + * + * if (lws_add_http_header_by_name(wsi, + * (unsigned char *)"set-cookie:", + * (unsigned char *)cookie, cookie_len, + * (unsigned char **)&args->p, + * (unsigned char *)args->p + args->max_len)) + * return 1; + * + * break; + */ + LWS_CALLBACK_SESSION_INFO = 54, + /**< This is only generated by user code using generic sessions. + * It's used to get a `struct lws_session_info` filled in by + * generic sessions with information about the logged-in user. + * See the messageboard sample for an example of how to use. */ + + LWS_CALLBACK_GS_EVENT = 55, + /**< Indicates an event happened to the Generic Sessions session. + * `in` contains a `struct lws_gs_event_args` describing the event. */ + LWS_CALLBACK_HTTP_PMO = 56, + /**< per-mount options for this connection, called before + * the normal LWS_CALLBACK_HTTP when the mount has per-mount + * options. + */ + LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57, + /**< when doing an HTTP type client connection, you can call + * lws_client_http_body_pending(wsi, 1) from + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks + * sending the HTTP headers. + * + * From this callback, when you have sent everything, you should let + * lws know by calling lws_client_http_body_pending(wsi, 0) + */ + LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58, + /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION + * this callback is called during OpenSSL verification of the cert + * sent from the server to the client. It is sent to protocol[0] + * callback as no protocol has been negotiated on the connection yet. + * Notice that the wsi is set because lws_client_connect_via_info was + * successful. + * + * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html + * to understand more detail about the OpenSSL callback that + * generates this libwebsockets callback and the meanings of the + * arguments passed. In this callback, user is the x509_ctx, + * in is the ssl pointer and len is preverify_ok. + * + * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be + * overruled and cert shall be accepted as ok, + * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be + * called and return value must be 0 to mean the cert is OK; + * returning 1 will fail the cert in any case. + * + * This also means that if you don't handle this callback then + * the default callback action of returning 0 will not accept the + * certificate in case of a validation error decided by the SSL lib. + * + * This is expected and secure behaviour when validating certificates. + * + * Note: LCCSCF_ALLOW_SELFSIGNED and + * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this + * callback being implemented. + */ + LWS_CALLBACK_RAW_RX = 59, + /**< RAW mode connection RX */ + LWS_CALLBACK_RAW_CLOSE = 60, + /**< RAW mode connection is closing */ + LWS_CALLBACK_RAW_WRITEABLE = 61, + /**< RAW mode connection may be written */ + LWS_CALLBACK_RAW_ADOPT = 62, + /**< RAW mode connection was adopted (equivalent to 'wsi created') */ + LWS_CALLBACK_RAW_ADOPT_FILE = 63, + /**< RAW mode file was adopted (equivalent to 'wsi created') */ + LWS_CALLBACK_RAW_RX_FILE = 64, + /**< RAW mode file has something to read */ + LWS_CALLBACK_RAW_WRITEABLE_FILE = 65, + /**< RAW mode file is writeable */ + LWS_CALLBACK_RAW_CLOSE_FILE = 66, + /**< RAW mode wsi that adopted a file is closing */ + LWS_CALLBACK_SSL_INFO = 67, + /**< SSL connections only. An event you registered an + * interest in at the vhost has occurred on a connection + * using the vhost. in is a pointer to a + * struct lws_ssl_info containing information about the + * event*/ + LWS_CALLBACK_CHILD_WRITE_VIA_PARENT = 68, + /**< Child has been marked with parent_carries_io attribute, so + * lws_write directs the to this callback at the parent, + * in is a struct lws_write_passthru containing the args + * the lws_write() was called with. + */ + LWS_CALLBACK_CHILD_CLOSING = 69, + /**< Sent to parent to notify them a child is closing / being + * destroyed. in is the child wsi. + */ + LWS_CALLBACK_CGI_PROCESS_ATTACH = 70, + /**< CGI: Sent when the CGI process is spawned for the wsi. The + * len parameter is the PID of the child process */ + + /****** add new things just above ---^ ******/ + + LWS_CALLBACK_USER = 1000, + /**< user code can use any including above without fear of clashes */ +}; + + + +/** + * typedef lws_callback_function() - User server actions + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * This callback is the way the user controls what is served. All the + * protocol detail is hidden and handled by the library. + * + * For each connection / session there is user data allocated that is + * pointed to by "user". You set the size of this user data area when + * the library is initialized with lws_create_server. + */ +typedef int +lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +#define LWS_CB_REASON_AUX_BF__CGI 1 +#define LWS_CB_REASON_AUX_BF__PROXY 2 +#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4 +#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 +///@} + +/*! \defgroup generic hash + * ## Generic Hash related functions + * + * Lws provides generic hash / digest accessors that abstract the ones + * provided by whatever OpenSSL library you are linking against. + * + * It lets you use the same code if you build against mbedtls or OpenSSL + * for example. + */ +///@{ + +#ifdef LWS_OPENSSL_SUPPORT + +#if defined(LWS_WITH_MBEDTLS) +#include <mbedtls/sha1.h> +#include <mbedtls/sha256.h> +#include <mbedtls/sha512.h> +#endif + +#define LWS_GENHASH_TYPE_SHA1 0 +#define LWS_GENHASH_TYPE_SHA256 1 +#define LWS_GENHASH_TYPE_SHA512 2 + +struct lws_genhash_ctx { + uint8_t type; +#if defined(LWS_WITH_MBEDTLS) + union { + mbedtls_sha1_context sha1; + mbedtls_sha256_context sha256; + mbedtls_sha512_context sha512; + } u; +#else + const EVP_MD *evp_type; + EVP_MD_CTX *mdctx; +#endif +}; + +/** lws_genhash_size() - get hash size in bytes + * + * \param type: one of LWS_GENHASH_TYPE_... + * + * Returns number of bytes in this type of hash + */ +LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT +lws_genhash_size(int type); + +/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use + * + * \param ctx: your struct lws_genhash_ctx + * \param type: one of LWS_GENHASH_TYPE_... + * + * Initializes the hash context for the type you requested + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_genhash_init(struct lws_genhash_ctx *ctx, int type); + +/** lws_genhash_update() - digest len bytes of the buffer starting at in + * + * \param ctx: your struct lws_genhash_ctx + * \param in: start of the bytes to digest + * \param len: count of bytes to digest + * + * Updates the state of your hash context to reflect digesting len bytes from in + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len); + +/** lws_genhash_destroy() - copy out the result digest and destroy the ctx + * + * \param ctx: your struct lws_genhash_ctx + * \param result: NULL, or where to copy the result hash + * + * Finalizes the hash and copies out the digest. Destroys any allocations such + * that ctx can safely go out of scope after calling this. + * + * NULL result is supported so that you can destroy the ctx cleanly on error + * conditions, where there is no valid result. + */ +LWS_VISIBLE LWS_EXTERN int +lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result); + +#endif + +///@} + +/*! \defgroup extensions Extension related functions + * ##Extension releated functions + * + * Ws defines optional extensions, lws provides the ability to implement these + * in user code if so desired. + * + * We provide one extensions permessage-deflate. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_extension_callback_reasons { + LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0, + LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1, + LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2, + LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3, + LWS_EXT_CB_CONSTRUCT = 4, + LWS_EXT_CB_CLIENT_CONSTRUCT = 5, + LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6, + LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7, + LWS_EXT_CB_DESTROY = 8, + LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9, + LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10, + LWS_EXT_CB_PACKET_RX_PREPARSE = 11, + LWS_EXT_CB_PACKET_TX_PRESEND = 12, + LWS_EXT_CB_PACKET_TX_DO_SEND = 13, + LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14, + LWS_EXT_CB_FLUSH_PENDING_TX = 15, + LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16, + LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17, + LWS_EXT_CB_1HZ = 18, + LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19, + LWS_EXT_CB_IS_WRITEABLE = 20, + LWS_EXT_CB_PAYLOAD_TX = 21, + LWS_EXT_CB_PAYLOAD_RX = 22, + LWS_EXT_CB_OPTION_DEFAULT = 23, + LWS_EXT_CB_OPTION_SET = 24, + LWS_EXT_CB_OPTION_CONFIRM = 25, + LWS_EXT_CB_NAMED_OPTION_SET = 26, + + /****** add new things just above ---^ ******/ +}; + +/** enum lws_ext_options_types */ +enum lws_ext_options_types { + EXTARG_NONE, /**< does not take an argument */ + EXTARG_DEC, /**< requires a decimal argument */ + EXTARG_OPT_DEC /**< may have an optional decimal argument */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_options - Option arguments to the extension. These are + * used in the negotiation at ws upgrade time. + * The helper function lws_ext_parse_options() + * uses these to generate callbacks */ +struct lws_ext_options { + const char *name; /**< Option name, eg, "server_no_context_takeover" */ + enum lws_ext_options_types type; /**< What kind of args the option can take */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** struct lws_ext_option_arg */ +struct lws_ext_option_arg { + const char *option_name; /**< may be NULL, option_index used then */ + int option_index; /**< argument ordinal to use if option_name missing */ + const char *start; /**< value */ + int len; /**< length of value */ +}; + +/** + * typedef lws_extension_callback_function() - Hooks to allow extensions to operate + * \param context: Websockets context + * \param ext: This extension + * \param wsi: Opaque websocket instance pointer + * \param reason: The reason for the call + * \param user: Pointer to ptr to per-session user data allocated by library + * \param in: Pointer used for some callback reasons + * \param len: Length set for some callback reasons + * + * Each extension that is active on a particular connection receives + * callbacks during the connection lifetime to allow the extension to + * operate on websocket data and manage itself. + * + * Libwebsockets takes care of allocating and freeing "user" memory for + * each active extension on each connection. That is what is pointed to + * by the user parameter. + * + * LWS_EXT_CB_CONSTRUCT: called when the server has decided to + * select this extension from the list provided by the client, + * just before the server will send back the handshake accepting + * the connection with this extension active. This gives the + * extension a chance to initialize its connection context found + * in user. + * + * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT + * but called when client is instantiating this extension. Some + * extensions will work the same on client and server side and then + * you can just merge handlers for both CONSTRUCTS. + * + * LWS_EXT_CB_DESTROY: called when the connection the extension was + * being used on is about to be closed and deallocated. It's the + * last chance for the extension to deallocate anything it has + * allocated in the user data (pointed to by user) before the + * user data is deleted. This same callback is used whether you + * are in client or server instantiation context. + * + * LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on + * a connection, and a packet of data arrived at the connection, + * it is passed to this callback to give the extension a chance to + * change the data, eg, decompress it. user is pointing to the + * extension's private connection context data, in is pointing + * to an lws_tokens struct, it consists of a char * pointer called + * token, and an int called token_len. At entry, these are + * set to point to the received buffer and set to the content + * length. If the extension will grow the content, it should use + * a new buffer allocated in its private user context data and + * set the pointed-to lws_tokens members to point to its buffer. + * + * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as + * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the + * extension a chance to change websocket data just before it will + * be sent out. Using the same lws_token pointer scheme in in, + * the extension can change the buffer and the length to be + * transmitted how it likes. Again if it wants to grow the + * buffer safely, it should copy the data into its own buffer and + * set the lws_tokens token pointer to it. + * + * LWS_EXT_CB_ARGS_VALIDATE: + */ +typedef int +lws_extension_callback_function(struct lws_context *context, + const struct lws_extension *ext, struct lws *wsi, + enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len); + +/** struct lws_extension - An extension we support */ +struct lws_extension { + const char *name; /**< Formal extension name, eg, "permessage-deflate" */ + lws_extension_callback_function *callback; /**< Service callback */ + const char *client_offer; /**< String containing exts and options client offers */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_set_extension_option(): set extension option if possible + * + * \param wsi: websocket connection + * \param ext_name: name of ext, like "permessage-deflate" + * \param opt_name: name of option, like "rx_buf_size" + * \param opt_val: value to set option to + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_extension_option(struct lws *wsi, const char *ext_name, + const char *opt_name, const char *opt_val); + +#ifndef LWS_NO_EXTENSIONS +/* lws_get_internal_extensions() - DEPRECATED + * + * \Deprecated There is no longer a set internal extensions table. The table is provided + * by user code along with application-specific settings. See the test + * client and server for how to do. + */ +static LWS_INLINE LWS_WARN_DEPRECATED const struct lws_extension * +lws_get_internal_extensions(void) { return NULL; } + +/** + * lws_ext_parse_options() - deal with parsing negotiated extension options + * + * \param ext: related extension struct + * \param wsi: websocket connection + * \param ext_user: per-connection extension private data + * \param opts: list of supported options + * \param o: option string to parse + * \param len: length + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, + void *ext_user, const struct lws_ext_options *opts, + const char *o, int len); +#endif + +/** lws_extension_callback_pm_deflate() - extension for RFC7692 + * + * \param context: lws context + * \param ext: related lws_extension struct + * \param wsi: websocket connection + * \param reason: incoming callback reason + * \param user: per-connection extension private data + * \param in: pointer parameter + * \param len: length parameter + * + * Built-in callback implementing RFC7692 permessage-deflate + */ +LWS_EXTERN +int lws_extension_callback_pm_deflate( + struct lws_context *context, const struct lws_extension *ext, + struct lws *wsi, enum lws_extension_callback_reasons reason, + void *user, void *in, size_t len); + +/* + * The internal exts are part of the public abi + * If we add more extensions, publish the callback here ------v + */ +///@} + +/*! \defgroup Protocols-and-Plugins Protocols and Plugins + * \ingroup lwsapi + * + * ##Protocol and protocol plugin -related apis + * + * Protocols bind ws protocol names to a custom callback specific to that + * protocol implementaion. + * + * A list of protocols can be passed in at context creation time, but it is + * also legal to leave that NULL and add the protocols and their callback code + * using plugins. + * + * Plugins are much preferable compared to cut and pasting code into an + * application each time, since they can be used standalone. + */ +///@{ +/** struct lws_protocols - List of protocols and handlers client or server + * supports. */ + +struct lws_protocols { + const char *name; + /**< Protocol name that must match the one given in the client + * Javascript new WebSocket(url, 'protocol') name. */ + lws_callback_function *callback; + /**< The service callback used for this protocol. It allows the + * service action for an entire protocol to be encapsulated in + * the protocol-specific callback */ + size_t per_session_data_size; + /**< Each new connection using this protocol gets + * this much memory allocated on connection establishment and + * freed on connection takedown. A pointer to this per-connection + * allocation is passed into the callback in the 'user' parameter */ + size_t rx_buffer_size; + /**< lws allocates this much space for rx data and informs callback + * when something came. Due to rx flow control, the callback may not + * be able to consume it all without having to return to the event + * loop. That is supported in lws. + * + * If .tx_packet_size is 0, this also controls how much may be sent at once + * for backwards compatibility. + */ + unsigned int id; + /**< ignored by lws, but useful to contain user information bound + * to the selected protocol. For example if this protocol was + * called "myprotocol-v2", you might set id to 2, and the user + * code that acts differently according to the version can do so by + * switch (wsi->protocol->id), user code might use some bits as + * capability flags based on selected protocol version, etc. */ + void *user; /**< ignored by lws, but user code can pass a pointer + here it can later access from the protocol callback */ + size_t tx_packet_size; + /**< 0 indicates restrict send() size to .rx_buffer_size for backwards- + * compatibility. + * If greater than zero, a single send() is restricted to this amount + * and any remainder is buffered by lws and sent afterwards also in + * these size chunks. Since that is expensive, it's preferable + * to restrict one fragment you are trying to send to match this + * size. + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +struct lws_vhost; + +/** + * lws_vhost_name_to_protocol() - get vhost's protocol object from its name + * + * \param vh: vhost to search + * \param name: protocol name + * + * Returns NULL or a pointer to the vhost's protocol of the requested name + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name); + +/** + * lws_get_protocol() - Returns a protocol pointer from a websocket + * connection. + * \param wsi: pointer to struct websocket you want to know the protocol of + * + * + * Some apis can act on all live connections of a given protocol, + * this is how you can get a pointer to the active protocol if needed. + */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_get_protocol(struct lws *wsi); + +/** lws_protocol_get() - deprecated: use lws_get_protocol */ +LWS_VISIBLE LWS_EXTERN const struct lws_protocols * +lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost + * storage + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * \param size: bytes to allocate + * + * Protocols often find it useful to allocate a per-vhost struct, this is a + * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot, + int size); + +/** + * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage + * + * \param vhost: vhost the instance is related to + * \param prot: protocol the instance is related to + * + * Recover a pointer to the allocated per-vhost storage for the protocol created + * by lws_protocol_vh_priv_zalloc() earlier + */ +LWS_VISIBLE LWS_EXTERN void * +lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot); + +/** + * lws_adjust_protocol_psds - change a vhost protocol's per session data size + * + * \param wsi: a connection with the protocol to change + * \param new_size: the new size of the per session data size for the protocol + * + * Returns user_space for the wsi, after allocating + * + * This should not be used except to initalize a vhost protocol's per session + * data size one time, before any connections are accepted. + * + * Sometimes the protocol wraps another protocol and needs to discover and set + * its per session data size at runtime. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_adjust_protocol_psds(struct lws *wsi, size_t new_size); + +/** + * lws_finalize_startup() - drop initial process privileges + * + * \param context: lws context + * + * This is called after the end of the vhost protocol initializations, but + * you may choose to call it earlier + */ +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN int +lws_protocol_init(struct lws_context *context); + +#ifdef LWS_WITH_PLUGINS + +/* PLUGINS implies LIBUV */ + +#define LWS_PLUGIN_API_MAGIC 180 + +/** struct lws_plugin_capability - how a plugin introduces itself to lws */ +struct lws_plugin_capability { + unsigned int api_magic; /**< caller fills this in, plugin fills rest */ + const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ + int count_protocols; /**< how many protocols */ + const struct lws_extension *extensions; /**< array of extensions provided by plugin */ + int count_extensions; /**< how many extensions */ +}; + +typedef int (*lws_plugin_init_func)(struct lws_context *, + struct lws_plugin_capability *); +typedef int (*lws_plugin_destroy_func)(struct lws_context *); + +/** struct lws_plugin */ +struct lws_plugin { + struct lws_plugin *list; /**< linked list */ +#if (UV_VERSION_MAJOR > 0) + uv_lib_t lib; /**< shared library pointer */ +#else + void *l; /**< so we can compile on ancient libuv */ +#endif + char name[64]; /**< name of the plugin */ + struct lws_plugin_capability caps; /**< plugin capabilities */ +}; + +#endif + +///@} + + +/*! \defgroup generic-sessions plugin: generic-sessions + * \ingroup Protocols-and-Plugins + * + * ##Plugin Generic-sessions related + * + * generic-sessions plugin provides a reusable, generic session and login / + * register / forgot password framework including email verification. + */ +///@{ + +#define LWSGS_EMAIL_CONTENT_SIZE 16384 +/**< Maximum size of email we might send */ + +/* SHA-1 binary and hexified versions */ +/** typedef struct lwsgw_hash_bin */ +typedef struct { unsigned char bin[20]; /**< binary representation of hash */} lwsgw_hash_bin; +/** typedef struct lwsgw_hash */ +typedef struct { char id[41]; /**< ascii hex representation of hash */ } lwsgw_hash; + +/** enum lwsgs_auth_bits */ +enum lwsgs_auth_bits { + LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ + LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ + LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ + LWSGS_AUTH_FORGOT_FLOW = 8, /**< he just completed "forgot password" flow */ +}; + +/** struct lws_session_info - information about user session status */ +struct lws_session_info { + char username[32]; /**< username logged in as, or empty string */ + char email[100]; /**< email address associated with login, or empty string */ + char ip[72]; /**< ip address session was started from */ + unsigned int mask; /**< access rights mask associated with session + * see enum lwsgs_auth_bits */ + char session[42]; /**< session id string, usable as opaque uid when not logged in */ +}; + +/** enum lws_gs_event */ +enum lws_gs_event { + LWSGSE_CREATED, /**< a new user was created */ + LWSGSE_DELETED /**< an existing user was deleted */ +}; + +/** struct lws_gs_event_args */ +struct lws_gs_event_args { + enum lws_gs_event event; /**< which event happened */ + const char *username; /**< which username the event happened to */ + const char *email; /**< the email address of that user */ +}; + +///@} + + +/*! \defgroup context-and-vhost context and vhost related functions + * ##Context and Vhost releated functions + * \ingroup lwsapi + * + * + * LWS requires that there is one context, in which you may define multiple + * vhosts. Each vhost is a virtual host, with either its own listen port + * or sharing an existing one. Each vhost has its own SSL context that can + * be set up individually or left disabled. + * + * If you don't care about multiple "site" support, you can ignore it and + * lws will create a single default vhost at context creation time. + */ +///@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ + +/** enum lws_context_options - context and vhost options */ +enum lws_context_options { + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = (1 << 1) | + (1 << 12), + /**< (VH) Don't allow the connection unless the client has a + * client cert that we recognize; provides + * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = (1 << 2), + /**< (CTX) Don't try to get the server's hostname */ + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = (1 << 3) | + (1 << 12), + /**< (VH) Allow non-SSL (plaintext) connections on the same + * port as SSL is listening... undermines the security of SSL; + * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + LWS_SERVER_OPTION_LIBEV = (1 << 4), + /**< (CTX) Use libev event loop */ + LWS_SERVER_OPTION_DISABLE_IPV6 = (1 << 5), + /**< (VH) Disable IPV6 support */ + LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS = (1 << 6), + /**< (VH) Don't load OS CA certs, you will need to load your + * own CA cert(s) */ + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED = (1 << 7), + /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */ + LWS_SERVER_OPTION_VALIDATE_UTF8 = (1 << 8), + /**< (VH) Check UT-8 correctness */ + LWS_SERVER_OPTION_SSL_ECDH = (1 << 9) | + (1 << 12), + /**< (VH) initialize ECDH ciphers */ + LWS_SERVER_OPTION_LIBUV = (1 << 10), + /**< (CTX) Use libuv event loop */ + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS = (1 << 11) | + (1 << 12), + /**< (VH) Use http redirect to force http to https + * (deprecated: use mount redirection) */ + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), + /**< (CTX) Initialize the SSL library at all */ + LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13), + /**< (CTX) Only create the context when calling context + * create api, implies user code will create its own vhosts */ + LWS_SERVER_OPTION_UNIX_SOCK = (1 << 14), + /**< (VH) Use Unix socket */ + LWS_SERVER_OPTION_STS = (1 << 15), + /**< (VH) Send Strict Transport Security header, making + * clients subsequently go to https even if user asked for http */ + LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY = (1 << 16), + /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */ + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE = (1 << 17), + /**< (VH) if set, only ipv6 allowed on the vhost */ + LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN = (1 << 18), + /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault + * normally makes the lib spin so you can attach a debugger to it + * even if it happened without a debugger in place. You can disable + * that by giving this option. + */ + LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN = (1 << 19), + /**< For backwards-compatibility reasons, by default + * lws prepends "http://" to the origin you give in the client + * connection info struct. If you give this flag when you create + * the context, only the string you give in the client connect + * info for .origin (if any) will be used directly. + */ + LWS_SERVER_OPTION_FALLBACK_TO_RAW = (1 << 20), + /**< (VH) if invalid http is coming in the first line, */ + LWS_SERVER_OPTION_LIBEVENT = (1 << 21), + /**< (CTX) Use libevent event loop */ + LWS_SERVER_OPTION_ONLY_RAW = (1 << 22), + /**< (VH) All connections to this vhost / port are RAW as soon as + * the connection is accepted, no HTTP is going to be coming. + */ + LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE = (1 << 23), + /**< (VH) Set to allow multiple listen sockets on one interface + + * address + port. The default is to strictly allow only one + * listen socket at a time. This is automatically selected if you + * have multiple service threads. + */ + LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX = (1 << 24), + /**< (VH) Force setting up the vhost SSL_CTX, even though the user + * code doesn't explicitly provide a cert in the info struct. It + * implies the user code is going to provide a cert at the + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which + * provides the vhost SSL_CTX * in the user parameter. + */ + + /****** add new things just above ---^ ******/ +}; + +#define lws_check_opt(c, f) (((c) & (f)) == (f)) + +struct lws_plat_file_ops; + +/** struct lws_context_creation_info - parameters to create context and /or vhost with + * + * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS + * is not given, then for backwards compatibility one vhost is created at + * context-creation time using the info from this struct. + * + * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created + * at the same time as the context, they are expected to be created afterwards. + */ +struct lws_context_creation_info { + int port; + /**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress + * listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are + * writing a server but you are using \ref sock-adopt instead of the + * built-in listener */ + const char *iface; + /**< VHOST: NULL to bind the listen socket to all interfaces, or the + * interface name, eg, "eth2" + * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is + * the pathname of a UNIX domain socket. you can use the UNIX domain + * sockets in abstract namespace, by prepending an at symbol to the + * socket name. */ + const struct lws_protocols *protocols; + /**< VHOST: Array of structures listing supported protocols and a protocol- + * specific callback for each one. The list is ended with an + * entry that has a NULL callback pointer. */ + const struct lws_extension *extensions; + /**< VHOST: NULL or array of lws_extension structs listing the + * extensions this context supports. */ + const struct lws_token_limits *token_limits; + /**< CONTEXT: NULL or struct lws_token_limits pointer which is initialized + * with a token length limit for each possible WSI_TOKEN_ */ + const char *ssl_private_key_password; + /**< VHOST: NULL or the passphrase needed for the private key. (For + * backwards compatibility, this can also be used to pass the client + * cert passphrase when setting up a vhost client SSL context, but it is + * preferred to use .client_ssl_private_key_password for that.) */ + const char *ssl_cert_filepath; + /**< VHOST: If libwebsockets was compiled to use ssl, and you want + * to listen using SSL, set to the filepath to fetch the + * server cert from, otherwise NULL for unencrypted. (For backwards + * compatibility, this can also be used to pass the client certificate + * when setting up a vhost client SSL context, but it is preferred to + * use .client_ssl_cert_filepath for that.) */ + const char *ssl_private_key_filepath; + /**< VHOST: filepath to private key if wanting SSL mode; + * if this is set to NULL but ssl_cert_filepath is set, the + * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called + * to allow setting of the private key directly via openSSL + * library calls. (For backwards compatibility, this can also be used + * to pass the client cert private key filepath when setting up a + * vhost client SSL context, but it is preferred to use + * .client_ssl_private_key_filepath for that.) */ + const char *ssl_ca_filepath; + /**< VHOST: CA certificate filepath or NULL. (For backwards + * compatibility, this can also be used to pass the client CA + * filepath when setting up a vhost client SSL context, + * but it is preferred to use .client_ssl_ca_filepath for that.) */ + const char *ssl_cipher_list; + /**< VHOST: List of valid ciphers to use (eg, + * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" + * or you can leave it as NULL to get "DEFAULT" (For backwards + * compatibility, this can also be used to pass the client cipher + * list when setting up a vhost client SSL context, + * but it is preferred to use .client_ssl_cipher_list for that.)*/ + const char *http_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int http_proxy_port; + /**< VHOST: If http_proxy_address was non-NULL, uses this port */ + int gid; + /**< CONTEXT: group id to change to after setting listen socket, or -1. */ + int uid; + /**< CONTEXT: user id to change to after setting listen socket, or -1. */ + unsigned int options; + /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ + void *user; + /**< VHOST + CONTEXT: optional user pointer that will be associated + * with the context when creating the context (and can be retrieved by + * lws_context_user(context), or with the vhost when creating the vhost + * (and can be retrieved by lws_vhost_user(vhost)). You will need to + * use LWS_SERVER_OPTION_EXPLICIT_VHOSTS and create the vhost separately + * if you care about giving the context and vhost different user pointer + * values. + */ + int ka_time; + /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive + * timeout to all libwebsocket sockets, client or server */ + int ka_probes; + /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many + * times to try to get a response from the peer before giving up + * and killing the connection */ + int ka_interval; + /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes + * attempt */ +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *provided_client_ssl_ctx; + /**< CONTEXT: If non-null, swap out libwebsockets ssl + * implementation for the one provided by provided_ssl_ctx. + * Libwebsockets no longer is responsible for freeing the context + * if this option is selected. */ +#else /* maintain structure layout either way */ + void *provided_client_ssl_ctx; /**< dummy if ssl disabled */ +#endif + + short max_http_header_data; + /**< CONTEXT: The max amount of header payload that can be handled + * in an http request (unrecognized header payload is dropped) */ + short max_http_header_pool; + /**< CONTEXT: The max number of connections with http headers that + * can be processed simultaneously (the corresponding memory is + * allocated for the lifetime of the context). If the pool is + * busy new incoming connections must wait for accept until one + * becomes free. */ + + unsigned int count_threads; + /**< CONTEXT: how many contexts to create in an array, 0 = 1 */ + unsigned int fd_limit_per_thread; + /**< CONTEXT: nonzero means restrict each service thread to this + * many fds, 0 means the default which is divide the process fd + * limit by the number of threads. */ + unsigned int timeout_secs; + /**< VHOST: various processes involving network roundtrips in the + * library are protected from hanging forever by timeouts. If + * nonzero, this member lets you set the timeout used in seconds. + * Otherwise a default timeout is used. */ + const char *ecdh_curve; + /**< VHOST: if NULL, defaults to initializing server with "prime256v1" */ + const char *vhost_name; + /**< VHOST: name of vhost, must match external DNS name used to + * access the site, like "warmcat.com" as it's used to match + * Host: header and / or SNI name for SSL. */ + const char * const *plugin_dirs; + /**< CONTEXT: NULL, or NULL-terminated array of directories to + * scan for lws protocol plugins at context creation time */ + const struct lws_protocol_vhost_options *pvo; + /**< VHOST: pointer to optional linked list of per-vhost + * options made accessible to protocols */ + int keepalive_timeout; + /**< VHOST: (default = 0 = 60s) seconds to allow remote + * client to hold on to an idle HTTP/1.1 connection */ + const char *log_filepath; + /**< VHOST: filepath to append logs to... this is opened before + * any dropping of initial privileges */ + const struct lws_http_mount *mounts; + /**< VHOST: optional linked list of mounts for this vhost */ + const char *server_string; + /**< CONTEXT: string used in HTTP headers to identify server + * software, if NULL, "libwebsockets". */ + unsigned int pt_serv_buf_size; + /**< CONTEXT: 0 = default of 4096. This buffer is used by + * various service related features including file serving, it + * defines the max chunk of file that can be sent at once. + * At the risk of lws having to buffer failed large sends, it + * can be increased to, eg, 128KiB to improve throughput. */ + unsigned int max_http_header_data2; + /**< CONTEXT: if max_http_header_data is 0 and this + * is nonzero, this will be used in place of the default. It's + * like this for compatibility with the original short version, + * this is unsigned int length. */ + long ssl_options_set; + /**< VHOST: Any bits set here will be set as SSL options */ + long ssl_options_clear; + /**< VHOST: Any bits set here will be cleared as SSL options */ + unsigned short ws_ping_pong_interval; + /**< CONTEXT: 0 for none, else interval in seconds between sending + * PINGs on idle websocket connections. When the PING is sent, + * the PONG must come within the normal timeout_secs timeout period + * or the connection will be dropped. + * Any RX or TX traffic on the connection restarts the interval timer, + * so a connection which always sends or receives something at intervals + * less than the interval given here will never send PINGs / expect + * PONGs. Conversely as soon as the ws connection is established, an + * idle connection will do the PING / PONG roundtrip as soon as + * ws_ping_pong_interval seconds has passed without traffic + */ + const struct lws_protocol_vhost_options *headers; + /**< VHOST: pointer to optional linked list of per-vhost + * canned headers that are added to server responses */ + + const struct lws_protocol_vhost_options *reject_service_keywords; + /**< CONTEXT: Optional list of keywords and rejection codes + text. + * + * The keywords are checked for existing in the user agent string. + * + * Eg, "badrobot" "404 Not Found" + */ + void *external_baggage_free_on_destroy; + /**< CONTEXT: NULL, or pointer to something externally malloc'd, that + * should be freed when the context is destroyed. This allows you to + * automatically sync the freeing action to the context destruction + * action, so there is no need for an external free() if the context + * succeeded to create. + */ + + const char *client_ssl_private_key_password; + /**< VHOST: Client SSL context init: NULL or the passphrase needed + * for the private key */ + const char *client_ssl_cert_filepath; + /**< VHOST: Client SSL context init:T he certificate the client + * should present to the peer on connection */ + const char *client_ssl_private_key_filepath; + /**< VHOST: Client SSL context init: filepath to client private key + * if this is set to NULL but client_ssl_cert_filepath is set, you + * can handle the LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS + * callback of protocols[0] to allow setting of the private key directly + * via openSSL library calls */ + const char *client_ssl_ca_filepath; + /**< VHOST: Client SSL context init: CA certificate filepath or NULL */ + const char *client_ssl_cipher_list; + /**< VHOST: Client SSL context init: List of valid ciphers to use (eg, + * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" + * or you can leave it as NULL to get "DEFAULT" */ + + const struct lws_plat_file_ops *fops; + /**< CONTEXT: NULL, or pointer to an array of fops structs, terminated + * by a sentinel with NULL .open. + * + * If NULL, lws provides just the platform file operations struct for + * backwards compatibility. + */ + int simultaneous_ssl_restriction; + /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions possible.*/ + const char *socks_proxy_address; + /**< VHOST: If non-NULL, attempts to proxy via the given address. + * If proxy auth is required, use format "username:password\@server:port" */ + unsigned int socks_proxy_port; + /**< VHOST: If socks_proxy_address was non-NULL, uses this port */ +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + cap_value_t caps[4]; + /**< CONTEXT: array holding Linux capabilities you want to + * continue to be available to the server after it transitions + * to a noprivileged user. Usually none are needed but for, eg, + * .bind_iface, CAP_NET_RAW is required. This gives you a way + * to still have the capability but drop root. + */ + char count_caps; + /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means + * no capabilities will be inherited from root (the default) */ +#endif + int bind_iface; + /**< VHOST: nonzero to strictly bind sockets to the interface name in + * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE. + * + * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW + * capability. + * + * Notice that common things like access network interface IP from + * your local machine use your lo / loopback interface and will be + * disallowed by this. + */ + int ssl_info_event_mask; + /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO + * callback for connections on this vhost. The mask values are of + * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of + * 0 means no info events will be reported. + */ + unsigned int timeout_secs_ah_idle; + /**< VHOST: seconds to allow a client to hold an ah without using it. + * 0 defaults to 10s. */ + unsigned short ip_limit_ah; + /**< CONTEXT: max number of ah a single IP may use simultaneously + * 0 is no limit. This is a soft limit: if the limit is + * reached, connections from that IP will wait in the ah + * waiting list and not be able to acquire an ah until + * a connection belonging to the IP relinquishes one it + * already has. + */ + unsigned short ip_limit_wsi; + /**< CONTEXT: max number of wsi a single IP may use simultaneously. + * 0 is no limit. This is a hard limit, connections from + * the same IP will simply be dropped once it acquires the + * amount of simultaneous wsi / accepted connections + * given here. + */ + uint32_t http2_settings[7]; + /**< CONTEXT: after context creation http2_settings[1] thru [6] have + * been set to the lws platform default values. + * VHOST: if http2_settings[0] is nonzero, the values given in + * http2_settings[1]..[6] are used instead of the lws + * platform default values. + * Just leave all at 0 if you don't care. + */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[8]; /**< dummy */ +}; + +/** + * lws_create_context() - Create the websocket handler + * \param info: pointer to struct with parameters + * + * This function creates the listening socket (if serving) and takes care + * of all initialization in one step. + * + * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is + * created; you're expected to create your own vhosts afterwards using + * lws_create_vhost(). Otherwise a vhost named "default" is also created + * using the information in the vhost-related members, for compatibility. + * + * After initialization, it returns a struct lws_context * that + * represents this server. After calling, user code needs to take care + * of calling lws_service() with the context pointer to get the + * server's sockets serviced. This must be done in the same process + * context as the initialization call. + * + * The protocol callback functions are called for a handful of events + * including http requests coming in, websocket connections becoming + * established, and data arriving; it's also called periodically to allow + * async transmission. + * + * HTTP requests are sent always to the FIRST protocol in protocol, since + * at that time websocket protocol has not been negotiated. Other + * protocols after the first one never see any HTTP callback activity. + * + * The server created is a simple http server by default; part of the + * websocket standard is upgrading this http connection to a websocket one. + * + * This allows the same server to provide files like scripts and favicon / + * images or whatever over http and dynamic data over websockets all in + * one place; they're all handled in the user callback. + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * +lws_create_context(struct lws_context_creation_info *info); + +/** + * lws_context_destroy() - Destroy the websocket context + * \param context: Websocket context + * + * This function closes any active connections and then frees the + * context. After calling this, any further use of the context is + * undefined. + */ +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy2(struct lws_context *context); + +typedef int (*lws_reload_func)(void); + +/** + * lws_context_deprecate() - Deprecate the websocket context + * + * \param context: Websocket context + * \param cb: Callback notified when old context listen sockets are closed + * + * This function is used on an existing context before superceding it + * with a new context. + * + * It closes any listen sockets in the context, so new connections are + * not possible. + * + * And it marks the context to be deleted when the number of active + * connections into it falls to zero. + * + * Otherwise if you attach the deprecated context to the replacement + * context when it has been created using lws_context_attach_deprecated() + * both any deprecated and the new context will service their connections. + * + * This is aimed at allowing seamless configuration reloads. + * + * The callback cb will be called after the listen sockets are actually + * closed and may be reopened. In the callback the new context should be + * configured and created. (With libuv, socket close happens async after + * more loop events). + */ +LWS_VISIBLE LWS_EXTERN void +lws_context_deprecate(struct lws_context *context, lws_reload_func cb); + +LWS_VISIBLE LWS_EXTERN int +lws_context_is_deprecated(struct lws_context *context); + +/** + * lws_set_proxy() - Setups proxy to lws_context. + * \param vhost: pointer to struct lws_vhost you want set proxy for + * \param proxy: pointer to c string containing proxy in format address:port + * + * Returns 0 if proxy string was parsed and proxy was setup. + * Returns -1 if proxy is NULL or has incorrect format. + * + * This is only required if your OS does not provide the http_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy); + +/** + * lws_set_socks() - Setup socks to lws_context. + * \param vhost: pointer to struct lws_vhost you want set socks for + * \param socks: pointer to c string containing socks in format address:port + * + * Returns 0 if socks string was parsed and socks was setup. + * Returns -1 if socks is NULL or has incorrect format. + * + * This is only required if your OS does not provide the socks_proxy + * environment variable (eg, OSX) + * + * IMPORTANT! You should call this function right after creation of the + * lws_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on lws_context + * creation with genenv() call. + */ +LWS_VISIBLE LWS_EXTERN int +lws_set_socks(struct lws_vhost *vhost, const char *socks); + +struct lws_vhost; + +/** + * lws_create_vhost() - Create a vhost (virtual server context) + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * + * This function creates a virtual server (vhost) using the vhost-related + * members of the info struct. You can create many vhosts inside one context + * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info); + +/** + * lws_vhost_destroy() - Destroy a vhost (virtual server context) + * + * \param vh: pointer to result of lws_create_vhost() + * + * This function destroys a vhost. Normally, if you just want to exit, + * then lws_destroy_context() will take care of everything. If you want + * to destroy an individual vhost and all connections and allocations, you + * can do it with this. + * + * If the vhost has a listen sockets shared by other vhosts, it will be given + * to one of the vhosts sharing it rather than closed. + */ +LWS_VISIBLE LWS_EXTERN void +lws_vhost_destroy(struct lws_vhost *vh); + +/** + * lwsws_get_config_globals() - Parse a JSON server config file + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function prepares a n lws_context_creation_info struct with global + * settings from a file d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** + * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file + * \param context: pointer to result of lws_create_context() + * \param info: pointer to struct with parameters + * \param d: filepath of the config file + * \param config_strings: storage for the config strings extracted from JSON, + * the pointer is incremented as strings are stored + * \param len: pointer to the remaining length left in config_strings + * the value is decremented as strings are stored + * + * This function creates vhosts into a context according to the settings in + *JSON files found in directory d. + * + * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled + */ +LWS_VISIBLE LWS_EXTERN int +lwsws_get_config_vhosts(struct lws_context *context, + struct lws_context_creation_info *info, const char *d, + char **config_strings, int *len); + +/** lws_vhost_get() - \deprecated deprecated: use lws_get_vhost() */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED; + +/** + * lws_get_vhost() - return the vhost a wsi belongs to + * + * \param wsi: which connection + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_get_vhost(struct lws *wsi); + +/** + * lws_json_dump_vhost() - describe vhost state and stats in JSON + * + * \param vh: the vhost + * \param buf: buffer to fill with JSON + * \param len: max length of buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len); + +/** + * lws_json_dump_context() - describe context state and stats in JSON + * + * \param context: the context + * \param buf: buffer to fill with JSON + * \param len: max length of buf + * \param hide_vhosts: nonzero to not provide per-vhost mount etc information + * + * Generates a JSON description of vhost state into buf + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_dump_context(const struct lws_context *context, char *buf, int len, + int hide_vhosts); + +/** + * lws_vhost_user() - get the user data associated with the vhost + * \param vhost: Websocket vhost + * + * This returns the optional user pointer that can be attached to + * a vhost when it was created. Lws never dereferences this pointer, it only + * sets it when the vhost is created, and returns it using this api. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_vhost_user(struct lws_vhost *vhost); + +/** + * lws_context_user() - get the user data associated with the context + * \param context: Websocket context + * + * This returns the optional user allocation that can be attached to + * the context the sockets live in at context_create time. It's a way + * to let all sockets serviced in the same context share data without + * using globals statics in the user code. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_context_user(struct lws_context *context); + +/*! \defgroup vhost-mounts Vhost mounts and options + * \ingroup context-and-vhost-creation + * + * ##Vhost mounts and options + */ +///@{ +/** struct lws_protocol_vhost_options - linked list of per-vhost protocol + * name=value options + * + * This provides a general way to attach a linked-list of name=value pairs, + * which can also have an optional child link-list using the options member. + */ +struct lws_protocol_vhost_options { + const struct lws_protocol_vhost_options *next; /**< linked list */ + const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */ + const char *name; /**< name of name=value pair */ + const char *value; /**< value of name=value pair */ +}; + +/** enum lws_mount_protocols + * This specifies the mount protocol for a mountpoint, whether it is to be + * served from a filesystem, or it is a cgi etc. + */ +enum lws_mount_protocols { + LWSMPRO_HTTP = 0, /**< http reverse proxy */ + LWSMPRO_HTTPS = 1, /**< https reverse proxy */ + LWSMPRO_FILE = 2, /**< serve from filesystem directory */ + LWSMPRO_CGI = 3, /**< pass to CGI to handle */ + LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */ + LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */ + LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */ +}; + +/** struct lws_http_mount + * + * arguments for mounting something in a vhost's url namespace + */ +struct lws_http_mount { + const struct lws_http_mount *mount_next; + /**< pointer to next struct lws_http_mount */ + const char *mountpoint; + /**< mountpoint in http pathspace, eg, "/" */ + const char *origin; + /**< path to be mounted, eg, "/var/www/warmcat.com" */ + const char *def; + /**< default target, eg, "index.html" */ + const char *protocol; + /**<"protocol-name" to handle mount */ + + const struct lws_protocol_vhost_options *cgienv; + /**< optional linked-list of cgi options. These are created + * as environment variables for the cgi process + */ + const struct lws_protocol_vhost_options *extra_mimetypes; + /**< optional linked-list of mimetype mappings */ + const struct lws_protocol_vhost_options *interpret; + /**< optional linked-list of files to be interpreted */ + + int cgi_timeout; + /**< seconds cgi is allowed to live, if cgi://mount type */ + int cache_max_age; + /**< max-age for reuse of client cache of files, seconds */ + unsigned int auth_mask; + /**< bits set here must be set for authorized client session */ + + unsigned int cache_reusable:1; /**< set if client cache may reuse this */ + unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */ + unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */ + + unsigned char origin_protocol; /**< one of enum lws_mount_protocols */ + unsigned char mountpoint_len; /**< length of mountpoint string */ + + const char *basic_auth_login_file; + /**<NULL, or filepath to use to check basic auth logins against */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[2]; /**< dummy */ +}; +///@} +///@} + +/*! \defgroup client Client related functions + * ##Client releated functions + * \ingroup lwsapi + * + * */ +///@{ + +/** enum lws_client_connect_ssl_connection_flags - flags that may be used + * with struct lws_client_connect_info ssl_connection member to control if + * and how SSL checks apply to the client connection being created + */ + +enum lws_client_connect_ssl_connection_flags { + LCCSCF_USE_SSL = (1 << 0), + LCCSCF_ALLOW_SELFSIGNED = (1 << 1), + LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2), + LCCSCF_ALLOW_EXPIRED = (1 << 3) +}; + +/** struct lws_client_connect_info - parameters to connect with when using + * lws_client_connect_via_info() */ + +struct lws_client_connect_info { + struct lws_context *context; + /**< lws context to create connection in */ + const char *address; + /**< remote address to connect to */ + int port; + /**< remote port to connect to */ + int ssl_connection; + /**< nonzero for ssl */ + const char *path; + /**< uri path */ + const char *host; + /**< content of host header */ + const char *origin; + /**< content of origin header */ + const char *protocol; + /**< list of ws protocols we could accept */ + int ietf_version_or_minus_one; + /**< deprecated: currently leave at 0 or -1 */ + void *userdata; + /**< if non-NULL, use this as wsi user_data instead of malloc it */ + const void *client_exts; + /**< UNUSED... provide in info.extensions at context creation time */ + const char *method; + /**< if non-NULL, do this http method instead of ws[s] upgrade. + * use "GET" to be a simple http client connection */ + struct lws *parent_wsi; + /**< if another wsi is responsible for this connection, give it here. + * this is used to make sure if the parent closes so do any + * child connections first. */ + const char *uri_replace_from; + /**< if non-NULL, when this string is found in URIs in + * text/html content-encoding, it's replaced with uri_replace_to */ + const char *uri_replace_to; + /**< see uri_replace_from */ + struct lws_vhost *vhost; + /**< vhost to bind to (used to determine related SSL_CTX) */ + struct lws **pwsi; + /**< if not NULL, store the new wsi here early in the connection + * process. Although we return the new wsi, the call to create the + * client connection does progress the connection somewhat and may + * meet an error that will result in the connection being scrubbed and + * NULL returned. While the wsi exists though, he may process a + * callback like CLIENT_CONNECTION_ERROR with his wsi: this gives the + * user callback a way to identify which wsi it is that faced the error + * even before the new wsi is returned and even if ultimately no wsi + * is returned. + */ + const char *iface; + /**< NULL to allow routing on any interface, or interface name or IP + * to bind the socket to */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility + * + * The below is to ensure later library versions with new + * members added above will see 0 (default) even if the app + * was not built against the newer headers. + */ + + void *_unused[4]; /**< dummy */ +}; + +/** + * lws_client_connect_via_info() - Connect to another websocket server + * \param ccinfo: pointer to lws_client_connect_info struct + * + * This function creates a connection to a remote server using the + * information provided in ccinfo. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_client_connect_via_info(struct lws_client_connect_info * ccinfo); + +/** + * lws_client_connect() - Connect to another websocket server + * \deprecated DEPRECATED use lws_client_connect_via_info + * \param clients: Websocket context + * \param address: Remote server address, eg, "myserver.com" + * \param port: Port to connect to on the remote server, eg, 80 + * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self + * signed certs + * \param path: Websocket path on server + * \param host: Hostname on server + * \param origin: Socket origin name + * \param protocol: Comma-separated list of protocols being asked for from + * the server, or just one. The server will pick the one it + * likes best. If you don't want to specify a protocol, which is + * legal, use NULL here. + * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest + * protocol supported, or the specific protocol ordinal + * + * This function creates a connection to a remote server + */ +/* deprecated, use lws_client_connect_via_info() */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect(struct lws_context *clients, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, const char *protocol, + int ietf_version_or_minus_one) LWS_WARN_DEPRECATED; +/* deprecated, use lws_client_connect_via_info() */ +/** + * lws_client_connect_extended() - Connect to another websocket server + * \deprecated DEPRECATED use lws_client_connect_via_info + * \param clients: Websocket context + * \param address: Remote server address, eg, "myserver.com" + * \param port: Port to connect to on the remote server, eg, 80 + * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self + * signed certs + * \param path: Websocket path on server + * \param host: Hostname on server + * \param origin: Socket origin name + * \param protocol: Comma-separated list of protocols being asked for from + * the server, or just one. The server will pick the one it + * likes best. + * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest + * protocol supported, or the specific protocol ordinal + * \param userdata: Pre-allocated user data + * + * This function creates a connection to a remote server + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_extended(struct lws_context *clients, const char *address, + int port, int ssl_connection, const char *path, + const char *host, const char *origin, + const char *protocol, int ietf_version_or_minus_one, + void *userdata) LWS_WARN_DEPRECATED; + +/** + * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost + * + * \param info: client ssl related info + * \param vhost: which vhost to initialize client ssl operations on + * + * You only need to call this if you plan on using SSL client connections on + * the vhost. For non-SSL client connections, it's not necessary to call this. + * + * The following members of info are used during the call + * + * - options must have LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT set, + * otherwise the call does nothing + * - provided_client_ssl_ctx must be NULL to get a generated client + * ssl context, otherwise you can pass a prepared one in by setting it + * - ssl_cipher_list may be NULL or set to the client valid cipher list + * - ssl_ca_filepath may be NULL or client cert filepath + * - ssl_cert_filepath may be NULL or client cert filepath + * - ssl_private_key_filepath may be NULL or client cert private key + * + * You must create your vhost explicitly if you want to use this, so you have + * a pointer to the vhost. Create the context first with the option flag + * LWS_SERVER_OPTION_EXPLICIT_VHOSTS and then call lws_create_vhost() with + * the same info struct. + */ +LWS_VISIBLE LWS_EXTERN int +lws_init_vhost_client_ssl(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); +/** + * lws_http_client_read() - consume waiting received http client data + * + * \param wsi: client connection + * \param buf: pointer to buffer pointer - fill with pointer to your buffer + * \param len: pointer to chunk length - fill with max length of buffer + * + * This is called when the user code is notified client http data has arrived. + * The user code may choose to delay calling it to consume the data, for example + * waiting until an onward connection is writeable. + * + * For non-chunked connections, up to len bytes of buf are filled with the + * received content. len is set to the actual amount filled before return. + * + * For chunked connections, the linear buffer content contains the chunking + * headers and it cannot be passed in one lump. Instead, this function will + * call back LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ with in pointing to the + * chunk start and len set to the chunk length. There will be as many calls + * as there are chunks or partial chunks in the buffer. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_client_read(struct lws *wsi, char **buf, int *len); + +/** + * lws_http_client_http_response() - get last HTTP response code + * + * \param wsi: client connection + * + * Returns the last server response code, eg, 200 for client http connections. + */ +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send); + +/** + * lws_client_http_body_pending() - control if client connection neeeds to send body + * + * \param wsi: client connection + * \param something_left_to_send: nonzero if need to send more body, 0 (default) + * if nothing more to send + * + * If you will send payload data with your HTTP client connection, eg, for POST, + * when you set the related http headers in + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER callback you should also call + * this API with something_left_to_send nonzero, and call + * lws_callback_on_writable(wsi); + * + * After sending the headers, lws will call your callback with + * LWS_CALLBACK_CLIENT_HTTP_WRITEABLE reason when writable. You can send the + * next part of the http body payload, calling lws_callback_on_writable(wsi); + * if there is more to come, or lws_client_http_body_pending(wsi, 0); to + * let lws know the last part is sent and the connection can move on. + */ + +///@} + +/** \defgroup service Built-in service loop entry + * + * ##Built-in service loop entry + * + * If you're not using libev / libuv, these apis are needed to enter the poll() + * wait in lws and service any connections with pending events. + */ +///@{ + +/** + * lws_service() - Service any pending websocket activity + * \param context: Websocket context + * \param timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * + * This function deals with any pending websocket traffic, for three + * kinds of event. It handles these events on both server and client + * types of connection the same. + * + * 1) Accept new connections to our context's server + * + * 2) Call the receive callback for incoming frame data received by + * server or client connections. + * + * You need to call this service function periodically to all the above + * functions to happen; if your application is single-threaded you can + * just call it in your main event loop. + * + * Alternatively you can fork a new process that asynchronously handles + * calling this service in a loop. In that case you are happy if this + * call blocks your thread until it needs to take care of something and + * would call it with a large nonzero timeout. Your loop then takes no + * CPU while there is nothing happening. + * + * If you are calling it in a single-threaded app, you don't want it to + * wait around blocking other things in your loop from happening, so you + * would call it with a timeout_ms of 0, so it returns immediately if + * nothing is pending, or as soon as it services whatever was pending. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service(struct lws_context *context, int timeout_ms); + +/** + * lws_service_tsi() - Service any pending websocket activity + * + * \param context: Websocket context + * \param timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * \param tsi: Thread service index, starting at 0 + * + * Same as lws_service(), but for a specific thread service index. Only needed + * if you are spawning multiple service threads. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi); + +/** + * lws_cancel_service_pt() - Cancel servicing of pending socket activity + * on one thread + * \param wsi: Cancel service on the thread this wsi is serviced by + * + * This function lets a call to lws_service() waiting for a timeout + * immediately return. + * + * It works by creating a phony event and then swallowing it silently. + * + * The reason it may be needed is when waiting in poll(), changes to + * the event masks are ignored by the OS until poll() is reentered. This + * lets you halt the poll() wait and make the reentry happen immediately + * instead of having the wait out the rest of the poll timeout. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cancel_service_pt(struct lws *wsi); + +/** + * lws_cancel_service() - Cancel wait for new pending socket activity + * \param context: Websocket context + * + * This function let a call to lws_service() waiting for a timeout + * immediately return. + * + * What it basically does is provide a fake event that will be swallowed, + * so the wait in poll() is ended. That's useful because poll() doesn't + * attend to changes in POLLIN/OUT/ERR until it re-enters the wait. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cancel_service(struct lws_context *context); + +/** + * lws_service_fd() - Service polled socket with something waiting + * \param context: Websocket context + * \param pollfd: The pollfd entry describing the socket fd and which events + * happened, or NULL to tell lws to do only timeout servicing. + * + * This function takes a pollfd that has POLLIN or POLLOUT activity and + * services it according to the state of the associated + * struct lws. + * + * The one call deals with all "service" that might happen on a socket + * including listen accepts, http files as well as websocket protocol. + * + * If a pollfd says it has something, you can just pass it to + * lws_service_fd() whether it is a socket handled by lws or not. + * If it sees it is a lws socket, the traffic will be handled and + * pollfd->revents will be zeroed now. + * + * If the socket is foreign to lws, it leaves revents alone. So you can + * see if you should service yourself by checking the pollfd revents + * after letting lws try to service it. + * + * You should also call this with pollfd = NULL to just allow the + * once-per-second global timeout checks; if less than a second since the last + * check it returns immediately then. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); + +/** + * lws_service_fd_tsi() - Service polled socket in specific service thread + * \param context: Websocket context + * \param pollfd: The pollfd entry describing the socket fd and which events + * happened. + * \param tsi: thread service index + * + * Same as lws_service_fd() but used with multiple service threads + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, + int tsi); + +/** + * lws_service_adjust_timeout() - Check for any connection needing forced service + * \param context: Websocket context + * \param timeout_ms: The original poll timeout value. You can just set this + * to 1 if you don't really have a poll timeout. + * \param tsi: thread service index + * + * Under some conditions connections may need service even though there is no + * pending network action on them, this is "forced service". For default + * poll() and libuv / libev, the library takes care of calling this and + * dealing with it for you. But for external poll() integration, you need + * access to the apis. + * + * If anybody needs "forced service", returned timeout is zero. In that case, + * you can call lws_service_tsi() with a timeout of -1 to only service + * guys who need forced service. + */ +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi); + +/* Backwards compatibility */ +#define lws_plat_service_tsi lws_service_tsi + +LWS_VISIBLE LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +///@} + +/*! \defgroup http HTTP + + Modules related to handling HTTP +*/ +//@{ + +/*! \defgroup httpft HTTP File transfer + * \ingroup http + + APIs for sending local files in response to HTTP requests +*/ +//@{ + +/** + * lws_get_mimetype() - Determine mimetype to use from filename + * + * \param file: filename + * \param m: NULL, or mount context + * + * This uses a canned list of known filetypes first, if no match and m is + * non-NULL, then tries a list of per-mount file suffix to mimtype mappings. + * + * Returns either NULL or a pointer to the mimetype matching the file. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m); + +/** + * lws_serve_http_file() - Send a file back to the client using http + * \param wsi: Websocket instance (available from user callback) + * \param file: The file to issue over http + * \param content_type: The http content type, eg, text/html + * \param other_headers: NULL or pointer to header string + * \param other_headers_len: length of the other headers if non-NULL + * + * This function is intended to be called from the callback in response + * to http requests from the client. It allows the callback to issue + * local files down the http link in a single step. + * + * Returning <0 indicates error and the wsi should be closed. Returning + * >0 indicates the file was completely sent and + * lws_http_transaction_completed() called on the wsi (and close if != 0) + * ==0 indicates the file transfer is started and needs more service later, + * the wsi should be left alone. + */ +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len); + +LWS_VISIBLE LWS_EXTERN int +lws_serve_http_file_fragment(struct lws *wsi); +//@} + + +enum http_status { + HTTP_STATUS_CONTINUE = 100, + + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_PARTIAL_CONTENT = 206, + + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED, + HTTP_STATUS_PAYMENT_REQUIRED, + HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_NOT_FOUND, + HTTP_STATUS_METHOD_NOT_ALLOWED, + HTTP_STATUS_NOT_ACCEPTABLE, + HTTP_STATUS_PROXY_AUTH_REQUIRED, + HTTP_STATUS_REQUEST_TIMEOUT, + HTTP_STATUS_CONFLICT, + HTTP_STATUS_GONE, + HTTP_STATUS_LENGTH_REQUIRED, + HTTP_STATUS_PRECONDITION_FAILED, + HTTP_STATUS_REQ_ENTITY_TOO_LARGE, + HTTP_STATUS_REQ_URI_TOO_LONG, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, + HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, + HTTP_STATUS_EXPECTATION_FAILED, + + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED, + HTTP_STATUS_BAD_GATEWAY, + HTTP_STATUS_SERVICE_UNAVAILABLE, + HTTP_STATUS_GATEWAY_TIMEOUT, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, +}; +/*! \defgroup html-chunked-substitution HTML Chunked Substitution + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +struct lws_process_html_args { + char *p; /**< pointer to the buffer containing the data */ + int len; /**< length of the original data at p */ + int max_len; /**< maximum length we can grow the data to */ + int final; /**< set if this is the last chunk of the file */ +}; + +typedef const char *(*lws_process_html_state_cb)(void *data, int index); + +struct lws_process_html_state { + char *start; /**< pointer to start of match */ + char swallow[16]; /**< matched character buffer */ + int pos; /**< position in match */ + void *data; /**< opaque pointer */ + const char * const *vars; /**< list of variable names */ + int count_vars; /**< count of variable names */ + + lws_process_html_state_cb replace; /**< called on match to perform substitution */ +}; + +/*! lws_chunked_html_process() - generic chunked substitution + * \param args: buffer to process using chunked encoding + * \param s: current processing state + */ +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s); +//@} + +/** \defgroup HTTP-headers-read HTTP headers: read + * \ingroup http + * + * ##HTTP header releated functions + * + * In lws the client http headers are temporarily stored in a pool, only for the + * duration of the http part of the handshake. It's because in most cases, + * the header content is ignored for the whole rest of the connection lifetime + * and would then just be taking up space needlessly. + * + * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time + * the http headers are still allocated, you can use these apis then to + * look at and copy out interesting header content (cookies, etc) + * + * Notice that the header total length reported does not include a terminating + * '\0', however you must allocate for it when using the _copy apis. So the + * length reported for a header containing "123" is 3, but you must provide + * a buffer of length 4 so that "123\0" may be copied into it, or the copy + * will fail with a nonzero return code. + * + * In the special case of URL arguments, like ?x=1&y=2, the arguments are + * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it + * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total + * length to confirm the method. + * + * For URL arguments, each argument is stored urldecoded in a "fragment", so + * you can use the fragment-aware api lws_hdr_copy_fragment() to access each + * argument in turn: the fragments contain urldecoded strings like x=1 or y=2. + * + * As a convenience, lws has an api that will find the fragment with a + * given name= part, lws_get_urlarg_by_name(). + */ +///@{ + +/** struct lws_tokens + * you need these to look at headers that have been parsed if using the + * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum + * list below is absent, .token = NULL and token_len = 0. Otherwise .token + * points to .token_len chars containing that header content. + */ +struct lws_tokens { + char *token; /**< pointer to start of the token */ + int token_len; /**< length of the token's value */ +}; + +/* enum lws_token_indexes + * these have to be kept in sync with lextable.h / minilex.c + * + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_token_indexes { + WSI_TOKEN_GET_URI = 0, + WSI_TOKEN_POST_URI = 1, + WSI_TOKEN_OPTIONS_URI = 2, + WSI_TOKEN_HOST = 3, + WSI_TOKEN_CONNECTION = 4, + WSI_TOKEN_UPGRADE = 5, + WSI_TOKEN_ORIGIN = 6, + WSI_TOKEN_DRAFT = 7, + WSI_TOKEN_CHALLENGE = 8, + WSI_TOKEN_EXTENSIONS = 9, + WSI_TOKEN_KEY1 = 10, + WSI_TOKEN_KEY2 = 11, + WSI_TOKEN_PROTOCOL = 12, + WSI_TOKEN_ACCEPT = 13, + WSI_TOKEN_NONCE = 14, + WSI_TOKEN_HTTP = 15, + WSI_TOKEN_HTTP2_SETTINGS = 16, + WSI_TOKEN_HTTP_ACCEPT = 17, + WSI_TOKEN_HTTP_AC_REQUEST_HEADERS = 18, + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE = 19, + WSI_TOKEN_HTTP_IF_NONE_MATCH = 20, + WSI_TOKEN_HTTP_ACCEPT_ENCODING = 21, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE = 22, + WSI_TOKEN_HTTP_PRAGMA = 23, + WSI_TOKEN_HTTP_CACHE_CONTROL = 24, + WSI_TOKEN_HTTP_AUTHORIZATION = 25, + WSI_TOKEN_HTTP_COOKIE = 26, + WSI_TOKEN_HTTP_CONTENT_LENGTH = 27, + WSI_TOKEN_HTTP_CONTENT_TYPE = 28, + WSI_TOKEN_HTTP_DATE = 29, + WSI_TOKEN_HTTP_RANGE = 30, + WSI_TOKEN_HTTP_REFERER = 31, + WSI_TOKEN_KEY = 32, + WSI_TOKEN_VERSION = 33, + WSI_TOKEN_SWORIGIN = 34, + + WSI_TOKEN_HTTP_COLON_AUTHORITY = 35, + WSI_TOKEN_HTTP_COLON_METHOD = 36, + WSI_TOKEN_HTTP_COLON_PATH = 37, + WSI_TOKEN_HTTP_COLON_SCHEME = 38, + WSI_TOKEN_HTTP_COLON_STATUS = 39, + + WSI_TOKEN_HTTP_ACCEPT_CHARSET = 40, + WSI_TOKEN_HTTP_ACCEPT_RANGES = 41, + WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN = 42, + WSI_TOKEN_HTTP_AGE = 43, + WSI_TOKEN_HTTP_ALLOW = 44, + WSI_TOKEN_HTTP_CONTENT_DISPOSITION = 45, + WSI_TOKEN_HTTP_CONTENT_ENCODING = 46, + WSI_TOKEN_HTTP_CONTENT_LANGUAGE = 47, + WSI_TOKEN_HTTP_CONTENT_LOCATION = 48, + WSI_TOKEN_HTTP_CONTENT_RANGE = 49, + WSI_TOKEN_HTTP_ETAG = 50, + WSI_TOKEN_HTTP_EXPECT = 51, + WSI_TOKEN_HTTP_EXPIRES = 52, + WSI_TOKEN_HTTP_FROM = 53, + WSI_TOKEN_HTTP_IF_MATCH = 54, + WSI_TOKEN_HTTP_IF_RANGE = 55, + WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE = 56, + WSI_TOKEN_HTTP_LAST_MODIFIED = 57, + WSI_TOKEN_HTTP_LINK = 58, + WSI_TOKEN_HTTP_LOCATION = 59, + WSI_TOKEN_HTTP_MAX_FORWARDS = 60, + WSI_TOKEN_HTTP_PROXY_AUTHENTICATE = 61, + WSI_TOKEN_HTTP_PROXY_AUTHORIZATION = 62, + WSI_TOKEN_HTTP_REFRESH = 63, + WSI_TOKEN_HTTP_RETRY_AFTER = 64, + WSI_TOKEN_HTTP_SERVER = 65, + WSI_TOKEN_HTTP_SET_COOKIE = 66, + WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY = 67, + WSI_TOKEN_HTTP_TRANSFER_ENCODING = 68, + WSI_TOKEN_HTTP_USER_AGENT = 69, + WSI_TOKEN_HTTP_VARY = 70, + WSI_TOKEN_HTTP_VIA = 71, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE = 72, + + WSI_TOKEN_PATCH_URI = 73, + WSI_TOKEN_PUT_URI = 74, + WSI_TOKEN_DELETE_URI = 75, + + WSI_TOKEN_HTTP_URI_ARGS = 76, + WSI_TOKEN_PROXY = 77, + WSI_TOKEN_HTTP_X_REAL_IP = 78, + WSI_TOKEN_HTTP1_0 = 79, + WSI_TOKEN_X_FORWARDED_FOR = 80, + WSI_TOKEN_CONNECT = 81, + WSI_TOKEN_HEAD_URI = 82, + WSI_TOKEN_TE = 83, + /****** add new things just above ---^ ******/ + + /* use token storage to stash these internally, not for + * user use */ + + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + _WSI_TOKEN_CLIENT_URI, + _WSI_TOKEN_CLIENT_HOST, + _WSI_TOKEN_CLIENT_ORIGIN, + _WSI_TOKEN_CLIENT_METHOD, + _WSI_TOKEN_CLIENT_IFACE, + + /* always last real token index*/ + WSI_TOKEN_COUNT, + + /* parser state additions, no storage associated */ + WSI_TOKEN_NAME_PART, + WSI_TOKEN_SKIPPING, + WSI_TOKEN_SKIPPING_SAW_CR, + WSI_PARSING_COMPLETE, + WSI_INIT_TOKEN_MUXURL, +}; + +struct lws_token_limits { + unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */ +}; + +/** + * lws_token_to_string() - returns a textual representation of a hdr token index + * + * \param token: token index + */ +LWS_VISIBLE LWS_EXTERN const unsigned char * +lws_token_to_string(enum lws_token_indexes token); + +/** + * lws_hdr_total_length: report length of all fragments of a header totalled up + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h); + +/** + * lws_hdr_fragment_length: report length of a single fragment of a header + * The returned length does not include the space for a + * terminating '\0' + * + * \param wsi: websocket connection + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to get the length of + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx); + +/** + * lws_hdr_copy() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * + * copies the whole, aggregated header, even if it was delivered in + * several actual headers piece by piece + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h); + +/** + * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer + * The buffer length len must include space for an additional + * terminating '\0', or it will fail returning -1. + * If the requested fragment index is not present, it fails + * returning -1. + * + * \param wsi: websocket connection + * \param dest: destination buffer + * \param len: length of destination buffer + * \param h: which header index we are interested in + * \param frag_idx: which fragment of h we want to copy + * + * Normally this is only useful + * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS + * fragment 0 will contain "x=1" and fragment 1 "y=2" + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len, + enum lws_token_indexes h, int frag_idx); + +/** + * lws_get_urlarg_by_name() - return pointer to arg value if present + * \param wsi: the connection to check + * \param name: the arg name, like "token=" + * \param buf: the buffer to receive the urlarg (including the name= part) + * \param len: the length of the buffer to receive the urlarg + * + * Returns NULL if not found or a pointer inside buf to just after the + * name= part. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len); +///@} + +/*! \defgroup HTTP-headers-create HTTP headers: create + * + * ## HTTP headers: Create + * + * These apis allow you to create HTTP response headers in a way compatible with + * both HTTP/1.x and HTTP/2. + * + * They each append to a buffer taking care about the buffer end, which is + * passed in as a pointer. When data is written to the buffer, the current + * position p is updated accordingly. + * + * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space + * and fail with nonzero return. + */ +///@{ + +#define LWSAHH_CODE_MASK ((1 << 16) - 1) +#define LWSAHH_FLAG_NO_SERVER_NAME (1 << 30) + +/** + * lws_add_http_header_status() - add the HTTP response status code + * + * \param wsi: the connection to check + * \param code: an HTTP code like 200, 404 etc (see enum http_status) + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Adds the initial response code, so should be called first. + * + * Code may additionally take OR'd flags: + * + * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +/** + * lws_add_http_header_by_name() - append named header and value + * + * \param wsi: the connection to check + * \param name: the hdr name, like "my-header" + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name: value to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +/** + * lws_add_http_header_by_token() - append given header and value + * + * \param wsi: the connection to check + * \param token: the token index for the hdr + * \param value: the value after the = for this header + * \param length: the length of the value + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends name=value to the headers, but is able to take advantage of better + * HTTP/2 coding mechanisms where possible. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +/** + * lws_add_http_header_content_length() - append content-length helper + * + * \param wsi: the connection to check + * \param content_length: the content length to use + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Appends content-length: content_length to the headers + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_add_http_header_content_length(struct lws *wsi, + lws_filepos_t content_length, + unsigned char **p, unsigned char *end); +/** + * lws_finalize_http_header() - terminate header block + * + * \param wsi: the connection to check + * \param p: pointer to current position in buffer pointer + * \param end: pointer to end of buffer + * + * Indicates no more headers will be added + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_finalize_http_header(struct lws *wsi, unsigned char **p, + unsigned char *end); +///@} + +/** \defgroup form-parsing Form Parsing + * \ingroup http + * ##POSTed form parsing functions + * + * These lws_spa (stateful post arguments) apis let you parse and urldecode + * POSTed form arguments, both using simple urlencoded and multipart transfer + * encoding. + * + * It's capable of handling file uploads as well a named input parsing, + * and the apis are the same for both form upload styles. + * + * You feed it a list of parameter names and it creates pointers to the + * urldecoded arguments: file upload parameters pass the file data in chunks to + * a user-supplied callback as they come. + * + * Since it's stateful, it handles the incoming data needing more than one + * POST_BODY callback and has no limit on uploaded file size. + */ +///@{ + +/** enum lws_spa_fileupload_states */ +enum lws_spa_fileupload_states { + LWS_UFS_CONTENT, + /**< a chunk of file content has arrived */ + LWS_UFS_FINAL_CONTENT, + /**< the last chunk (possibly zero length) of file content has arrived */ + LWS_UFS_OPEN + /**< a new file is starting to arrive */ +}; + +/** + * lws_spa_fileupload_cb() - callback to receive file upload data + * + * \param data: opt_data pointer set in lws_spa_create + * \param name: name of the form field being uploaded + * \param filename: original filename from client + * \param buf: start of data to receive + * \param len: length of data to receive + * \param state: information about how this call relates to file + * + * Notice name and filename shouldn't be trusted, as they are passed from + * HTTP provided by the client. + */ +typedef int (*lws_spa_fileupload_cb)(void *data, const char *name, + const char *filename, char *buf, int len, + enum lws_spa_fileupload_states state); + +/** struct lws_spa - opaque urldecode parser capable of handling multipart + * and file uploads */ +struct lws_spa; + +/** + * lws_spa_create() - create urldecode parser + * + * \param wsi: lws connection (used to find Content Type) + * \param param_names: array of form parameter names, like "username" + * \param count_params: count of param_names + * \param max_storage: total amount of form parameter values we can store + * \param opt_cb: NULL, or callback to receive file upload data. + * \param opt_data: NULL, or user pointer provided to opt_cb. + * + * Creates a urldecode parser and initializes it. + * + * opt_cb can be NULL if you just want normal name=value parsing, however + * if one or more entries in your form are bulk data (file transfer), you + * can provide this callback and filter on the name callback parameter to + * treat that urldecoded data separately. The callback should return -1 + * in case of fatal error, and 0 if OK. + */ +LWS_VISIBLE LWS_EXTERN struct lws_spa * +lws_spa_create(struct lws *wsi, const char * const *param_names, + int count_params, int max_storage, lws_spa_fileupload_cb opt_cb, + void *opt_data); + +/** + * lws_spa_process() - parses a chunk of input data + * + * \param spa: the parser object previously created + * \param in: incoming, urlencoded data + * \param len: count of bytes valid at \param in + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_process(struct lws_spa *spa, const char *in, int len); + +/** + * lws_spa_finalize() - indicate incoming data completed + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_finalize(struct lws_spa *spa); + +/** + * lws_spa_get_length() - return length of parameter value + * + * \param spa: the parser object previously created + * \param n: parameter ordinal to return length of value for + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_get_length(struct lws_spa *spa, int n); + +/** + * lws_spa_get_string() - return pointer to parameter value + * \param spa: the parser object previously created + * \param n: parameter ordinal to return pointer to value for + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_spa_get_string(struct lws_spa *spa, int n); + +/** + * lws_spa_destroy() - destroy parser object + * + * \param spa: the parser object previously created + */ +LWS_VISIBLE LWS_EXTERN int +lws_spa_destroy(struct lws_spa *spa); +///@} + +/*! \defgroup urlendec Urlencode and Urldecode + * \ingroup http + * + * ##HTML chunked Substitution + * + * APIs for receiving chunks of text, replacing a set of variable names via + * a callback, and then prepending and appending HTML chunked encoding + * headers. + */ +//@{ + +/** + * lws_urlencode() - like strncpy but with urlencoding + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because urlencoding expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_urlencode(char *escaped, const char *string, int len); + +/* + * URLDECODE 1 / 2 + * + * This simple urldecode only operates until the first '\0' and requires the + * data to exist all at once + */ +/** + * lws_urldecode() - like strncpy but with urldecoding + * + * \param string: output buffer + * \param escaped: input buffer ('\0' terminated) + * \param len: output buffer max length + * + * This is only useful for '\0' terminated strings + * + * Since urldecoding only shrinks the output string, it is possible to + * do it in-place, ie, string == escaped + * + * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars + * where hex required, etc) + */ +LWS_VISIBLE LWS_EXTERN int +lws_urldecode(char *string, const char *escaped, int len); +///@} +/** + * lws_return_http_status() - Return simple http status + * \param wsi: Websocket instance (available from user callback) + * \param code: Status index, eg, 404 + * \param html_body: User-readable HTML description < 1KB, or NULL + * + * Helper to report HTTP errors back to the client cleanly and + * consistently + */ +LWS_VISIBLE LWS_EXTERN int +lws_return_http_status(struct lws *wsi, unsigned int code, + const char *html_body); + +/** + * lws_http_redirect() - write http redirect into buffer + * + * \param wsi: websocket connection + * \param code: HTTP response code (eg, 301) + * \param loc: where to redirect to + * \param len: length of loc + * \param p: pointer current position in buffer (updated as we write) + * \param end: pointer to end of buffer + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, + unsigned char **p, unsigned char *end); + +/** + * lws_http_transaction_completed() - wait for new http transaction or close + * \param wsi: websocket connection + * + * Returns 1 if the HTTP connection must close now + * Returns 0 and resets connection to wait for new HTTP header / + * transaction if possible + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi); +///@} + +/*! \defgroup pur Sanitize / purify SQL and JSON helpers + * + * ##Sanitize / purify SQL and JSON helpers + * + * APIs for escaping untrusted JSON and SQL safely before use + */ +//@{ + +/** + * lws_sql_purify() - like strncpy but with escaping for sql quotes + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_sql_purify(char *escaped, const char *string, int len); + +/** + * lws_json_purify() - like strncpy but with escaping for json chars + * + * \param escaped: output buffer + * \param string: input buffer ('/0' terminated) + * \param len: output buffer max length + * + * Because escaping expands the output string, it's not + * possible to do it in-place, ie, with escaped == string + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_json_purify(char *escaped, const char *string, int len); +///@} + +/*! \defgroup ev libev helpers + * + * ##libev helpers + * + * APIs specific to libev event loop itegration + */ +///@{ + +#ifdef LWS_WITH_LIBEV +typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); +#endif /* LWS_WITH_LIBEV */ + +///@} + +/*! \defgroup uv libuv helpers + * + * ##libuv helpers + * + * APIs specific to libuv event loop itegration + */ +///@{ +#ifdef LWS_WITH_LIBUV +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + uv_signal_cb cb); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN void +lws_libuv_stop_without_kill(const struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE LWS_EXTERN uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_uv_sigint_cb(uv_signal_t *watcher, int signum); + +LWS_VISIBLE LWS_EXTERN void +lws_close_all_handles_in_loop(uv_loop_t *loop); +#endif /* LWS_WITH_LIBUV */ +///@} + +/*! \defgroup event libevent helpers + * + * ##libevent helpers + * + * APIs specific to libevent event loop itegration + */ +///@{ + +#ifdef LWS_WITH_LIBEVENT +typedef void (lws_event_signal_cb_t) (evutil_socket_t sock_fd, short revents, + void *ctx); + +LWS_VISIBLE LWS_EXTERN int +lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, + lws_event_signal_cb_t cb); + +LWS_VISIBLE LWS_EXTERN int +lws_event_initloop(struct lws_context *context, struct event_base *loop, + int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, + void *ctx); +#endif /* LWS_WITH_LIBEVENT */ + +///@} + +/*! \defgroup timeout Connection timeouts + + APIs related to setting connection timeouts +*/ +//@{ + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum pending_timeout { + NO_PENDING_TIMEOUT = 0, + PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1, + PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3, + PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4, + PENDING_TIMEOUT_AWAITING_PING = 5, + PENDING_TIMEOUT_CLOSE_ACK = 6, + PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE = 7, + PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8, + PENDING_TIMEOUT_SSL_ACCEPT = 9, + PENDING_TIMEOUT_HTTP_CONTENT = 10, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11, + PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12, + PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13, + PENDING_TIMEOUT_CGI = 14, + PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15, + PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, + PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, + PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, + PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, + PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, + PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22, + PENDING_TIMEOUT_KILLED_BY_PARENT = 23, + PENDING_TIMEOUT_CLOSE_SEND = 24, + PENDING_TIMEOUT_HOLDING_AH = 25, + + /****** add new things just above ---^ ******/ + + PENDING_TIMEOUT_USER_REASON_BASE = 1000 +}; + +#define LWS_TO_KILL_ASYNC -1 +/**< If LWS_TO_KILL_ASYNC is given as the timeout sec in a lws_set_timeout() + * call, then the connection is marked to be killed at the next timeout + * check. This is how you should force-close the wsi being serviced if + * you are doing it outside the callback (where you should close by nonzero + * return). + */ +#define LWS_TO_KILL_SYNC -2 +/**< If LWS_TO_KILL_SYNC is given as the timeout sec in a lws_set_timeout() + * call, then the connection is closed before returning (which may delete + * the wsi). This should only be used where the wsi being closed is not the + * wsi currently being serviced. + */ +/** + * lws_set_timeout() - marks the wsi as subject to a timeout + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + * \param reason: timeout reason + * \param secs: how many seconds. You may set to LWS_TO_KILL_ASYNC to + * force the connection to timeout at the next opportunity, or + * LWS_TO_KILL_SYNC to close it synchronously if you know the + * wsi is not the one currently being serviced. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); +///@} + +/*! \defgroup sending-data Sending data + + APIs related to writing data on a connection +*/ +//@{ +#if !defined(LWS_SIZEOFPTR) +#define LWS_SIZEOFPTR (sizeof (void *)) +#endif +#if !defined(u_int64_t) +#define u_int64_t unsigned long long +#endif + +#if defined(__x86_64__) +#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ +#else +#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */ +#endif +#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \ + ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n)) +/* last 2 is for lws-meta */ +#define LWS_PRE _LWS_PAD(4 + 10 + 2) +/* used prior to 1.7 and retained for backward compatibility */ +#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE +#define LWS_SEND_BUFFER_POST_PADDING 0 + +/* + * NOTE: These public enums are part of the abi. If you want to add one, + * add it at where specified so existing users are unaffected. + */ +enum lws_write_protocol { + LWS_WRITE_TEXT = 0, + /**< Send a ws TEXT message,the pointer must have LWS_PRE valid + * memory behind it. The receiver expects only valid utf-8 in the + * payload */ + LWS_WRITE_BINARY = 1, + /**< Send a ws BINARY message, the pointer must have LWS_PRE valid + * memory behind it. Any sequence of bytes is valid */ + LWS_WRITE_CONTINUATION = 2, + /**< Continue a previous ws message, the pointer must have LWS_PRE valid + * memory behind it */ + LWS_WRITE_HTTP = 3, + /**< Send HTTP content */ + + /* LWS_WRITE_CLOSE is handled by lws_close_reason() */ + LWS_WRITE_PING = 5, + LWS_WRITE_PONG = 6, + + /* Same as write_http but we know this write ends the transaction */ + LWS_WRITE_HTTP_FINAL = 7, + + /* HTTP2 */ + + LWS_WRITE_HTTP_HEADERS = 8, + /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP + * payload differently, http 1.x links also handle this correctly. so + * to be compatible with both in the future,header response part should + * be sent using this regardless of http version expected) + */ + LWS_WRITE_HTTP_HEADERS_CONTINUATION = 9, + /**< Continuation of http/2 headers + */ + + /****** add new things just above ---^ ******/ + + /* flags */ + + LWS_WRITE_NO_FIN = 0x40, + /**< This part of the message is not the end of the message */ + + LWS_WRITE_H2_STREAM_END = 0x80, + /**< Flag indicates this packet should go out with STREAM_END if h2 + * STREAM_END is allowed on DATA or HEADERS. + */ + + LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80 + /**< client packet payload goes out on wire unmunged + * only useful for security tests since normal servers cannot + * decode the content if used */ +}; + +/* used with LWS_CALLBACK_CHILD_WRITE_VIA_PARENT */ + +struct lws_write_passthru { + struct lws *wsi; + unsigned char *buf; + size_t len; + enum lws_write_protocol wp; +}; + + +/** + * lws_write() - Apply protocol then write data to client + * \param wsi: Websocket instance (available from user callback) + * \param buf: The data to send. For data being sent on a websocket + * connection (ie, not default http), this buffer MUST have + * LWS_PRE bytes valid BEFORE the pointer. + * This is so the protocol header data can be added in-situ. + * \param len: Count of the data bytes in the payload starting from buf + * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one + * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate + * data on a websockets connection. Remember to allow the extra + * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT + * are used. + * + * This function provides the way to issue data back to the client + * for both http and websocket protocols. + * + * IMPORTANT NOTICE! + * + * When sending with websocket protocol + * + * LWS_WRITE_TEXT, + * LWS_WRITE_BINARY, + * LWS_WRITE_CONTINUATION, + * LWS_WRITE_PING, + * LWS_WRITE_PONG + * + * the send buffer has to have LWS_PRE bytes valid BEFORE + * the buffer pointer you pass to lws_write(). + * + * This allows us to add protocol info before and after the data, and send as + * one packet on the network without payload copying, for maximum efficiency. + * + * So for example you need this kind of code to use lws_write with a + * 128-byte payload + * + * char buf[LWS_PRE + 128]; + * + * // fill your part of the buffer... for example here it's all zeros + * memset(&buf[LWS_PRE], 0, 128); + * + * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT); + * + * When sending HTTP, with + * + * LWS_WRITE_HTTP, + * LWS_WRITE_HTTP_HEADERS + * LWS_WRITE_HTTP_FINAL + * + * there is no protocol data prepended, and don't need to take care about the + * LWS_PRE bytes valid before the buffer pointer. + * + * LWS_PRE is at least the frame nonce + 2 header + 8 length + * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off. + * The example apps no longer use it. + * + * Pad LWS_PRE to the CPU word size, so that word references + * to the address immediately after the padding won't cause an unaligned access + * error. Sometimes for performance reasons the recommended padding is even + * larger than sizeof(void *). + * + * In the case of sending using websocket protocol, be sure to allocate + * valid storage before and after buf as explained above. This scheme + * allows maximum efficiency of sending data and protocol in a single + * packet while not burdening the user code with any protocol knowledge. + * + * Return may be -1 for a fatal error needing connection close, or the + * number of bytes sent. + * + * Truncated Writes + * ================ + * + * The OS may not accept everything you asked to write on the connection. + * + * Posix defines POLLOUT indication from poll() to show that the connection + * will accept more write data, but it doesn't specifiy how much. It may just + * accept one byte of whatever you wanted to send. + * + * LWS will buffer the remainder automatically, and send it out autonomously. + * + * During that time, WRITABLE callbacks will be suppressed. + * + * This is to handle corner cases where unexpectedly the OS refuses what we + * usually expect it to accept. You should try to send in chunks that are + * almost always accepted in order to avoid the inefficiency of the buffering. + */ +LWS_VISIBLE LWS_EXTERN int +lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol protocol); + +/* helper for case where buffer may be const */ +#define lws_write_http(wsi, buf, len) \ + lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) +///@} + +/** \defgroup callback-when-writeable Callback when writeable + * + * ##Callback When Writeable + * + * lws can only write data on a connection when it is able to accept more + * data without blocking. + * + * So a basic requirement is we should only use the lws_write() apis when the + * connection we want to write on says that he can accept more data. + * + * When lws cannot complete your send at the time, it will buffer the data + * and send it in the background, suppressing any further WRITEABLE callbacks + * on that connection until it completes. So it is important to write new + * things in a new writeable callback. + * + * These apis reflect the various ways we can indicate we would like to be + * called back when one or more connections is writeable. + */ +///@{ + +/** + * lws_callback_on_writable() - Request a callback when this socket + * becomes able to be written to without + * blocking + * + * \param wsi: Websocket connection instance to get callback for + * + * - Which: only this wsi + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable(struct lws *wsi); + +/** + * lws_callback_on_writable_all_protocol() - Request a callback for all + * connections using the given protocol when it + * becomes possible to write to each socket without + * blocking in turn. + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on ANY VHOST + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_callback_on_writable_all_protocol_vhost() - Request a callback for + * all connections on same vhost using the given protocol + * when it becomes possible to write to each socket without + * blocking in turn. + * + * \param vhost: Only consider connections on this lws_vhost + * \param protocol: Protocol whose connections will get callbacks + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: when the individual connection becomes writeable + * - What: LWS_CALLBACK_*_WRITEABLE + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol); + +/** + * lws_callback_all_protocol() - Callback all connections using + * the given protocol with the given reason + * + * \param context: lws_context + * \param protocol: Protocol whose connections will get callbacks + * \param reason: Callback reason index + * + * - Which: connections using this protocol on ALL VHOSTS + * - When: before returning + * - What: reason + * + * This isn't normally what you want... normally any update of connection- + * specific information can wait until a network-related callback like rx, + * writable, or close. + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol(struct lws_context *context, + const struct lws_protocols *protocol, int reason); + +/** + * lws_callback_all_protocol_vhost() - Callback all connections using + * the given protocol with the given reason. This is + * deprecated since v2.4: use lws_callback_all_protocol_vhost_args + * + * \param vh: Vhost whose connections will get callbacks + * \param protocol: Which protocol to match. NULL means all. + * \param reason: Callback reason index + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_all_protocol_vhost(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason) +LWS_WARN_DEPRECATED; + +/** + * lws_callback_all_protocol_vhost_args() - Callback all connections using + * the given protocol with the given reason and args + * + * \param vh: Vhost whose connections will get callbacks + * \param protocol: Which protocol to match. NULL means all. + * \param reason: Callback reason index + * \param argp: Callback "in" parameter + * \param len: Callback "len" parameter + * + * - Which: connections using this protocol on GIVEN VHOST ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE int +lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, + const struct lws_protocols *protocol, int reason, + void *argp, size_t len); + +/** + * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost + * with the given reason + * + * \param wsi: wsi whose vhost will get callbacks + * \param reason: Callback reason index + * \param in: in argument to callback + * \param len: len argument to callback + * + * - Which: connections using this protocol on same VHOST as wsi ONLY + * - When: now + * - What: reason + */ +LWS_VISIBLE LWS_EXTERN int +lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len); + +LWS_VISIBLE LWS_EXTERN int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +/** + * lws_get_socket_fd() - returns the socket file descriptor + * + * You will not need this unless you are doing something special + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_socket_fd(struct lws *wsi); + +/** + * lws_get_peer_write_allowance() - get the amount of data writeable to peer + * if known + * + * \param wsi: Websocket connection instance + * + * if the protocol does not have any guidance, returns -1. Currently only + * http2 connections get send window information from this API. But your code + * should use it so it can work properly with any protocol. + * + * If nonzero return is the amount of payload data the peer or intermediary has + * reported it has buffer space for. That has NO relationship with the amount + * of buffer space your OS can accept on this connection for a write action. + * + * This number represents the maximum you could send to the peer or intermediary + * on this connection right now without the protocol complaining. + * + * lws manages accounting for send window updates and payload writes + * automatically, so this number reflects the situation at the peer or + * intermediary dynamically. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_get_peer_write_allowance(struct lws *wsi); +///@} + +enum { + /* + * Flags for enable and disable rxflow with reason bitmap and with + * backwards-compatible single bool + */ + LWS_RXFLOW_REASON_USER_BOOL = (1 << 0), + LWS_RXFLOW_REASON_HTTP_RXBUFFER = (1 << 6), + LWS_RXFLOW_REASON_H2_PPS_PENDING = (1 << 7), + + LWS_RXFLOW_REASON_APPLIES = (1 << 14), + LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT = (1 << 13), + LWS_RXFLOW_REASON_APPLIES_ENABLE = LWS_RXFLOW_REASON_APPLIES | + LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT, + LWS_RXFLOW_REASON_APPLIES_DISABLE = LWS_RXFLOW_REASON_APPLIES, + LWS_RXFLOW_REASON_FLAG_PROCESS_NOW = (1 << 12), + +}; + +/** + * lws_rx_flow_control() - Enable and disable socket servicing for + * received packets. + * + * If the output side of a server process becomes choked, this allows flow + * control for the input side. + * + * \param wsi: Websocket connection instance to get callback for + * \param enable: 0 = disable read servicing for this connection, 1 = enable + * + * If you need more than one additive reason for rxflow control, you can give + * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of + * b5..b0 set to idicate which bits to enable or disable. If any bits are + * enabled, rx on the connection is suppressed. + * + * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW flag may also be given to force any change + * in rxflowbstatus to benapplied immediately, this should be used when you are + * changing a wsi flow control state from outside a callback on that wsi. + */ +LWS_VISIBLE LWS_EXTERN int +lws_rx_flow_control(struct lws *wsi, int enable); + +/** + * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive + * + * When the user server code realizes it can accept more input, it can + * call this to have the RX flow restriction removed from all connections using + * the given protocol. + * \param context: lws_context + * \param protocol: all connections using this protocol will be allowed to receive + */ +LWS_VISIBLE LWS_EXTERN void +lws_rx_flow_allow_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol); + +/** + * lws_remaining_packet_payload() - Bytes to come before "overall" + * rx packet is complete + * \param wsi: Websocket instance (available from user callback) + * + * This function is intended to be called from the callback if the + * user code is interested in "complete packets" from the client. + * libwebsockets just passes through payload as it comes and issues a buffer + * additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE + * callback handler can use this API to find out if the buffer it has just + * been given is the last piece of a "complete packet" from the client -- + * when that is the case lws_remaining_packet_payload() will return + * 0. + * + * Many protocols won't care becuse their packets are always small. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_remaining_packet_payload(struct lws *wsi); + + +/** \defgroup sock-adopt Socket adoption helpers + * ##Socket adoption helpers + * + * When integrating with an external app with its own event loop, these can + * be used to accept connections from someone else's listening socket. + * + * When using lws own event loop, these are not needed. + */ +///@{ + +/** + * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it + * for the default vhost of context. + * + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); +/** + * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted it + * for vhost + * + * \param vh: lws vhost + * \param accept_fd: fd of already-accepted socket to adopt + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + +typedef enum { + LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */ + LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */ + LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ + LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ + LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO + * if given must be only flag + * wsi put directly into ws mode + */ +} lws_adoption_type; + +typedef union { + lws_sockfd_type sockfd; + lws_filefd_type filefd; +} lws_sock_file_fd_type; + +/* +* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor +* if socket descriptor, should already have been accepted from listen socket +* +* \param vhost: lws vhost +* \param type: OR-ed combinations of lws_adoption_type flags +* \param fd: union with either .sockfd or .filefd set +* \param vh_prot_name: NULL or vh protocol name to bind raw connection to +* \param parent: NULL or struct lws to attach new_wsi to as a child +* +* Either returns new wsi bound to accept_fd, or closes accept_fd and +* returns NULL, having cleaned up any new wsi pieces. +* +* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's +* ready to accept an upgrade to ws or just serve http. +* +* parent may be NULL, if given it should be an existing wsi that will become the +* parent of the new wsi created by this call. +*/ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, + lws_sock_file_fd_type fd, const char *vh_prot_name, + struct lws *parent); + +/** + * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it + * for the default vhost of context. + * \param context: lws context + * \param accept_fd: fd of already-accepted socket to adopt + * \param readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * \param len: The length of the data held at \param readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at \param readbuf first, before reading from + * the socket. + * + * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); +/** + * lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket + * accepted it for vhost. + * \param vhost: lws vhost + * \param accept_fd: fd of already-accepted socket to adopt + * \param readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * \param len: The length of the data held at \param readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at \param readbuf first, before reading from + * the socket. + * + * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); +///@} + +/** \defgroup net Network related helper APIs + * ##Network related helper APIs + * + * These wrap miscellaneous useful network-related functions + */ +///@{ + +/** + * lws_canonical_hostname() - returns this host's hostname + * + * This is typically used by client code to fill in the host parameter + * when making a client connection. You can only call it after the context + * has been created. + * + * \param context: Websocket context + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_canonical_hostname(struct lws_context *context); + +/** + * lws_get_peer_addresses() - Get client address information + * \param wsi: Local struct lws associated with + * \param fd: Connection socket descriptor + * \param name: Buffer to take client address name + * \param name_len: Length of client address name buffer + * \param rip: Buffer to take client address IP dotted quad + * \param rip_len: Length of client address IP buffer + * + * This function fills in name and rip with the name and IP of + * the client connected with socket descriptor fd. Names may be + * truncated if there is not enough room. If either cannot be + * determined, they will be returned as valid zero-length strings. + */ +LWS_VISIBLE LWS_EXTERN void +lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, + int name_len, char *rip, int rip_len); + +/** + * lws_get_peer_simple() - Get client address information without RDNS + * + * \param wsi: Local struct lws associated with + * \param name: Buffer to take client address name + * \param namelen: Length of client address name buffer + * + * This provides a 123.123.123.123 type IP address in name from the + * peer that has connected to wsi + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_peer_simple(struct lws *wsi, char *name, int namelen); +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +/** + * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct + * + * \param ipv6: Allow IPV6 addresses + * \param ifname: Interface name or IP + * \param addr: struct sockaddr_in * to be written + * \param addrlen: Length of addr + * + * This converts a textual network interface name to a sockaddr usable by + * other network functions + */ +LWS_VISIBLE LWS_EXTERN int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen); +///@} +#endif + +/** \defgroup misc Miscellaneous APIs +* ##Miscellaneous APIs +* +* Various APIs outside of other categories +*/ +///@{ + +/** + * lws_start_foreach_ll(): linkedlist iterator helper start + * + * \param type: type of iteration, eg, struct xyz * + * \param it: iterator var name to create + * \param start: start of list + * + * This helper creates an iterator and starts a while (it) { + * loop. The iterator runs through the linked list starting at start and + * ends when it gets a NULL. + * The while loop should be terminated using lws_start_foreach_ll(). + */ +#define lws_start_foreach_ll(type, it, start)\ +{ \ + type it = start; \ + while (it) { + +/** + * lws_end_foreach_ll(): linkedlist iterator helper end + * + * \param it: same iterator var name given when starting + * \param nxt: member name in the iterator pointing to next list element + * + * This helper is the partner for lws_start_foreach_ll() that ends the + * while loop. + */ + +#define lws_end_foreach_ll(it, nxt) \ + it = it->nxt; \ + } \ +} + +/** + * lws_start_foreach_llp(): linkedlist pointer iterator helper start + * + * \param type: type of iteration, eg, struct xyz ** + * \param it: iterator var name to create + * \param start: start of list + * + * This helper creates an iterator and starts a while (it) { + * loop. The iterator runs through the linked list starting at the + * address of start and ends when it gets a NULL. + * The while loop should be terminated using lws_start_foreach_llp(). + * + * This helper variant iterates using a pointer to the previous linked-list + * element. That allows you to easily delete list members by rewriting the + * previous pointer to the element's next pointer. + */ +#define lws_start_foreach_llp(type, it, start)\ +{ \ + type it = &(start); \ + while (*(it)) { + +/** + * lws_end_foreach_llp(): linkedlist pointer iterator helper end + * + * \param it: same iterator var name given when starting + * \param nxt: member name in the iterator pointing to next list element + * + * This helper is the partner for lws_start_foreach_llp() that ends the + * while loop. + */ + +#define lws_end_foreach_llp(it, nxt) \ + it = &(*(it))->nxt; \ + } \ +} + +/** + * lws_snprintf(): snprintf that truncates the returned length too + * + * \param str: destination buffer + * \param size: bytes left in destination buffer + * \param format: format string + * \param ...: args for format + * + * This lets you correctly truncate buffers by concatenating lengths, if you + * reach the limit the reported length doesn't exceed the limit. + */ +LWS_VISIBLE LWS_EXTERN int +lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); + +/** + * lws_get_random(): fill a buffer with platform random data + * + * \param context: the lws context + * \param buf: buffer to fill + * \param len: how much to fill + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_random(struct lws_context *context, void *buf, int len); +/** + * lws_daemonize(): make current process run in the background + * + * \param _lock_path: the filepath to write the lock file + * + * Spawn lws as a background process, taking care of various things + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_daemonize(const char *_lock_path); +/** + * lws_get_library_version(): return string describing the version of lws + * + * On unix, also includes the git describe + */ +LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_get_library_version(void); + +/** + * lws_wsi_user() - get the user data associated with the connection + * \param wsi: lws connection + * + * Not normally needed since it's passed into the callback + */ +LWS_VISIBLE LWS_EXTERN void * +lws_wsi_user(struct lws *wsi); + +/** + * lws_wsi_set_user() - set the user data associated with the client connection + * \param wsi: lws connection + * \param user: user data + * + * By default lws allocates this and it's not legal to externally set it + * yourself. However client connections may have it set externally when the + * connection is created... if so, this api can be used to modify it at + * runtime additionally. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_wsi_user(struct lws *wsi, void *user); + +/** + * lws_parse_uri: cut up prot:/ads:port/path into pieces + * Notice it does so by dropping '\0' into input string + * and the leading / on the path is consequently lost + * + * \param p: incoming uri string.. will get written to + * \param prot: result pointer for protocol part (https://) + * \param ads: result pointer for address part + * \param port: result pointer for port part + * \param path: result pointer for path part + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_uri(char *p, const char **prot, const char **ads, int *port, + const char **path); + +/** + * lws_now_secs(): return seconds since 1970-1-1 + */ +LWS_VISIBLE LWS_EXTERN unsigned long +lws_now_secs(void); + +/** + * lws_get_context - Allow geting lws_context from a Websocket connection + * instance + * + * With this function, users can access context in the callback function. + * Otherwise users may have to declare context as a global variable. + * + * \param wsi: Websocket connection instance + */ +LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT +lws_get_context(const struct lws *wsi); + +/** + * lws_get_count_threads(): how many service threads the context uses + * + * \param context: the lws context + * + * By default this is always 1, if you asked for more than lws can handle it + * will clip the number of threads. So you can use this to find out how many + * threads are actually in use. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_get_count_threads(struct lws_context *context); + +/** + * lws_get_parent() - get parent wsi or NULL + * \param wsi: lws connection + * + * Specialized wsi like cgi stdin/out/err are associated to a parent wsi, + * this allows you to get their parent. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_parent(const struct lws *wsi); + +/** + * lws_get_child() - get child wsi or NULL + * \param wsi: lws connection + * + * Allows you to find a related wsi from the parent wsi. + */ +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_child(const struct lws *wsi); + +/** + * lws_parent_carries_io() - mark wsi as needing to send messages via parent + * + * \param wsi: child lws connection + */ + +LWS_VISIBLE LWS_EXTERN void +lws_set_parent_carries_io(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void * +lws_get_opaque_parent_data(const struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_set_opaque_parent_data(struct lws *wsi, void *data); + +LWS_VISIBLE LWS_EXTERN int +lws_get_child_pending_on_writable(const struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lws_clear_child_pending_on_writable(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_get_close_length(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_get_close_payload(struct lws *wsi); + +/** + * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi + * + * \param wsi: wsi you have + * + * Returns wsi that has the tcp connection (which may be the incoming wsi) + * + * HTTP/1 connections will always return the incoming wsi + * HTTP/2 connections may return a different wsi that has the tcp connection + */ +LWS_VISIBLE LWS_EXTERN +struct lws *lws_get_network_wsi(struct lws *wsi); + +/* + * \deprecated DEPRECATED Note: this is not normally needed as a user api. + * It's provided in case it is + * useful when integrating with other app poll loop service code. + */ +LWS_VISIBLE LWS_EXTERN int +lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len); + +/** + * lws_set_allocator() - custom allocator support + * + * \param realloc + * + * Allows you to replace the allocator (and deallocator) used by lws + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason)); +///@} + +/** \defgroup wsstatus Websocket status APIs + * ##Websocket connection status APIs + * + * These provide information about ws connection or message status + */ +///@{ +/** + * lws_send_pipe_choked() - tests if socket is writable or not + * \param wsi: lws connection + * + * Allows you to check if you can write more on the socket + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_send_pipe_choked(struct lws *wsi); + +/** + * lws_is_final_fragment() - tests if last part of ws message + * + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_final_fragment(struct lws *wsi); + +/** + * lws_is_first_fragment() - tests if first part of ws message + * + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_first_fragment(struct lws *wsi); + +/** + * lws_get_reserved_bits() - access reserved bits of ws frame + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN unsigned char +lws_get_reserved_bits(struct lws *wsi); + +/** + * lws_partial_buffered() - find out if lws buffered the last write + * \param wsi: websocket connection to check + * + * Returns 1 if you cannot use lws_write because the last + * write on this connection is still buffered, and can't be cleared without + * returning to the service loop and waiting for the connection to be + * writeable again. + * + * If you will try to do >1 lws_write call inside a single + * WRITEABLE callback, you must check this after every write and bail if + * set, ask for a new writeable callback and continue writing from there. + * + * This is never set at the start of a writeable callback, but any write + * may set it. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_partial_buffered(struct lws *wsi); + +/** + * lws_frame_is_binary(): true if the current frame was sent in binary mode + * + * \param wsi: the connection we are inquiring about + * + * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if + * it's interested to see if the frame it's dealing with was sent in binary + * mode. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_frame_is_binary(struct lws *wsi); + +/** + * lws_is_ssl() - Find out if connection is using SSL + * \param wsi: websocket connection to check + * + * Returns 0 if the connection is not using SSL, 1 if using SSL and + * using verified cert, and 2 if using SSL but the cert was not + * checked (appears for client wsi told to skip check on connection) + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_ssl(struct lws *wsi); +/** + * lws_is_cgi() - find out if this wsi is running a cgi process + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN int +lws_is_cgi(struct lws *wsi); + +#ifdef LWS_OPENSSL_SUPPORT +/** + * lws_get_ssl() - Return wsi's SSL context structure + * \param wsi: websocket connection + * + * Returns pointer to the SSL library's context structure + */ +LWS_VISIBLE LWS_EXTERN SSL* +lws_get_ssl(struct lws *wsi); +#endif +///@} + +/** \defgroup lws_ring LWS Ringbuffer APIs + * ##lws_ring: generic ringbuffer struct + * + * Provides an abstract ringbuffer api supporting one head and one or an + * unlimited number of tails. + * + * All of the members are opaque and manipulated by lws_ring_...() apis. + * + * The lws_ring and its buffer is allocated at runtime on the heap, using + * + * - lws_ring_create() + * - lws_ring_destroy() + * + * It may contain any type, the size of the "element" stored in the ring + * buffer and the number of elements is given at creation time. + * + * When you create the ringbuffer, you can optionally provide an element + * destroy callback that frees any allocations inside the element. This is then + * automatically called for elements with no tail behind them, ie, elements + * which don't have any pending consumer are auto-freed. + * + * Whole elements may be inserted into the ringbuffer and removed from it, using + * + * - lws_ring_insert() + * - lws_ring_consume() + * + * You can find out how many whole elements are free or waiting using + * + * - lws_ring_get_count_free_elements() + * - lws_ring_get_count_waiting_elements() + * + * In addition there are special purpose optional byte-centric apis + * + * - lws_ring_next_linear_insert_range() + * - lws_ring_bump_head() + * + * which let you, eg, read() directly into the ringbuffer without needing + * an intermediate bounce buffer. + * + * The accessors understand that the ring wraps, and optimizes insertion and + * consumption into one or two memcpy()s depending on if the head or tail + * wraps. + * + * lws_ring only supports a single head, but optionally multiple tails with + * an API to inform it when the "oldest" tail has moved on. You can give + * NULL where-ever an api asks for a tail pointer, and it will use an internal + * single tail pointer for convenience. + * + * The "oldest tail", which is the only tail if you give it NULL instead of + * some other tail, is used to track which elements in the ringbuffer are + * still unread by anyone. + * + * - lws_ring_update_oldest_tail() + */ +///@{ +struct lws_ring; + +/** + * lws_ring_create(): create a new ringbuffer + * + * \param element_len: the size in bytes of one element in the ringbuffer + * \param count: the number of elements the ringbuffer can contain + * \param destroy_element: NULL, or callback to be called for each element + * that is removed from the ringbuffer due to the + * oldest tail moving beyond it + * + * Creates the ringbuffer and allocates the storage. Returns the new + * lws_ring *, or NULL if the allocation failed. + * + * If non-NULL, destroy_element will get called back for every element that is + * retired from the ringbuffer after the oldest tail has gone past it, and for + * any element still left in the ringbuffer when it is destroyed. It replaces + * all other element destruction code in your user code. + */ +LWS_VISIBLE LWS_EXTERN struct lws_ring * +lws_ring_create(size_t element_len, size_t count, + void (*destroy_element)(void *element)); + +/** + * lws_ring_destroy(): destroy a previously created ringbuffer + * + * \param ring: the struct lws_ring to destroy + * + * Destroys the ringbuffer allocation and the struct lws_ring itself. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_destroy(struct lws_ring *ring); + +/** + * lws_ring_get_count_free_elements(): return how many elements can fit + * in the free space + * + * \param ring: the struct lws_ring to report on + * + * Returns how much room is left in the ringbuffer for whole element insertion. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_get_count_free_elements(struct lws_ring *ring); + +/** + * lws_ring_get_count_waiting_elements(): return how many elements can be consumed + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * Returns how many elements are waiting to be consumed from the perspective + * of the tail pointer given. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail); + +/** + * lws_ring_insert(): attempt to insert up to max_count elements from src + * + * \param ring: the struct lws_ring to report on + * \param src: the array of elements to be inserted + * \param max_count: the number of available elements at src + * + * Attempts to insert as many of the elements at src as possible, up to the + * maximum max_count. Returns the number of elements actually inserted. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count); + +/** + * lws_ring_consume(): attempt to copy out and remove up to max_count elements + * to src + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * \param dest: the array of elements to be inserted. or NULL for no copy + * \param max_count: the number of available elements at src + * + * Attempts to copy out as many waiting elements as possible into dest, from + * the perspective of the given tail, up to max_count. If dest is NULL, the + * copying out is not done but the elements are logically consumed as usual. + * NULL dest is useful in combination with lws_ring_get_element(), where you + * can use the element direct from the ringbuffer and then call this with NULL + * dest to logically consume it. + * + * Increments the tail position according to how many elements could be + * consumed. + * + * Returns the number of elements consumed. + */ +LWS_VISIBLE LWS_EXTERN size_t +lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest, + size_t max_count); + +/** + * lws_ring_get_element(): get a pointer to the next waiting element for tail + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * Points to the next element that tail would consume, directly in the + * ringbuffer. This lets you write() or otherwise use the element without + * having to copy it out somewhere first. + * + * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1) + * which will logically consume the element you used up and increment your + * tail (tail may also be NULL there if you use a single tail). + * + * Returns NULL if no waiting element, or a const void * pointing to it. + */ +LWS_VISIBLE LWS_EXTERN const void * +lws_ring_get_element(struct lws_ring *ring, uint32_t *tail); + +/** + * lws_ring_update_oldest_tail(): free up elements older than tail for reuse + * + * \param ring: the struct lws_ring to report on + * \param tail: a pointer to the tail struct to use, or NULL for single tail + * + * If you are using multiple tails, you must use this API to inform the + * lws_ring when none of the tails still need elements in the fifo any more, + * by updating it when the "oldest" tail has moved on. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail); + +/** + * lws_ring_get_oldest_tail(): get current oldest available data index + * + * \param ring: the struct lws_ring to report on + * + * If you are initializing a new ringbuffer consumer, you can set its tail to + * this to start it from the oldest ringbuffer entry still available. + */ +LWS_VISIBLE LWS_EXTERN uint32_t +lws_ring_get_oldest_tail(struct lws_ring *ring); + +/** + * lws_ring_next_linear_insert_range(): used to write directly into the ring + * + * \param ring: the struct lws_ring to report on + * \param start: pointer to a void * set to the start of the next ringbuffer area + * \param bytes: pointer to a size_t set to the max length you may use from *start + * + * This provides a low-level, bytewise access directly into the ringbuffer + * allowing direct insertion of data without having to use a bounce buffer. + * + * The api reports the position and length of the next linear range that can + * be written in the ringbuffer, ie, up to the point it would wrap, and sets + * *start and *bytes accordingly. You can then, eg, directly read() into + * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring + * with what you have done. + * + * Returns nonzero if no insertion is currently possible. + */ +LWS_VISIBLE LWS_EXTERN int +lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start, + size_t *bytes); + +/** + * lws_ring_bump_head(): used to write directly into the ring + * + * \param ring: the struct lws_ring to operate on + * \param bytes: the number of bytes you inserted at the current head + */ +LWS_VISIBLE LWS_EXTERN void +lws_ring_bump_head(struct lws_ring *ring, size_t bytes); +///@} + +/** \defgroup sha SHA and B64 helpers + * ##SHA and B64 helpers + * + * These provide SHA-1 and B64 helper apis + */ +///@{ +#ifdef LWS_SHA1_USE_OPENSSL_NAME +#define lws_SHA1 SHA1 +#else +/** + * lws_SHA1(): make a SHA-1 digest of a buffer + * + * \param d: incoming buffer + * \param n: length of incoming buffer + * \param md: buffer for message digest (must be >= 20 bytes) + * + * Reduces any size buffer into a 20-byte SHA-1 hash. + */ +LWS_VISIBLE LWS_EXTERN unsigned char * +lws_SHA1(const unsigned char *d, size_t n, unsigned char *md); +#endif +/** + * lws_b64_encode_string(): encode a string into base 64 + * + * \param in: incoming buffer + * \param in_len: length of incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Encodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size); +/** + * lws_b64_decode_string(): decode a string from base 64 + * + * \param in: incoming buffer + * \param out: result buffer + * \param out_size: length of result buffer + * + * Decodes a string using b64 + */ +LWS_VISIBLE LWS_EXTERN int +lws_b64_decode_string(const char *in, char *out, int out_size); +///@} + + +/*! \defgroup cgi cgi handling + * + * ##CGI handling + * + * These functions allow low-level control over stdin/out/err of the cgi. + * + * However for most cases, binding the cgi to http in and out, the default + * lws implementation already does the right thing. + */ + +enum lws_enum_stdinouterr { + LWS_STDIN = 0, + LWS_STDOUT = 1, + LWS_STDERR = 2, +}; + +enum lws_cgi_hdr_state { + LCHS_HEADER, + LCHS_CR1, + LCHS_LF1, + LCHS_CR2, + LCHS_LF2, + LHCS_RESPONSE, + LHCS_DUMP_HEADERS, + LHCS_PAYLOAD, + LCHS_SINGLE_0A, +}; + +struct lws_cgi_args { + struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */ + enum lws_enum_stdinouterr ch; /**< channel index */ + unsigned char *data; /**< for messages with payload */ + enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */ + int len; /**< length */ +}; + +#ifdef LWS_WITH_CGI +/** + * lws_cgi: spawn network-connected cgi process + * + * \param wsi: connection to own the process + * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL + * \param script_uri_path_len: how many chars on the left of the uri are the + * path to the cgi, or -1 to spawn without URL-related env vars + * \param timeout_secs: seconds script should be allowed to run + * \param mp_cgienv: pvo list with per-vhost cgi options to put in env + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi(struct lws *wsi, const char * const *exec_array, + int script_uri_path_len, int timeout_secs, + const struct lws_protocol_vhost_options *mp_cgienv); + +/** + * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_write_split_stdout_headers(struct lws *wsi); + +/** + * lws_cgi_kill: terminate cgi process associated with wsi + * + * \param wsi: connection to own the process + */ +LWS_VISIBLE LWS_EXTERN int +lws_cgi_kill(struct lws *wsi); + +/** + * lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr + * + * \param wsi: parent wsi that has cgi + * \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR + */ +LWS_VISIBLE LWS_EXTERN struct lws * +lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch); + +#endif +///@} + + +/*! \defgroup fops file operation wrapping + * + * ##File operation wrapping + * + * Use these helper functions if you want to access a file from the perspective + * of a specific wsi, which is usually the case. If you just want contextless + * file access, use the fops callbacks directly with NULL wsi instead of these + * helpers. + * + * If so, then it calls the platform handler or user overrides where present + * (as defined in info->fops) + * + * The advantage from all this is user code can be portable for file operations + * without having to deal with differences between platforms. + */ +//@{ + +/** struct lws_plat_file_ops - Platform-specific file operations + * + * These provide platform-agnostic ways to deal with filesystem access in the + * library and in the user code. + */ + +#if defined(LWS_WITH_ESP32) +/* sdk preprocessor defs? compiler issue? gets confused with member names */ +#define LWS_FOP_OPEN _open +#define LWS_FOP_CLOSE _close +#define LWS_FOP_SEEK_CUR _seek_cur +#define LWS_FOP_READ _read +#define LWS_FOP_WRITE _write +#else +#define LWS_FOP_OPEN open +#define LWS_FOP_CLOSE close +#define LWS_FOP_SEEK_CUR seek_cur +#define LWS_FOP_READ read +#define LWS_FOP_WRITE write +#endif + +#define LWS_FOP_FLAGS_MASK ((1 << 23) - 1) +#define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24) +#define LWS_FOP_FLAG_COMPR_IS_GZIP (1 << 25) +#define LWS_FOP_FLAG_MOD_TIME_VALID (1 << 26) +#define LWS_FOP_FLAG_VIRTUAL (1 << 27) + +struct lws_plat_file_ops; + +struct lws_fop_fd { + lws_filefd_type fd; + /**< real file descriptor related to the file... */ + const struct lws_plat_file_ops *fops; + /**< fops that apply to this fop_fd */ + void *filesystem_priv; + /**< ignored by lws; owned by the fops handlers */ + lws_filepos_t pos; + /**< generic "position in file" */ + lws_filepos_t len; + /**< generic "length of file" */ + lws_fop_flags_t flags; + /**< copy of the returned flags */ + uint32_t mod_time; + /**< optional "modification time of file", only valid if .open() + * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */ +}; +typedef struct lws_fop_fd *lws_fop_fd_t; + +struct lws_fops_index { + const char *sig; /* NULL or vfs signature, eg, ".zip/" */ + uint8_t len; /* length of above string */ +}; + +struct lws_plat_file_ops { + lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops, + const char *filename, const char *vpath, + lws_fop_flags_t *flags); + /**< Open file (always binary access if plat supports it) + * vpath may be NULL, or if the fops understands it, the point at which + * the filename's virtual part starts. + * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR. + * If the file may be gzip-compressed, + * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set. If it actually is + * gzip-compressed, then the open handler should OR + * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning. + */ + int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd); + /**< close file AND set the pointer to NULL */ + lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd, + lws_fileofs_t offset_from_cur_pos); + /**< seek from current position */ + int (*LWS_FOP_READ)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + /**< Read from file, on exit *amount is set to amount actually read */ + int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + /**< Write to file, on exit *amount is set to amount actually written */ + + struct lws_fops_index fi[3]; + /**< vfs path signatures implying use of this fops */ + + const struct lws_plat_file_ops *next; + /**< NULL or next fops in list */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ +}; + +/** + * lws_get_fops() - get current file ops + * + * \param context: context + */ +LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT +lws_get_fops(struct lws_context *context); +LWS_VISIBLE LWS_EXTERN void +lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops); +/** + * lws_vfs_tell() - get current file position + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT +lws_vfs_tell(lws_fop_fd_t fop_fd); +/** + * lws_vfs_get_length() - get current file total length in bytes + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT +lws_vfs_get_length(lws_fop_fd_t fop_fd); +/** + * lws_vfs_get_mod_time() - get time file last modified + * + * \param fop_fd: fop_fd we are asking about + */ +LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT +lws_vfs_get_mod_time(lws_fop_fd_t fop_fd); +/** + * lws_vfs_file_seek_set() - seek relative to start of file + * + * \param fop_fd: fop_fd we are seeking in + * \param offset: offset from start of file + */ +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset); +/** + * lws_vfs_file_seek_end() - seek relative to end of file + * + * \param fop_fd: fop_fd we are seeking in + * \param offset: offset from start of file + */ +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset); + +extern struct lws_plat_file_ops fops_zip; + +/** + * lws_plat_file_open() - open vfs filepath + * + * \param fops: file ops struct that applies to this descriptor + * \param vfs_path: filename to open + * \param flags: pointer to open flags + * + * The vfs_path is scanned for known fops signatures, and the open directed + * to any matching fops open. + * + * User code should use this api to perform vfs opens. + * + * returns semi-opaque handle + */ +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT +lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + lws_fop_flags_t *flags); + +/** + * lws_plat_file_close() - close file + * + * \param fop_fd: file handle to close + */ +static LWS_INLINE int +lws_vfs_file_close(lws_fop_fd_t *fop_fd) +{ + return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd); +} + +/** + * lws_plat_file_seek_cur() - close file + * + * + * \param fop_fd: file handle + * \param offset: position to seek to + */ +static LWS_INLINE lws_fileofs_t +lws_vfs_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset); +} +/** + * lws_plat_file_read() - read from file + * + * \param fop_fd: file handle + * \param amount: how much to read (rewritten by call) + * \param buf: buffer to write to + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_vfs_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + return fop_fd->fops->LWS_FOP_READ(fop_fd, amount, buf, len); +} +/** + * lws_plat_file_write() - write from file + * + * \param fop_fd: file handle + * \param amount: how much to write (rewritten by call) + * \param buf: buffer to read from + * \param len: max length + */ +static LWS_INLINE int LWS_WARN_UNUSED_RESULT +lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len); +} + +/* these are the platform file operations implementations... they can + * be called directly and used in fops arrays + */ + +LWS_VISIBLE LWS_EXTERN lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_close(lws_fop_fd_t *fop_fd); +LWS_VISIBLE LWS_EXTERN lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); +LWS_VISIBLE LWS_EXTERN int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_alloc_vfs_file(struct lws_context *context, const char *filename, + uint8_t **buf, lws_filepos_t *amount); +//@} + +/** \defgroup smtp SMTP related functions + * ##SMTP related functions + * \ingroup lwsapi + * + * These apis let you communicate with a local SMTP server to send email from + * lws. It handles all the SMTP sequencing and protocol actions. + * + * Your system should have postfix, sendmail or another MTA listening on port + * 25 and able to send email using the "mail" commandline app. Usually distro + * MTAs are configured for this by default. + * + * It runs via its own libuv events if initialized (which requires giving it + * a libuv loop to attach to). + * + * It operates using three callbacks, on_next() queries if there is a new email + * to send, on_get_body() asks for the body of the email, and on_sent() is + * called after the email is successfully sent. + * + * To use it + * + * - create an lws_email struct + * + * - initialize data, loop, the email_* strings, max_content_size and + * the callbacks + * + * - call lws_email_init() + * + * When you have at least one email to send, call lws_email_check() to + * schedule starting to send it. + */ +//@{ +#ifdef LWS_WITH_SMTP + +/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ +enum lwsgs_smtp_states { + LGSSMTP_IDLE, /**< awaiting new email */ + LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ + LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ + LGSSMTP_SENT_HELO, /**< sent the HELO */ + LGSSMTP_SENT_FROM, /**< sent FROM */ + LGSSMTP_SENT_TO, /**< sent TO */ + LGSSMTP_SENT_DATA, /**< sent DATA request */ + LGSSMTP_SENT_BODY, /**< sent the email body */ + LGSSMTP_SENT_QUIT, /**< sent the session quit */ +}; + +/** struct lws_email - abstract context for performing SMTP operations */ +struct lws_email { + void *data; + /**< opaque pointer set by user code and available to the callbacks */ + uv_loop_t *loop; + /**< the libuv loop we will work on */ + + char email_smtp_ip[32]; /**< Fill before init, eg, "127.0.0.1" */ + char email_helo[32]; /**< Fill before init, eg, "myserver.com" */ + char email_from[100]; /**< Fill before init or on_next */ + char email_to[100]; /**< Fill before init or on_next */ + + unsigned int max_content_size; + /**< largest possible email body size */ + + /* Fill all the callbacks before init */ + + int (*on_next)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when idle, 0 = another email to send, nonzero is idle. + * If you return 0, all of the email_* char arrays must be set + * to something useful. */ + int (*on_sent)(struct lws_email *email); + /**< (Fill in before calling lws_email_init) + * called when transfer of the email to the SMTP server was + * successful, your callback would remove the current email + * from its queue */ + int (*on_get_body)(struct lws_email *email, char *buf, int len); + /**< (Fill in before calling lws_email_init) + * called when the body part of the queued email is about to be + * sent to the SMTP server. */ + + + /* private things */ + uv_timer_t timeout_email; /**< private */ + enum lwsgs_smtp_states estate; /**< private */ + uv_connect_t email_connect_req; /**< private */ + uv_tcp_t email_client; /**< private */ + time_t email_connect_started; /**< private */ + char email_buf[256]; /**< private */ + char *content; /**< private */ +}; + +/** + * lws_email_init() - Initialize a struct lws_email + * + * \param email: struct lws_email to init + * \param loop: libuv loop to use + * \param max_content: max email content size + * + * Prepares a struct lws_email for use ending SMTP + */ +LWS_VISIBLE LWS_EXTERN int +lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content); + +/** + * lws_email_check() - Request check for new email + * + * \param email: struct lws_email context to check + * + * Schedules a check for new emails in 1s... call this when you have queued an + * email for send. + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_check(struct lws_email *email); +/** + * lws_email_destroy() - stop using the struct lws_email + * + * \param email: the struct lws_email context + * + * Stop sending email using email and free allocations + */ +LWS_VISIBLE LWS_EXTERN void +lws_email_destroy(struct lws_email *email); + +#endif +//@} + +/* + * Stats are all uint64_t numbers that start at 0. + * Index names here have the convention + * + * _C_ counter + * _B_ byte count + * _MS_ millisecond count + */ + +enum { + LWSSTATS_C_CONNECTIONS, /**< count incoming connections */ + LWSSTATS_C_API_CLOSE, /**< count calls to close api */ + LWSSTATS_C_API_READ, /**< count calls to read from socket api */ + LWSSTATS_C_API_LWS_WRITE, /**< count calls to lws_write API */ + LWSSTATS_C_API_WRITE, /**< count calls to write API */ + LWSSTATS_C_WRITE_PARTIALS, /**< count of partial writes */ + LWSSTATS_C_WRITEABLE_CB_REQ, /**< count of writable callback requests */ + LWSSTATS_C_WRITEABLE_CB_EFF_REQ, /**< count of effective writable callback requests */ + LWSSTATS_C_WRITEABLE_CB, /**< count of writable callbacks */ + LWSSTATS_C_SSL_CONNECTIONS_FAILED, /**< count of failed SSL connections */ + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, /**< count of accepted SSL connections */ + LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, /**< count of SSL_accept() attempts */ + LWSSTATS_C_SSL_CONNS_HAD_RX, /**< count of accepted SSL conns that have had some RX */ + LWSSTATS_C_TIMEOUTS, /**< count of timed-out connections */ + LWSSTATS_C_SERVICE_ENTRY, /**< count of entries to lws service loop */ + LWSSTATS_B_READ, /**< aggregate bytes read */ + LWSSTATS_B_WRITE, /**< aggregate bytes written */ + LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, /**< aggreate of size of accepted write data from new partials */ + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, /**< aggregate delay in accepting connection */ + LWSSTATS_MS_WRITABLE_DELAY, /**< aggregate delay between asking for writable and getting cb */ + LWSSTATS_MS_WORST_WRITABLE_DELAY, /**< single worst delay between asking for writable and getting cb */ + LWSSTATS_MS_SSL_RX_DELAY, /**< aggregate delay between ssl accept complete and first RX */ + LWSSTATS_C_PEER_LIMIT_AH_DENIED, /**< number of times we would have given an ah but for the peer limit */ + LWSSTATS_C_PEER_LIMIT_WSI_DENIED, /**< number of times we would have given a wsi but for the peer limit */ + + /* Add new things just above here ---^ + * This is part of the ABI, don't needlessly break compatibility */ + LWSSTATS_SIZE +}; + +#if defined(LWS_WITH_STATS) + +LWS_VISIBLE LWS_EXTERN uint64_t +lws_stats_get(struct lws_context *context, int index); +LWS_VISIBLE LWS_EXTERN void +lws_stats_log_dump(struct lws_context *context); +#else +static LWS_INLINE uint64_t +lws_stats_get(struct lws_context *context, int index) { return 0; } +static LWS_INLINE void +lws_stats_log_dump(struct lws_context *context) { } +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/lws/lws_config.h b/thirdparty/lws/lws_config.h new file mode 100644 index 0000000000..6d2a3c93ac --- /dev/null +++ b/thirdparty/lws/lws_config.h @@ -0,0 +1,166 @@ +/* lws_config.h Generated from lws_config.h.in */ +#include "lws_config_private.h" + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif + +#define LWS_INSTALL_DATADIR "/usr/local/share" + +/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +/* #undef USE_WOLFSSL */ + +/* Also define to 1 (in addition to USE_WOLFSSL) when using the + (older) CyaSSL library */ +/* #undef USE_OLD_CYASSL */ +/* #undef LWS_WITH_BORINGSSL */ + +/* #undef LWS_WITH_MBEDTLS */ +/* #undef LWS_WITH_POLARSSL */ +/* #undef LWS_WITH_ESP8266 */ +/* #undef LWS_WITH_ESP32 */ + +/* #undef LWS_WITH_PLUGINS */ +/* #undef LWS_WITH_NO_LOGS */ +#ifndef DEBUG_ENABLED +#define LWS_WITH_NO_LOGS +#endif + +/* The Libwebsocket version */ +#define LWS_LIBRARY_VERSION "2.4.1" + +#define LWS_LIBRARY_VERSION_MAJOR 2 +#define LWS_LIBRARY_VERSION_MINOR 4 +#define LWS_LIBRARY_VERSION_PATCH 1 +/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ +#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH + +/* The current git commit hash that we're building from */ +#define LWS_BUILD_HASH "55f97b7806e07db2d4c8a158172cd309d0faf450" + +/* Build with OpenSSL support */ +#define LWS_OPENSSL_SUPPORT + +/* The client should load and trust CA root certs it finds in the OS */ +#define LWS_SSL_CLIENT_USE_OS_CA_CERTS + +/* Sets the path where the client certs should be installed. */ +#define LWS_OPENSSL_CLIENT_CERTS "../share" + +/* Turn off websocket extensions */ +/* #undef LWS_NO_EXTENSIONS */ + +/* Enable libev io loop */ +/* #undef LWS_WITH_LIBEV */ +#undef LWS_WITH_LIBEV + +/* Enable libuv io loop */ +/* #undef LWS_WITH_LIBUV */ +#undef LWS_WITH_LIBUV + +/* Enable libevent io loop */ +/* #undef LWS_WITH_LIBEVENT */ +#undef LWS_WITH_LIBEVENT + +/* Build with support for ipv6 */ +/* #undef LWS_WITH_IPV6 */ + +/* Build with support for UNIX domain socket */ +/* #undef LWS_WITH_UNIX_SOCK */ +#ifdef WINDOWS_ENABLED +#undef LWS_USE_UNIX_SOCK +#endif + +/* Build with support for HTTP2 */ +/* #undef LWS_WITH_HTTP2 */ + +/* Turn on latency measuring code */ +/* #undef LWS_LATENCY */ + +/* Don't build the daemonizeation api */ +#define LWS_NO_DAEMONIZE + +/* Build without server support */ +/* #undef LWS_NO_SERVER */ + +/* Build without client support */ +/* #undef LWS_NO_CLIENT */ + +/* If we should compile with MinGW support */ +/* #undef LWS_MINGW_SUPPORT */ + +/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */ +/* #undef LWS_BUILTIN_GETIFADDRS */ + +/* use SHA1() not internal libwebsockets_SHA1 */ +/* #undef LWS_SHA1_USE_OPENSSL_NAME */ + +/* SSL server using ECDH certificate */ +/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */ +#define LWS_HAVE_SSL_CTX_set1_param +#define LWS_HAVE_X509_VERIFY_PARAM_set1_host +/* #undef LWS_HAVE_RSA_SET0_KEY */ + +/* #undef LWS_HAVE_UV_VERSION_H */ + +/* CGI apis */ +/* #undef LWS_WITH_CGI */ + +/* whether the Openssl is recent enough, and / or built with, ecdh */ +#define LWS_HAVE_OPENSSL_ECDH_H + +/* HTTP Proxy support */ +/* #undef LWS_WITH_HTTP_PROXY */ + +/* HTTP Ranges support */ +#define LWS_WITH_RANGES + +/* Http access log support */ +/* #undef LWS_WITH_ACCESS_LOG */ +/* #undef LWS_WITH_SERVER_STATUS */ + +/* #undef LWS_WITH_STATEFUL_URLDECODE */ +/* #undef LWS_WITH_PEER_LIMITS */ + +/* Maximum supported service threads */ +#define LWS_MAX_SMP 1 + +/* Lightweight JSON Parser */ +/* #undef LWS_WITH_LEJP */ + +/* SMTP */ +/* #undef LWS_WITH_SMTP */ + +/* OPTEE */ +/* #undef LWS_PLAT_OPTEE */ + +/* ZIP FOPS */ +#define LWS_WITH_ZIP_FOPS +#define LWS_HAVE_STDINT_H + +/* #undef LWS_AVOID_SIGPIPE_IGN */ + +/* #undef LWS_FALLBACK_GETHOSTBYNAME */ + +/* #undef LWS_WITH_STATS */ +/* #undef LWS_WITH_SOCKS5 */ + +/* #undef LWS_HAVE_SYS_CAPABILITY_H */ +/* #undef LWS_HAVE_LIBCAP */ + +#define LWS_HAVE_ATOLL +/* #undef LWS_HAVE__ATOI64 */ +/* #undef LWS_HAVE__STAT32I64 */ + +/* OpenSSL various APIs */ + +/* #undef LWS_HAVE_TLS_CLIENT_METHOD */ +#define LWS_HAVE_TLSV1_2_CLIENT_METHOD +#define LWS_HAVE_SSL_SET_INFO_CALLBACK + +#define LWS_HAS_INTPTR_T + + diff --git a/thirdparty/lws/lws_config_private.h b/thirdparty/lws/lws_config_private.h new file mode 100644 index 0000000000..235f2ba26d --- /dev/null +++ b/thirdparty/lws/lws_config_private.h @@ -0,0 +1,143 @@ +/* lws_config_private.h.in. Private compilation options. */ +#ifndef DEBUG_ENABLED +#define NDEBUG +#endif + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif + +/* Define to 1 to use CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +/* #undef USE_CYASSL */ + +/* Define to 1 if you have the `bzero' function. */ +#define LWS_HAVE_BZERO +/* Windows has no bzero function */ +#ifdef WINDOWS_ENABLED +#undef LWS_HAVE_BZERO +#endif + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define LWS_HAVE_DLFCN_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define LWS_HAVE_FCNTL_H +#ifdef NO_FCNTL +#undef LWS_HAVE_FCNTL_H +#endif + +/* Define to 1 if you have the `fork' function. */ +#define LWS_HAVE_FORK + +/* Define to 1 if you have the `getenv’ function. */ +#define LWS_HAVE_GETENV + +/* Define to 1 if you have the <in6addr.h> header file. */ +/* #undef LWS_HAVE_IN6ADDR_H */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define LWS_HAVE_INTTYPES_H + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef LWS_HAVE_LIBSSL */ + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define LWS_HAVE_MALLOC + +/* Define to 1 if you have the <memory.h> header file. */ +#define LWS_HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#define LWS_HAVE_MEMSET + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#define LWS_HAVE_NETINET_IN_H + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define LWS_HAVE_REALLOC + +/* Define to 1 if you have the `socket' function. */ +#define LWS_HAVE_SOCKET + +/* Define to 1 if you have the <stdint.h> header file. */ +#define LWS_HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define LWS_HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#define LWS_HAVE_STRERROR + +/* Define to 1 if you have the <strings.h> header file. */ +#define LWS_HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#define LWS_HAVE_STRING_H + +/* Define to 1 if you have the <sys/prctl.h> header file. */ +#define LWS_HAVE_SYS_PRCTL_H +#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED) +#undef LWS_HAVE_SYS_PRCTL_H +#endif + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define LWS_HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the <sys/sockio.h> header file. */ +/* #undef LWS_HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define LWS_HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define LWS_HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#define LWS_HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#define LWS_HAVE_VFORK + +/* Define to 1 if you have the <vfork.h> header file. */ +/* #undef LWS_HAVE_VFORK_H */ + +/* Define to 1 if `fork' works. */ +#define LWS_HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#define LWS_HAVE_WORKING_VFORK + +/* Define to 1 if execvpe() exists */ +#define LWS_HAVE_EXECVPE + +/* Define to 1 if you have the <zlib.h> header file. */ +#define LWS_HAVE_ZLIB_H + +#define LWS_HAVE_GETLOADAVG + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR // We're not using libtool + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to 1 if we have getifaddrs */ +#define LWS_HAVE_GETIFADDRS +#if defined(ANDROID_ENABLED) +#undef LWS_HAVE_GETIFADDRS +#define LWS_BUILTIN_GETIFADDRS +#endif + +/* Define if the inline keyword doesn't exist. */ +/* #undef inline */ + + diff --git a/thirdparty/lws/minilex.c b/thirdparty/lws/minilex.c new file mode 100644 index 0000000000..3cb1e33696 --- /dev/null +++ b/thirdparty/lws/minilex.c @@ -0,0 +1,272 @@ +/* + * minilex.c + * + * High efficiency lexical state parser + * + * Copyright (C)2011-2014 Andy Green <andy@warmcat.com> + * + * Licensed under LGPL2 + * + * Usage: gcc minilex.c -o minilex && ./minilex > lextable.h + * + * Run it twice to test parsing on the generated table on stderr + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lextable-strings.h" + +/* + * b7 = 0 = 1-byte seq + * 0x08 = fail + * 2-byte seq + * 0x00 - 0x07, then terminal as given in 2nd byte + 3-byte seq + * no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes + * = 1 = 1-byte seq + * no match: die, match go fwd 1 byte + */ + +unsigned char lextable[] = { + #include "lextable.h" +}; + +#define PARALLEL 30 + +struct state { + char c[PARALLEL]; + int state[PARALLEL]; + int count; + int bytepos; + + int real_pos; +}; + +struct state state[1000]; +int next = 1; + +#define FAIL_CHAR 0x08 + +int lextable_decode(int pos, char c) +{ + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } else { /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } + } +} + +int main(void) +{ + int n = 0; + int m = 0; + int prev; + char c; + int walk; + int saw; + int y; + int j; + int pos = 0; + + while (n < sizeof(set) / sizeof(set[0])) { + + m = 0; + walk = 0; + prev = 0; + + if (set[n][0] == '\0') { + n++; + continue; + } + + while (set[n][m]) { + + saw = 0; + for (y = 0; y < state[walk].count; y++) + if (state[walk].c[y] == set[n][m]) { + /* exists -- go forward */ + walk = state[walk].state[y]; + saw = 1; + break; + } + + if (saw) + goto again; + + /* something we didn't see before */ + + state[walk].c[state[walk].count] = set[n][m]; + + state[walk].state[state[walk].count] = next; + state[walk].count++; + walk = next++; +again: + m++; + } + + state[walk].c[0] = n++; + state[walk].state[0] = 0; /* terminal marker */ + state[walk].count = 1; + } + + walk = 0; + for (n = 0; n < next; n++) { + state[n].bytepos = walk; + walk += (2 * state[n].count); + } + + /* compute everyone's position first */ + + pos = 0; + walk = 0; + for (n = 0; n < next; n++) { + + state[n].real_pos = pos; + + for (m = 0; m < state[n].count; m++) { + + if (state[n].state[m] == 0) + pos += 2; /* terminal marker */ + else { /* c is a character */ + if ((state[state[n].state[m]].bytepos - + walk) == 2) + pos++; + else { + pos += 3; + if (m == state[n].count - 1) + pos++; /* fail */ + } + } + walk += 2; + } + } + + walk = 0; + pos = 0; + for (n = 0; n < next; n++) { + for (m = 0; m < state[n].count; m++) { + + if (!m) + fprintf(stdout, "/* pos %04x: %3d */ ", + state[n].real_pos, n); + else + fprintf(stdout, " "); + + y = state[n].c[m]; + saw = state[n].state[m]; + + if (saw == 0) { // c is a terminal then + + if (y > 0x7ff) { + fprintf(stderr, "terminal too big\n"); + return 2; + } + + fprintf(stdout, " 0x%02X, 0x%02X " + " " + "/* - terminal marker %2d - */,\n", + y >> 8, y & 0xff, y & 0x7f); + pos += 2; + walk += 2; + continue; + } + + /* c is a character */ + + prev = y &0x7f; + if (prev < 32 || prev > 126) + prev = '.'; + + + if ((state[saw].bytepos - walk) == 2) { + fprintf(stdout, " 0x%02X /* '%c' -> */,\n", + y | 0x80, prev); + pos++; + walk += 2; + continue; + } + + j = state[saw].real_pos - pos; + + if (j > 0xffff) { + fprintf(stderr, + "Jump > 64K bytes ahead (%d to %d)\n", + state[n].real_pos, state[saw].real_pos); + return 1; + } + fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X " + "/* (to 0x%04X state %3d) */,\n", + y, prev, + j & 0xff, j >> 8, + state[saw].real_pos, saw); + pos += 3; + + if (m == state[n].count - 1) { + fprintf(stdout, + " 0x%02X, /* fail */\n", + FAIL_CHAR); + pos++; /* fail */ + } + + walk += 2; + } + } + + fprintf(stdout, "/* total size %d bytes */\n", pos); + + /* + * Try to parse every legal input string + */ + + for (n = 0; n < sizeof(set) / sizeof(set[0]); n++) { + walk = 0; + m = 0; + y = -1; + + if (set[n][0] == '\0') + continue; + + fprintf(stderr, " trying '%s'\n", set[n]); + + while (set[n][m]) { + walk = lextable_decode(walk, set[n][m]); + if (walk < 0) { + fprintf(stderr, "failed\n"); + return 3; + } + + if (lextable[walk] < FAIL_CHAR) { + y = (lextable[walk] << 8) + lextable[walk + 1]; + break; + } + m++; + } + + if (y != n) { + fprintf(stderr, "decode failed %d\n", y); + return 4; + } + } + + fprintf(stderr, "All decode OK\n"); + + return 0; +} diff --git a/thirdparty/lws/misc/base64-decode.c b/thirdparty/lws/misc/base64-decode.c new file mode 100644 index 0000000000..c8f11d21b8 --- /dev/null +++ b/thirdparty/lws/misc/base64-decode.c @@ -0,0 +1,206 @@ +/* + * This code originally came from here + * + * http://base64.sourceforge.net/b64.c + * + * with the following license: + * + * LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * VERSION HISTORY: + * Bob Trower 08/04/01 -- Create Version 0.00.00B + * + * I cleaned it up quite a bit to match the (linux kernel) style of the rest + * of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws + * since he explicitly allows sublicensing, but I give the URL above so you can + * get the original with Bob's super-liberal terms directly if you prefer. + */ + +#include <stdio.h> +#include <string.h> +#include "private-libwebsockets.h" + +static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW" + "$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +LWS_VISIBLE int +lws_b64_encode_string(const char *in, int in_len, char *out, int out_size) +{ + unsigned char triple[3]; + int i; + int len; + int line = 0; + int done = 0; + + while (in_len) { + len = 0; + for (i = 0; i < 3; i++) { + if (in_len) { + triple[i] = *in++; + len++; + in_len--; + } else + triple[i] = 0; + } + + if (done + 4 >= out_size) + return -1; + + *out++ = encode[triple[0] >> 2]; + *out++ = encode[((triple[0] & 0x03) << 4) | + ((triple[1] & 0xf0) >> 4)]; + *out++ = (len > 1 ? encode[((triple[1] & 0x0f) << 2) | + ((triple[2] & 0xc0) >> 6)] : '='); + *out++ = (len > 2 ? encode[triple[2] & 0x3f] : '='); + + done += 4; + line += 4; + } + + if (done + 1 >= out_size) + return -1; + + *out++ = '\0'; + + return done; +} + +/* + * returns length of decoded string in out, or -1 if out was too small + * according to out_size + */ + +LWS_VISIBLE int +lws_b64_decode_string(const char *in, char *out, int out_size) +{ + int len, i, c = 0, done = 0; + unsigned char v, quad[4]; + + while (*in) { + + len = 0; + for (i = 0; i < 4 && *in; i++) { + + v = 0; + c = 0; + while (*in && !v) { + c = v = *in++; + v = (v < 43 || v > 122) ? 0 : decode[v - 43]; + if (v) + v = (v == '$') ? 0 : v - 61; + } + if (c) { + len++; + if (v) + quad[i] = v - 1; + } else + quad[i] = 0; + } + + if (out_size < (done + len - 1)) + /* out buffer is too small */ + return -1; + + /* + * "The '==' sequence indicates that the last group contained + * only one byte, and '=' indicates that it contained two + * bytes." (wikipedia) + */ + + if (!*in && c == '=') + len--; + + if (len >= 2) + *out++ = quad[0] << 2 | quad[1] >> 4; + if (len >= 3) + *out++ = quad[1] << 4 | quad[2] >> 2; + if (len >= 4) + *out++ = ((quad[2] << 6) & 0xc0) | quad[3]; + + done += len - 1; + } + + if (done + 1 >= out_size) + return -1; + + *out = '\0'; + + return done; +} + +#if 0 +int +lws_b64_selftest(void) +{ + char buf[64]; + unsigned int n, r = 0; + unsigned int test; + /* examples from https://en.wikipedia.org/wiki/Base64 */ + static const char * const plaintext[] = { + "any carnal pleasure.", + "any carnal pleasure", + "any carnal pleasur", + "any carnal pleasu", + "any carnal pleas", + "Admin:kloikloi" + }; + static const char * const coded[] = { + "YW55IGNhcm5hbCBwbGVhc3VyZS4=", + "YW55IGNhcm5hbCBwbGVhc3VyZQ==", + "YW55IGNhcm5hbCBwbGVhc3Vy", + "YW55IGNhcm5hbCBwbGVhc3U=", + "YW55IGNhcm5hbCBwbGVhcw==", + "QWRtaW46a2xvaWtsb2k=" + }; + + for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) { + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_encode_string(plaintext[test], + strlen(plaintext[test]), buf, sizeof buf); + if (n != strlen(coded[test]) || strcmp(buf, coded[test])) { + lwsl_err("Failed lws_b64 encode selftest " + "%d result '%s' %d\n", test, buf, n); + r = -1; + } + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_decode_string(coded[test], buf, sizeof buf); + if (n != strlen(plaintext[test]) || + strcmp(buf, plaintext[test])) { + lwsl_err("Failed lws_b64 decode selftest " + "%d result '%s' / '%s', %d / %d\n", + test, buf, plaintext[test], n, strlen(plaintext[test])); + r = -1; + } + } + + lwsl_notice("Base 64 selftests passed\n"); + + return r; +} +#endif diff --git a/thirdparty/lws/misc/getifaddrs.c b/thirdparty/lws/misc/getifaddrs.c new file mode 100644 index 0000000000..4f42ab4595 --- /dev/null +++ b/thirdparty/lws/misc/getifaddrs.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2000 - 2001 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * originally downloaded from + * + * http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef LWS_HAVE_NETINET_IN6_VAR_H +#include <netinet/in6_var.h> +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#include "getifaddrs.h" + +static int +getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags, + size_t ifreq_sz) +{ + int ret; + int fd; + size_t buf_size; + char *buf; + struct ifconf ifconf; + char *p; + size_t sz; + struct sockaddr sa_zero; + struct ifreq *ifr; + struct ifaddrs *start, **end = &start; + + buf = NULL; + + memset(&sa_zero, 0, sizeof(sa_zero)); + fd = socket(af, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + buf_size = 8192; + for (;;) { + buf = lws_zalloc(buf_size, "getifaddrs2"); + if (buf == NULL) { + ret = ENOMEM; + goto error_out; + } + ifconf.ifc_len = buf_size; + ifconf.ifc_buf = buf; + + /* + * Solaris returns EINVAL when the buffer is too small. + */ + if (ioctl(fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { + ret = errno; + goto error_out; + } + /* + * Can the difference between a full and a overfull buf + * be determined? + */ + + if (ifconf.ifc_len < (int)buf_size) + break; + lws_free(buf); + buf_size *= 2; + } + + for (p = ifconf.ifc_buf; p < ifconf.ifc_buf + ifconf.ifc_len; p += sz) { + struct ifreq ifreq; + struct sockaddr *sa; + size_t salen; + + ifr = (struct ifreq *)p; + sa = &ifr->ifr_addr; + + sz = ifreq_sz; + salen = sizeof(struct sockaddr); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + salen = sa->sa_len; + sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); +#endif +#ifdef SA_LEN + salen = SA_LEN(sa); + sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); +#endif + memset(&ifreq, 0, sizeof(ifreq)); + memcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + + if (ioctl(fd, siocgifflags, &ifreq) < 0) { + ret = errno; + goto error_out; + } + + *end = lws_malloc(sizeof(**end), "getifaddrs"); + + (*end)->ifa_next = NULL; + (*end)->ifa_name = strdup(ifr->ifr_name); + (*end)->ifa_flags = ifreq.ifr_flags; + (*end)->ifa_addr = lws_malloc(salen, "getifaddrs"); + memcpy((*end)->ifa_addr, sa, salen); + (*end)->ifa_netmask = NULL; + +#if 0 + /* fix these when we actually need them */ + if (ifreq.ifr_flags & IFF_BROADCAST) { + (*end)->ifa_broadaddr = + lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs"); + memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, + sizeof(ifr->ifr_broadaddr)); + } else if (ifreq.ifr_flags & IFF_POINTOPOINT) { + (*end)->ifa_dstaddr = + lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs"); + memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, + sizeof(ifr->ifr_dstaddr)); + } else + (*end)->ifa_dstaddr = NULL; +#else + (*end)->ifa_dstaddr = NULL; +#endif + (*end)->ifa_data = NULL; + + end = &(*end)->ifa_next; + + } + *ifap = start; + close(fd); + lws_free(buf); + return 0; + +error_out: + close(fd); + lws_free(buf); + errno = ret; + + return -1; +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + int ret = -1; + errno = ENXIO; +#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, + sizeof(struct in6_ifreq)); +#endif +#if defined(LWS_HAVE_IPV6) && defined(SIOCGIFCONF) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif +#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif + return ret; +} + +void +freeifaddrs(struct ifaddrs *ifp) +{ + struct ifaddrs *p, *q; + + for (p = ifp; p; ) { + lws_free(p->ifa_name); + lws_free(p->ifa_addr); + lws_free(p->ifa_dstaddr); + lws_free(p->ifa_netmask); + lws_free(p->ifa_data); + q = p; + p = p->ifa_next; + lws_free(q); + } +} + +#ifdef TEST + +void +print_addr(const char *s, struct sockaddr *sa) +{ + int i; + printf(" %s=%d/", s, sa->sa_family); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + for (i = 0; + i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#else + for (i = 0; i < sizeof(sa->sa_data); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#endif + printf("\n"); +} + +void +print_ifaddrs(struct ifaddrs *x) +{ + struct ifaddrs *p; + + for (p = x; p; p = p->ifa_next) { + printf("%s\n", p->ifa_name); + printf(" flags=%x\n", p->ifa_flags); + if (p->ifa_addr) + print_addr("addr", p->ifa_addr); + if (p->ifa_dstaddr) + print_addr("dstaddr", p->ifa_dstaddr); + if (p->ifa_netmask) + print_addr("netmask", p->ifa_netmask); + printf(" %p\n", p->ifa_data); + } +} + +int +main() +{ + struct ifaddrs *a = NULL, *b; + getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); + print_ifaddrs(a); + printf("---\n"); + getifaddrs(&b); + print_ifaddrs(b); + return 0; +} +#endif diff --git a/thirdparty/lws/misc/getifaddrs.h b/thirdparty/lws/misc/getifaddrs.h new file mode 100644 index 0000000000..d26670c082 --- /dev/null +++ b/thirdparty/lws/misc/getifaddrs.h @@ -0,0 +1,80 @@ +#ifndef LWS_HAVE_GETIFADDRS +#define LWS_HAVE_GETIFADDRS 0 +#endif + +#if LWS_HAVE_GETIFADDRS +#include <sys/types.h> +#include <ifaddrs.h> +#else +#ifdef __cplusplus +extern "C" { +#endif +/* + * Copyright (c) 2000 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */ + +#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 +#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 + +/* + * the interface is defined in terms of the fields below, and this is + * sometimes #define'd, so there seems to be no simple way of solving + * this and this seemed the best. */ + +#undef ifa_dstaddr + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr +#endif + +int getifaddrs(struct ifaddrs **); + +void freeifaddrs(struct ifaddrs *); + +#endif /* __ifaddrs_h__ */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/lws/misc/lejp.c new file mode 100644 index 0000000000..5407c90f97 --- /dev/null +++ b/thirdparty/lws/misc/lejp.c @@ -0,0 +1,709 @@ +/* + * Lightweight Embedded JSON Parser + * + * Copyright (C) 2013 Andy Green <andy@warmcat.com> + * This code is licensed under LGPL 2.1 + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + +#include <string.h> +#include "lejp.h" + +#include <stdio.h> + +/** + * lejp_construct - prepare a struct lejp_ctx for use + * + * \param ctx: pointer to your struct lejp_ctx + * \param callback: your user callback which will received parsed tokens + * \param user: optional user data pointer untouched by lejp + * \param paths: your array of name elements you are interested in + * \param count_paths: ARRAY_SIZE() of @paths + * + * Prepares your context struct for use with lejp + */ + +void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user, + const char * const *paths, unsigned char count_paths) +{ + ctx->st[0].s = 0; + ctx->st[0].p = 0; + ctx->st[0].i = 0; + ctx->st[0].b = 0; + ctx->sp = 0; + ctx->ipos = 0; + ctx->ppos = 0; + ctx->path_match = 0; + ctx->path[0] = '\0'; + ctx->callback = callback; + ctx->user = user; + ctx->paths = paths; + ctx->count_paths = count_paths; + ctx->line = 1; + ctx->callback(ctx, LEJPCB_CONSTRUCTED); +} + +/** + * lejp_destruct - retire a previously constructed struct lejp_ctx + * + * \param ctx: pointer to your struct lejp_ctx + * + * lejp does not perform any allocations, but since your user code might, this + * provides a one-time LEJPCB_DESTRUCTED callback at destruction time where + * you can clean up in your callback. + */ + +void +lejp_destruct(struct lejp_ctx *ctx) +{ + /* no allocations... just let callback know what it happening */ + ctx->callback(ctx, LEJPCB_DESTRUCTED); +} + +/** + * lejp_change_callback - switch to a different callback from now on + * + * \param ctx: pointer to your struct lejp_ctx + * \param callback: your user callback which will received parsed tokens + * + * This tells the old callback it was destroyed, in case you want to take any + * action because that callback "lost focus", then changes to the new + * callback and tells it first that it was constructed, and then started. + * + * Changing callback is a cheap and powerful trick to split out handlers + * according to information earlier in the parse. For example you may have + * a JSON pair "schema" whose value defines what can be expected for the rest + * of the JSON. Rather than having one huge callback for all cases, you can + * have an initial one looking for "schema" which then calls + * lejp_change_callback() to a handler specific for the schema. + * + * Notice that afterwards, you need to construct the context again anyway to + * parse another JSON object, and the callback is reset then to the main, + * schema-interpreting one. The construction action is very lightweight. + */ + +void +lejp_change_callback(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason)) +{ + ctx->callback(ctx, LEJPCB_DESTRUCTED); + ctx->callback = callback; + ctx->callback(ctx, LEJPCB_CONSTRUCTED); + ctx->callback(ctx, LEJPCB_START); +} + +static void +lejp_check_path_match(struct lejp_ctx *ctx) +{ + const char *p, *q; + int n; + + /* we only need to check if a match is not active */ + for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) { + ctx->wildcount = 0; + p = ctx->path; + q = ctx->paths[n]; + while (*p && *q) { + if (*q != '*') { + if (*p != *q) + break; + p++; + q++; + continue; + } + ctx->wild[ctx->wildcount++] = p - ctx->path; + q++; + /* + * if * has something after it, match to . + * if ends with *, eat everything. + * This implies match sequences must be ordered like + * x.*.* + * x.* + * if both options are possible + */ + while (*p && (*p != '.' || !*q)) + p++; + } + if (*p || *q) + continue; + + ctx->path_match = n + 1; + ctx->path_match_len = ctx->ppos; + return; + } + + if (!ctx->path_match) + ctx->wildcount = 0; +} + +int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len) +{ + int n; + + if (wildcard >= ctx->wildcount || !len) + return 0; + + n = ctx->wild[wildcard]; + + while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.')) + *dest++ = ctx->path[n++]; + + *dest = '\0'; + n++; + + return n - ctx->wild[wildcard]; +} + +/** + * lejp_parse - interpret some more incoming data incrementally + * + * \param ctx: previously constructed parsing context + * \param json: char buffer with the new data to interpret + * \param len: amount of data in the buffer + * + * Because lejp is a stream parser, it incrementally parses as new data + * becomes available, maintaining all state in the context struct. So an + * incomplete JSON is a normal situation, getting you a LEJP_CONTINUE + * return, signalling there's no error but to call again with more data when + * it comes to complete the parsing. Successful parsing completes with a + * 0 or positive integer indicating how much of the last input buffer was + * unused. + */ + +int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) +{ + unsigned char c, n, s, ret = LEJP_REJECT_UNKNOWN; + static const char esc_char[] = "\"\\/bfnrt"; + static const char esc_tran[] = "\"\\/\b\f\n\r\t"; + static const char tokens[] = "rue alse ull "; + + if (!ctx->sp && !ctx->ppos) + ctx->callback(ctx, LEJPCB_START); + + while (len--) { + c = *json++; + + s = ctx->st[ctx->sp].s; + + /* skip whitespace unless we should care */ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') { + if (c == '\n') { + ctx->line++; + ctx->st[ctx->sp].s &= ~LEJP_FLAG_WS_COMMENTLINE; + } + if (!(s & LEJP_FLAG_WS_KEEP)) { + if (c == '#') + ctx->st[ctx->sp].s |= + LEJP_FLAG_WS_COMMENTLINE; + continue; + } + } + + if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE) + continue; + + switch (s) { + case LEJP_IDLE: + if (c != '{') { + ret = LEJP_REJECT_IDLE_NO_BRACE; + goto reject; + } + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_MEMBERS; + break; + case LEJP_MEMBERS: + if (c == '}') { + ctx->st[ctx->sp].s = LEJP_IDLE; + ret = LEJP_REJECT_MEMBERS_NO_CLOSE; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_M_P; + goto redo_character; + case LEJP_M_P: + if (c != '\"') { + ret = LEJP_REJECT_MP_NO_OPEN_QUOTE; + goto reject; + } + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_DELIM; + c = LEJP_MP_STRING; + goto add_stack_level; + + case LEJP_MP_STRING: + if (c == '\"') { + if (!ctx->sp) { + ret = LEJP_REJECT_MP_STRING_UNDERRUN; + goto reject; + } + if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { + ctx->buf[ctx->npos] = '\0'; + if (ctx->callback(ctx, + LEJPCB_VAL_STR_END) < 0) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } + /* pop */ + ctx->sp--; + break; + } + if (c == '\\') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC; + break; + } + if (c < ' ') {/* "control characters" not allowed */ + ret = LEJP_REJECT_MP_ILLEGAL_CTRL; + goto reject; + } + goto emit_string_char; + + case LEJP_MP_STRING_ESC: + if (c == 'u') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1; + ctx->uni = 0; + break; + } + for (n = 0; n < sizeof(esc_char); n++) { + if (c != esc_char[n]) + continue; + /* found it */ + c = esc_tran[n]; + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + } + ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC; + /* illegal escape char */ + goto reject; + + case LEJP_MP_STRING_ESC_U1: + case LEJP_MP_STRING_ESC_U2: + case LEJP_MP_STRING_ESC_U3: + case LEJP_MP_STRING_ESC_U4: + ctx->uni <<= 4; + if (c >= '0' && c <= '9') + ctx->uni |= c - '0'; + else + if (c >= 'a' && c <= 'f') + ctx->uni = c - 'a' + 10; + else + if (c >= 'A' && c <= 'F') + ctx->uni = c - 'A' + 10; + else { + ret = LEJP_REJECT_ILLEGAL_HEX; + goto reject; + } + ctx->st[ctx->sp].s++; + switch (s) { + case LEJP_MP_STRING_ESC_U2: + if (ctx->uni < 0x08) + break; + /* + * 0x08-0xff (0x0800 - 0xffff) + * emit 3-byte UTF-8 + */ + c = 0xe0 | ((ctx->uni >> 4) & 0xf); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U3: + if (ctx->uni >= 0x080) { + /* + * 0x080 - 0xfff (0x0800 - 0xffff) + * middle 3-byte seq + * send ....XXXXXX.. + */ + c = 0x80 | ((ctx->uni >> 2) & 0x3f); + goto emit_string_char; + } + if (ctx->uni < 0x008) + break; + /* + * 0x008 - 0x7f (0x0080 - 0x07ff) + * start 2-byte seq + */ + c = 0xc0 | (ctx->uni >> 2); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U4: + if (ctx->uni >= 0x0080) + /* end of 2 or 3-byte seq */ + c = 0x80 | (ctx->uni & 0x3f); + else + /* literal */ + c = (unsigned char)ctx->uni; + + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + default: + break; + } + break; + + case LEJP_MP_DELIM: + if (c != ':') { + ret = LEJP_REJECT_MP_DELIM_MISSING_COLON; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + ctx->path[ctx->ppos] = '\0'; + + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + + case LEJP_MP_VALUE: + if (c >= '0' && c <= '9') { + ctx->npos = 0; + ctx->dcount = 0; + ctx->f = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + goto redo_character; + } + switch (c) { + case'\"': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MP_STRING; + ctx->npos = 0; + ctx->buf[0] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + goto add_stack_level; + + case '{': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MEMBERS; + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->path_match = 0; + goto add_stack_level; + + case '[': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; + c = LEJP_MP_VALUE; + ctx->path[ctx->ppos++] = '['; + ctx->path[ctx->ppos++] = ']'; + ctx->path[ctx->ppos] = '\0'; + if (ctx->callback(ctx, LEJPCB_ARRAY_START)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->i[ctx->ipos++] = 0; + if (ctx->ipos > ARRAY_SIZE(ctx->i)) { + ret = LEJP_REJECT_MP_DELIM_ISTACK; + goto reject; + } + goto add_stack_level; + + case 't': /* true */ + ctx->uni = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'f': + ctx->uni = 4; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'n': + ctx->uni = 4 + 5; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + default: + ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START; + goto reject; + } + break; + + case LEJP_MP_VALUE_NUM_INT: + if (!ctx->npos && c == '-') { + ctx->f |= LEJP_SEEN_MINUS; + goto append_npos; + } + + if (ctx->dcount < 10 && c >= '0' && c <= '9') { + if (ctx->f & LEJP_SEEN_POINT) + ctx->f |= LEJP_SEEN_POST_POINT; + ctx->dcount++; + goto append_npos; + } + if (c == '.') { + if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_POINT; + goto append_npos; + } + /* + * before exponent, if we had . we must have had at + * least one more digit + */ + if ((ctx->f & + (LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) == + LEJP_SEEN_POINT) { + ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC; + goto reject; + } + if (c == 'e' || c == 'E') { + if (ctx->f & LEJP_SEEN_EXP) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_EXP; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP; + goto append_npos; + } + /* if none of the above, did we even have a number? */ + if (!ctx->dcount) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + + ctx->buf[ctx->npos] = '\0'; + if (ctx->f & LEJP_SEEN_POINT) { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } else { + if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + } + + /* then this is the post-number character, loop */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + goto redo_character; + + case LEJP_MP_VALUE_NUM_EXP: + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + if (c >= '0' && c <= '9') + goto redo_character; + if (c == '+' || c == '-') + goto append_npos; + ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP; + goto reject; + + case LEJP_MP_VALUE_TOK: /* true, false, null */ + if (c != tokens[ctx->uni]) { + ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN; + goto reject; + } + ctx->uni++; + if (tokens[ctx->uni] != ' ') + break; + switch (ctx->uni) { + case 3: + ctx->buf[0] = '1'; + ctx->buf[1] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + case 8: + ctx->buf[0] = '0'; + ctx->buf[1] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + case 12: + ctx->buf[0] = '\0'; + if (ctx->callback(ctx, LEJPCB_VAL_NULL)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + } + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + break; + + case LEJP_MP_COMMA_OR_END: + ctx->path[ctx->ppos] = '\0'; + if (c == ',') { + /* increment this stack level's index */ + ctx->st[ctx->sp].s = LEJP_M_P; + if (!ctx->sp) { + ctx->ppos = 0; + /* + * since we came back to root level, + * no path can still match + */ + ctx->path_match = 0; + break; + } + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + + if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END) + break; + /* top level is definitely an array... */ + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + break; + } + if (c == ']') { + if (!ctx->sp) { + ret = LEJP_REJECT_MP_C_OR_E_UNDERF; + goto reject; + } + /* pop */ + ctx->sp--; + if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { + ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; + goto reject; + } + /* drop the path [n] bit */ + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + + /* do LEJP_MP_ARRAY_END processing */ + goto redo_character; + } + if (c == '}') { + if (ctx->sp == 0) { + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->callback(ctx, LEJPCB_COMPLETE); + /* done, return unused amount */ + return len; + } + /* pop */ + ctx->sp--; + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + ctx->path[ctx->ppos] = '\0'; + if (ctx->path_match && + ctx->ppos <= ctx->path_match_len) + /* + * we shrank the path to be + * smaller than the matching point + */ + ctx->path_match = 0; + lejp_check_path_match(ctx); + if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + break; + } + + ret = LEJP_REJECT_MP_C_OR_E_NEITHER; + goto reject; + + case LEJP_MP_ARRAY_END: + ctx->path[ctx->ppos] = '\0'; + if (c == ',') { + /* increment this stack level's index */ + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + if (ctx->sp) + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->ppos] = '\0'; + break; + } + if (c != ']') { + ret = LEJP_REJECT_MP_ARRAY_END_MISSING; + goto reject; + } + + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + ctx->callback(ctx, LEJPCB_ARRAY_END); + break; + } + + continue; + +emit_string_char: + if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { + /* assemble the string value into chunks */ + ctx->buf[ctx->npos++] = c; + if (ctx->npos == sizeof(ctx->buf) - 1) { + if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) { + ret = LEJP_REJECT_CALLBACK; + goto reject; + } + ctx->npos = 0; + } + continue; + } + /* name part of name:value pair */ + ctx->path[ctx->ppos++] = c; + continue; + +add_stack_level: + /* push on to the object stack */ + if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && + ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) + ctx->path[ctx->ppos++] = '.'; + + ctx->st[ctx->sp].p = ctx->ppos; + ctx->st[ctx->sp].i = ctx->ipos; + if (++ctx->sp == ARRAY_SIZE(ctx->st)) { + ret = LEJP_REJECT_STACK_OVERFLOW; + goto reject; + } + ctx->path[ctx->ppos] = '\0'; + ctx->st[ctx->sp].s = c; + ctx->st[ctx->sp].b = 0; + continue; + +append_npos: + if (ctx->npos >= sizeof(ctx->buf)) { + ret = LEJP_REJECT_NUM_TOO_LONG; + goto reject; + } + ctx->buf[ctx->npos++] = c; + continue; + +redo_character: + json--; + len++; + } + + return LEJP_CONTINUE; + +reject: + ctx->callback(ctx, LEJPCB_FAILED); + return ret; +} diff --git a/thirdparty/lws/misc/lejp.h b/thirdparty/lws/misc/lejp.h new file mode 100644 index 0000000000..0b37bb3e42 --- /dev/null +++ b/thirdparty/lws/misc/lejp.h @@ -0,0 +1,232 @@ +#include "libwebsockets.h" +struct lejp_ctx; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) +#endif +#define LEJP_FLAG_WS_KEEP 64 +#define LEJP_FLAG_WS_COMMENTLINE 32 + +enum lejp_states { + LEJP_IDLE = 0, + LEJP_MEMBERS = 1, + LEJP_M_P = 2, + LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3, + LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4, + LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5, + LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6, + LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7, + LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8, + LEJP_MP_DELIM = 9, + LEJP_MP_VALUE = 10, + LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11, + LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12, + LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13, + LEJP_MP_COMMA_OR_END = 14, + LEJP_MP_ARRAY_END = 15, +}; + +enum lejp_reasons { + LEJP_CONTINUE = -1, + LEJP_REJECT_IDLE_NO_BRACE = -2, + LEJP_REJECT_MEMBERS_NO_CLOSE = -3, + LEJP_REJECT_MP_NO_OPEN_QUOTE = -4, + LEJP_REJECT_MP_STRING_UNDERRUN = -5, + LEJP_REJECT_MP_ILLEGAL_CTRL = -6, + LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7, + LEJP_REJECT_ILLEGAL_HEX = -8, + LEJP_REJECT_MP_DELIM_MISSING_COLON = -9, + LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10, + LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11, + LEJP_REJECT_MP_VAL_NUM_FORMAT = -12, + LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13, + LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14, + LEJP_REJECT_MP_C_OR_E_UNDERF = -15, + LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16, + LEJP_REJECT_MP_ARRAY_END_MISSING = -17, + LEJP_REJECT_STACK_OVERFLOW = -18, + LEJP_REJECT_MP_DELIM_ISTACK = -19, + LEJP_REJECT_NUM_TOO_LONG = -20, + LEJP_REJECT_MP_C_OR_E_NEITHER = -21, + LEJP_REJECT_UNKNOWN = -22, + LEJP_REJECT_CALLBACK = -23 +}; + +#define LEJP_FLAG_CB_IS_VALUE 64 + +enum lejp_callbacks { + LEJPCB_CONSTRUCTED = 0, + LEJPCB_DESTRUCTED = 1, + + LEJPCB_START = 2, + LEJPCB_COMPLETE = 3, + LEJPCB_FAILED = 4, + + LEJPCB_PAIR_NAME = 5, + + LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6, + LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7, + LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8, + LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9, + LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10, + LEJPCB_VAL_STR_START = 11, /* notice handle separately */ + LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12, + LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13, + + LEJPCB_ARRAY_START = 14, + LEJPCB_ARRAY_END = 15, + + LEJPCB_OBJECT_START = 16, + LEJPCB_OBJECT_END = 17 +}; + +/** + * _lejp_callback() - User parser actions + * \param ctx: LEJP context + * \param reason: Callback reason + * + * Your user callback is associated with the context at construction time, + * and receives calls as the parsing progresses. + * + * All of the callbacks may be ignored and just return 0. + * + * The reasons it might get called, found in @reason, are: + * + * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to + * perform one-time allocation for the life of the context. + * + * LEJPCB_DESTRUCTED: The context is being destructed... if you made any + * allocations at construction-time, you can free them now + * + * LEJPCB_START: Parsing is beginning at the first byte of input + * + * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or + * positive return code from lejp_parse indicating the + * amount of unused bytes left in the input buffer + * + * LEJPCB_FAILED: Parsing failed. You'll get a negative error code + * returned from lejp_parse + * + * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed, + * this callback occurs. You can find the new name at + * the end of ctx->path[] + * + * LEJPCB_VAL_TRUE: The "true" value appeared + * + * LEJPCB_VAL_FALSE: The "false" value appeared + * + * LEJPCB_VAL_NULL: The "null" value appeared + * + * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf + * + * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf + * + * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet + * + * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in + * ctx->buf, which is as much as we can buffer, so we are + * spilling it. If all your strings are less than + * LEJP_STRING_CHUNK - 1 bytes, you will never see this + * callback. + * + * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the + * string is in ctx->buf. + * + * LEJPCB_ARRAY_START: An array started + * + * LEJPCB_ARRAY_END: An array ended + * + * LEJPCB_OBJECT_START: An object started + * + * LEJPCB_OBJECT_END: An object ended + */ +LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason); + +typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); + +#ifndef LEJP_MAX_DEPTH +#define LEJP_MAX_DEPTH 12 +#endif +#ifndef LEJP_MAX_INDEX_DEPTH +#define LEJP_MAX_INDEX_DEPTH 5 +#endif +#ifndef LEJP_MAX_PATH +#define LEJP_MAX_PATH 128 +#endif +#ifndef LEJP_STRING_CHUNK +/* must be >= 30 to assemble floats */ +#define LEJP_STRING_CHUNK 255 +#endif + +enum num_flags { + LEJP_SEEN_MINUS = (1 << 0), + LEJP_SEEN_POINT = (1 << 1), + LEJP_SEEN_POST_POINT = (1 << 2), + LEJP_SEEN_EXP = (1 << 3) +}; + +struct _lejp_stack { + char s; /* lejp_state stack*/ + char p; /* path length */ + char i; /* index array length */ + char b; /* user bitfield */ +}; + +struct lejp_ctx { + + /* sorted by type for most compact alignment + * + * pointers + */ + + signed char (*callback)(struct lejp_ctx *ctx, char reason); + void *user; + const char * const *paths; + + /* arrays */ + + struct _lejp_stack st[LEJP_MAX_DEPTH]; + unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */ + unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ + char path[LEJP_MAX_PATH]; + char buf[LEJP_STRING_CHUNK]; + + /* int */ + + unsigned int line; + + /* short */ + + unsigned short uni; + + /* char */ + + unsigned char npos; + unsigned char dcount; + unsigned char f; + unsigned char sp; /* stack head */ + unsigned char ipos; /* index stack depth */ + unsigned char ppos; + unsigned char count_paths; + unsigned char path_match; + unsigned char path_match_len; + unsigned char wildcount; +}; + +LWS_VISIBLE LWS_EXTERN void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), + void *user, const char * const *paths, unsigned char paths_count); + +LWS_VISIBLE LWS_EXTERN void +lejp_destruct(struct lejp_ctx *ctx); + +LWS_VISIBLE LWS_EXTERN int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len); + +LWS_VISIBLE LWS_EXTERN void +lejp_change_callback(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason)); + +LWS_VISIBLE LWS_EXTERN int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len); diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/lws/misc/sha-1.c new file mode 100644 index 0000000000..9353fbefe4 --- /dev/null +++ b/thirdparty/lws/misc/sha-1.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) + * based on: http://csrc.nist.gov/fips/fip180-1.txt + * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org> + */ + +#include "private-libwebsockets.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +struct sha1_ctxt { + union { + unsigned char b8[20]; + unsigned int b32[5]; + } h; + union { + unsigned char b8[8]; + u_int64_t b64[1]; + } c; + union { + unsigned char b8[64]; + unsigned int b32[16]; + } m; + unsigned char count; +}; + +/* sanity check */ +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) +# define unsupported 1 +#elif BYTE_ORDER != BIG_ENDIAN +# if BYTE_ORDER != LITTLE_ENDIAN +# define unsupported 1 +# endif +#endif + +#ifndef unsupported + +/* constant table */ +static const unsigned int _K[] = + { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; +#define K(t) _K[(t) / 20] + +#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F1(b, c, d) (((b) ^ (c)) ^ (d)) +#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) +#define F3(b, c, d) (((b) ^ (c)) ^ (d)) + +#define S(n, x) (((x) << (n)) | ((x) >> (32 - n))) + +#define H(n) (ctxt->h.b32[(n)]) +#define COUNT (ctxt->count) +#define BCOUNT (ctxt->c.b64[0] / 8) +#define W(n) (ctxt->m.b32[(n)]) + +#define PUTBYTE(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + ctxt->c.b64[0] += 8; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + +#define PUTPAD(x) { \ + ctxt->m.b8[(COUNT % 64)] = (x); \ + COUNT++; \ + COUNT %= 64; \ + if (COUNT % 64 == 0) \ + sha1_step(ctxt); \ + } + + +static void +sha1_step(struct sha1_ctxt *ctxt) +{ + unsigned int a, b, c, d, e, tmp; + size_t t, s; + +#if BYTE_ORDER == LITTLE_ENDIAN + struct sha1_ctxt tctxt; + + memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64); + ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2]; + ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0]; + ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6]; + ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4]; + ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10]; + ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8]; + ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14]; + ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12]; + ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18]; + ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16]; + ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22]; + ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20]; + ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26]; + ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24]; + ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30]; + ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28]; + ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34]; + ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32]; + ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38]; + ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36]; + ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42]; + ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40]; + ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46]; + ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44]; + ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50]; + ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48]; + ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54]; + ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52]; + ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58]; + ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56]; + ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62]; + ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60]; +#endif + + a = H(0); b = H(1); c = H(2); d = H(3); e = H(4); + + for (t = 0; t < 20; t++) { + s = t & 0x0f; + if (t >= 16) + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + + tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 20; t < 40; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 40; t < 60; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + for (t = 60; t < 80; t++) { + s = t & 0x0f; + W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ + W((s+2) & 0x0f) ^ W(s)); + tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); + e = d; d = c; c = S(30, b); b = a; a = tmp; + } + + H(0) = H(0) + a; + H(1) = H(1) + b; + H(2) = H(2) + c; + H(3) = H(3) + d; + H(4) = H(4) + e; + + bzero(&ctxt->m.b8[0], 64); +} + +/*------------------------------------------------------------*/ + +static void +_sha1_init(struct sha1_ctxt *ctxt) +{ + bzero(ctxt, sizeof(struct sha1_ctxt)); + H(0) = 0x67452301; + H(1) = 0xefcdab89; + H(2) = 0x98badcfe; + H(3) = 0x10325476; + H(4) = 0xc3d2e1f0; +} + +void +sha1_pad(struct sha1_ctxt *ctxt) +{ + size_t padlen; /*pad length in bytes*/ + size_t padstart; + + PUTPAD(0x80); + + padstart = COUNT % 64; + padlen = 64 - padstart; + if (padlen < 8) { + bzero(&ctxt->m.b8[padstart], padlen); + COUNT += (unsigned char)padlen; + COUNT %= 64; + sha1_step(ctxt); + padstart = COUNT % 64; /* should be 0 */ + padlen = 64 - padstart; /* should be 64 */ + } + bzero(&ctxt->m.b8[padstart], padlen - 8); + COUNT += ((unsigned char)padlen - 8); + COUNT %= 64; +#if BYTE_ORDER == BIG_ENDIAN + PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]); + PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]); + PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]); + PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]); +#else + PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]); + PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]); + PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]); + PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]); +#endif +} + +void +sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len) +{ + size_t gaplen; + size_t gapstart; + size_t off; + size_t copysiz; + + off = 0; + + while (off < len) { + gapstart = COUNT % 64; + gaplen = 64 - gapstart; + + copysiz = (gaplen < len - off) ? gaplen : len - off; + memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz); + COUNT += (unsigned char)copysiz; + COUNT %= 64; + ctxt->c.b64[0] += copysiz * 8; + if (COUNT % 64 == 0) + sha1_step(ctxt); + off += copysiz; + } +} + +void +sha1_result(struct sha1_ctxt *ctxt, void *digest0) +{ + unsigned char *digest; + + digest = (unsigned char *)digest0; + sha1_pad(ctxt); +#if BYTE_ORDER == BIG_ENDIAN + memcpy(digest, &ctxt->h.b8[0], 20); +#else + digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; + digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; + digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; + digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; + digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; + digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; + digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; + digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; + digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; + digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; +#endif +} + +/* + * This should look and work like the libcrypto implementation + */ + +LWS_VISIBLE unsigned char * +lws_SHA1(const unsigned char *d, size_t n, unsigned char *md) +{ + struct sha1_ctxt ctx; + + _sha1_init(&ctx); + sha1_loop(&ctx, d, n); + sha1_result(&ctx, (void *)md); + + return md; +} + +#endif /*unsupported*/ diff --git a/thirdparty/lws/output.c b/thirdparty/lws/output.c new file mode 100644 index 0000000000..ed4752490e --- /dev/null +++ b/thirdparty/lws/output.c @@ -0,0 +1,877 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +static int +lws_0405_frame_mask_generate(struct lws *wsi) +{ +#if 0 + wsi->u.ws.mask[0] = 0; + wsi->u.ws.mask[1] = 0; + wsi->u.ws.mask[2] = 0; + wsi->u.ws.mask[3] = 0; +#else + int n; + /* fetch the per-frame nonce */ + + n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4); + if (n != 4) { + lwsl_parser("Unable to read from random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, n); + return 1; + } +#endif + /* start masking from first byte of masking key buffer */ + wsi->u.ws.mask_idx = 0; + + return 0; +} + +/* + * notice this returns number of bytes consumed, or -1 + */ +int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + size_t real_len = len; + unsigned int n; + int m; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1); + + if (!len) + return 0; + /* just ignore sends after we cleared the truncation buffer */ + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + !wsi->trunc_len) + return len; + + if (wsi->trunc_len && (buf < wsi->trunc_alloc || + buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) { + char dump[20]; + strncpy(dump, (char *)buf, sizeof(dump) - 1); + dump[sizeof(dump) - 1] = '\0'; +#if defined(LWS_WITH_ESP8266) + lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n", + wsi, (unsigned long)len, dump); +#else + lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n" + " It's illegal to do an lws_write outside of\n" + " the writable callback: fix your code\n", + wsi, (unsigned long)len, dump); +#endif + assert(0); + + return -1; + } + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len); + if (m < 0) + return -1; + if (m) /* handled */ { + n = m; + goto handle_truncated_send; + } + + if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd)) + lwsl_warn("** error invalid sock but expected to send\n"); + + /* limit sending */ + if (wsi->protocol->tx_packet_size) + n = wsi->protocol->tx_packet_size; + else { + n = wsi->protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + } + n += LWS_PRE + 4; + if (n > len) + n = len; +#if defined(LWS_WITH_ESP8266) + if (wsi->pending_send_completion) { + n = 0; + goto handle_truncated_send; + } +#endif + + /* nope, send it on the socket directly */ + lws_latency_pre(context, wsi); + n = lws_ssl_capable_write(wsi, buf, n); + lws_latency(context, wsi, "send lws_issue_raw", n, n == len); + + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + /* we're going to close, let close know sends aren't possible */ + wsi->socket_is_permanently_unusable = 1; + return -1; + case LWS_SSL_CAPABLE_MORE_SERVICE: + /* nothing got sent, not fatal, retry the whole thing later */ + n = 0; + break; + } + +handle_truncated_send: + /* + * we were already handling a truncated send? + */ + if (wsi->trunc_len) { + lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len); + wsi->trunc_offset += n; + wsi->trunc_len -= n; + + if (!wsi->trunc_len) { + lwsl_info("***** %p partial send completed\n", wsi); + /* done with it, but don't free it */ + n = real_len; + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + lwsl_info("***** %p signalling to close now\n", wsi); + return -1; /* retry closing now */ + } + } + /* always callback on writeable */ + lws_callback_on_writable(wsi); + + return n; + } + + if ((unsigned int)n == real_len) + /* what we just sent went out cleanly */ + return n; + + /* + * Newly truncated send. Buffer the remainder (it will get + * first priority next time the socket is writable) + */ + lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n, + (unsigned long)real_len); + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n); + + /* + * - if we still have a suitable malloc lying around, use it + * - or, if too small, reallocate it + * - or, if no buffer, create it + */ + if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) { + lws_free(wsi->trunc_alloc); + + wsi->trunc_alloc_len = real_len - n; + wsi->trunc_alloc = lws_malloc(real_len - n, "truncated send alloc"); + if (!wsi->trunc_alloc) { + lwsl_err("truncated send: unable to malloc %lu\n", + (unsigned long)(real_len - n)); + return -1; + } + } + wsi->trunc_offset = 0; + wsi->trunc_len = real_len - n; + memcpy(wsi->trunc_alloc, buf + n, real_len - n); + + /* since something buffered, force it to get another chance to send */ + lws_callback_on_writable(wsi); + + return real_len; +} + +LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len, + enum lws_write_protocol wp) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int masked7 = (wsi->mode == LWSCM_WS_CLIENT); + unsigned char is_masked_bit = 0; + unsigned char *dropmask = NULL; + struct lws_tokens eff_buf; + size_t orig_len = len; + int pre = 0, n; + + if (wsi->parent_carries_io) { + struct lws_write_passthru pas; + + pas.buf = buf; + pas.len = len; + pas.wp = wp; + pas.wsi = wsi; + + if (wsi->parent->protocol->callback(wsi->parent, + LWS_CALLBACK_CHILD_WRITE_VIA_PARENT, + wsi->parent->user_space, + (void *)&pas, 0)) + return 1; + + return len; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1); + + if ((int)len < 0) { + lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__, + (int)len, (unsigned long)len); + return -1; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len); + +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent += len; +#endif + if (wsi->vhost) + wsi->vhost->conn_stats.tx += len; + + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + /* remove us from the list */ + struct lws **w = &pt->tx_draining_ext_list; + + wsi->u.ws.tx_draining_ext = 0; + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + *w = wsi->u.ws.tx_draining_ext_list; + break; + } + w = &((*w)->u.ws.tx_draining_ext_list); + } + wsi->u.ws.tx_draining_ext_list = NULL; + wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) | + LWS_WRITE_CONTINUATION; + + lwsl_ext("FORCED draining wp to 0x%02X\n", wp); + } + + lws_restart_ws_ping_pong_timer(wsi); + + if ((wp & 0x1f) == LWS_WRITE_HTTP || + (wp & 0x1f) == LWS_WRITE_HTTP_FINAL || + (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION || + (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) + goto send_raw; + + /* if not in a state to send stuff, then just send nothing */ + + if (wsi->state != LWSS_ESTABLISHED && + ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY && + wsi->state != LWSS_AWAITING_CLOSE_ACK) || + wp != LWS_WRITE_CLOSE)) + return 0; + + /* if we are continuing a frame that already had its header done */ + + if (wsi->u.ws.inside_frame) { + lwsl_debug("INSIDE FRAME\n"); + goto do_more_inside_frame; + } + + wsi->u.ws.clean_buffer = 1; + + /* + * give a chance to the extensions to modify payload + * the extension may decide to produce unlimited payload erratically + * (eg, compression extension), so we require only that if he produces + * something, it will be a complete fragment of the length known at + * the time (just the fragment length known), and if he has + * more we will come back next time he is writeable and allow him to + * produce more fragments until he's drained. + * + * This allows what is sent each time it is writeable to be limited to + * a size that can be sent without partial sends or blocking, allows + * interleaving of control frames and other connection service. + */ + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + switch ((int)wp) { + case LWS_WRITE_PING: + case LWS_WRITE_PONG: + case LWS_WRITE_CLOSE: + break; + default: + lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n"); + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp); + if (n < 0) + return -1; + + if (n && eff_buf.token_len) { + lwsl_debug("drain len %d\n", (int)eff_buf.token_len); + /* extension requires further draining */ + wsi->u.ws.tx_draining_ext = 1; + wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list; + pt->tx_draining_ext_list = wsi; + /* we must come back to do more */ + lws_callback_on_writable(wsi); + /* + * keep a copy of the write type for the overall + * action that has provoked generation of these + * fragments, so the last guy can use its FIN state. + */ + wsi->u.ws.tx_draining_stashed_wp = wp; + /* this is definitely not actually the last fragment + * because the extension asserted he has more coming + * So make sure this intermediate one doesn't go out + * with a FIN. + */ + wp |= LWS_WRITE_NO_FIN; + } + + if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) { + wsi->u.ws.stashed_write_pending = 0; + wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type; + } + } + + /* + * an extension did something we need to keep... for example, if + * compression extension, it has already updated its state according + * to this being issued + */ + if ((char *)buf != eff_buf.token) { + /* + * ext might eat it, but not have anything to issue yet. + * In that case we have to follow his lead, but stash and + * replace the write type that was lost here the first time. + */ + if (len && !eff_buf.token_len) { + if (!wsi->u.ws.stashed_write_pending) + wsi->u.ws.stashed_write_type = (char)wp & 0x3f; + wsi->u.ws.stashed_write_pending = 1; + return len; + } + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + } + + buf = (unsigned char *)eff_buf.token; + len = eff_buf.token_len; + + if (!buf) { + lwsl_err("null buf (%d)\n", (int)len); + return -1; + } + + switch (wsi->ietf_spec_revision) { + case 13: + if (masked7) { + pre += 4; + dropmask = &buf[0 - pre]; + is_masked_bit = 0x80; + } + + switch (wp & 0xf) { + case LWS_WRITE_TEXT: + n = LWSWSOPC_TEXT_FRAME; + break; + case LWS_WRITE_BINARY: + n = LWSWSOPC_BINARY_FRAME; + break; + case LWS_WRITE_CONTINUATION: + n = LWSWSOPC_CONTINUATION; + break; + + case LWS_WRITE_CLOSE: + n = LWSWSOPC_CLOSE; + break; + case LWS_WRITE_PING: + n = LWSWSOPC_PING; + break; + case LWS_WRITE_PONG: + n = LWSWSOPC_PONG; + break; + default: + lwsl_warn("lws_write: unknown write opc / wp\n"); + return -1; + } + + if (!(wp & LWS_WRITE_NO_FIN)) + n |= 1 << 7; + + if (len < 126) { + pre += 2; + buf[-pre] = n; + buf[-pre + 1] = (unsigned char)(len | is_masked_bit); + } else { + if (len < 65536) { + pre += 4; + buf[-pre] = n; + buf[-pre + 1] = 126 | is_masked_bit; + buf[-pre + 2] = (unsigned char)(len >> 8); + buf[-pre + 3] = (unsigned char)len; + } else { + pre += 10; + buf[-pre] = n; + buf[-pre + 1] = 127 | is_masked_bit; +#if defined __LP64__ + buf[-pre + 2] = (len >> 56) & 0x7f; + buf[-pre + 3] = len >> 48; + buf[-pre + 4] = len >> 40; + buf[-pre + 5] = len >> 32; +#else + buf[-pre + 2] = 0; + buf[-pre + 3] = 0; + buf[-pre + 4] = 0; + buf[-pre + 5] = 0; +#endif + buf[-pre + 6] = (unsigned char)(len >> 24); + buf[-pre + 7] = (unsigned char)(len >> 16); + buf[-pre + 8] = (unsigned char)(len >> 8); + buf[-pre + 9] = (unsigned char)len; + } + } + break; + } + +do_more_inside_frame: + + /* + * Deal with masking if we are in client -> server direction and + * the wp demands it + */ + + if (masked7) { + if (!wsi->u.ws.inside_frame) + if (lws_0405_frame_mask_generate(wsi)) { + lwsl_err("frame mask generation failed\n"); + return -1; + } + + /* + * in v7, just mask the payload + */ + if (dropmask) { /* never set if already inside frame */ + for (n = 4; n < (int)len + 4; n++) + dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + /* copy the frame nonce into place */ + memcpy(dropmask, wsi->u.ws.mask, 4); + } + } + +send_raw: + switch ((int)(wp & 0x1f)) { + case LWS_WRITE_CLOSE: +/* lwsl_hexdump(&buf[-pre], len); */ + case LWS_WRITE_HTTP: + case LWS_WRITE_HTTP_FINAL: + case LWS_WRITE_HTTP_HEADERS: + case LWS_WRITE_HTTP_HEADERS_CONTINUATION: + case LWS_WRITE_PONG: + case LWS_WRITE_PING: +#ifdef LWS_WITH_HTTP2 + if (wsi->mode == LWSCM_HTTP2_SERVING) { + unsigned char flags = 0; + + n = LWS_H2_FRAME_TYPE_DATA; + if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) { + n = LWS_H2_FRAME_TYPE_HEADERS; + if (!(wp & LWS_WRITE_NO_FIN)) + flags = LWS_H2_FLAG_END_HEADERS; + if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) { + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + } + + if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) { + n = LWS_H2_FRAME_TYPE_CONTINUATION; + if (!(wp & LWS_WRITE_NO_FIN)) + flags = LWS_H2_FLAG_END_HEADERS; + if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) { + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + } + + if (((wp & 0x1f) == LWS_WRITE_HTTP || + (wp & 0x1f) == LWS_WRITE_HTTP_FINAL) && + wsi->u.http.tx_content_length) { + wsi->u.http.tx_content_remain -= len; + lwsl_info("%s: content_remain = %llu\n", __func__, + (unsigned long long)wsi->u.http.tx_content_remain); + if (!wsi->u.http.tx_content_remain) { + lwsl_info("%s: selecting final write mode\n", __func__); + wp = LWS_WRITE_HTTP_FINAL; + } + } + + if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) { + //lws_get_network_wsi(wsi)->u.h2.END_STREAM) { + lwsl_info("%s: setting END_STREAM\n", __func__); + flags |= LWS_H2_FLAG_END_STREAM; + wsi->u.h2.send_END_STREAM = 1; + } + + return lws_h2_frame_write(wsi, n, flags, + wsi->u.h2.my_sid, len, buf); + } +#endif + return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre); + default: + break; + } + + /* + * give any active extensions a chance to munge the buffer + * before send. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + * + * callback returns 1 in case it wants to spill more buffers + * + * This takes care of holding the buffer if send is incomplete, ie, + * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with + * the buffer). If wsi->u.ws.clean_buffer is 1, it will instead + * return to the user code how much OF THE USER BUFFER was consumed. + */ + + n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre); + wsi->u.ws.inside_frame = 1; + if (n <= 0) + return n; + + if (n == (int)len + pre) { + /* everything in the buffer was handled (or rebuffered...) */ + wsi->u.ws.inside_frame = 0; + return orig_len; + } + + /* + * it is how many bytes of user buffer got sent... may be < orig_len + * in which case callback when writable has already been arranged + * and user code can call lws_write() again with the rest + * later. + */ + + return n - pre; +} + +LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_process_html_args args; + lws_filepos_t amount, poss; + unsigned char *p, *pstart; +#if defined(LWS_WITH_RANGES) + unsigned char finished = 0; +#endif + int n, m; + + lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream); + + while (!lws_send_pipe_choked(wsi)) { + + if (wsi->trunc_len) { + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s: closing\n", __func__); + goto file_had_it; + } + continue; + } + + if (wsi->u.http.filepos == wsi->u.http.filelen) + goto all_sent; + + n = 0; + + pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH; + + p = pstart; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) { + + lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start); + + if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd, + wsi->u.http.range.start - + wsi->u.http.filepos) < 0) + goto file_had_it; + + wsi->u.http.filepos = wsi->u.http.range.start; + + if (wsi->u.http.range.count_ranges > 1) { + n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH, + "_lws\x0d\x0a" + "Content-Type: %s\x0d\x0a" + "Content-Range: bytes %llu-%llu/%llu\x0d\x0a" + "\x0d\x0a", + wsi->u.http.multipart_content_type, + wsi->u.http.range.start, + wsi->u.http.range.end, + wsi->u.http.range.extent); + p += n; + } + + wsi->u.http.range.budget = wsi->u.http.range.end - + wsi->u.http.range.start + 1; + wsi->u.http.range.inside = 1; + } +#endif + + poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH; + + /* + * if there is a hint about how much we will do well to send at one time, + * restrict ourselves to only trying to send that. + */ + if (wsi->protocol->tx_packet_size && + poss > wsi->protocol->tx_packet_size) + poss = wsi->protocol->tx_packet_size; + +#if defined(LWS_WITH_HTTP2) + m = lws_h2_tx_cr_get(wsi); + if (!m) { + lwsl_info("%s: came here with no tx credit", __func__); + return 0; + } + if (m < poss) + poss = m; + /* + * consumption of the actual payload amount sent will be handled + * when the http2 data frame is sent + */ +#endif + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges) { + if (wsi->u.http.range.count_ranges > 1) + poss -= 7; /* allow for final boundary */ + if (poss > wsi->u.http.range.budget) + poss = wsi->u.http.range.budget; + } +#endif + if (wsi->sending_chunked) { + /* we need to drop the chunk size in here */ + p += 10; + /* allow for the chunk to grow by 128 in translation */ + poss -= 10 + 128; + } + + if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0) + goto file_had_it; /* caller will close */ + + if (wsi->sending_chunked) + n = (int)amount; + else + n = (p - pstart) + (int)amount; + + lwsl_debug("%s: sending %d\n", __func__, n); + + if (n) { + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + context->timeout_secs); + + if (wsi->sending_chunked) { + args.p = (char *)p; + args.len = n; + args.max_len = (unsigned int)poss + 128; + args.final = wsi->u.http.filepos + n == + wsi->u.http.filelen; + if (user_callback_handle_rxflow( + wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi, + LWS_CALLBACK_PROCESS_HTML, + wsi->user_space, &args, 0) < 0) + goto file_had_it; + n = args.len; + p = (unsigned char *)args.p; + } else + p = pstart; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.send_ctr + 1 == + wsi->u.http.range.count_ranges && // last range + wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart) + wsi->u.http.range.budget - amount == 0) {// final part + n += lws_snprintf((char *)pstart + n, 6, + "_lws\x0d\x0a"); // append trailing boundary + lwsl_debug("added trailing boundary\n"); + } +#endif + m = lws_write(wsi, p, n, + wsi->u.http.filepos == wsi->u.http.filelen ? + LWS_WRITE_HTTP_FINAL : + LWS_WRITE_HTTP + ); + if (m < 0) + goto file_had_it; + + wsi->u.http.filepos += amount; + +#if defined(LWS_WITH_RANGES) + if (wsi->u.http.range.count_ranges >= 1) { + wsi->u.http.range.budget -= amount; + if (wsi->u.http.range.budget == 0) { + lwsl_notice("range budget exhausted\n"); + wsi->u.http.range.inside = 0; + wsi->u.http.range.send_ctr++; + + if (lws_ranges_next(&wsi->u.http.range) < 1) { + finished = 1; + goto all_sent; + } + } + } +#endif + + if (m != n) { + /* adjust for what was not sent */ + if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd, + m - n) == + (unsigned long)-1) + goto file_had_it; + } + } + +all_sent: + if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen) +#if defined(LWS_WITH_RANGES) + || finished) +#else + ) +#endif + { + wsi->state = LWSS_HTTP; + /* we might be in keepalive, so close it off here */ + lws_vfs_file_close(&wsi->u.http.fop_fd); + + lwsl_debug("file completed\n"); + + if (wsi->protocol->callback && + user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, + wsi->user_space, NULL, + 0) < 0) { + /* + * For http/1.x, the choices from + * transaction_completed are either + * 0 to use the connection for pipelined + * or nonzero to hang it up. + * + * However for http/2. while we are + * still interested in hanging up the + * nwsi if there was a network-level + * fatal error, simply completing the + * transaction is a matter of the stream + * state, not the root connection at the + * network level + */ + if (wsi->http2_substream) + return 1; + else + return -1; + } + + return 1; /* >0 indicates completed */ + } + } + + lws_callback_on_writable(wsi); + + return 0; /* indicates further processing must be done */ + +file_had_it: + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return -1; +} + +#if LWS_POSIX +LWS_VISIBLE int +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n; + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1); + + n = recv(wsi->desc.sockfd, (char *)buf, len, 0); + if (n >= 0) { + if (wsi->vhost) + wsi->vhost->conn_stats.rx += n; + lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); + lws_restart_ws_ping_pong_timer(wsi); + return n; + } +#if LWS_POSIX + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) + return LWS_SSL_CAPABLE_MORE_SERVICE; +#endif + lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len) +{ + int n = 0; + +#if LWS_POSIX + n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL); +// lwsl_info("%s: sent len %d result %d", __func__, len, n); + if (n >= 0) + return n; + + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) { + if (LWS_ERRNO == LWS_EWOULDBLOCK) { + lws_set_blocking_send(wsi); + } + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } +#else + (void)n; + (void)wsi; + (void)buf; + (void)len; + // !!! +#endif + + lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", + len, wsi->desc.sockfd, n, LWS_ERRNO); + return LWS_SSL_CAPABLE_ERROR; +} +#endif +LWS_VISIBLE int +lws_ssl_pending_no_ssl(struct lws *wsi) +{ + (void)wsi; +#if defined(LWS_WITH_ESP32) + return 100; +#else + return 0; +#endif +} diff --git a/thirdparty/lws/plat/lws-plat-unix.c b/thirdparty/lws/plat/lws-plat-unix.c new file mode 100644 index 0000000000..a51e67bb81 --- /dev/null +++ b/thirdparty/lws/plat/lws-plat-unix.c @@ -0,0 +1,850 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include <pwd.h> +#include <grp.h> + +#ifdef LWS_WITH_PLUGINS +#include <dlfcn.h> +#endif +#include <dirent.h> + +unsigned long long time_in_microseconds(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec; +} + +LWS_VISIBLE int +lws_get_random(struct lws_context *context, void *buf, int len) +{ + return read(context->fd_random, (char *)buf, len); +} + +LWS_VISIBLE int +lws_send_pipe_choked(struct lws *wsi) +{ + struct lws_pollfd fds; + struct lws *wsi_eff = wsi; + +#if defined(LWS_WITH_HTTP2) + wsi_eff = lws_get_network_wsi(wsi); +#endif + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi_eff->trunc_len) + return 1; + + fds.fd = wsi_eff->desc.sockfd; + fds.events = POLLOUT; + fds.revents = 0; + + if (poll(&fds, 1, 0) != 1) + return 1; + + if ((fds.revents & POLLOUT) == 0) + return 1; + + /* okay to send another packet without blocking */ + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct lws_pollfd *fd) +{ + return poll(fd, 1, 0); +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + char buf = 0; + + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + char buf = 0, m = context->count_threads; + + while (m--) { + if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); + pt++; + } +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + int syslog_level = LOG_DEBUG; + + switch (level) { + case LLL_ERR: + syslog_level = LOG_ERR; + break; + case LLL_WARN: + syslog_level = LOG_WARNING; + break; + case LLL_NOTICE: + syslog_level = LOG_NOTICE; + break; + case LLL_INFO: + syslog_level = LOG_INFO; + break; + } + syslog(syslog_level, "%s", line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + int n = -1, m, c; + char buf; + + /* stay dead once we are dead */ + + if (!context || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1); + + if (timeout_ms < 0) + goto faked_service; + + lws_libev_run(context, tsi); + lws_libuv_run(context, tsi); + lws_libevent_run(context, tsi); + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = + context->vhost_list->protocols[0].callback( + &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + n = poll(pt->fds, pt->fds_count, timeout_ms); + +#ifdef LWS_OPENSSL_SUPPORT + if (!n && !pt->rx_draining_ext_list && + !lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { +#else + if (!pt->rx_draining_ext_list && !n) /* poll timeout */ { +#endif + lws_service_fd_tsi(context, NULL, tsi); + return 0; + } + +faked_service: + m = lws_service_flag_pending(context, tsi); + if (m) + c = -1; /* unknown limit */ + else + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } else + c = n; + + /* any socket with events to service? */ + for (n = 0; n < pt->fds_count && c; n++) { + if (!pt->fds[n].revents) + continue; + + c--; + + if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) { + if (read(pt->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) +{ + int optval = 1; + socklen_t optlen = sizeof(optval); + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + defined(__HAIKU__) + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&optval, optlen) < 0) + return 1; + +#if defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \ + defined(__HAIKU__) + + /* + * didn't find a way to set these per-socket, need to + * tune kernel systemwide values + */ +#else + /* set the keepalive conditions we want on it too */ + optval = vhost->ka_time; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *)&optval, optlen) < 0) + return 1; + + optval = vhost->ka_probes; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *)&optval, optlen) < 0) + return 1; +#endif + } + +#if defined(SO_BINDTODEVICE) + if (vhost->bind_iface && vhost->iface) { + lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface, + strlen(vhost->iface)) < 0) { + lwsl_warn("Failed to bind to device %s\n", vhost->iface); + return 1; + } + } +#endif + + /* Disable Nagle */ + optval = 1; +#if defined (__sun) + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#elif !defined(__APPLE__) && \ + !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \ + !defined(__NetBSD__) && \ + !defined(__OpenBSD__) && \ + !defined(__HAIKU__) + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) + return 1; +#else + tcp_proto = getprotobyname("TCP"); + if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0) + return 1; +#endif + + /* We are nonblocking... */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + return 1; + + return 0; +} + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) +static void +_lws_plat_apply_caps(int mode, cap_value_t *cv, int count) +{ + cap_t caps; + + if (!count) + return; + + caps = cap_get_proc(); + + cap_set_flag(caps, mode, count, cv, CAP_SET); + cap_set_proc(caps); + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + cap_free(caps); +} +#endif + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + int n; +#endif + + if (info->gid && info->gid != -1) + if (setgid(info->gid)) + lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO)); + + if (info->uid && info->uid != -1) { + struct passwd *p = getpwuid(info->uid); + + if (p) { + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + _lws_plat_apply_caps(CAP_PERMITTED, info->caps, info->count_caps); +#endif + + initgroups(p->pw_name, info->gid); + if (setuid(info->uid)) + lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); + else + lwsl_notice("Set privs to user '%s'\n", p->pw_name); + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + _lws_plat_apply_caps(CAP_EFFECTIVE, info->caps, info->count_caps); + + if (info->count_caps) + for (n = 0; n < info->count_caps; n++) + lwsl_notice(" RETAINING CAPABILITY %d\n", (int)info->caps[n]); +#endif + + } else + lwsl_warn("getpwuid: unable to find uid %d", info->uid); + } +} + +#ifdef LWS_WITH_PLUGINS + +#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0 + +/* libuv.c implements these in a cross-platform way */ + +#else + +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} + +LWS_VISIBLE int +lws_plat_plugins_init(struct lws_context * context, const char * const *d) +{ + struct lws_plugin_capability lcaps; + struct lws_plugin *plugin; + lws_plugin_init_func initfunc; + struct dirent **namelist; + int n, i, m, ret = 0; + char path[256]; + void *l; + + lwsl_notice(" Plugins:\n"); + + while (d && *d) { + n = scandir(*d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", *d); + return 1; + } + + for (i = 0; i < n; i++) { + if (strlen(namelist[i]->d_name) < 7) + goto inval; + + lwsl_notice(" %s\n", namelist[i]->d_name); + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, + namelist[i]->d_name); + l = dlopen(path, RTLD_NOW); + if (!l) { + lwsl_err("Error loading DSO: %s\n", dlerror()); + while (i++ < n) + free(namelist[i]); + goto bail; + } + /* we could open it, can we get his init function? */ + m = lws_snprintf(path, sizeof(path) - 1, "init_%s", + namelist[i]->d_name + 3 /* snip lib... */); + path[m - 3] = '\0'; /* snip the .so */ + initfunc = dlsym(l, path); + if (!initfunc) { + lwsl_err("Failed to get init on %s: %s", + namelist[i]->d_name, dlerror()); + dlclose(l); + } + lcaps.api_magic = LWS_PLUGIN_API_MAGIC; + m = initfunc(context, &lcaps); + if (m) { + lwsl_err("Initializing %s failed %d\n", + namelist[i]->d_name, m); + dlclose(l); + goto skip; + } + + plugin = lws_malloc(sizeof(*plugin), "plugin"); + if (!plugin) { + lwsl_err("OOM\n"); + goto bail; + } + plugin->list = context->plugin_list; + context->plugin_list = plugin; + strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1); + plugin->name[sizeof(plugin->name) - 1] = '\0'; + plugin->l = l; + plugin->caps = lcaps; + context->plugin_protocol_count += lcaps.count_protocols; + context->plugin_extension_count += lcaps.count_extensions; + + free(namelist[i]); + continue; + + skip: + dlclose(l); + inval: + free(namelist[i]); + } + free(namelist); + d++; + } + +bail: + free(namelist); + + return ret; +} + +LWS_VISIBLE int +lws_plat_plugins_destroy(struct lws_context * context) +{ + struct lws_plugin *plugin = context->plugin_list, *p; + lws_plugin_destroy_func func; + char path[256]; + int m; + + if (!plugin) + return 0; + + lwsl_notice("%s\n", __func__); + + while (plugin) { + p = plugin; + m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3); + path[m - 3] = '\0'; + func = dlsym(plugin->l, path); + if (!func) { + lwsl_err("Failed to get destroy on %s: %s", + plugin->name, dlerror()); + goto next; + } + m = func(context); + if (m) + lwsl_err("Initializing %s failed %d\n", + plugin->name, m); +next: + dlclose(p->l); + plugin = p->list; + p->list = NULL; + free(p); + } + + context->plugin_list = NULL; + + return 0; +} + +#endif +#endif + + +#if 0 +static void +sigabrt_handler(int x) +{ + printf("%s\n", __func__); +} +#endif + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ +#if !defined(LWS_AVOID_SIGPIPE_IGN) + signal(SIGPIPE, SIG_IGN); +#endif + + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int m = context->count_threads; + +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_plat_plugins_destroy(context); +#endif + + if (context->lws_lookup) + lws_free(context->lws_lookup); + + while (m--) { + if (pt->dummy_pipe_fds[0]) + close(pt->dummy_pipe_fds[0]); + if (pt->dummy_pipe_fds[1]) + close(pt->dummy_pipe_fds[1]); + pt++; + } + if (!context->fd_random) + lwsl_err("ZERO RANDOM FD\n"); + if (context->fd_random != LWS_INVALID_FILE) + close(context->fd_random); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, + size_t addrlen) +{ + int rc = -1; + + struct ifaddrs *ifr; + struct ifaddrs *ifc; +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif + + getifaddrs(&ifr); + for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { + if (!ifc->ifa_addr) + continue; + + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + + if (strcmp(ifc->ifa_name, ifname)) + continue; + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_WITH_IPV6 + if (ipv6) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr[10] = 0xff; + addr6->sin6_addr.s6_addr[11] = 0xff; + memcpy(&addr6->sin6_addr.s6_addr[12], + &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_WITH_IPV6 + case AF_INET6: + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: + continue; + } + rc = 0; + } + + freeifaddrs(ifr); + + if (rc == -1) { + /* check if bind to IP address */ +#ifdef LWS_WITH_IPV6 + if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) + rc = 0; + else +#endif + if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) + rc = 0; + } + + return rc; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ); + + pt->fds[pt->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + + pt->fds_count--; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ + /* if our parent went down, don't linger around */ + if (context->started_with_parent && + kill(context->started_with_parent, 0) < 0) + kill(getpid(), SIGTERM); +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + return inet_ntop(af, src, dst, cnt); +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + return inet_pton(af, src, dst); +} + +LWS_VISIBLE lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags) +{ + struct stat stat_buf; + int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664); + lws_fop_fd_t fop_fd; + + if (ret < 0) + return NULL; + + if (fstat(ret, &stat_buf) < 0) + goto bail; + + fop_fd = malloc(sizeof(*fop_fd)); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->flags = *flags; + fop_fd->fd = ret; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->len = stat_buf.st_size; + fop_fd->pos = 0; + + return fop_fd; + +bail: + close(ret); + return NULL; +} + +LWS_VISIBLE int +_lws_plat_file_close(lws_fop_fd_t *fop_fd) +{ + int fd = (*fop_fd)->fd; + + free(*fop_fd); + *fop_fd = NULL; + + return close(fd); +} + +LWS_VISIBLE lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + lws_fileofs_t r; + + if (offset > 0 && offset > fop_fd->len - fop_fd->pos) + offset = fop_fd->len - fop_fd->pos; + + if ((lws_fileofs_t)fop_fd->pos + offset < 0) + offset = -fop_fd->pos; + + r = lseek(fop_fd->fd, offset, SEEK_CUR); + + if (r >= 0) + fop_fd->pos = r; + else + lwsl_err("error seeking from cur %ld, offset %ld\n", + (long)fop_fd->pos, (long)offset); + + return r; +} + +LWS_VISIBLE int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = read((int)fop_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + fop_fd->pos += n; + lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n, + (long)len, (long)fop_fd->pos, (long)fop_fd->len); + *amount = n; + + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + long n; + + n = write((int)fop_fd->fd, buf, len); + if (n == -1) { + *amount = 0; + return -1; + } + + fop_fd->pos += n; + *amount = n; + + return 0; +} + + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads, fd; + + /* master context has the global fd lookup array */ + context->lws_lookup = lws_zalloc(sizeof(struct lws *) * + context->max_fds, "lws_lookup"); + if (context->lws_lookup == NULL) { + lwsl_err("OOM on lws_lookup array for %d connections\n", + context->max_fds); + return 1; + } + + lwsl_info(" mem: platform fd map: %5lu bytes\n", + (unsigned long)(sizeof(struct lws *) * context->max_fds)); + fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); + + context->fd_random = fd; + if (context->fd_random < 0) { + lwsl_err("Unable to open random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, context->fd_random); + return 1; + } + + if (!lws_libev_init_fd_table(context) && + !lws_libuv_init_fd_table(context) && + !lws_libevent_init_fd_table(context)) { + /* otherwise libev/uv/event handled it instead */ + + while (n--) { + if (pipe(pt->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + return 1; + } + + /* use the read end of pipe as first item */ + pt->fds[0].fd = pt->dummy_pipe_fds[0]; + pt->fds[0].events = LWS_POLLIN; + pt->fds[0].revents = 0; + pt->fds_count = 1; + pt++; + } + } + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} diff --git a/thirdparty/lws/plat/lws-plat-win.c b/thirdparty/lws/plat/lws-plat-win.c new file mode 100644 index 0000000000..f5b178ce85 --- /dev/null +++ b/thirdparty/lws/plat/lws-plat-win.c @@ -0,0 +1,745 @@ +#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif +#include "private-libwebsockets.h" + +unsigned long long +time_in_microseconds() +{ +#ifndef DELTA_EPOCH_IN_MICROSECS +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + FILETIME filetime; + ULARGE_INTEGER datetime; + +#ifdef _WIN32_WCE + GetCurrentFT(&filetime); +#else + GetSystemTimeAsFileTime(&filetime); +#endif + + /* + * As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a + * ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can + * prevent alignment faults on 64-bit Windows). + */ + memcpy(&datetime, &filetime, sizeof(datetime)); + + /* Windows file times are in 100s of nanoseconds. */ + return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10; +} + +#ifdef _WIN32_WCE +time_t time(time_t *t) +{ + time_t ret = time_in_microseconds() / 1000000; + + if(t != NULL) + *t = ret; + + return ret; +} +#endif + +/* file descriptor hash management */ + +struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd) +{ + int h = LWS_FD_HASH(fd); + int n = 0; + + for (n = 0; n < context->fd_hashtable[h].length; n++) + if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) + return context->fd_hashtable[h].wsi[n]; + + return NULL; +} + +int +insert_wsi(struct lws_context *context, struct lws *wsi) +{ + int h = LWS_FD_HASH(wsi->desc.sockfd); + + if (context->fd_hashtable[h].length == (getdtablesize() - 1)) { + lwsl_err("hash table overflow\n"); + return 1; + } + + context->fd_hashtable[h].wsi[context->fd_hashtable[h].length++] = wsi; + + return 0; +} + +int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd) +{ + int h = LWS_FD_HASH(fd); + int n = 0; + + for (n = 0; n < context->fd_hashtable[h].length; n++) + if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) { + while (n < context->fd_hashtable[h].length) { + context->fd_hashtable[h].wsi[n] = + context->fd_hashtable[h].wsi[n + 1]; + n++; + } + context->fd_hashtable[h].length--; + + return 0; + } + + lwsl_err("Failed to find fd %d requested for " + "delete in hashtable\n", fd); + return 1; +} + +LWS_VISIBLE int lws_get_random(struct lws_context *context, + void *buf, int len) +{ + int n; + char *p = (char *)buf; + + for (n = 0; n < len; n++) + p[n] = (unsigned char)rand(); + + return n; +} + +LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi) +{ + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi->trunc_len) + return 1; + + return (int)wsi->sock_send_blocking; +} + +LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd) +{ + fd_set readfds; + struct timeval tv = { 0, 0 }; + + assert((fd->events & LWS_POLLIN) == LWS_POLLIN); + + FD_ZERO(&readfds); + FD_SET(fd->fd, &readfds); + + return select(fd->fd + 1, &readfds, NULL, NULL, &tv); +} + +LWS_VISIBLE void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads; + + while (n--) { + WSASetEvent(pt->events[0]); + pt++; + } +} + +LWS_VISIBLE void +lws_cancel_service_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + WSASetEvent(pt->events[0]); +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + lwsl_emit_stderr(level, line); +} + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt; + WSANETWORKEVENTS networkevents; + struct lws_pollfd *pfd; + struct lws *wsi; + unsigned int i; + DWORD ev; + int n, m; + + /* stay dead once we are dead */ + if (context == NULL || !context->vhost_list) + return 1; + + pt = &context->pt[tsi]; + + if (!context->service_tid_detected) { + struct lws _lws; + + memset(&_lws, 0, sizeof(_lws)); + _lws.context = context; + + context->service_tid_detected = context->vhost_list-> + protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID, + NULL, NULL, 0); + context->service_tid = context->service_tid_detected; + context->service_tid_detected = 1; + } + + if (timeout_ms < 0) + { + if (lws_service_flag_pending(context, tsi)) { + /* any socket with events to service? */ + for (n = 0; n < (int)pt->fds_count; n++) { + if (!pt->fds[n].revents) + continue; + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + } + return 0; + } + + for (i = 0; i < pt->fds_count; ++i) { + pfd = &pt->fds[i]; + + if (!(pfd->events & LWS_POLLOUT)) + continue; + + wsi = wsi_from_fd(context, pfd->fd); + if (wsi->listener) + continue; + if (!wsi || wsi->sock_send_blocking) + continue; + pfd->revents = LWS_POLLOUT; + n = lws_service_fd(context, pfd); + if (n < 0) + return -1; + /* if something closed, retry this slot */ + if (n) + i--; + + if (wsi->trunc_len) + WSASetEvent(pt->events[0]); + } + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(context, 1, tsi)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_tsi(context, -1, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(context, 1, pt->tid)) + /* yes... come back again quickly */ + timeout_ms = 0; + } + + ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE); + if (ev == WSA_WAIT_EVENT_0) { + unsigned int eIdx; + + WSAResetEvent(pt->events[0]); + + for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) { + if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) { + lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO); + return -1; + } + + pfd = &pt->fds[eIdx]; + pfd->revents = (short)networkevents.lNetworkEvents; + + if ((networkevents.lNetworkEvents & FD_CONNECT) && + networkevents.iErrorCode[FD_CONNECT_BIT] && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS && + networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK && + networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) { + lwsl_debug("Unable to connect errno=%d\n", + networkevents.iErrorCode[FD_CONNECT_BIT]); + pfd->revents |= LWS_POLLHUP; + } + + if (pfd->revents & LWS_POLLOUT) { + wsi = wsi_from_fd(context, pfd->fd); + if (wsi) + wsi->sock_send_blocking = 0; + } + /* if something closed, retry this slot */ + if (pfd->revents & LWS_POLLHUP) + --eIdx; + + if( pfd->revents != 0 ) { + lws_service_fd_tsi(context, pfd, tsi); + + } + } + } + + context->service_tid = 0; + + if (ev == WSA_WAIT_TIMEOUT) { + lws_service_fd(context, NULL); + } + return 0;; +} + +LWS_VISIBLE int +lws_plat_service(struct lws_context *context, int timeout_ms) +{ + return _lws_plat_service_tsi(context, timeout_ms, 0); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd) +{ + int optval = 1; + int optlen = sizeof(optval); + u_long optl = 1; + DWORD dwBytesRet; + struct tcp_keepalive alive; + int protonbr; +#ifndef _WIN32_WCE + struct protoent *tcp_proto; +#endif + + if (vhost->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&optval, optlen) < 0) + return 1; + + alive.onoff = TRUE; + alive.keepalivetime = vhost->ka_time; + alive.keepaliveinterval = vhost->ka_interval; + + if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), + NULL, 0, &dwBytesRet, NULL, NULL)) + return 1; + } + + /* Disable Nagle */ + optval = 1; +#ifndef _WIN32_WCE + tcp_proto = getprotobyname("TCP"); + if (!tcp_proto) { + lwsl_err("getprotobyname() failed with error %d\n", LWS_ERRNO); + return 1; + } + protonbr = tcp_proto->p_proto; +#else + protonbr = 6; +#endif + + setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen); + + /* We are nonblocking... */ + ioctlsocket(fd, FIONBIO, &optl); + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (!err) + return 0; + /* + * Tell the user that we could not find a usable + * Winsock DLL + */ + lwsl_err("WSAStartup failed with error: %d\n", err); + + return 1; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n = context->count_threads; + + while (n--) { + if (pt->events) { + WSACloseEvent(pt->events[0]); + lws_free(pt->events); + } + pt++; + } +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct lws_context *context) +{ + int n; + + for (n = 0; n < FD_HASHTABLE_MODULUS; n++) { + if (context->fd_hashtable[n].wsi) + lws_free(context->fd_hashtable[n].wsi); + } + + WSACleanup(); +} + +LWS_VISIBLE LWS_EXTERN int +lws_interface_to_sa(int ipv6, + const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + + if (ipv6) { + if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) { + return 0; + } + } +#endif + + long long address = inet_addr(ifname); + + if (address == INADDR_NONE) { + struct hostent *entry = gethostbyname(ifname); + if (entry) + address = ((struct in_addr *)entry->h_addr_list[0])->s_addr; + } + + if (address == INADDR_NONE) + return -1; + + addr->sin_addr.s_addr = (lws_intptr_t)address; + + return 0; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->fds[pt->fds_count++].revents = 0; + pt->events[pt->fds_count] = pt->events[0]; + WSAEventSelect(wsi->desc.sockfd, pt->events[0], + LWS_POLLIN | LWS_POLLHUP | FD_CONNECT); +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + pt->events[m + 1] = pt->events[pt->fds_count--]; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct lws_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_check_connection_error(struct lws *wsi) +{ + int optVal; + int optLen = sizeof(int); + + if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, + (char*)&optVal, &optLen) != SOCKET_ERROR && optVal && + optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS && + optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) { + lwsl_debug("Connect failed SO_ERROR=%d\n", optVal); + return 1; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct lws_context *context, + struct lws *wsi, struct lws_pollfd *pfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + long networkevents = LWS_POLLHUP | FD_CONNECT; + + if ((pfd->events & LWS_POLLIN)) + networkevents |= LWS_POLLIN; + + if ((pfd->events & LWS_POLLOUT)) + networkevents |= LWS_POLLOUT; + + if (WSAEventSelect(wsi->desc.sockfd, + pt->events[0], + networkevents) != SOCKET_ERROR) + return 0; + + lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO); + + return 1; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + WCHAR *buffer; + DWORD bufferlen = cnt; + BOOL ok = FALSE; + + buffer = lws_malloc(bufferlen * 2, "inet_ntop"); + if (!buffer) { + lwsl_err("Out of memory\n"); + return NULL; + } + + if (af == AF_INET) { + struct sockaddr_in srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin_family = AF_INET; + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#ifdef LWS_WITH_IPV6 + } else if (af == AF_INET6) { + struct sockaddr_in6 srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin6_family = AF_INET6; + memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#endif + } else + lwsl_err("Unsupported type\n"); + + if (!ok) { + int rv = WSAGetLastError(); + lwsl_err("WSAAddressToString() : %d\n", rv); + } else { + if (WideCharToMultiByte(CP_ACP, 0, buffer, bufferlen, dst, cnt, 0, NULL) <= 0) + ok = FALSE; + } + + lws_free(buffer); + return ok ? dst : NULL; +} + +LWS_VISIBLE int +lws_plat_inet_pton(int af, const char *src, void *dst) +{ + WCHAR *buffer; + DWORD bufferlen = strlen(src) + 1; + BOOL ok = FALSE; + + buffer = lws_malloc(bufferlen * 2, "inet_pton"); + if (!buffer) { + lwsl_err("Out of memory\n"); + return -1; + } + + if (MultiByteToWideChar(CP_ACP, 0, src, bufferlen, buffer, bufferlen) <= 0) { + lwsl_err("Failed to convert multi byte to wide char\n"); + lws_free(buffer); + return -1; + } + + if (af == AF_INET) { + struct sockaddr_in dstaddr; + int dstaddrlen = sizeof(dstaddr); + bzero(&dstaddr, sizeof(dstaddr)); + dstaddr.sin_family = AF_INET; + + if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { + ok = TRUE; + memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr)); + } +#ifdef LWS_WITH_IPV6 + } else if (af == AF_INET6) { + struct sockaddr_in6 dstaddr; + int dstaddrlen = sizeof(dstaddr); + bzero(&dstaddr, sizeof(dstaddr)); + dstaddr.sin6_family = AF_INET6; + + if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) { + ok = TRUE; + memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr)); + } +#endif + } else + lwsl_err("Unsupported type\n"); + + if (!ok) { + int rv = WSAGetLastError(); + lwsl_err("WSAAddressToString() : %d\n", rv); + } + + lws_free(buffer); + return ok ? 1 : -1; +} + +LWS_VISIBLE lws_fop_fd_t +_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + const char *vpath, lws_fop_flags_t *flags) +{ + HANDLE ret; + WCHAR buf[MAX_PATH]; + lws_fop_fd_t fop_fd; + LARGE_INTEGER llFileSize = {0}; + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf)); + if (((*flags) & 7) == _O_RDONLY) { + ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } else { + ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (ret == LWS_INVALID_FILE) + goto bail; + + fop_fd = malloc(sizeof(*fop_fd)); + if (!fop_fd) + goto bail; + + fop_fd->fops = fops; + fop_fd->fd = ret; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->flags = *flags; + fop_fd->len = GetFileSize(ret, NULL); + if(GetFileSizeEx(ret, &llFileSize)) + fop_fd->len = llFileSize.QuadPart; + + fop_fd->pos = 0; + + return fop_fd; + +bail: + return NULL; +} + +LWS_VISIBLE int +_lws_plat_file_close(lws_fop_fd_t *fop_fd) +{ + HANDLE fd = (*fop_fd)->fd; + + free(*fop_fd); + *fop_fd = NULL; + + CloseHandle((HANDLE)fd); + + return 0; +} + +LWS_VISIBLE lws_fileofs_t +_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) +{ + LARGE_INTEGER l; + + l.QuadPart = offset; + return SetFilePointerEx((HANDLE)fop_fd->fd, l, NULL, FILE_CURRENT); +} + +LWS_VISIBLE int +_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + DWORD _amount; + + if (!ReadFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { + *amount = 0; + + return 1; + } + + fop_fd->pos += _amount; + *amount = (unsigned long)_amount; + + return 0; +} + +LWS_VISIBLE int +_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t* buf, lws_filepos_t len) +{ + DWORD _amount; + + if (!WriteFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) { + *amount = 0; + + return 1; + } + + fop_fd->pos += _amount; + *amount = (unsigned long)_amount; + + return 0; +} + +LWS_VISIBLE int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int i, n = context->count_threads; + + for (i = 0; i < FD_HASHTABLE_MODULUS; i++) { + context->fd_hashtable[i].wsi = + lws_zalloc(sizeof(struct lws*) * context->max_fds, "win hashtable"); + + if (!context->fd_hashtable[i].wsi) + return -1; + } + + while (n--) { + pt->events = lws_malloc(sizeof(WSAEVENT) * + (context->fd_limit_per_thread + 1), "event table"); + if (pt->events == NULL) { + lwsl_err("Unable to allocate events array for %d connections\n", + context->fd_limit_per_thread + 1); + return 1; + } + + pt->fds_count = 0; + pt->events[0] = WSACreateEvent(); + + pt++; + } + + context->fd_random = 0; + +#ifdef LWS_WITH_PLUGINS + if (info->plugin_dirs) + lws_plat_plugins_init(context, info->plugin_dirs); +#endif + + return 0; +} + + +int kill(int pid, int sig) +{ + lwsl_err("Sorry Windows doesn't support kill()."); + exit(0); +} + +int fork(void) +{ + lwsl_err("Sorry Windows doesn't support fork()."); + exit(0); +} + diff --git a/thirdparty/lws/pollfd.c b/thirdparty/lws/pollfd.c new file mode 100644 index 0000000000..4d6704d41c --- /dev/null +++ b/thirdparty/lws/pollfd.c @@ -0,0 +1,557 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + int ret = 0, pa_events = 1; + struct lws_pollfd *pfd; + int sampled_tid, tid; + + if (!wsi || wsi->position_in_fds_table < 0) + return 0; + + if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) { + /* + * Happening alongside service thread handling POLLOUT. + * The danger is when he is finished, he will disable POLLOUT, + * countermanding what we changed here. + * + * Instead of changing the fds, inform the service thread + * what happened, and ask it to leave POLLOUT active on exit + */ + wsi->leave_pollout_active = 1; + /* + * by definition service thread is not in poll wait, so no need + * to cancel service + */ + + lwsl_debug("%s: using leave_pollout_active\n", __func__); + + return 0; + } + + context = wsi->context; + pt = &context->pt[(int)wsi->tsi]; + assert(wsi->position_in_fds_table >= 0 && + wsi->position_in_fds_table < pt->fds_count); + + pfd = &pt->fds[wsi->position_in_fds_table]; + pa->fd = wsi->desc.sockfd; + pa->prev_events = pfd->events; + pa->events = pfd->events = (pfd->events & ~_and) | _or; + + if (wsi->http2_substream) + return 0; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, + wsi->user_space, (void *)pa, 0)) { + ret = -1; + goto bail; + } + + if (_and & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ); + } + if (_or & LWS_POLLIN) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ); + } + if (_and & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + } + if (_or & LWS_POLLOUT) { + lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE); + } + + /* + * if we changed something in this pollfd... + * ... and we're running in a different thread context + * than the service thread... + * ... and the service thread is waiting ... + * then cancel it to force a restart with our changed events + */ +#if LWS_POSIX + pa_events = pa->prev_events != pa->events; +#endif + + if (pa_events) { + + if (lws_plat_change_pollfd(context, wsi, pfd)) { + lwsl_info("%s failed\n", __func__); + ret = -1; + goto bail; + } + + sampled_tid = context->service_tid; + if (sampled_tid) { + tid = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + if (tid == -1) { + ret = -1; + goto bail; + } + if (tid != sampled_tid) + lws_cancel_service_pt(wsi); + } + } +bail: + return ret; +} + +#ifndef LWS_NO_SERVER +static void +lws_accept_modulation(struct lws_context_per_thread *pt, int allow) +{ +// multithread listen seems broken +#if 0 + struct lws_vhost *vh = context->vhost_list; + struct lws_pollargs pa1; + + while (vh) { + if (allow) + _lws_change_pollfd(pt->wsi_listening, + 0, LWS_POLLIN, &pa1); + else + _lws_change_pollfd(pt->wsi_listening, + LWS_POLLIN, 0, &pa1); + vh = vh->vhost_next; + } +#endif +} +#endif + +int +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) +{ + struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 }; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int ret = 0; + + + lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n", + __func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count); + + if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { + lwsl_err("Too many fds (%d vs %d)\n", context->max_fds, + context->fd_limit_per_thread ); + return 1; + } + +#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) + if (wsi->desc.sockfd >= context->max_fds) { + lwsl_err("Socket fd %d is too high (%d)\n", + wsi->desc.sockfd, context->max_fds); + return 1; + } +#endif + + assert(wsi); + assert(wsi->vhost); + assert(lws_socket_is_valid(wsi->desc.sockfd)); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + return -1; + + lws_pt_lock(pt); + pt->count_conns++; + insert_wsi(context, wsi); +#if defined(LWS_WITH_ESP8266) + if (wsi->position_in_fds_table == -1) +#endif + wsi->position_in_fds_table = pt->fds_count; + + pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd; +#if LWS_POSIX + pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN; +#else + pt->fds[wsi->position_in_fds_table].events = 0; +#endif + pa.events = pt->fds[pt->fds_count].events; + + lws_plat_insert_socket_into_fds(context, wsi); + + /* external POLL support via protocol 0 */ + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + /* if no more room, defeat accepts on this thread */ + if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1) + lws_accept_modulation(pt, 0); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + ret = -1; + + return ret; +} + +int +remove_wsi_socket_from_fds(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 }; +#if !defined(LWS_WITH_ESP8266) + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws *end_wsi; + int v; +#endif + int m, ret = 0; + + if (wsi->parent_carries_io) { + lws_same_vh_protocol_remove(wsi); + return 0; + } + +#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266) + if (wsi->desc.sockfd > context->max_fds) { + lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, + context->max_fds); + return 1; + } +#endif + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *)&pa, 1)) + return -1; + + lws_same_vh_protocol_remove(wsi); + + /* the guy who is to be deleted's slot index in pt->fds */ + m = wsi->position_in_fds_table; + +#if !defined(LWS_WITH_ESP8266) + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | + LWS_EV_PREPARE_DELETION); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | + LWS_EV_PREPARE_DELETION); + + lws_pt_lock(pt); + + lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", + __func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table, + pt->fds_count, pt->fds[pt->fds_count].fd); + + /* have the last guy take up the now vacant slot */ + pt->fds[m] = pt->fds[pt->fds_count - 1]; +#endif + /* this decrements pt->fds_count */ + lws_plat_delete_socket_from_fds(context, wsi, m); +#if !defined(LWS_WITH_ESP8266) + v = (int) pt->fds[m].fd; + /* end guy's "position in fds table" is now the deletion guy's old one */ + end_wsi = wsi_from_fd(context, v); + if (!end_wsi) { + lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", + (int)pt->fds[m].fd, m, pt->fds_count); + assert(0); + } else + end_wsi->position_in_fds_table = m; + + /* deletion guy's lws_lookup entry needs nuking */ + delete_from_fd(context, wsi->desc.sockfd); + /* removed wsi has no position any more */ + wsi->position_in_fds_table = -1; + + /* remove also from external POLL support via protocol 0 */ + if (lws_socket_is_valid(wsi->desc.sockfd)) + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, + wsi->user_space, (void *) &pa, 0)) + ret = -1; +#ifndef LWS_NO_SERVER + if (!context->being_destroyed) + /* if this made some room, accept connects on this thread */ + if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1) + lws_accept_modulation(pt, 1); +#endif + lws_pt_unlock(pt); + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 1)) + ret = -1; +#endif + return ret; +} + +int +lws_change_pollfd(struct lws *wsi, int _and, int _or) +{ + struct lws_context_per_thread *pt; + struct lws_context *context; + struct lws_pollargs pa; + int ret = 0; + + if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) + return 1; + + context = lws_get_context(wsi); + if (!context) + return 1; + + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + return -1; + + pt = &context->pt[(int)wsi->tsi]; + + lws_pt_lock(pt); + ret = _lws_change_pollfd(wsi, _and, _or, &pa); + lws_pt_unlock(pt); + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 0)) + ret = -1; + + return ret; +} + +LWS_VISIBLE int +lws_callback_on_writable(struct lws *wsi) +{ + struct lws_context_per_thread *pt; +#ifdef LWS_WITH_HTTP2 + struct lws *network_wsi, *wsi2; + int already; +#endif + int n; + + if (wsi->state == LWSS_SHUTDOWN) + return 0; + + if (wsi->socket_is_permanently_unusable) + return 0; + + pt = &wsi->context->pt[(int)wsi->tsi]; + + if (wsi->parent_carries_io) { +#if defined(LWS_WITH_STATS) + if (!wsi->active_writable_req_us) { + wsi->active_writable_req_us = time_in_microseconds(); + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); + } +#endif + n = lws_callback_on_writable(wsi->parent); + if (n < 0) + return n; + + wsi->parent_pending_cb_on_writable = 1; + return 1; + } + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1); +#if defined(LWS_WITH_STATS) + if (!wsi->active_writable_req_us) { + wsi->active_writable_req_us = time_in_microseconds(); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); + } +#endif + +#ifdef LWS_WITH_HTTP2 + lwsl_info("%s: %p\n", __func__, wsi); + + if (wsi->mode != LWSCM_HTTP2_SERVING) + goto network_sock; + + if (wsi->u.h2.requested_POLLOUT) { + lwsl_info("already pending writable\n"); + return 1; + } + + /* is this for DATA or for control messages? */ + if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps && + !lws_h2_tx_cr_get(wsi)) { + /* + * other side is not able to cope with us sending DATA + * anything so no matter if we have POLLOUT on our side if it's + * DATA we want to send. + * + * Delay waiting for our POLLOUT until peer indicates he has + * space for more using tx window command in http2 layer + */ + lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr); + wsi->u.h2.skint = 1; + return 0; + } + + wsi->u.h2.skint = 0; + network_wsi = lws_get_network_wsi(wsi); + already = network_wsi->u.h2.requested_POLLOUT; + + /* mark everybody above him as requesting pollout */ + + wsi2 = wsi; + while (wsi2) { + wsi2->u.h2.requested_POLLOUT = 1; + lwsl_info("mark %p pending writable\n", wsi2); + wsi2 = wsi2->u.h2.parent_wsi; + } + + /* for network action, act only on the network wsi */ + + wsi = network_wsi; + if (already) + return 1; +network_sock: +#endif + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0)) + return 1; + + if (wsi->position_in_fds_table < 0) { + lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd); + return -1; + } + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + return -1; + + return 1; +} + +/* + * stitch protocol choice into the vh protocol linked list + * We always insert ourselves at the start of the list + * + * X <-> B + * X <-> pAn <-> pB + * + * Illegal to attach more than once without detach inbetween + */ +void +lws_same_vh_protocol_insert(struct lws *wsi, int n) +{ + if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) { + lws_same_vh_protocol_remove(wsi); + lwsl_notice("Attempted to attach wsi twice to same vh prot\n"); + } + + wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n]; + /* old first guy is our next */ + wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n]; + /* we become the new first guy */ + wsi->vhost->same_vh_protocol_list[n] = wsi; + + if (wsi->same_vh_protocol_next) + /* old first guy points back to us now */ + wsi->same_vh_protocol_next->same_vh_protocol_prev = + &wsi->same_vh_protocol_next; +} + +void +lws_same_vh_protocol_remove(struct lws *wsi) +{ + /* + * detach ourselves from vh protocol list if we're on one + * A -> B -> C + * A -> C , or, B -> C, or A -> B + * + * OK to call on already-detached wsi + */ + lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi); + + if (wsi->same_vh_protocol_prev) { + assert (*(wsi->same_vh_protocol_prev) == wsi); + lwsl_info("have prev %p, setting him to our next %p\n", + wsi->same_vh_protocol_prev, + wsi->same_vh_protocol_next); + + /* guy who pointed to us should point to our next */ + *(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next; + } + + /* our next should point back to our prev */ + if (wsi->same_vh_protocol_next) { + wsi->same_vh_protocol_next->same_vh_protocol_prev = + wsi->same_vh_protocol_prev; + } + + wsi->same_vh_protocol_prev = NULL; + wsi->same_vh_protocol_next = NULL; +} + + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, + const struct lws_protocols *protocol) +{ + struct lws *wsi; + + if (protocol < vhost->protocols || + protocol >= (vhost->protocols + vhost->count_protocols)) { + lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n", + __func__, protocol, vhost->protocols, vhost, + (vhost->protocols + vhost->count_protocols)); + + return -1; + } + + wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols]; + while (wsi) { + assert(wsi->protocol == protocol); + assert(*wsi->same_vh_protocol_prev == wsi); + if (wsi->same_vh_protocol_next) + assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == + &wsi->same_vh_protocol_next); + + lws_callback_on_writable(wsi); + wsi = wsi->same_vh_protocol_next; + } + + return 0; +} + +LWS_VISIBLE int +lws_callback_on_writable_all_protocol(const struct lws_context *context, + const struct lws_protocols *protocol) +{ + struct lws_vhost *vhost = context->vhost_list; + int n; + + while (vhost) { + for (n = 0; n < vhost->count_protocols; n++) + if (protocol->callback == + vhost->protocols[n].callback && + !strcmp(protocol->name, vhost->protocols[n].name)) + break; + if (n != vhost->count_protocols) + lws_callback_on_writable_all_protocol_vhost( + vhost, &vhost->protocols[n]); + + vhost = vhost->vhost_next; + } + + return 0; +} diff --git a/thirdparty/lws/private-libwebsockets.h b/thirdparty/lws/private-libwebsockets.h new file mode 100644 index 0000000000..4f0b374332 --- /dev/null +++ b/thirdparty/lws/private-libwebsockets.h @@ -0,0 +1,2618 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2016 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "lws_config.h" +#include "lws_config_private.h" + + +#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK) +#define _GNU_SOURCE +#endif + +#if defined(__COVERITY__) +typedef struct { long double x, y; } _Float128; +#endif + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <limits.h> +#include <stdarg.h> +#include <inttypes.h> + +#if defined(LWS_WITH_ESP32) +#define MSG_NOSIGNAL 0 +#define SOMAXCONN 3 +#endif + +#if defined(LWS_WITH_ESP8266) +#include <user_interface.h> +#define assert(n) + +/* rom-provided stdc functions for free, ensure use these instead of libc ones */ + +int ets_vsprintf(char *str, const char *format, va_list argptr); +int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); +int ets_snprintf(char *str, size_t size, const char *format, ...); +int ets_sprintf(char *str, const char *format, ...); +int os_printf_plus(const char *format, ...); +#undef malloc +#undef realloc +#undef free +void *pvPortMalloc(size_t s, const char *f, int line); +#define malloc(s) pvPortMalloc(s, "", 0) +void *pvPortRealloc(void *p, size_t s, const char *f, int line); +#define realloc(p, s) pvPortRealloc(p, s, "", 0) +void vPortFree(void *p, const char *f, int line); +#define free(p) vPortFree(p, "", 0) +#undef memcpy +void *ets_memcpy(void *dest, const void *src, size_t n); +#define memcpy ets_memcpy +void *ets_memset(void *dest, int v, size_t n); +#define memset ets_memset +char *ets_strcpy(char *dest, const char *src); +#define strcpy ets_strcpy +char *ets_strncpy(char *dest, const char *src, size_t n); +#define strncpy ets_strncpy +char *ets_strstr(const char *haystack, const char *needle); +#define strstr ets_strstr +int ets_strcmp(const char *s1, const char *s2); +int ets_strncmp(const char *s1, const char *s2, size_t n); +#define strcmp ets_strcmp +#define strncmp ets_strncmp +size_t ets_strlen(const char *s); +#define strlen ets_strlen +void *ets_memmove(void *dest, const void *src, size_t n); +#define memmove ets_memmove +char *ets_strchr(const char *s, int c); +#define strchr_ets_strchr +#undef _DEBUG +#include <osapi.h> + +#else +#define STORE_IN_ROM +#include <assert.h> +#endif +#if LWS_MAX_SMP > 1 +#include <pthread.h> +#endif + +#ifdef LWS_HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#if defined(WIN32) || defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#if (WINVER < 0x0501) +#undef WINVER +#undef _WIN32_WINNT +#define WINVER 0x0501 +#define _WIN32_WINNT WINVER +#endif +#define LWS_NO_DAEMONIZE +#define LWS_ERRNO WSAGetLastError() +#define LWS_EAGAIN WSAEWOULDBLOCK +#define LWS_EALREADY WSAEALREADY +#define LWS_EINPROGRESS WSAEINPROGRESS +#define LWS_EINTR WSAEINTR +#define LWS_EISCONN WSAEISCONN +#define LWS_EWOULDBLOCK WSAEWOULDBLOCK +#define MSG_NOSIGNAL 0 +#define SHUT_RDWR SD_BOTH +#define SOL_TCP IPPROTO_TCP +#define SHUT_WR SD_SEND + +#define compatible_close(fd) closesocket(fd) +#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = 1 +#define lws_socket_is_valid(x) (!!x) +#define LWS_SOCK_INVALID 0 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> +#include <tchar.h> +#ifdef LWS_HAVE_IN6ADDR_H +#include <in6addr.h> +#endif +#include <mstcpip.h> +#include <io.h> + +#if !defined(LWS_HAVE_ATOLL) +#if defined(LWS_HAVE__ATOI64) +#define atoll _atoi64 +#else +#warning No atoll or _atoi64 available, using atoi +#define atoll atoi +#endif +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +#ifdef LWS_HAVE__VSNPRINTF +#define vsnprintf _vsnprintf +#endif + +/* we don't have an implementation for this on windows... */ +int kill(int pid, int sig); +int fork(void); +#ifndef SIGINT +#define SIGINT 2 +#endif + +#else /* not windows --> */ + +#include <fcntl.h> +#include <strings.h> +#include <unistd.h> +#include <sys/types.h> + +#ifndef __cplusplus +#include <errno.h> +#endif +#include <netdb.h> +#include <signal.h> +#ifdef LWS_WITH_ESP8266 +#include <sockets.h> +#define vsnprintf ets_vsnprintf +#define snprintf ets_snprintf +#define sprintf ets_sprintf + +int kill(int pid, int sig); + +#else +#include <sys/socket.h> +#endif +#ifdef LWS_WITH_HTTP_PROXY +#include <hubbub/hubbub.h> +#include <hubbub/parser.h> +#endif +#if defined(LWS_BUILTIN_GETIFADDRS) + #include "./misc/getifaddrs.h" +#else + #if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) + #if defined(__HAIKU__) + #define _BSD_SOURCE + #endif + #include <ifaddrs.h> + #endif +#endif +#if defined (__ANDROID__) +#include <syslog.h> +#include <sys/resource.h> +#elif defined (__sun) || defined(__HAIKU__) +#include <syslog.h> +#else +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +#include <sys/syslog.h> +#endif +#endif +#include <netdb.h> +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) +#include <sys/mman.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <poll.h> +#endif +#ifdef LWS_WITH_LIBEV +#include <ev.h> +#endif +#ifdef LWS_WITH_LIBUV +#include <uv.h> +#endif +#ifdef LWS_WITH_LIBEVENT +#include <event2/event.h> +#endif + +#ifndef LWS_NO_FORK +#ifdef LWS_HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif +#endif + +#include <sys/time.h> + +#define LWS_ERRNO errno +#define LWS_EAGAIN EAGAIN +#define LWS_EALREADY EALREADY +#define LWS_EINPROGRESS EINPROGRESS +#define LWS_EINTR EINTR +#define LWS_EISCONN EISCONN +#define LWS_EWOULDBLOCK EWOULDBLOCK + +#define lws_set_blocking_send(wsi) + +#if defined(LWS_WITH_ESP8266) +#define lws_socket_is_valid(x) ((x) != NULL) +#define LWS_SOCK_INVALID (NULL) +struct lws; +const char * +lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen); +#else +#define lws_socket_is_valid(x) (x >= 0) +#define LWS_SOCK_INVALID (-1) +#endif +#endif + +#ifndef LWS_HAVE_BZERO +#ifndef bzero +#define bzero(b, len) (memset((b), '\0', (len)), (void) 0) +#endif +#endif + +#ifndef LWS_HAVE_STRERROR +#define strerror(x) "" +#endif + +#ifdef LWS_OPENSSL_SUPPORT + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL +#include <cyassl/openssl/ssl.h> +#include <cyassl/error-ssl.h> +#else +#include <wolfssl/openssl/ssl.h> +#include <wolfssl/error-ssl.h> +#define OPENSSL_NO_TLSEXT +#endif /* not USE_OLD_CYASSL */ +#else +#if defined(LWS_WITH_ESP32) +#define OPENSSL_NO_TLSEXT +#else +#if defined(LWS_WITH_MBEDTLS) +#include <mbedtls/ssl.h> +#include <mbedtls/x509_crt.h> +#else +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/sha.h> +#ifdef LWS_HAVE_OPENSSL_ECDH_H +#include <openssl/ecdh.h> +#endif +#include <openssl/x509v3.h> +#endif +#if defined(OPENSSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x0009080afL) +/* later openssl defines this to negate the presence of tlsext... but it was only + * introduced at 0.9.8j. Earlier versions don't know it exists so don't + * define it... making it look like the feature exists... + */ +#define OPENSSL_NO_TLSEXT +#endif +#endif +#endif /* not ESP32 */ +#endif /* not USE_WOLFSSL */ +#endif + +#include "libwebsockets.h" +#if defined(WIN32) || defined(_WIN32) +#else +static inline int compatible_close(int fd) { return close(fd); } +#endif + +#if defined(WIN32) || defined(_WIN32) +#include <gettimeofday.h> +#endif + +#if defined(LWS_WITH_ESP8266) +#undef compatible_close +#define compatible_close(fd) { fd->state=ESPCONN_CLOSE; espconn_delete(fd); } +lws_sockfd_type +esp8266_create_tcp_stream_socket(void); +void +esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi); +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + + +#if defined(WIN32) || defined(_WIN32) + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#ifndef u_int64_t +typedef unsigned __int64 u_int64_t; +#endif + +#undef __P +#ifndef __P +#if __STDC__ +#define __P(protos) protos +#else +#define __P(protos) () +#endif +#endif + +#else + +#include <sys/stat.h> +#include <sys/time.h> + +#if defined(__APPLE__) +#include <machine/endian.h> +#elif defined(__FreeBSD__) +#include <sys/endian.h> +#elif defined(__linux__) +#include <endian.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__QNX__) + #include <gulliver.h> + #if defined(__LITTLEENDIAN__) + #define BYTE_ORDER __LITTLEENDIAN__ + #define LITTLE_ENDIAN __LITTLEENDIAN__ + #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */ + #endif + #if defined(__BIGENDIAN__) + #define BYTE_ORDER __BIGENDIAN__ + #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */ + #define BIG_ENDIAN __BIGENDIAN__ + #endif +#endif + +#if defined(__sun) && defined(__GNUC__) + +#include <arpa/nameser_compat.h> + +#if !defined (BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER__ +#endif + +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif + +#endif /* sun + GNUC */ + +#if !defined(BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER +#endif +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __BIG_ENDIAN +#endif + +#endif + +/* + * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, + * but happily have something equivalent in the SO_NOSIGPIPE flag. + */ +#ifdef __APPLE__ +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +/* + * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in + * POSIX 2008. + */ +#ifdef __sun +#define MSG_NOSIGNAL 0 +#endif + +#ifdef _WIN32 +#ifndef FD_HASHTABLE_MODULUS +#define FD_HASHTABLE_MODULUS 32 +#endif +#endif + +#ifndef LWS_DEF_HEADER_LEN +#define LWS_DEF_HEADER_LEN 4096 +#endif +#ifndef LWS_DEF_HEADER_POOL +#define LWS_DEF_HEADER_POOL 4 +#endif +#ifndef LWS_MAX_PROTOCOLS +#define LWS_MAX_PROTOCOLS 5 +#endif +#ifndef LWS_MAX_EXTENSIONS_ACTIVE +#define LWS_MAX_EXTENSIONS_ACTIVE 2 +#endif +#ifndef LWS_MAX_EXT_OFFERS +#define LWS_MAX_EXT_OFFERS 8 +#endif +#ifndef SPEC_LATEST_SUPPORTED +#define SPEC_LATEST_SUPPORTED 13 +#endif +#ifndef AWAITING_TIMEOUT +#define AWAITING_TIMEOUT 20 +#endif +#ifndef CIPHERS_LIST_STRING +#define CIPHERS_LIST_STRING "DEFAULT" +#endif +#ifndef LWS_SOMAXCONN +#define LWS_SOMAXCONN SOMAXCONN +#endif + +#define MAX_WEBSOCKET_04_KEY_LEN 128 + +#ifndef SYSTEM_RANDOM_FILEPATH +#define SYSTEM_RANDOM_FILEPATH "/dev/urandom" +#endif + +enum lws_websocket_opcodes_07 { + LWSWSOPC_CONTINUATION = 0, + LWSWSOPC_TEXT_FRAME = 1, + LWSWSOPC_BINARY_FRAME = 2, + + LWSWSOPC_NOSPEC__MUX = 7, + + /* control extensions 8+ */ + + LWSWSOPC_CLOSE = 8, + LWSWSOPC_PING = 9, + LWSWSOPC_PONG = 0xa, +}; + + +enum lws_connection_states { + LWSS_HTTP, + LWSS_HTTP_ISSUING_FILE, + LWSS_HTTP_HEADERS, + LWSS_HTTP_BODY, + LWSS_DEAD_SOCKET, + LWSS_ESTABLISHED, + LWSS_CLIENT_HTTP_ESTABLISHED, + LWSS_CLIENT_UNCONNECTED, + LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION, + LWSS_RETURNED_CLOSE_ALREADY, + LWSS_AWAITING_CLOSE_ACK, + LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE, + LWSS_SHUTDOWN, + + LWSS_HTTP2_AWAIT_CLIENT_PREFACE, + LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS, + LWSS_HTTP2_ESTABLISHED, + + LWSS_CGI, +}; + +enum http_version { + HTTP_VERSION_1_0, + HTTP_VERSION_1_1, + HTTP_VERSION_2 +}; + +enum http_connection_type { + HTTP_CONNECTION_CLOSE, + HTTP_CONNECTION_KEEP_ALIVE +}; + +enum lws_rx_parse_state { + LWS_RXPS_NEW, + + LWS_RXPS_04_mask_1, + LWS_RXPS_04_mask_2, + LWS_RXPS_04_mask_3, + + LWS_RXPS_04_FRAME_HDR_1, + LWS_RXPS_04_FRAME_HDR_LEN, + LWS_RXPS_04_FRAME_HDR_LEN16_2, + LWS_RXPS_04_FRAME_HDR_LEN16_1, + LWS_RXPS_04_FRAME_HDR_LEN64_8, + LWS_RXPS_04_FRAME_HDR_LEN64_7, + LWS_RXPS_04_FRAME_HDR_LEN64_6, + LWS_RXPS_04_FRAME_HDR_LEN64_5, + LWS_RXPS_04_FRAME_HDR_LEN64_4, + LWS_RXPS_04_FRAME_HDR_LEN64_3, + LWS_RXPS_04_FRAME_HDR_LEN64_2, + LWS_RXPS_04_FRAME_HDR_LEN64_1, + + LWS_RXPS_07_COLLECT_FRAME_KEY_1, + LWS_RXPS_07_COLLECT_FRAME_KEY_2, + LWS_RXPS_07_COLLECT_FRAME_KEY_3, + LWS_RXPS_07_COLLECT_FRAME_KEY_4, + + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED +}; + +#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32 + +enum connection_mode { + LWSCM_HTTP_SERVING, + LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */ + LWSCM_PRE_WS_SERVING_ACCEPT, + + LWSCM_WS_SERVING, + LWSCM_WS_CLIENT, + + LWSCM_HTTP2_SERVING, + + /* transient, ssl delay hiding */ + LWSCM_SSL_ACK_PENDING, + LWSCM_SSL_INIT, + /* as above, but complete into LWSCM_RAW */ + LWSCM_SSL_ACK_PENDING_RAW, + LWSCM_SSL_INIT_RAW, + + /* special internal types */ + LWSCM_SERVER_LISTENER, + LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */ + LWSCM_RAW, /* raw with bulk handling */ + LWSCM_RAW_FILEDESC, /* raw without bulk handling */ + + /* HTTP Client related */ + LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP, + LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */ + LWSCM_WSCL_WAITING_CONNECT, + LWSCM_WSCL_WAITING_PROXY_REPLY, + LWSCM_WSCL_ISSUE_HANDSHAKE, + LWSCM_WSCL_ISSUE_HANDSHAKE2, + LWSCM_WSCL_ISSUE_HTTP_BODY, + LWSCM_WSCL_WAITING_SSL, + LWSCM_WSCL_WAITING_SERVER_REPLY, + LWSCM_WSCL_WAITING_EXTENSION_CONNECT, + LWSCM_WSCL_PENDING_CANDIDATE_CHILD, + LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY, + LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY, + LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY, + + /****** add new things just above ---^ ******/ + + +}; + +/* enums of socks version */ +enum socks_version { + SOCKS_VERSION_4 = 4, + SOCKS_VERSION_5 = 5 +}; + +/* enums of subnegotiation version */ +enum socks_subnegotiation_version { + SOCKS_SUBNEGOTIATION_VERSION_1 = 1, +}; + +/* enums of socks commands */ +enum socks_command { + SOCKS_COMMAND_CONNECT = 1, + SOCKS_COMMAND_BIND = 2, + SOCKS_COMMAND_UDP_ASSOCIATE = 3 +}; + +/* enums of socks address type */ +enum socks_atyp { + SOCKS_ATYP_IPV4 = 1, + SOCKS_ATYP_DOMAINNAME = 3, + SOCKS_ATYP_IPV6 = 4 +}; + +/* enums of socks authentication methods */ +enum socks_auth_method { + SOCKS_AUTH_NO_AUTH = 0, + SOCKS_AUTH_GSSAPI = 1, + SOCKS_AUTH_USERNAME_PASSWORD = 2 +}; + +/* enums of subnegotiation status */ +enum socks_subnegotiation_status { + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, +}; + +/* enums of socks request reply */ +enum socks_request_reply { + SOCKS_REQUEST_REPLY_SUCCESS = 0, + SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, + SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, + SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, + SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, + SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, + SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, + SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, + SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 +}; + +/* enums used to generate socks messages */ +enum socks_msg_type { + /* greeting */ + SOCKS_MSG_GREETING, + /* credential, user name and password */ + SOCKS_MSG_USERNAME_PASSWORD, + /* connect command */ + SOCKS_MSG_CONNECT +}; + +enum { + LWS_RXFLOW_ALLOW = (1 << 0), + LWS_RXFLOW_PENDING_CHANGE = (1 << 1), +}; + +struct lws_ring { + void *buf; + void (*destroy_element)(void *element); + size_t buflen; + size_t element_len; + uint32_t head; + uint32_t oldest_tail; +}; + +/* this is not usable directly by user code any more, lws_close_reason() */ +#define LWS_WRITE_CLOSE 4 + +struct lws_protocols; +struct lws; + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + +struct lws_io_watcher { +#ifdef LWS_WITH_LIBEV + ev_io ev_watcher; +#endif +#ifdef LWS_WITH_LIBUV + uv_poll_t uv_watcher; +#endif +#ifdef LWS_WITH_LIBEVENT + struct event *event_watcher; +#endif + struct lws_context *context; + + uint8_t actual_events; +}; + +struct lws_signal_watcher { +#ifdef LWS_WITH_LIBEV + ev_signal ev_watcher; +#endif +#ifdef LWS_WITH_LIBUV + uv_signal_t uv_watcher; +#endif +#ifdef LWS_WITH_LIBEVENT + struct event *event_watcher; +#endif + struct lws_context *context; +}; +#endif + +#ifdef _WIN32 +#define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) +struct lws_fd_hashtable { + struct lws **wsi; + int length; +}; +#endif + +/* + * This is totally opaque to code using the library. It's exported as a + * forward-reference pointer-only declaration; the user can use the pointer with + * other APIs to get information out of it. + */ + +#if defined(LWS_WITH_ESP32) +typedef uint16_t ah_data_idx_t; +#else +typedef uint32_t ah_data_idx_t; +#endif + +struct lws_fragments { + ah_data_idx_t offset; + uint16_t len; + uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */ + uint8_t flags; /* only http2 cares */ +}; + +/* + * these are assigned from a pool held in the context. + * Both client and server mode uses them for http header analysis + */ + +struct allocated_headers { + struct allocated_headers *next; /* linked list */ + struct lws *wsi; /* owner */ + char *data; /* prepared by context init to point to dedicated storage */ + ah_data_idx_t data_length; + /* + * the randomly ordered fragments, indexed by frag_index and + * lws_fragments->nfrag for continuation. + */ + struct lws_fragments frags[WSI_TOKEN_COUNT]; + time_t assigned; + /* + * for each recognized token, frag_index says which frag[] his data + * starts in (0 means the token did not appear) + * the actual header data gets dumped as it comes in, into data[] + */ + uint8_t frag_index[WSI_TOKEN_COUNT]; +#if defined(LWS_WITH_ESP32) + uint8_t rx[256]; +#else + uint8_t rx[2048]; +#endif + + int16_t rxpos; + int16_t rxlen; + uint32_t pos; + uint32_t http_response; + int hdr_token_idx; + +#ifndef LWS_NO_CLIENT + char initial_handshake_hash_base64[30]; +#endif + + uint8_t in_use; + uint8_t nfrag; +}; + +/* + * so we can have n connections being serviced simultaneously, + * these things need to be isolated per-thread. + */ + +struct lws_context_per_thread { +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; +#endif + struct lws_pollfd *fds; +#if defined(LWS_WITH_ESP8266) + struct lws **lws_vs_fds_index; +#endif + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + struct lws *timeout_list; +#if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_context *context; +#endif +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi_list; +#endif + void *http_header_data; + struct allocated_headers *ah_list; + struct lws *ah_wait_list; + int ah_wait_list_length; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *pending_read_list; /* linked list */ +#endif +#if defined(LWS_WITH_LIBEV) + struct ev_loop *io_loop_ev; +#endif +#if defined(LWS_WITH_LIBUV) + uv_loop_t *io_loop_uv; + uv_signal_t signals[8]; + uv_timer_t uv_timeout_watcher; + uv_idle_t uv_idle; +#endif +#if defined(LWS_WITH_LIBEVENT) + struct event_base *io_loop_event_base; +#endif +#if defined(LWS_WITH_LIBEV) + struct lws_io_watcher w_accept; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_signal_watcher w_sigint; + unsigned char ev_loop_foreign:1; +#endif + + unsigned long count_conns; + /* + * usable by anything in the service code, but only if the scope + * does not last longer than the service action (since next service + * of any socket can likewise use it and overwrite) + */ + unsigned char *serv_buf; +#ifdef _WIN32 + WSAEVENT *events; +#else + lws_sockfd_type dummy_pipe_fds[2]; +#endif + unsigned int fds_count; + uint32_t ah_pool_length; + + short ah_count_in_use; + unsigned char tid; + unsigned char lock_depth; +}; + +struct lws_conn_stats { + unsigned long long rx, tx; + unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, + h2_upg, rejected; +}; + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); + + +enum lws_h2_settings { + H2SET_HEADER_TABLE_SIZE = 1, + H2SET_ENABLE_PUSH, + H2SET_MAX_CONCURRENT_STREAMS, + H2SET_INITIAL_WINDOW_SIZE, + H2SET_MAX_FRAME_SIZE, + H2SET_MAX_HEADER_LIST_SIZE, + + H2SET_COUNT /* always last */ +}; + +struct http2_settings { + uint32_t s[H2SET_COUNT]; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * hierarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + +struct lws_vhost { +#if !defined(LWS_WITH_ESP8266) + char http_proxy_address[128]; + char proxy_basic_auth_token[128]; +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif +#endif +#if defined(LWS_WITH_ESP8266) + /* listen sockets need a place to hang their hat */ + esp_tcp tcp; +#endif + struct lws_conn_stats conn_stats; + struct lws_context *context; + struct lws_vhost *vhost_next; + const struct lws_http_mount *mount_list; + struct lws *lserv_wsi; + const char *name; + const char *iface; +#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + int bind_iface; +#endif + const struct lws_protocols *protocols; + void **protocol_vh_privs; + const struct lws_protocol_vhost_options *pvo; + const struct lws_protocol_vhost_options *headers; + struct lws **same_vh_protocol_list; +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *ssl_ctx; + SSL_CTX *ssl_client_ctx; +#endif +#if defined(LWS_WITH_MBEDTLS) + X509 *x509_client_CA; +#endif +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *extensions; +#endif + void *user; + + int listen_port; + unsigned int http_proxy_port; +#if defined(LWS_WITH_SOCKS5) + unsigned int socks_proxy_port; +#endif + unsigned int options; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + int keepalive_timeout; + int timeout_secs_ah_idle; + int ssl_info_event_mask; +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + +#ifdef LWS_OPENSSL_SUPPORT + int use_ssl; + int allow_non_ssl_on_ssl_port; + unsigned int user_supplied_ssl_ctx:1; +#endif + + unsigned int created_vhost_protocols:1; + unsigned int being_destroyed:1; + + unsigned char default_protocol_index; + unsigned char raw_protocol_index; +}; + +struct lws_deferred_free +{ + struct lws_deferred_free *next; + time_t deadline; + void *payload; +}; + +typedef union { +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sa6; +#endif + struct sockaddr_in sa4; +} sockaddr46; + + +#if defined(LWS_WITH_PEER_LIMITS) +struct lws_peer { + struct lws_peer *next; + struct lws_peer *peer_wait_list; + + time_t time_created; + time_t time_closed_all; + + uint8_t addr[32]; + uint32_t hash; + uint32_t count_wsi; + uint32_t count_ah; + + uint32_t total_wsi; + uint32_t total_ah; + + uint8_t af; +}; +#endif + +/* + * the rest is managed per-context, that includes + * + * - processwide single fd -> wsi lookup + * - contextwide headers pool + */ + +struct lws_context { + time_t last_timeout_check_s; + time_t last_ws_ping_pong_check_s; + time_t time_up; + const struct lws_plat_file_ops *fops; + struct lws_plat_file_ops fops_platform; +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif +#if defined(LWS_WITH_ZIP_FOPS) + struct lws_plat_file_ops fops_zip; +#endif + struct lws_context_per_thread pt[LWS_MAX_SMP]; + struct lws_conn_stats conn_stats; +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; + int lock_depth; +#endif +#ifdef _WIN32 +/* different implementation between unix and windows */ + struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; +#else +#if defined(LWS_WITH_ESP8266) + struct espconn **connpool; /* .reverse points to the wsi */ + void *rxd; + int rxd_len; + os_timer_t to_timer; +#else + struct lws **lws_lookup; /* fd to wsi */ +#endif +#endif + struct lws_vhost *vhost_list; + struct lws_vhost *vhost_pending_destruction_list; + struct lws_plugin *plugin_list; + struct lws_deferred_free *deferred_free_list; +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer **pl_hash_table; + struct lws_peer *peer_wait_list; + time_t next_cull; +#endif + + void *external_baggage_free_on_destroy; + const struct lws_token_limits *token_limits; + void *user_space; + const char *server_string; + const struct lws_protocol_vhost_options *reject_service_keywords; + lws_reload_func deprecation_cb; + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + cap_value_t caps[4]; + char count_caps; +#endif + +#if defined(LWS_WITH_LIBEV) + lws_ev_signal_cb_t * lws_ev_sigint_cb; +#endif +#if defined(LWS_WITH_LIBUV) + uv_signal_cb lws_uv_sigint_cb; + uv_loop_t pu_loop; +#endif +#if defined(LWS_WITH_LIBEVENT) + lws_event_signal_cb_t * lws_event_sigint_cb; +#endif + char canonical_hostname[128]; +#ifdef LWS_LATENCY + unsigned long worst_latency; + char worst_latency_info[256]; +#endif + +#if defined(LWS_WITH_STATS) + uint64_t lws_stats[LWSSTATS_SIZE]; + uint64_t last_dump; + int updated; +#endif +#if defined(LWS_WITH_ESP32) + unsigned long time_last_state_dump; + uint32_t last_free_heap; +#endif + + int max_fds; +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + int use_ev_sigint; +#endif + int started_with_parent; + int uid, gid; + + int fd_random; + + int count_wsi_allocated; + int count_cgi_spawned; + unsigned int options; + unsigned int fd_limit_per_thread; + unsigned int timeout_secs; + unsigned int pt_serv_buf_size; + int max_http_header_data; + int simultaneous_ssl_restriction; + int simultaneous_ssl; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t pl_hash_elements; /* protected by context->lock */ + uint32_t count_peers; /* protected by context->lock */ + unsigned short ip_limit_ah; + unsigned short ip_limit_wsi; +#endif + unsigned int deprecated:1; + unsigned int being_destroyed:1; + unsigned int being_destroyed1:1; + unsigned int requested_kill:1; + unsigned int protocol_init_done:1; + unsigned int ssl_gate_accepts:1; + unsigned int doing_protocol_init; + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; + int service_tid_detected; + + short max_http_header_pool; + short count_threads; + short plugin_protocol_count; + short plugin_extension_count; + short server_string_len; + unsigned short ws_ping_pong_interval; + unsigned short deprecation_pending_listen_close_count; + + uint8_t max_fi; +}; + +int +lws_check_deferred_free(struct lws_context *context, int force); + +#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] +#define lws_get_vh_protocol(vh, x) vh->protocols[x] + +LWS_EXTERN void +lws_close_free_wsi_final(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle_manually(struct lws *wsi); +LWS_EXTERN int +lws_libuv_check_watcher_active(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_init(struct lws_context * context, const char * const *d); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_destroy(struct lws_context * context); + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi); + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), + + LWS_EV_PREPARE_DELETION = (1 << 31), +}; + +#if defined(LWS_WITH_LIBEV) +LWS_EXTERN void +lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libev_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libev_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libev_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libev_run(const struct lws_context *context, int tsi); +#define LWS_LIBEV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV) +LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); +#else +#define lws_libev_accept(_a, _b) ((void) 0) +#define lws_libev_io(_a, _b) ((void) 0) +#define lws_libev_init_fd_table(_a) (0) +#define lws_libev_run(_a, _b) ((void) 0) +#define lws_libev_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBEV_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libev(_a) \ + lwsl_info("libev support not compiled in\n") +#else +#define lws_feature_status_libev(_a) +#endif +#endif + +#if defined(LWS_WITH_LIBUV) +LWS_EXTERN void +lws_libuv_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libuv_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libuv_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libuv_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN int +lws_uv_initvhost(struct lws_vhost* vh, struct lws*); +#define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) +LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info); +#else +#define lws_libuv_accept(_a, _b) ((void) 0) +#define lws_libuv_io(_a, _b) ((void) 0) +#define lws_libuv_init_fd_table(_a) (0) +#define lws_libuv_run(_a, _b) ((void) 0) +#define lws_libuv_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBUV_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libuv(_a) \ + lwsl_notice("libuv support not compiled in\n") +#else +#define lws_feature_status_libuv(_a) +#endif +#endif + +#if defined(LWS_WITH_LIBEVENT) +LWS_EXTERN void +lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc); +LWS_EXTERN void +lws_libevent_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libevent_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libevent_destroyloop(struct lws_context *context, int tsi); +LWS_EXTERN void +lws_libevent_run(const struct lws_context *context, int tsi); +#define LWS_LIBEVENT_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT) +LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *info); +#else +#define lws_libevent_accept(_a, _b) ((void) 0) +#define lws_libevent_io(_a, _b) ((void) 0) +#define lws_libevent_init_fd_table(_a) (0) +#define lws_libevent_run(_a, _b) ((void) 0) +#define lws_libevent_destroyloop(_a, _b) ((void) 0) +#define LWS_LIBEVENT_ENABLED(context) (0) +#if LWS_POSIX && !defined(LWS_WITH_ESP32) +#define lws_feature_status_libevent(_a) \ + lwsl_notice("libevent support not compiled in\n") +#else +#define lws_feature_status_libevent(_a) +#endif +#endif + + +#ifdef LWS_WITH_IPV6 +#define LWS_IPV6_ENABLED(vh) \ + (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ + !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +#ifdef LWS_WITH_UNIX_SOCK +#define LWS_UNIX_SOCK_ENABLED(vhost) \ + (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) +#else +#define LWS_UNIX_SOCK_ENABLED(vhost) (0) +#endif + +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; + +/* notice that these union members: + * + * hdr + * http + * http2 + * + * all have a pointer to allocated_headers struct as their first member. + * + * It means for allocated_headers access, the three union paths can all be + * used interchangeably to access the same data + */ + + +#ifndef LWS_NO_CLIENT +struct client_info_stash { + char address[256]; + char path[4096]; + char host[256]; + char origin[256]; + char protocol[256]; + char method[16]; + char iface[16]; +}; +#endif + +struct _lws_header_related { + /* MUST be first in struct */ + struct allocated_headers *ah; + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + enum uri_path_states ups; + enum uri_esc_states ues; + short lextable_pos; + unsigned int current_token_limit; + + char esc_stash; + char post_literal_equal; + unsigned char parser_state; /* enum lws_token_indexes */ +}; + +#if defined(LWS_WITH_RANGES) +enum range_states { + LWSRS_NO_ACTIVE_RANGE, + LWSRS_BYTES_EQ, + LWSRS_FIRST, + LWSRS_STARTING, + LWSRS_ENDING, + LWSRS_COMPLETED, + LWSRS_SYNTAX, +}; + +struct lws_range_parsing { + unsigned long long start, end, extent, agg, budget; + const char buf[128]; + int pos; + enum range_states state; + char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr; +}; + +int +lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, unsigned long long extent); +int +lws_ranges_next(struct lws_range_parsing *rp); +void +lws_ranges_reset(struct lws_range_parsing *rp); +#endif + +struct _lws_http_mode_related { + /* MUST be first in struct */ + struct allocated_headers *ah; /* mirroring _lws_header_related */ + struct lws *ah_wait_list; + unsigned char *preamble_rx; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; +#endif + unsigned int preamble_rx_len; + struct lws *new_wsi_list; + lws_filepos_t filepos; + lws_filepos_t filelen; + lws_fop_fd_t fop_fd; + +#if defined(LWS_WITH_RANGES) + struct lws_range_parsing range; + char multipart_content_type[64]; +#endif + + enum http_version request_version; + enum http_connection_type connection_type; + lws_filepos_t tx_content_length; + lws_filepos_t tx_content_remain; + lws_filepos_t rx_content_length; + lws_filepos_t rx_content_remain; +}; + +#define LWS_H2_FRAME_HEADER_LENGTH 9 + +#ifdef LWS_WITH_HTTP2 + +enum lws_h2_wellknown_frame_types { + LWS_H2_FRAME_TYPE_DATA, + LWS_H2_FRAME_TYPE_HEADERS, + LWS_H2_FRAME_TYPE_PRIORITY, + LWS_H2_FRAME_TYPE_RST_STREAM, + LWS_H2_FRAME_TYPE_SETTINGS, + LWS_H2_FRAME_TYPE_PUSH_PROMISE, + LWS_H2_FRAME_TYPE_PING, + LWS_H2_FRAME_TYPE_GOAWAY, + LWS_H2_FRAME_TYPE_WINDOW_UPDATE, + LWS_H2_FRAME_TYPE_CONTINUATION, + + LWS_H2_FRAME_TYPE_COUNT /* always last */ +}; + +enum lws_h2_flags { + LWS_H2_FLAG_END_STREAM = 1, + LWS_H2_FLAG_END_HEADERS = 4, + LWS_H2_FLAG_PADDED = 8, + LWS_H2_FLAG_PRIORITY = 0x20, + + LWS_H2_FLAG_SETTINGS_ACK = 1, +}; + +enum lws_h2_errors { + H2_ERR_NO_ERROR, /* Graceful shutdown */ + H2_ERR_PROTOCOL_ERROR, /* Protocol error detected */ + H2_ERR_INTERNAL_ERROR, /* Implementation fault */ + H2_ERR_FLOW_CONTROL_ERROR, /* Flow-control limits exceeded */ + H2_ERR_SETTINGS_TIMEOUT, /* Settings not acknowledged */ + H2_ERR_STREAM_CLOSED, /* Frame received for closed stream */ + H2_ERR_FRAME_SIZE_ERROR, /* Frame size incorrect */ + H2_ERR_REFUSED_STREAM, /* Stream not processed */ + H2_ERR_CANCEL, /* Stream cancelled */ + H2_ERR_COMPRESSION_ERROR, /* Compression state not updated */ + H2_ERR_CONNECT_ERROR, /* TCP connection error for CONNECT method */ + H2_ERR_ENHANCE_YOUR_CALM, /* Processing capacity exceeded */ + H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */ + H2_ERR_HTTP_1_1_REQUIRED, /* Use HTTP/1.1 for the request */ +}; + +enum lws_h2_states { + LWS_H2_STATE_IDLE, + /* + * Send PUSH_PROMISE -> LWS_H2_STATE_RESERVED_LOCAL + * Recv PUSH_PROMISE -> LWS_H2_STATE_RESERVED_REMOTE + * Send HEADERS -> LWS_H2_STATE_OPEN + * Recv HEADERS -> LWS_H2_STATE_OPEN + * + * - Only PUSH_PROMISE + HEADERS valid to send + * - Only HEADERS or PRIORITY valid to receive + */ + LWS_H2_STATE_RESERVED_LOCAL, + /* + * Send RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv RST_STREAM -> LWS_H2_STATE_CLOSED + * Send HEADERS -> LWS_H2_STATE_HALF_CLOSED_REMOTE + * + * - Only HEADERS, RST_STREAM, or PRIORITY valid to send + * - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive + */ + LWS_H2_STATE_RESERVED_REMOTE, + /* + * Send RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv HEADERS -> LWS_H2_STATE_HALF_CLOSED_LOCAL + * + * - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send + * - Only HEADERS, RST_STREAM, or PRIORITY valid to receive + */ + LWS_H2_STATE_OPEN, + /* + * Send RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv RST_STREAM -> LWS_H2_STATE_CLOSED + * Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL + * Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE + */ + LWS_H2_STATE_HALF_CLOSED_REMOTE, + /* + * Send RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv RST_STREAM -> LWS_H2_STATE_CLOSED + * Send END_STREAM flag -> LWS_H2_STATE_CLOSED + * + * - Any frame valid to send + * - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive + */ + LWS_H2_STATE_HALF_CLOSED_LOCAL, + /* + * Send RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv RST_STREAM -> LWS_H2_STATE_CLOSED + * Recv END_STREAM flag -> LWS_H2_STATE_CLOSED + * + * - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send + * - Any frame valid to receive + */ + LWS_H2_STATE_CLOSED, + /* + * - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE) + * may be received + * + * - Only PRIORITY valid to send + */ +}; + +#define LWS_H2_STREAM_ID_MASTER 0 +#define LWS_H2_SETTINGS_LEN 6 + +enum http2_hpack_state { + HPKS_TYPE, + + HPKS_IDX_EXT, + + HPKS_HLEN, + HPKS_HLEN_EXT, + + HPKS_DATA, +}; + +/* + * lws general parsimonious header strategy is only store values from known + * headers, and refer to them by index. + * + * That means if we can't map the peer header name to one that lws knows, we + * will drop the content but track the indexing with associated_lws_hdr_idx = + * LWS_HPACK_IGNORE_ENTRY. + */ + +enum http2_hpack_type { + HPKT_INDEXED_HDR_7, /* 1xxxxxxx: just "header field" */ + HPKT_INDEXED_HDR_6_VALUE_INCR, /* 01xxxxxx: NEW indexed hdr with value */ + HPKT_LITERAL_HDR_VALUE_INCR, /* 01000000: NEW literal hdr with value */ + HPKT_INDEXED_HDR_4_VALUE, /* 0000xxxx: indexed hdr with value */ + HPKT_INDEXED_HDR_4_VALUE_NEVER, /* 0001xxxx: indexed hdr with value NEVER NEW */ + HPKT_LITERAL_HDR_VALUE, /* 00000000: literal hdr with value */ + HPKT_LITERAL_HDR_VALUE_NEVER, /* 00010000: literal hdr with value NEVER NEW */ + HPKT_SIZE_5 +}; + +#define LWS_HPACK_IGNORE_ENTRY 0xffff + + +struct hpack_dt_entry { + char *value; /* malloc'd */ + uint16_t value_len; + uint16_t hdr_len; /* virtual, for accounting */ + uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */ +}; + +struct hpack_dynamic_table { + struct hpack_dt_entry *entries; /* malloc'd */ + uint32_t virtual_payload_usage; + uint32_t virtual_payload_max; + uint16_t pos; + uint16_t used_entries; + uint16_t num_entries; +}; + +enum lws_h2_protocol_send_type { + LWS_PPS_NONE, + LWS_H2_PPS_MY_SETTINGS, + LWS_H2_PPS_ACK_SETTINGS, + LWS_H2_PPS_PONG, + LWS_H2_PPS_GOAWAY, + LWS_H2_PPS_RST_STREAM, + LWS_H2_PPS_UPDATE_WINDOW, +}; + +struct lws_h2_protocol_send { + struct lws_h2_protocol_send *next; /* linked list */ + enum lws_h2_protocol_send_type type; + + union uu { + struct { + char str[32]; + uint32_t highest_sid; + uint32_t err; + } ga; + struct { + uint32_t sid; + uint32_t err; + } rs; + struct { + uint8_t ping_payload[8]; + } ping; + struct { + uint32_t sid; + uint32_t credit; + } update_window; + } u; +}; + +struct lws_h2_ghost_sid { + struct lws_h2_ghost_sid *next; + uint32_t sid; +}; + +#define LWS_H2_RX_SCRATCH_SIZE 512 + +/* + * http/2 connection info that is only used by the root connection that has + * the network connection. + * + * h2 tends to spawn many child connections from one network connection, so + * it's necessary to make members only needed by the network connection + * distinct and only malloc'd on network connections. + * + * There's only one HPACK parser per network connection. + * + * But there is an ah per logical child connection... the network connection + * fills it but it belongs to the logical child. + */ +struct lws_h2_netconn { + struct http2_settings set; + struct hpack_dynamic_table hpack_dyn_table; + uint8_t ping_payload[8]; + uint8_t one_setting[LWS_H2_SETTINGS_LEN]; + char goaway_str[32]; /* for rx */ + struct lws *swsi; + struct lws_h2_protocol_send *pps; /* linked list */ + char *rx_scratch; + + enum http2_hpack_state hpack; + enum http2_hpack_type hpack_type; + + unsigned int huff:1; + unsigned int value:1; + unsigned int unknown_header:1; + unsigned int cont_exp:1; + unsigned int cont_exp_headers:1; + unsigned int we_told_goaway:1; + unsigned int pad_length:1; + unsigned int collected_priority:1; + unsigned int is_first_header_char:1; + unsigned int seen_nonpseudoheader:1; + unsigned int zero_huff_padding:1; + unsigned int last_action_dyntable_resize:1; + + uint32_t hdr_idx; + uint32_t hpack_len; + uint32_t hpack_e_dep; + uint32_t count; + uint32_t preamble; + uint32_t length; + uint32_t sid; + uint32_t inside; + uint32_t highest_sid; + uint32_t highest_sid_opened; + uint32_t cont_exp_sid; + uint32_t dep; + uint32_t goaway_last_sid; + uint32_t goaway_err; + uint32_t hpack_hdr_len; + + uint32_t rx_scratch_pos; + uint32_t rx_scratch_len; + + uint16_t hpack_pos; + + uint8_t frame_state; + uint8_t type; + uint8_t flags; + uint8_t padding; + uint8_t weight_temp; + uint8_t huff_pad; + char first_hdr_char; + uint8_t hpack_m; + uint8_t ext_count; +}; + +struct _lws_h2_related { + /* + * having this first lets us also re-use all HTTP union code + * and in turn, http_mode_related has allocated headers in right + * place so we can use the header apis on the wsi directly still + */ + struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */ + + struct lws_h2_netconn *h2n; /* malloc'd for root net conn */ + struct lws *parent_wsi; + struct lws *child_list; + struct lws *sibling_list; + + char *pending_status_body; + + int tx_cr; + int peer_tx_cr_est; + unsigned int my_sid; + unsigned int child_count; + int my_priority; + uint32_t dependent_on; + + unsigned int END_STREAM:1; + unsigned int END_HEADERS:1; + unsigned int send_END_STREAM:1; + unsigned int GOING_AWAY; + unsigned int requested_POLLOUT:1; + unsigned int skint:1; + + uint16_t round_robin_POLLOUT; + uint16_t count_POLLOUT_children; + uint8_t h2_state; /* the RFC7540 state of the connection */ + uint8_t weight; + + uint8_t initialized; +}; + +#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.h2.parent_wsi) + +#endif + +struct _lws_websocket_related { + /* cheapest way to deal with ah overlap with ws union transition */ + struct _lws_header_related hdr; + char *rx_ubuf; + unsigned int rx_ubuf_alloc; + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; + time_t time_next_ping_check; + size_t rx_packet_length; + unsigned int rx_ubuf_head; + unsigned char mask[4]; + /* Also used for close content... control opcode == < 128 */ + unsigned char ping_payload_buf[128 - 3 + LWS_PRE]; + + unsigned char ping_payload_len; + unsigned char mask_idx; + unsigned char opcode; + unsigned char rsv; + unsigned char rsv_first_msg; + /* zero if no info, or length including 2-byte close code */ + unsigned char close_in_ping_buffer_len; + unsigned char utf8; + unsigned char stashed_write_type; + unsigned char tx_draining_stashed_wp; + + unsigned int final:1; + unsigned int frame_is_binary:1; + unsigned int all_zero_nonce:1; + unsigned int this_frame_masked:1; + unsigned int inside_frame:1; /* next write will be more of frame */ + unsigned int clean_buffer:1; /* buffer not rewritten by extension */ + unsigned int payload_is_close:1; /* process as PONG, but it is close */ + unsigned int ping_pending_flag:1; + unsigned int continuation_possible:1; + unsigned int owed_a_fin:1; + unsigned int check_utf8:1; + unsigned int defeat_check_utf8:1; + unsigned int pmce_compressed_message:1; + unsigned int stashed_write_pending:1; + unsigned int rx_draining_ext:1; + unsigned int tx_draining_ext:1; + unsigned int send_check_ping:1; + unsigned int first_fragment:1; +}; + +#ifdef LWS_WITH_CGI + +#define LWS_HTTP_CHUNK_HDR_SIZE 16 + +enum { + SIGNIFICANT_HDR_CONTENT_LENGTH, + SIGNIFICANT_HDR_LOCATION, + SIGNIFICANT_HDR_STATUS, + SIGNIFICANT_HDR_TRANSFER_ENCODING, + + SIGNIFICANT_HDR_COUNT +}; + +/* wsi who is master of the cgi points to an lws_cgi */ + +struct lws_cgi { + struct lws_cgi *cgi_list; + struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */ + struct lws *wsi; /* owner */ + unsigned char *headers_buf; + unsigned char *headers_start; + unsigned char *headers_pos; + unsigned char *headers_dumped; + unsigned char *headers_end; + lws_filepos_t content_length; + lws_filepos_t content_length_seen; + int pipe_fds[3][2]; + int match[SIGNIFICANT_HDR_COUNT]; + int pid; + int response_code; + int lp; + char l[12]; + + unsigned int being_closed:1; + unsigned int explicitly_chunked:1; + + unsigned char chunked_grace; +}; +#endif + +signed char char_to_hex(const char c); + +#ifndef LWS_NO_CLIENT +enum lws_chunk_parser { + ELCP_HEX, + ELCP_CR, + ELCP_CONTENT, + ELCP_POST_CR, + ELCP_POST_LF, +}; +#endif + +enum lws_parse_urldecode_results { + LPUR_CONTINUE, + LPUR_SWALLOW, + LPUR_FORBID, + LPUR_EXCESSIVE, +}; + +struct lws_rewrite; + +#ifdef LWS_WITH_ACCESS_LOG +struct lws_access_log { + char *header_log; + char *user_agent; + char *referrer; + unsigned long sent; + int response; +}; +#endif + +struct lws { + + /* structs */ + /* members with mutually exclusive lifetimes are unionized */ + + union u { + struct _lws_http_mode_related http; +#ifdef LWS_WITH_HTTP2 + struct _lws_h2_related h2; +#endif + struct _lws_header_related hdr; + struct _lws_websocket_related ws; + } u; + + /* lifetime members */ + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_read; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_write; +#endif +#ifdef LWS_WITH_ACCESS_LOG + struct lws_access_log access_log; +#endif + time_t pending_timeout_limit; + + /* pointers */ + + struct lws_context *context; + struct lws_vhost *vhost; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi; /* wsi being cgi master have one of these */ +#endif + const struct lws_protocols *protocol; + struct lws **same_vh_protocol_prev, *same_vh_protocol_next; + struct lws *timeout_list; + struct lws **timeout_list_prev; +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer; +#endif + + void *user_space; + void *opaque_parent_data; + /* rxflow handling */ + unsigned char *rxflow_buffer; + /* truncated send handling */ + unsigned char *trunc_alloc; /* non-NULL means buffering in progress */ + +#if defined (LWS_WITH_ESP8266) + void *premature_rx; + unsigned short prem_rx_size, prem_rx_pos; +#endif + +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE]; + void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE]; +#endif +#ifdef LWS_OPENSSL_SUPPORT + SSL *ssl; + BIO *client_bio; + struct lws *pending_read_list_prev, *pending_read_list_next; +#if defined(LWS_WITH_STATS) + uint64_t accept_start_us; + char seen_rx; +#endif +#endif +#ifdef LWS_WITH_HTTP_PROXY + struct lws_rewrite *rw; +#endif +#ifdef LWS_LATENCY + unsigned long action_start; + unsigned long latency_start; +#endif + lws_sock_file_fd_type desc; /* .filefd / .sockfd */ +#if defined(LWS_WITH_STATS) + uint64_t active_writable_req_us; +#endif + /* ints */ + int position_in_fds_table; + uint32_t rxflow_len; + uint32_t rxflow_pos; + unsigned int trunc_alloc_len; /* size of malloc */ + unsigned int trunc_offset; /* where we are in terms of spilling */ + unsigned int trunc_len; /* how much is buffered */ +#ifndef LWS_NO_CLIENT + int chunk_remaining; +#endif + unsigned int cache_secs; + + unsigned int hdr_parsing_completed:1; + unsigned int http2_substream:1; + unsigned int upgraded_to_http2:1; + unsigned int listener:1; + unsigned int user_space_externally_allocated:1; + unsigned int socket_is_permanently_unusable:1; + unsigned int rxflow_change_to:2; + unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */ + unsigned int conn_stat_done:1; + unsigned int cache_reuse:1; + unsigned int cache_revalidate:1; + unsigned int cache_intermediaries:1; + unsigned int favoured_pollin:1; + unsigned int sending_chunked:1; + unsigned int already_did_cce:1; + unsigned int told_user_closed:1; + unsigned int waiting_to_send_close_frame:1; + unsigned int ipv6:1; + unsigned int parent_carries_io:1; + unsigned int parent_pending_cb_on_writable:1; + unsigned int cgi_stdout_zero_length:1; + unsigned int seen_zero_length_recv:1; + unsigned int rxflow_will_be_applied:1; + +#if defined(LWS_WITH_ESP8266) + unsigned int pending_send_completion:3; + unsigned int close_is_pending_send_completion:1; +#endif +#ifdef LWS_WITH_ACCESS_LOG + unsigned int access_log_pending:1; +#endif +#ifndef LWS_NO_CLIENT + unsigned int do_ws:1; /* whether we are doing http or ws flow */ + unsigned int chunked:1; /* if the clientside connection is chunked */ + unsigned int client_rx_avail:1; + unsigned int client_http_body_pending:1; +#endif +#ifdef LWS_WITH_HTTP_PROXY + unsigned int perform_rewrite:1; +#endif +#ifndef LWS_NO_EXTENSIONS + unsigned int extension_data_pending:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int use_ssl:4; +#endif +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif +#ifdef LWS_OPENSSL_SUPPORT + unsigned int redirect_to_https:1; +#endif + + /* volatile to make sure code is aware other thread can change */ + volatile unsigned int handling_pollout:1; + volatile unsigned int leave_pollout_active:1; + +#ifndef LWS_NO_CLIENT + unsigned short c_port; +#endif + + /* chars */ +#ifndef LWS_NO_EXTENSIONS + unsigned char count_act_ext; +#endif + uint8_t ietf_spec_revision; + char mode; /* enum connection_mode */ + char state; /* enum lws_connection_states */ + char state_pre_close; + char lws_rx_parse_state; /* enum lws_rx_parse_state */ + char rx_frame_type; /* enum lws_write_protocol */ + char pending_timeout; /* enum pending_timeout */ + char tsi; /* thread service index we belong to */ + char protocol_interpret_idx; + char redirects; + uint8_t rxflow_bitmap; +#ifdef LWS_WITH_CGI + char cgi_channel; /* which of stdin/out/err */ + char hdr_state; +#endif +#ifndef LWS_NO_CLIENT + char chunk_parser; /* enum lws_chunk_parser */ +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +}; + +#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) + +LWS_EXTERN int log_level; + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface); + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr); +#endif + +LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status); + +LWS_EXTERN void +lws_free_wsi(struct lws *wsi); + +LWS_EXTERN int +remove_wsi_socket_from_fds(struct lws *wsi); +LWS_EXTERN int +lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); + +#ifndef LWS_LATENCY +static inline void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion) { + do { + (void)context; (void)wsi; (void)action; (void)ret; + (void)completion; + } while (0); +} +static inline void +lws_latency_pre(struct lws_context *context, struct lws *wsi) { + do { (void)context; (void)wsi; } while (0); +} +#else +#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0) +extern void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion); +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_urldecode(struct lws *wsi, uint8_t *_c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_action(struct lws *wsi); + +LWS_EXTERN int +lws_b64_selftest(void); + +LWS_EXTERN int +lws_service_flag_pending(struct lws_context *context, int tsi); + +#if defined(_WIN32) || defined(LWS_WITH_ESP8266) +LWS_EXTERN struct lws * +wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd); + +LWS_EXTERN int +insert_wsi(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int +delete_from_fd(struct lws_context *context, lws_sockfd_type fd); +#else +#define wsi_from_fd(A,B) A->lws_lookup[B] +#define insert_wsi(A,B) assert(A->lws_lookup[B->desc.sockfd] == 0); A->lws_lookup[B->desc.sockfd]=B +#define delete_from_fd(A,B) A->lws_lookup[B]=0 +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); + + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_service_timeout_check(struct lws *wsi, unsigned int sec); + +LWS_EXTERN void +lws_remove_from_timeout_list(struct lws *wsi); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_2(struct lws *wsi); + +LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT +lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, + const char *path, const char *host); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_create_new_server_wsi(struct lws_vhost *vhost); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_generate_client_handshake(struct lws *wsi, char *pkt); + +LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +LWS_EXTERN struct lws * +lws_client_connect_via_info2(struct lws *wsi); + +LWS_EXTERN int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah); + +/* + * EXTENSIONS + */ + +#ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct lws_context *context); +LWS_EXTERN int +lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r, + void *v, size_t len); + +LWS_EXTERN int +lws_ext_cb_active(struct lws *wsi, int reason, void *buf, int len); +LWS_EXTERN int +lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason, + void *arg, int len); + +#else +#define lws_any_extension_handled(_a, _b, _c, _d) (0) +#define lws_ext_cb_active(_a, _b, _c, _d) (0) +#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_interpret_server_handshake(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_union_transition(struct lws *wsi, enum connection_mode mode); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len); +#ifdef LWS_WITH_HTTP2 +struct lws * lws_h2_get_nth_child(struct lws *wsi, int n); +LWS_EXTERN void lws_h2_init(struct lws *wsi); +LWS_EXTERN int +lws_h2_settings(struct lws *nwsi, struct http2_settings *settings, + unsigned char *buf, int len); +LWS_EXTERN int +lws_h2_parser(struct lws *wsi, unsigned char c); +LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi); +LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags, + unsigned int sid, unsigned int len, + unsigned char *buf); +LWS_EXTERN struct lws * +lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid); +LWS_EXTERN int lws_hpack_interpret(struct lws *wsi, + unsigned char c); +LWS_EXTERN int +lws_add_http2_header_by_name(struct lws *wsi, + const unsigned char *name, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +LWS_EXTERN int +lws_add_http2_header_by_token(struct lws *wsi, + enum lws_token_indexes token, + const unsigned char *value, int length, + unsigned char **p, unsigned char *end); +LWS_EXTERN int +lws_add_http2_header_status(struct lws *wsi, + unsigned int code, unsigned char **p, + unsigned char *end); +LWS_EXTERN int +lws_h2_configure_if_upgraded(struct lws *wsi); +LWS_EXTERN void +lws_hpack_destroy_dynamic_header(struct lws *wsi); +LWS_EXTERN int +lws_hpack_dynamic_size(struct lws *wsi, int size); +LWS_EXTERN int +lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason); +LWS_EXTERN int +lws_h2_tx_cr_get(struct lws *wsi); +LWS_EXTERN void +lws_h2_tx_cr_consume(struct lws *wsi, int consumed); +LWS_EXTERN int +lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h); +LWS_EXTERN void +lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss); + +LWS_EXTERN const struct http2_settings lws_h2_defaults; +#else +#define lws_h2_configure_if_upgraded(x) +#endif + +LWS_EXTERN int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd); + +LWS_EXTERN int +lws_plat_check_connection_error(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice); + +LWS_EXTERN int +lws_header_table_detach(struct lws *wsi, int autoservice); + +LWS_EXTERN void +lws_header_table_reset(struct lws *wsi, int autoservice); +void +_lws_header_table_reset(struct allocated_headers *ah); + +void +lws_header_table_force_to_detachable_state(struct lws *wsi); +int +lws_header_table_is_in_detachable_state(struct lws *wsi); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ensure_user_space(struct lws *wsi); + +LWS_EXTERN int +lws_change_pollfd(struct lws *wsi, int _and, int _or); + +#ifndef LWS_NO_SERVER +int lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +LWS_EXTERN struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername); +LWS_EXTERN int +handshake_0405(struct lws_context *context, struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info); +#else +#define lws_context_init_server(_a, _b) (0) +#define lws_interpret_incoming_packet(_a, _b, _c) (0) +#define lws_server_get_canonical_hostname(_a, _b) +#endif + +#ifndef LWS_NO_DAEMONIZE +LWS_EXTERN int get_daemonize_pid(); +#else +#define get_daemonize_pid() (0) +#endif + +#if !defined(LWS_WITH_ESP8266) +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +interface_to_sa(struct lws_vhost *vh, const char *ifname, + struct sockaddr_in *addr, size_t addrlen); +#endif +LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); + +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, + LWS_SSL_CAPABLE_MORE_SERVICE = -2, +}; + +#ifndef LWS_OPENSSL_SUPPORT +#define LWS_SSL_ENABLED(context) (0) +#define lws_context_init_server_ssl(_a, _b) (0) +#define lws_ssl_destroy(_a) +#define lws_context_init_http2_ssl(_a) +#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl +#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl +#define lws_ssl_pending lws_ssl_pending_no_ssl +#define lws_server_socket_service_ssl(_b, _c) (0) +#define lws_ssl_close(_a) (0) +#define lws_ssl_context_destroy(_a) +#define lws_ssl_SSL_CTX_destroy(_a) +#define lws_ssl_remove_wsi_from_buffered_list(_a) +#define lws_context_init_ssl_library(_a) +#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0) +#else +#define LWS_SSL_ENABLED(context) (context->use_ssl) +LWS_EXTERN int openssl_websocket_private_data_index; +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending(struct lws *wsi); +LWS_EXTERN int +lws_context_init_ssl_library(struct lws_context_creation_info *info); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN int +lws_ssl_close(struct lws *wsi); +LWS_EXTERN void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); +LWS_EXTERN void +lws_ssl_context_destroy(struct lws_context *context); +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_bio_create(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect1(struct lws *wsi); +LWS_EXTERN int +lws_ssl_client_connect2(struct lws *wsi); +LWS_EXTERN void +lws_ssl_elaborate_error(void); +LWS_EXTERN int +lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi); +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); +#else +#define lws_context_init_server_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_ssl_destroy(struct lws_vhost *vhost); +/* HTTP2-related */ + +#ifdef LWS_WITH_HTTP2 +LWS_EXTERN void +lws_context_init_http2_ssl(struct lws_vhost *vhost); +#else +#define lws_context_init_http2_ssl(_a) +#endif +#endif + +#if LWS_MAX_SMP > 1 +static LWS_INLINE void +lws_pt_mutex_init(struct lws_context_per_thread *pt) +{ + pthread_mutex_init(&pt->lock, NULL); +} + +static LWS_INLINE void +lws_pt_mutex_destroy(struct lws_context_per_thread *pt) +{ + pthread_mutex_destroy(&pt->lock); +} + +static LWS_INLINE void +lws_pt_lock(struct lws_context_per_thread *pt) +{ + if (!pt->lock_depth++) + pthread_mutex_lock(&pt->lock); +} + +static LWS_INLINE void +lws_pt_unlock(struct lws_context_per_thread *pt) +{ + if (!(--pt->lock_depth)) + pthread_mutex_unlock(&pt->lock); +} +static LWS_INLINE void +lws_context_lock(struct lws_context *context) +{ + if (!context->lock_depth++) + pthread_mutex_lock(&context->lock); +} + +static LWS_INLINE void +lws_context_unlock(struct lws_context *context) +{ + if (!(--context->lock_depth)) + pthread_mutex_unlock(&context->lock); +} + +#else +#define lws_pt_mutex_init(_a) (void)(_a) +#define lws_pt_mutex_destroy(_a) (void)(_a) +#define lws_pt_lock(_a) (void)(_a) +#define lws_pt_unlock(_a) (void)(_a) +#define lws_context_lock(_a) (void)(_a) +#define lws_context_unlock(_a) (void)(_a) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending_no_ssl(struct lws *wsi); + +#ifdef LWS_WITH_HTTP_PROXY +struct lws_rewrite { + hubbub_parser *parser; + hubbub_parser_optparams params; + const char *from, *to; + int from_len, to_len; + unsigned char *p, *end; + struct lws *wsi; +}; +static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len) +{ + if (s->len != len) + return 1; + + return strncmp((const char *)s->ptr, p, len); +} +typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw); +LWS_EXTERN struct lws_rewrite * +lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to); +LWS_EXTERN void +lws_rewrite_destroy(struct lws_rewrite *r); +LWS_EXTERN int +lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len); +#endif + +#ifndef LWS_NO_CLIENT +LWS_EXTERN int lws_client_socket_service(struct lws_context *context, + struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi); +#ifdef LWS_OPENSSL_SUPPORT +LWS_EXTERN int +lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost); + +LWS_EXTERN void +lws_ssl_info_callback(const SSL *ssl, int where, int ret); + +#else + #define lws_context_init_client_ssl(_a, _b) (0) +#endif +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len); +LWS_EXTERN void +lws_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#define lws_handshake_client(_a, _b, _c) (0) +#endif + +LWS_EXTERN int +_lws_rx_flow_control(struct lws *wsi); + +LWS_EXTERN int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); + +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd); +LWS_EXTERN int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); +#else +#define lws_server_socket_service(_a, _b, _c) (0) +#define lws_handshake_server(_a, _b, _c) (0) +#endif + +#ifdef LWS_WITH_ACCESS_LOG +LWS_EXTERN int +lws_access_log(struct lws *wsi); +LWS_EXTERN void +lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth); +#else +#define lws_access_log(_a) +#endif + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt); + +LWS_EXTERN void +lws_cgi_remove_and_kill(struct lws *wsi); + +int +lws_protocol_init(struct lws_context *context); + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p); + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len); + +/* + * custom allocator + */ +LWS_EXTERN void * +lws_realloc(void *ptr, size_t size, const char *reason); + +LWS_EXTERN void * LWS_WARN_UNUSED_RESULT +lws_zalloc(size_t size, const char *reason); + +#ifdef LWS_PLAT_OPTEE +void *lws_malloc(size_t size, const char *reason); +void lws_free(void *p); +#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0) +#else +#define lws_malloc(S, R) lws_realloc(NULL, S, R) +#define lws_free(P) lws_realloc(P, 0, "lws_free") +#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) +#endif + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath); + +/* lws_plat_ */ +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct lws_context *context, + struct lws *wsi); +LWS_EXTERN void +lws_plat_service_periodic(struct lws_context *context); + +LWS_EXTERN int +lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pfd); +LWS_EXTERN void +lws_add_wsi_to_draining_ext_list(struct lws *wsi); +LWS_EXTERN void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi); +LWS_EXTERN int +lws_plat_context_early_init(void); +LWS_EXTERN void +lws_plat_context_early_destroy(struct lws_context *context); +LWS_EXTERN void +lws_plat_context_late_destroy(struct lws_context *context); +LWS_EXTERN int +lws_poll_listen_fd(struct lws_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct lws_context *context, int timeout_ms); +LWS_EXTERN LWS_VISIBLE int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); +LWS_EXTERN int +lws_plat_init(struct lws_context *context, + struct lws_context_creation_info *info); +LWS_EXTERN void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info); +LWS_EXTERN unsigned long long +time_in_microseconds(void); +LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_plat_inet_pton(int af, const char *src, void *dst); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); +LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount); +LWS_EXTERN int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount); + +LWS_EXTERN void +lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +lws_same_vh_protocol_insert(struct lws *wsi, int n); + +#if defined(LWS_WITH_STATS) +void +lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump); +void +lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val); +#else +static inline uint64_t lws_stats_atomic_bump(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t bump) { + (void)context; (void)pt; (void)index; (void)bump; return 0; } +static inline uint64_t lws_stats_atomic_max(struct lws_context * context, + struct lws_context_per_thread *pt, int index, uint64_t val) { + (void)context; (void)pt; (void)index; (void)val; return 0; } +#endif + +/* socks */ +void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, + ssize_t *msg_len); + +#if defined(LWS_WITH_PEER_LIMITS) +void +lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); +int +lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_cull_peer_wait_list(struct lws_context *context); +struct lws_peer * +lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); +void +lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, + struct lws *wsi); +#endif + +#ifdef __cplusplus +}; +#endif diff --git a/thirdparty/lws/server/fops-zip.c b/thirdparty/lws/server/fops-zip.c new file mode 100644 index 0000000000..2b254f67af --- /dev/null +++ b/thirdparty/lws/server/fops-zip.c @@ -0,0 +1,669 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Original code used in this source file: + * + * https://github.com/PerBothner/DomTerm.git @912add15f3d0aec + * + * ./lws-term/io.c + * ./lws-term/junzip.c + * + * Copyright (C) 2017 Per Bothner <per@bothner.com> + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * ( copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * lws rewrite: + * + * Copyright (C) 2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#include <zlib.h> + +/* + * This code works with zip format containers which may have files compressed + * with gzip deflate (type 8) or store uncompressed (type 0). + * + * Linux zip produces such zipfiles by default, eg + * + * $ zip ../myzip.zip file1 file2 file3 + */ + +#define ZIP_COMPRESSION_METHOD_STORE 0 +#define ZIP_COMPRESSION_METHOD_DEFLATE 8 + +typedef struct { + lws_filepos_t filename_start; + uint32_t crc32; + uint32_t comp_size; + uint32_t uncomp_size; + uint32_t offset; + uint32_t mod_time; + uint16_t filename_len; + uint16_t extra; + uint16_t method; + uint16_t file_com_len; +} lws_fops_zip_hdr_t; + +typedef struct { + struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into + * file inside zip: fops_zip fops */ + lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file + * itself: using platform fops */ + lws_fops_zip_hdr_t hdr; + z_stream inflate; + lws_filepos_t content_start; + lws_filepos_t exp_uncomp_pos; + union { + uint8_t trailer8[8]; + uint32_t trailer32[2]; + } u; + uint8_t rbuf[128]; /* decompression chunk size */ + int entry_count; + + unsigned int decompress:1; /* 0 = direct from file */ + unsigned int add_gzip_container:1; +} *lws_fops_zip_t; + +struct lws_plat_file_ops fops_zip; +#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD)) + +static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 }; + +enum { + ZC_SIGNATURE = 0, + ZC_VERSION_MADE_BY = 4, + ZC_VERSION_NEEDED_TO_EXTRACT = 6, + ZC_GENERAL_PURPOSE_BIT_FLAG = 8, + ZC_COMPRESSION_METHOD = 10, + ZC_LAST_MOD_FILE_TIME = 12, + ZC_LAST_MOD_FILE_DATE = 14, + ZC_CRC32 = 16, + ZC_COMPRESSED_SIZE = 20, + ZC_UNCOMPRESSED_SIZE = 24, + ZC_FILE_NAME_LENGTH = 28, + ZC_EXTRA_FIELD_LENGTH = 30, + + ZC_FILE_COMMENT_LENGTH = 32, + ZC_DISK_NUMBER_START = 34, + ZC_INTERNAL_FILE_ATTRIBUTES = 36, + ZC_EXTERNAL_FILE_ATTRIBUTES = 38, + ZC_REL_OFFSET_LOCAL_HEADER = 42, + ZC_DIRECTORY_LENGTH = 46, + + ZE_SIGNATURE_OFFSET = 0, + ZE_DESK_NUMBER = 4, + ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6, + ZE_NUM_ENTRIES_THIS_DISK = 8, + ZE_NUM_ENTRIES = 10, + ZE_CENTRAL_DIRECTORY_SIZE = 12, + ZE_CENTRAL_DIR_OFFSET = 16, + ZE_ZIP_COMMENT_LENGTH = 20, + ZE_DIRECTORY_LENGTH = 22, + + ZL_REL_OFFSET_CONTENT = 28, + ZL_HEADER_LENGTH = 30, + + LWS_FZ_ERR_SEEK_END_RECORD = 1, + LWS_FZ_ERR_READ_END_RECORD, + LWS_FZ_ERR_END_RECORD_MAGIC, + LWS_FZ_ERR_END_RECORD_SANITY, + LWS_FZ_ERR_CENTRAL_SEEK, + LWS_FZ_ERR_CENTRAL_READ, + LWS_FZ_ERR_CENTRAL_SANITY, + LWS_FZ_ERR_NAME_TOO_LONG, + LWS_FZ_ERR_NAME_SEEK, + LWS_FZ_ERR_NAME_READ, + LWS_FZ_ERR_CONTENT_SANITY, + LWS_FZ_ERR_CONTENT_SEEK, + LWS_FZ_ERR_SCAN_SEEK, + LWS_FZ_ERR_NOT_FOUND, + LWS_FZ_ERR_ZLIB_INIT, + LWS_FZ_ERR_READ_CONTENT, + LWS_FZ_ERR_SEEK_COMPRESSED, +}; + +static uint16_t +get_u16(void *p) +{ + const uint8_t *c = (const uint8_t *)p; + + return (uint16_t)((c[0] | (c[1] << 8))); +} + +static uint32_t +get_u32(void *p) +{ + const uint8_t *c = (const uint8_t *)p; + + return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24))); +} + +int +lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len) +{ + lws_filepos_t amount; + uint8_t buf[96]; + int i; + + if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0) + return LWS_FZ_ERR_SEEK_END_RECORD; + + if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf, + ZE_DIRECTORY_LENGTH)) + return LWS_FZ_ERR_READ_END_RECORD; + + if (amount != ZE_DIRECTORY_LENGTH) + return LWS_FZ_ERR_READ_END_RECORD; + + /* + * We require the zip to have the last record right at the end + * Linux zip always does this if no zip comment. + */ + if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6) + return LWS_FZ_ERR_END_RECORD_MAGIC; + + i = get_u16(buf + ZE_NUM_ENTRIES); + + if (get_u16(buf + ZE_DESK_NUMBER) || + get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) || + i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK)) + return LWS_FZ_ERR_END_RECORD_SANITY; + + /* end record is OK... look for our file in the central dir */ + + if (lws_vfs_file_seek_set(priv->zip_fop_fd, + get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0) + return LWS_FZ_ERR_CENTRAL_SEEK; + + while (i--) { + priv->content_start = lws_vfs_tell(priv->zip_fop_fd); + + if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf, + ZC_DIRECTORY_LENGTH)) + return LWS_FZ_ERR_CENTRAL_READ; + + if (amount != ZC_DIRECTORY_LENGTH) + return LWS_FZ_ERR_CENTRAL_READ; + + if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50) + return LWS_FZ_ERR_CENTRAL_SANITY; + + lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start); + + priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH); + priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH); + priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd); + + priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD); + priv->hdr.crc32 = get_u32(buf + ZC_CRC32); + priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE); + priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE); + priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER); + priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME); + priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH); + + if (priv->hdr.filename_len != len) + goto next; + + if (len >= sizeof(buf) - 1) + return LWS_FZ_ERR_NAME_TOO_LONG; + + if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, + &amount, buf, len)) + return LWS_FZ_ERR_NAME_READ; + if (amount != len) + return LWS_FZ_ERR_NAME_READ; + + buf[len] = '\0'; + lwsl_debug("check %s vs %s\n", buf, name); + + if (strcmp((const char *)buf, name)) + goto next; + + /* we found a match */ + if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0) + return LWS_FZ_ERR_NAME_SEEK; + if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, + &amount, buf, + ZL_HEADER_LENGTH)) + return LWS_FZ_ERR_NAME_READ; + if (amount != ZL_HEADER_LENGTH) + return LWS_FZ_ERR_NAME_READ; + + priv->content_start = priv->hdr.offset + + ZL_HEADER_LENGTH + + priv->hdr.filename_len + + get_u16(buf + ZL_REL_OFFSET_CONTENT); + + lwsl_debug("content supposed to start at 0x%lx\n", + (unsigned long)priv->content_start); + + if (priv->content_start > priv->zip_fop_fd->len) + return LWS_FZ_ERR_CONTENT_SANITY; + + if (lws_vfs_file_seek_set(priv->zip_fop_fd, + priv->content_start) < 0) + return LWS_FZ_ERR_CONTENT_SEEK; + + /* we are aligned at the start of the content */ + + priv->exp_uncomp_pos = 0; + + return 0; + +next: + if (i && lws_vfs_file_seek_set(priv->zip_fop_fd, + priv->content_start + + ZC_DIRECTORY_LENGTH + + priv->hdr.filename_len + + priv->hdr.extra + + priv->hdr.file_com_len) < 0) + return LWS_FZ_ERR_SCAN_SEEK; + } + + return LWS_FZ_ERR_NOT_FOUND; +} + +static int +lws_fops_zip_reset_inflate(lws_fops_zip_t priv) +{ + if (priv->decompress) + inflateEnd(&priv->inflate); + + priv->inflate.zalloc = Z_NULL; + priv->inflate.zfree = Z_NULL; + priv->inflate.opaque = Z_NULL; + priv->inflate.avail_in = 0; + priv->inflate.next_in = Z_NULL; + + if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) { + lwsl_err("inflate init failed\n"); + return LWS_FZ_ERR_ZLIB_INIT; + } + + if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0) + return LWS_FZ_ERR_CONTENT_SEEK; + + priv->exp_uncomp_pos = 0; + + return 0; +} + +static lws_fop_fd_t +lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char *vpath, lws_fop_flags_t *flags) +{ + lws_fop_flags_t local_flags = 0; + lws_fops_zip_t priv; + char rp[192]; + int m; + + /* + * vpath points at the / after the fops signature in vfs_path, eg + * with a vfs_path "/var/www/docs/manual.zip/index.html", vpath + * will come pointing at "/index.html" + */ + + priv = lws_zalloc(sizeof(*priv), "fops_zip priv"); + if (!priv) + return NULL; + + priv->fop_fd.fops = &fops_zip; + + m = sizeof(rp) - 1; + if ((vpath - vfs_path - 1) < m) + m = vpath - vfs_path - 1; + strncpy(rp, vfs_path, m); + rp[m] = '\0'; + + /* open the zip file itself using the incoming fops, not fops_zip */ + + priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags); + if (!priv->zip_fop_fd) { + lwsl_err("unable to open zip %s\n", rp); + goto bail1; + } + + if (*vpath == '/') + vpath++; + + m = lws_fops_zip_scan(priv, vpath, strlen(vpath)); + if (m) { + lwsl_err("unable to find record matching '%s' %d\n", vpath, m); + goto bail2; + } + + /* the directory metadata tells us modification time, so pass it on */ + priv->fop_fd.mod_time = priv->hdr.mod_time; + *flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL; + priv->fop_fd.flags = *flags; + + /* The zip fop_fd is left pointing at the start of the content. + * + * 1) Content could be uncompressed (STORE), and we can always serve + * that directly + * + * 2) Content could be compressed (GZIP), and the client can handle + * receiving GZIP... we can wrap it in a GZIP header and trailer + * and serve the content part directly. The flag indicating we + * are providing GZIP directly is set so lws will send the right + * headers. + * + * 3) Content could be compressed (GZIP) but the client can't handle + * receiving GZIP... we can decompress it and serve as it is + * inflated piecemeal. + * + * 4) Content may be compressed some unknown way... fail + * + */ + if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) { + /* + * it is stored uncompressed, leave it indicated as + * uncompressed, and just serve it from inside the + * zip with no gzip container; + */ + + lwsl_info("direct zip serving (stored)\n"); + + priv->fop_fd.len = priv->hdr.uncomp_size; + + return &priv->fop_fd; + } + + if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) && + priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) { + + /* + * We can serve the gzipped file contents directly as gzip + * from inside the zip container; client says it is OK. + * + * To convert to standalone gzip, we have to add a 10-byte + * constant header and a variable 8-byte trailer around the + * content. + * + * The 8-byte trailer is prepared now and held in the priv. + */ + + lwsl_info("direct zip serving (gzipped)\n"); + + priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size + + sizeof(priv->u); + + if (lws_is_be()) { + uint8_t *p = priv->u.trailer8; + + *p++ = (uint8_t)priv->hdr.crc32; + *p++ = (uint8_t)(priv->hdr.crc32 >> 8); + *p++ = (uint8_t)(priv->hdr.crc32 >> 16); + *p++ = (uint8_t)(priv->hdr.crc32 >> 24); + *p++ = (uint8_t)priv->hdr.uncomp_size; + *p++ = (uint8_t)(priv->hdr.uncomp_size >> 8); + *p++ = (uint8_t)(priv->hdr.uncomp_size >> 16); + *p = (uint8_t)(priv->hdr.uncomp_size >> 24); + } else { + priv->u.trailer32[0] = priv->hdr.crc32; + priv->u.trailer32[1] = priv->hdr.uncomp_size; + } + + *flags |= LWS_FOP_FLAG_COMPR_IS_GZIP; + priv->fop_fd.flags = *flags; + priv->add_gzip_container = 1; + + return &priv->fop_fd; + } + + if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) { + + /* we must decompress it to serve it */ + + lwsl_info("decompressed zip serving\n"); + + priv->fop_fd.len = priv->hdr.uncomp_size; + + if (lws_fops_zip_reset_inflate(priv)) { + lwsl_err("inflate init failed\n"); + goto bail2; + } + + priv->decompress = 1; + + return &priv->fop_fd; + } + + /* we can't handle it ... */ + + lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path, + priv->hdr.method); + +bail2: + lws_vfs_file_close(&priv->zip_fop_fd); +bail1: + free(priv); + + return NULL; +} + +/* ie, we are closing the fop_fd for the file inside the gzip */ + +static int +lws_fops_zip_close(lws_fop_fd_t *fd) +{ + lws_fops_zip_t priv = fop_fd_to_priv(*fd); + + if (priv->decompress) + inflateEnd(&priv->inflate); + + lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */ + + free(priv); + *fd = NULL; + + return 0; +} + +static lws_fileofs_t +lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos) +{ + fd->pos += offset_from_cur_pos; + + return fd->pos; +} + +static int +lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf, + lws_filepos_t len) +{ + lws_fops_zip_t priv = fop_fd_to_priv(fd); + lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd); + int ret; + + if (priv->decompress) { + + if (priv->exp_uncomp_pos != fd->pos) { + /* + * there has been a seek in the uncompressed fop_fd + * we have to restart the decompression and loop eating + * the decompressed data up to the seek point + */ + lwsl_info("seek in decompressed\n"); + + lws_fops_zip_reset_inflate(priv); + + while (priv->exp_uncomp_pos != fd->pos) { + rlen = len; + if (rlen > fd->pos - priv->exp_uncomp_pos) + rlen = fd->pos - priv->exp_uncomp_pos; + if (lws_fops_zip_read(fd, amount, buf, rlen)) + return LWS_FZ_ERR_SEEK_COMPRESSED; + } + *amount = 0; + } + + priv->inflate.avail_out = (unsigned int)len; + priv->inflate.next_out = buf; + +spin: + if (!priv->inflate.avail_in) { + rlen = sizeof(priv->rbuf); + if (rlen > priv->hdr.comp_size - + (cur - priv->content_start)) + rlen = priv->hdr.comp_size - + (priv->hdr.comp_size - + priv->content_start); + + if (priv->zip_fop_fd->fops->LWS_FOP_READ( + priv->zip_fop_fd, &ramount, priv->rbuf, + rlen)) + return LWS_FZ_ERR_READ_CONTENT; + + cur += ramount; + + priv->inflate.avail_in = (unsigned int)ramount; + priv->inflate.next_in = priv->rbuf; + } + + ret = inflate(&priv->inflate, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) + return ret; + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + + return ret; + } + + if (!priv->inflate.avail_in && priv->inflate.avail_out && + cur != priv->content_start + priv->hdr.comp_size) + goto spin; + + *amount = len - priv->inflate.avail_out; + + priv->exp_uncomp_pos += *amount; + fd->pos += *amount; + + return 0; + } + + if (priv->add_gzip_container) { + + lwsl_info("%s: gzip + container\n", __func__); + *amount = 0; + + /* place the canned header at the start */ + + if (len && fd->pos < sizeof(hd)) { + rlen = sizeof(hd) - fd->pos; + if (rlen > len) + rlen = len; + /* provide stuff from canned header */ + memcpy(buf, hd + fd->pos, (size_t)rlen); + fd->pos += rlen; + buf += rlen; + len -= rlen; + *amount += rlen; + } + + /* serve gzipped data direct from zipfile */ + + if (len && fd->pos >= sizeof(hd) && + fd->pos < priv->hdr.comp_size + sizeof(hd)) { + + rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos - + priv->content_start); + if (rlen > len) + rlen = len; + + if (rlen && + priv->zip_fop_fd->pos < (priv->hdr.comp_size + + priv->content_start)) { + if (lws_vfs_file_read(priv->zip_fop_fd, + &ramount, buf, rlen)) + return LWS_FZ_ERR_READ_CONTENT; + *amount += ramount; + fd->pos += ramount; // virtual pos + buf += ramount; + len -= ramount; + } + } + + /* place the prepared trailer at the end */ + + if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) && + fd->pos < priv->hdr.comp_size + sizeof(hd) + + sizeof(priv->u)) { + cur = fd->pos - priv->hdr.comp_size - sizeof(hd); + rlen = sizeof(priv->u) - cur; + if (rlen > len) + rlen = len; + + memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen); + + *amount += rlen; + fd->pos += rlen; + } + + return 0; + } + + lwsl_info("%s: store\n", __func__); + + if (len > priv->hdr.uncomp_size - (cur - priv->content_start)) + len = priv->hdr.comp_size - (priv->hdr.comp_size - + priv->content_start); + + if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd, + amount, buf, len)) + return LWS_FZ_ERR_READ_CONTENT; + + return 0; +} + +struct lws_plat_file_ops fops_zip = { + lws_fops_zip_open, + lws_fops_zip_close, + lws_fops_zip_seek_cur, + lws_fops_zip_read, + NULL, + { { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } }, + NULL, +}; diff --git a/thirdparty/lws/server/lejp-conf.c b/thirdparty/lws/server/lejp-conf.c new file mode 100644 index 0000000000..c2b684c278 --- /dev/null +++ b/thirdparty/lws/server/lejp-conf.c @@ -0,0 +1,929 @@ +/* + * libwebsockets web server application + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" +#include "../misc/lejp.h" + +#ifndef _WIN32 +/* this is needed for Travis CI */ +#include <dirent.h> +#endif + +#define ESC_INSTALL_DATADIR "_lws_ddir_" + +static const char * const paths_global[] = { + "global.uid", + "global.gid", + "global.count-threads", + "global.init-ssl", + "global.server-string", + "global.plugin-dir", + "global.ws-pingpong-secs", + "global.timeout-secs", + "global.reject-service-keywords[].*", + "global.reject-service-keywords[]", +}; + +enum lejp_global_paths { + LEJPGP_UID, + LEJPGP_GID, + LEJPGP_COUNT_THREADS, + LWJPGP_INIT_SSL, + LEJPGP_SERVER_STRING, + LEJPGP_PLUGIN_DIR, + LWJPGP_PINGPONG_SECS, + LWJPGP_TIMEOUT_SECS, + LWJPGP_REJECT_SERVICE_KEYWORDS_NAME, + LWJPGP_REJECT_SERVICE_KEYWORDS +}; + +static const char * const paths_vhosts[] = { + "vhosts[]", + "vhosts[].mounts[]", + "vhosts[].name", + "vhosts[].port", + "vhosts[].interface", + "vhosts[].unix-socket", + "vhosts[].sts", + "vhosts[].host-ssl-key", + "vhosts[].host-ssl-cert", + "vhosts[].host-ssl-ca", + "vhosts[].access-log", + "vhosts[].mounts[].mountpoint", + "vhosts[].mounts[].origin", + "vhosts[].mounts[].protocol", + "vhosts[].mounts[].default", + "vhosts[].mounts[].auth-mask", + "vhosts[].mounts[].cgi-timeout", + "vhosts[].mounts[].cgi-env[].*", + "vhosts[].mounts[].cache-max-age", + "vhosts[].mounts[].cache-reuse", + "vhosts[].mounts[].cache-revalidate", + "vhosts[].mounts[].basic-auth", + "vhosts[].mounts[].cache-intermediaries", + "vhosts[].mounts[].extra-mimetypes.*", + "vhosts[].mounts[].interpret.*", + "vhosts[].ws-protocols[].*.*", + "vhosts[].ws-protocols[].*", + "vhosts[].ws-protocols[]", + "vhosts[].keepalive_timeout", + "vhosts[].enable-client-ssl", + "vhosts[].ciphers", + "vhosts[].ecdh-curve", + "vhosts[].noipv6", + "vhosts[].ipv6only", + "vhosts[].ssl-option-set", + "vhosts[].ssl-option-clear", + "vhosts[].mounts[].pmo[].*", + "vhosts[].headers[].*", + "vhosts[].headers[]", + "vhosts[].client-ssl-key", + "vhosts[].client-ssl-cert", + "vhosts[].client-ssl-ca", + "vhosts[].client-ssl-ciphers", + "vhosts[].onlyraw", +}; + +enum lejp_vhost_paths { + LEJPVP, + LEJPVP_MOUNTS, + LEJPVP_NAME, + LEJPVP_PORT, + LEJPVP_INTERFACE, + LEJPVP_UNIXSKT, + LEJPVP_STS, + LEJPVP_HOST_SSL_KEY, + LEJPVP_HOST_SSL_CERT, + LEJPVP_HOST_SSL_CA, + LEJPVP_ACCESS_LOG, + LEJPVP_MOUNTPOINT, + LEJPVP_ORIGIN, + LEJPVP_MOUNT_PROTOCOL, + LEJPVP_DEFAULT, + LEJPVP_DEFAULT_AUTH_MASK, + LEJPVP_CGI_TIMEOUT, + LEJPVP_CGI_ENV, + LEJPVP_MOUNT_CACHE_MAX_AGE, + LEJPVP_MOUNT_CACHE_REUSE, + LEJPVP_MOUNT_CACHE_REVALIDATE, + LEJPVP_MOUNT_BASIC_AUTH, + LEJPVP_MOUNT_CACHE_INTERMEDIARIES, + LEJPVP_MOUNT_EXTRA_MIMETYPES, + LEJPVP_MOUNT_INTERPRET, + LEJPVP_PROTOCOL_NAME_OPT, + LEJPVP_PROTOCOL_NAME, + LEJPVP_PROTOCOL, + LEJPVP_KEEPALIVE_TIMEOUT, + LEJPVP_ENABLE_CLIENT_SSL, + LEJPVP_CIPHERS, + LEJPVP_ECDH_CURVE, + LEJPVP_NOIPV6, + LEJPVP_IPV6ONLY, + LEJPVP_SSL_OPTION_SET, + LEJPVP_SSL_OPTION_CLEAR, + LEJPVP_PMO, + LEJPVP_HEADERS_NAME, + LEJPVP_HEADERS, + LEJPVP_CLIENT_SSL_KEY, + LEJPVP_CLIENT_SSL_CERT, + LEJPVP_CLIENT_SSL_CA, + LEJPVP_CLIENT_CIPHERS, + LEJPVP_FLAG_ONLYRAW, +}; + +static const char * const parser_errs[] = { + "", + "", + "No opening '{'", + "Expected closing '}'", + "Expected '\"'", + "String underrun", + "Illegal unescaped control char", + "Illegal escape format", + "Illegal hex number", + "Expected ':'", + "Illegal value start", + "Digit required after decimal point", + "Bad number format", + "Bad exponent format", + "Unknown token", + "Too many ']'", + "Mismatched ']'", + "Expected ']'", + "JSON nesting limit exceeded", + "Nesting tracking used up", + "Number too long", + "Comma or block end expected", + "Unknown", + "Parser callback errored (see earlier error)", +}; + +#define MAX_PLUGIN_DIRS 10 + +struct jpargs { + struct lws_context_creation_info *info; + struct lws_context *context; + const struct lws_protocols *protocols; + const struct lws_extension *extensions; + char *p, *end, valid; + struct lws_http_mount *head, *last; + + struct lws_protocol_vhost_options *pvo; + struct lws_protocol_vhost_options *pvo_em; + struct lws_protocol_vhost_options *pvo_int; + struct lws_http_mount m; + const char **plugin_dirs; + int count_plugin_dirs; + + unsigned int enable_client_ssl:1; + unsigned int fresh_mount:1; + unsigned int any_vhosts:1; +}; + +static void * +lwsws_align(struct jpargs *a) +{ + if ((lws_intptr_t)(a->p) & 15) + a->p += 16 - ((lws_intptr_t)(a->p) & 15); + + return a->p; +} + +static int +arg_to_bool(const char *s) +{ + static const char * const on[] = { "on", "yes", "true" }; + int n = atoi(s); + + if (n) + return 1; + + for (n = 0; n < ARRAY_SIZE(on); n++) + if (!strcasecmp(s, on[n])) + return 1; + + return 0; +} + +static signed char +lejp_globals_cb(struct lejp_ctx *ctx, char reason) +{ + struct jpargs *a = (struct jpargs *)ctx->user; + struct lws_protocol_vhost_options *rej; + int n; + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + /* this catches, eg, vhosts[].headers[].xxx */ + if (reason == LEJPCB_VAL_STR_END && + ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) { + rej = lwsws_align(a); + a->p += sizeof(*rej); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + rej->next = a->info->reject_service_keywords; + a->info->reject_service_keywords = rej; + rej->name = a->p; + lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf); + a->p += n - 1; + *(a->p++) = '\0'; + rej->value = a->p; + rej->options = NULL; + goto dostring; + } + + switch (ctx->path_match - 1) { + case LEJPGP_UID: + a->info->uid = atoi(ctx->buf); + return 0; + case LEJPGP_GID: + a->info->gid = atoi(ctx->buf); + return 0; + case LEJPGP_COUNT_THREADS: + a->info->count_threads = atoi(ctx->buf); + return 0; + case LWJPGP_INIT_SSL: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + return 0; + case LEJPGP_SERVER_STRING: + a->info->server_string = a->p; + break; + case LEJPGP_PLUGIN_DIR: + if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) { + lwsl_err("Too many plugin dirs\n"); + return -1; + } + a->plugin_dirs[a->count_plugin_dirs++] = a->p; + break; + + case LWJPGP_PINGPONG_SECS: + a->info->ws_ping_pong_interval = atoi(ctx->buf); + return 0; + + case LWJPGP_TIMEOUT_SECS: + a->info->timeout_secs = atoi(ctx->buf); + return 0; + + default: + return 0; + } + +dostring: + a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf); + *(a->p)++ = '\0'; + + return 0; +} + +static signed char +lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) +{ + struct jpargs *a = (struct jpargs *)ctx->user; + struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers; + struct lws_http_mount *m; + char *p, *p1; + int n; + +#if 0 + lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match); + for (n = 0; n < ctx->wildcount; n++) + lwsl_notice(" %d\n", ctx->wild[n]); +#endif + + if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) { + /* set the defaults for this vhost */ + a->valid = 1; + a->head = NULL; + a->last = NULL; + a->info->port = 0; + a->info->iface = NULL; + a->info->protocols = a->protocols; + a->info->extensions = a->extensions; + a->info->ssl_cert_filepath = NULL; + a->info->ssl_private_key_filepath = NULL; + a->info->ssl_ca_filepath = NULL; + a->info->client_ssl_cert_filepath = NULL; + a->info->client_ssl_private_key_filepath = NULL; + a->info->client_ssl_ca_filepath = NULL; + a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "HIGH:!aNULL:!eNULL:!EXPORT:" + "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" + "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" + "!DHE-RSA-AES128-SHA256:" + "!AES128-GCM-SHA256:" + "!AES128-SHA256:" + "!DHE-RSA-AES256-SHA256:" + "!AES256-GCM-SHA384:" + "!AES256-SHA256"; + a->info->timeout_secs = 5; + a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "HIGH:!aNULL:!eNULL:!EXPORT:" + "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:" + "!SHA1:!DHE-RSA-AES128-GCM-SHA256:" + "!DHE-RSA-AES128-SHA256:" + "!AES128-GCM-SHA256:" + "!AES128-SHA256:" + "!DHE-RSA-AES256-SHA256:" + "!AES256-GCM-SHA384:" + "!AES256-SHA256"; + a->info->pvo = NULL; + a->info->headers = NULL; + a->info->keepalive_timeout = 5; + a->info->log_filepath = NULL; + a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK | + LWS_SERVER_OPTION_STS | LWS_SERVER_OPTION_ONLY_RAW); + a->enable_client_ssl = 0; + } + + if (reason == LEJPCB_OBJECT_START && + ctx->path_match == LEJPVP_MOUNTS + 1) { + a->fresh_mount = 1; + memset(&a->m, 0, sizeof(a->m)); + } + + /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */ + if (reason == LEJPCB_OBJECT_START && + ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) { + a->pvo = lwsws_align(a); + a->p += sizeof(*a->pvo); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo->next = a->info->pvo; + a->info->pvo = a->pvo; + a->pvo->name = a->p; + lwsl_notice(" adding protocol %s\n", a->p); + a->p += n; + a->pvo->value = a->p; + a->pvo->options = NULL; + goto dostring; + } + + /* this catches, eg, vhosts[].headers[].xxx */ + if (reason == LEJPCB_VAL_STR_END && + ctx->path_match == LEJPVP_HEADERS_NAME + 1) { + headers = lwsws_align(a); + a->p += sizeof(*headers); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + headers->next = a->info->headers; + a->info->headers = headers; + headers->name = a->p; + // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf); + a->p += n - 1; + *(a->p++) = ':'; + if (a->p < a->end) + *(a->p++) = '\0'; + else + *(a->p - 1) = '\0'; + headers->value = a->p; + headers->options = NULL; + goto dostring; + } + + if (reason == LEJPCB_OBJECT_END && + (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) && + a->valid) { + + struct lws_vhost *vhost; + + //lwsl_notice("%s\n", ctx->path); + if (!a->info->port) { + lwsl_err("Port required (eg, 443)"); + return 1; + } + a->valid = 0; + a->info->mounts = a->head; + + vhost = lws_create_vhost(a->context, a->info); + if (!vhost) { + lwsl_err("Failed to create vhost %s\n", + a->info->vhost_name); + return 1; + } + a->any_vhosts = 1; + + if (a->enable_client_ssl) { + const char *cert_filepath = a->info->client_ssl_cert_filepath; + const char *private_key_filepath = a->info->client_ssl_private_key_filepath; + const char *ca_filepath = a->info->client_ssl_ca_filepath; + const char *cipher_list = a->info->client_ssl_cipher_list; + memset(a->info, 0, sizeof(*a->info)); + a->info->client_ssl_cert_filepath = cert_filepath; + a->info->client_ssl_private_key_filepath = private_key_filepath; + a->info->client_ssl_ca_filepath = ca_filepath; + a->info->client_ssl_cipher_list = cipher_list; + a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + lws_init_vhost_client_ssl(a->info, vhost); + } + + return 0; + } + + if (reason == LEJPCB_OBJECT_END && + ctx->path_match == LEJPVP_MOUNTS + 1) { + static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://", + ">http://", + ">https://", + "callback://", + "gzip://", + }; + + if (!a->fresh_mount) + return 0; + + if (!a->m.mountpoint || !a->m.origin) { + lwsl_err("mountpoint and origin required\n"); + return 1; + } + lwsl_debug("adding mount %s\n", a->m.mountpoint); + m = lwsws_align(a); + memcpy(m, &a->m, sizeof(*m)); + if (a->last) + a->last->mount_next = m; + + for (n = 0; n < ARRAY_SIZE(mount_protocols); n++) + if (!strncmp(a->m.origin, mount_protocols[n], + strlen(mount_protocols[n]))) { + lwsl_info("----%s\n", a->m.origin); + m->origin_protocol = n; + m->origin = a->m.origin + + strlen(mount_protocols[n]); + break; + } + + if (n == ARRAY_SIZE(mount_protocols)) { + lwsl_err("unsupported protocol:// %s\n", a->m.origin); + return 1; + } + + a->p += sizeof(*m); + if (!a->head) + a->head = m; + + a->last = m; + a->fresh_mount = 0; + } + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + switch (ctx->path_match - 1) { + case LEJPVP_NAME: + a->info->vhost_name = a->p; + break; + case LEJPVP_PORT: + a->info->port = atoi(ctx->buf); + return 0; + case LEJPVP_INTERFACE: + a->info->iface = a->p; + break; + case LEJPVP_UNIXSKT: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK; + else + a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK); + return 0; + case LEJPVP_STS: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_STS; + else + a->info->options &= ~(LWS_SERVER_OPTION_STS); + return 0; + case LEJPVP_HOST_SSL_KEY: + a->info->ssl_private_key_filepath = a->p; + break; + case LEJPVP_HOST_SSL_CERT: + a->info->ssl_cert_filepath = a->p; + break; + case LEJPVP_HOST_SSL_CA: + a->info->ssl_ca_filepath = a->p; + break; + case LEJPVP_ACCESS_LOG: + a->info->log_filepath = a->p; + break; + case LEJPVP_MOUNTPOINT: + a->m.mountpoint = a->p; + a->m.mountpoint_len = (unsigned char)strlen(ctx->buf); + break; + case LEJPVP_ORIGIN: + if (!strncmp(ctx->buf, "callback://", 11)) + a->m.protocol = a->p + 11; + + if (!a->m.origin) + a->m.origin = a->p; + break; + case LEJPVP_DEFAULT: + a->m.def = a->p; + break; + case LEJPVP_DEFAULT_AUTH_MASK: + a->m.auth_mask = atoi(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_MAX_AGE: + a->m.cache_max_age = atoi(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_REUSE: + a->m.cache_reusable = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_REVALIDATE: + a->m.cache_revalidate = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_MOUNT_CACHE_INTERMEDIARIES: + a->m.cache_intermediaries = arg_to_bool(ctx->buf);; + return 0; + case LEJPVP_MOUNT_BASIC_AUTH: + a->m.basic_auth_login_file = a->p; + break; + case LEJPVP_CGI_TIMEOUT: + a->m.cgi_timeout = atoi(ctx->buf); + return 0; + case LEJPVP_KEEPALIVE_TIMEOUT: + a->info->keepalive_timeout = atoi(ctx->buf); + return 0; + case LEJPVP_CLIENT_CIPHERS: + a->info->client_ssl_cipher_list = a->p; + break; + case LEJPVP_CIPHERS: + a->info->ssl_cipher_list = a->p; + break; + case LEJPVP_ECDH_CURVE: + a->info->ecdh_curve = a->p; + break; + case LEJPVP_PMO: + case LEJPVP_CGI_ENV: + mp_cgienv = lwsws_align(a); + a->p += sizeof(*a->m.cgienv); + + mp_cgienv->next = a->m.cgienv; + a->m.cgienv = mp_cgienv; + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + mp_cgienv->name = a->p; + a->p += n; + mp_cgienv->value = a->p; + mp_cgienv->options = NULL; + //lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name, + // mp_cgienv->value); + goto dostring; + + case LEJPVP_PROTOCOL_NAME_OPT: + /* this catches, eg, + * vhosts[].ws-protocols[].xxx-protocol.yyy-option + * ie, these are options attached to a protocol with { } + */ + pvo = lwsws_align(a); + a->p += sizeof(*a->pvo); + + n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + pvo->next = a->pvo->options; + a->pvo->options = pvo; + pvo->name = a->p; + a->p += n; + pvo->value = a->p; + pvo->options = NULL; + break; + + case LEJPVP_MOUNT_EXTRA_MIMETYPES: + a->pvo_em = lwsws_align(a); + a->p += sizeof(*a->pvo_em); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo_em->next = a->m.extra_mimetypes; + a->m.extra_mimetypes = a->pvo_em; + a->pvo_em->name = a->p; + lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf); + a->p += n; + a->pvo_em->value = a->p; + a->pvo_em->options = NULL; + break; + + case LEJPVP_MOUNT_INTERPRET: + a->pvo_int = lwsws_align(a); + a->p += sizeof(*a->pvo_int); + + n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); + /* ie, enable this protocol, no options yet */ + a->pvo_int->next = a->m.interpret; + a->m.interpret = a->pvo_int; + a->pvo_int->name = a->p; + lwsl_notice(" adding interpret %s -> %s\n", a->p, + ctx->buf); + a->p += n; + a->pvo_int->value = a->p; + a->pvo_int->options = NULL; + break; + + case LEJPVP_ENABLE_CLIENT_SSL: + a->enable_client_ssl = arg_to_bool(ctx->buf); + return 0; + case LEJPVP_CLIENT_SSL_KEY: + a->info->client_ssl_private_key_filepath = a->p; + break; + case LEJPVP_CLIENT_SSL_CERT: + a->info->client_ssl_cert_filepath = a->p; + break; + case LEJPVP_CLIENT_SSL_CA: + a->info->client_ssl_ca_filepath = a->p; + break; + + case LEJPVP_NOIPV6: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6; + else + a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6); + return 0; + + case LEJPVP_FLAG_ONLYRAW: + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_ONLY_RAW; + else + a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW); + return 0; + + case LEJPVP_IPV6ONLY: + a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY; + if (arg_to_bool(ctx->buf)) + a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE; + else + a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); + return 0; + + case LEJPVP_SSL_OPTION_SET: + a->info->ssl_options_set |= atol(ctx->buf); + return 0; + case LEJPVP_SSL_OPTION_CLEAR: + a->info->ssl_options_clear |= atol(ctx->buf); + return 0; + + default: + return 0; + } + +dostring: + p = ctx->buf; + p1 = strstr(p, ESC_INSTALL_DATADIR); + if (p1) { + n = p1 - p; + if (n > a->end - a->p) + n = a->end - a->p; + strncpy(a->p, p, n); + a->p += n; + a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR); + p += n + strlen(ESC_INSTALL_DATADIR); + } + + a->p += lws_snprintf(a->p, a->end - a->p, "%s", p); + *(a->p)++ = '\0'; + + return 0; +} + +/* + * returns 0 = OK, 1 = can't open, 2 = parsing error + */ + +static int +lwsws_get_config(void *user, const char *f, const char * const *paths, + int count_paths, lejp_callback cb) +{ + unsigned char buf[128]; + struct lejp_ctx ctx; + int n, m, fd; + + fd = open(f, O_RDONLY); + if (fd < 0) { + lwsl_err("Cannot open %s\n", f); + return 2; + } + lwsl_info("%s: %s\n", __func__, f); + lejp_construct(&ctx, cb, user, paths, count_paths); + + do { + n = read(fd, buf, sizeof(buf)); + if (!n) + break; + + m = (int)(signed char)lejp_parse(&ctx, buf, n); + } while (m == LEJP_CONTINUE); + + close(fd); + n = ctx.line; + lejp_destruct(&ctx); + + if (m < 0) { + lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m, + parser_errs[-m]); + return 2; + } + + return 0; +} + +#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0 + +static int +lwsws_get_config_d(void *user, const char *d, const char * const *paths, + int count_paths, lejp_callback cb) +{ + uv_dirent_t dent; + uv_fs_t req; + char path[256]; + int ret = 0, ir; + uv_loop_t loop; + + ir = uv_loop_init(&loop); + if (ir) { + lwsl_err("%s: loop init failed %d\n", __func__, ir); + } + + if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) { + lwsl_err("Scandir on %s failed\n", d); + return 2; + } + + while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { + lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name); + ret = lwsws_get_config(user, path, paths, count_paths, cb); + if (ret) + goto bail; + } + +bail: + uv_fs_req_cleanup(&req); + while (uv_loop_close(&loop)) + ; + + return ret; +} + +#else + +#ifndef _WIN32 +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} +#endif + +static int +lwsws_get_config_d(void *user, const char *d, const char * const *paths, + int count_paths, lejp_callback cb) +{ +#ifndef _WIN32 + struct dirent **namelist; + char path[256]; + int n, i, ret = 0; + + n = scandir(d, &namelist, filter, alphasort); + if (n < 0) { + lwsl_err("Scandir on %s failed\n", d); + return 1; + } + + for (i = 0; i < n; i++) { + if (strchr(namelist[i]->d_name, '~')) + goto skip; + lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, + namelist[i]->d_name); + ret = lwsws_get_config(user, path, paths, count_paths, cb); + if (ret) { + while (i++ < n) + free(namelist[i]); + goto bail; + } +skip: + free(namelist[i]); + } + +bail: + free(namelist); + + return ret; +#else + return 0; +#endif +} + +#endif + +int +lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, + char **cs, int *len) +{ + struct jpargs a; + const char * const *old = info->plugin_dirs; + char dd[128]; + + memset(&a, 0, sizeof(a)); + + a.info = info; + a.p = *cs; + a.end = (a.p + *len) - 1; + a.valid = 0; + + lwsws_align(&a); + info->plugin_dirs = (void *)a.p; + a.plugin_dirs = (void *)a.p; /* writeable version */ + a.p += MAX_PLUGIN_DIRS * sizeof(void *); + + /* copy any default paths */ + + while (old && *old) { + a.plugin_dirs[a.count_plugin_dirs++] = *old; + old++; + } + + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); + if (lwsws_get_config(&a, dd, paths_global, + ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + return 1; + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); + if (lwsws_get_config_d(&a, dd, paths_global, + ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + return 1; + + a.plugin_dirs[a.count_plugin_dirs] = NULL; + + *cs = a.p; + *len = a.end - a.p; + + return 0; +} + +int +lwsws_get_config_vhosts(struct lws_context *context, + struct lws_context_creation_info *info, const char *d, + char **cs, int *len) +{ + struct jpargs a; + char dd[128]; + + memset(&a, 0, sizeof(a)); + + a.info = info; + a.p = *cs; + a.end = a.p + *len; + a.valid = 0; + a.context = context; + a.protocols = info->protocols; + a.extensions = info->extensions; + + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); + if (lwsws_get_config(&a, dd, paths_vhosts, + ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + return 1; + lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); + if (lwsws_get_config_d(&a, dd, paths_vhosts, + ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + return 1; + + *cs = a.p; + *len = a.end - a.p; + + if (!a.any_vhosts) { + lwsl_err("Need at least one vhost\n"); + return 1; + } + +// lws_finalize_startup(context); + + return 0; +} diff --git a/thirdparty/lws/server/parsers.c b/thirdparty/lws/server/parsers.c new file mode 100644 index 0000000000..fb345ab04c --- /dev/null +++ b/thirdparty/lws/server/parsers.c @@ -0,0 +1,1783 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +const unsigned char lextable[] = { + #include "lextable.h" +}; + +#define FAIL_CHAR 0x08 + +int LWS_WARN_UNUSED_RESULT +lextable_decode(int pos, char c) +{ + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } + + if (lextable[pos] == FAIL_CHAR) + return -1; + + /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } +} + +static struct allocated_headers * +_lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size) +{ + struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct"); + + if (!ah) + return NULL; + + ah->data = lws_malloc(data_size, "ah data"); + if (!ah->data) { + lws_free(ah); + + return NULL; + } + ah->next = pt->ah_list; + pt->ah_list = ah; + ah->data_length = data_size; + pt->ah_pool_length++; + + lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__, + ah, (int)data_size, pt->ah_pool_length); + + return ah; +} + +int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah) +{ + lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) { + if ((*a) == ah) { + *a = ah->next; + pt->ah_pool_length--; + lwsl_info("%s: freed ah %p : pool length %d\n", + __func__, ah, pt->ah_pool_length); + if (ah->data) + lws_free(ah->data); + lws_free(ah); + + return 0; + } + } lws_end_foreach_llp(a, next); + + return 1; +} + +void +_lws_header_table_reset(struct allocated_headers *ah) +{ + /* init the ah to reflect no headers or data have appeared yet */ + memset(ah->frag_index, 0, sizeof(ah->frag_index)); + memset(ah->frags, 0, sizeof(ah->frags)); + ah->nfrag = 0; + ah->pos = 0; + ah->http_response = 0; +} + +// doesn't scrub the ah rxbuffer by default, parent must do if needed + +void +lws_header_table_reset(struct lws *wsi, int autoservice) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt; + struct lws_pollfd *pfd; + + /* if we have the idea we're resetting 'our' ah, must be bound to one */ + assert(ah); + /* ah also concurs with ownership */ + assert(ah->wsi == wsi); + + _lws_header_table_reset(ah); + + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + + /* since we will restart the ah, our new headers are not completed */ + wsi->hdr_parsing_completed = 0; + + /* while we hold the ah, keep a timeout on the wsi */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, + wsi->vhost->timeout_secs_ah_idle); + + time(&ah->assigned); + + /* + * if we inherited pending rx (from socket adoption deferred + * processing), apply and free it. + */ + if (wsi->u.hdr.preamble_rx) { + memcpy(ah->rx, wsi->u.hdr.preamble_rx, + wsi->u.hdr.preamble_rx_len); + ah->rxlen = wsi->u.hdr.preamble_rx_len; + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + if (autoservice) { + lwsl_debug("%s: service on readbuf ah\n", __func__); + + pt = &wsi->context->pt[(int)wsi->tsi]; + /* + * Unlike a normal connect, we have the headers already + * (or the first part of them anyway) + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + lws_service_fd_tsi(wsi->context, pfd, wsi->tsi); + } + } +} + +static void +_lws_header_ensure_we_are_on_waiting_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi = &pt->ah_wait_list; + + while (*pwsi) { + if (*pwsi == wsi) + return; + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + lwsl_info("%s: wsi: %p\n", __func__, wsi); + wsi->u.hdr.ah_wait_list = pt->ah_wait_list; + pt->ah_wait_list = wsi; + pt->ah_wait_list_length++; + + /* we cannot accept input then */ + + _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa); +} + +static int +__lws_remove_from_ah_waiting_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws **pwsi =&pt->ah_wait_list; + + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_info("%s: wsi %p\n", __func__, wsi); + /* point prev guy to our next */ + *pwsi = wsi->u.hdr.ah_wait_list; + /* we shouldn't point anywhere now */ + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + + return 1; + } + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + return 0; +} + +int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + int n; + + lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__, + (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi, + pt->ah_count_in_use); + + /* if we are already bound to one, just clear it down */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: cleardown\n", __func__); + goto reset; + } + + lws_pt_lock(pt); + + n = pt->ah_count_in_use == context->max_http_header_pool; +#if defined(LWS_WITH_PEER_LIMITS) + if (!n) { + n = lws_peer_confirm_ah_attach_ok(context, wsi->peer); + if (n) + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); + } +#endif + if (n) { + /* + * Pool is either all busy, or we don't want to give this + * particular guy an ah right now... + * + * Make sure we are on the waiting list, and return that we + * weren't able to provide the ah + */ + _lws_header_ensure_we_are_on_waiting_list(wsi); + + goto bail; + } + + __lws_remove_from_ah_waiting_list(wsi); + + wsi->u.hdr.ah = _lws_create_ah(pt, context->max_http_header_data); + if (!wsi->u.hdr.ah) { /* we could not create an ah */ + _lws_header_ensure_we_are_on_waiting_list(wsi); + + goto bail; + } + + wsi->u.hdr.ah->in_use = 1; + wsi->u.hdr.ah->wsi = wsi; /* mark our owner */ + pt->ah_count_in_use++; + +#if defined(LWS_WITH_PEER_LIMITS) + if (wsi->peer) + wsi->peer->count_ah++; +#endif + + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + + lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__, + (void *)wsi, (void *)wsi->u.hdr.ah, pt->ah_count_in_use); + + lws_pt_unlock(pt); + +reset: + + /* and reset the rx state */ + wsi->u.hdr.ah->rxpos = 0; + wsi->u.hdr.ah->rxlen = 0; + + lws_header_table_reset(wsi, autoservice); + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) + if (!lws_client_connect_via_info2(wsi)) + /* our client connect has failed, the wsi + * has been closed + */ + return -1; +#endif + + return 0; + +bail: + lws_pt_unlock(pt); + + return 1; +} + +void +lws_header_table_force_to_detachable_state(struct lws *wsi) +{ + if (wsi->u.hdr.ah) { + wsi->u.hdr.ah->rxpos = -1; + wsi->u.hdr.ah->rxlen = -1; + wsi->hdr_parsing_completed = 1; + } +} + +int +lws_header_table_is_in_detachable_state(struct lws *wsi) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + + return ah && ah->rxpos == ah->rxlen && wsi->hdr_parsing_completed; +} + +int lws_header_table_detach(struct lws *wsi, int autoservice) +{ + struct lws_context *context = wsi->context; + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pollargs pa; + struct lws **pwsi, **pwsi_eligible; + time_t now; + + lws_pt_lock(pt); + __lws_remove_from_ah_waiting_list(wsi); + lws_pt_unlock(pt); + + if (!ah) + return 0; + + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, wsi->tsi, + pt->ah_count_in_use); + + if (wsi->u.hdr.preamble_rx) + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + + /* may not be detached while he still has unprocessed rx */ + if (!lws_header_table_is_in_detachable_state(wsi)) { + lwsl_err("%s: %p: CANNOT DETACH rxpos:%d, rxlen:%d, " + "wsi->hdr_parsing_completed = %d\n", __func__, wsi, + ah->rxpos, ah->rxlen, wsi->hdr_parsing_completed); + return 0; + } + + lws_pt_lock(pt); + + /* we did have an ah attached */ + time(&now); + if (ah->assigned && now - ah->assigned > 3) { + /* + * we're detaching the ah, but it was held an + * unreasonably long time + */ + lwsl_debug("%s: wsi %p: ah held %ds, " + "ah.rxpos %d, ah.rxlen %d, mode/state %d %d," + "wsi->more_rx_waiting %d\n", __func__, wsi, + (int)(now - ah->assigned), + ah->rxpos, ah->rxlen, wsi->mode, wsi->state, + wsi->more_rx_waiting); + } + + ah->assigned = 0; + + /* if we think we're detaching one, there should be one in use */ + assert(pt->ah_count_in_use > 0); + /* and this specific one should have been in use */ + assert(ah->in_use); + wsi->u.hdr.ah = NULL; + ah->wsi = NULL; /* no owner */ +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_track_ah_detach(context, wsi->peer); +#endif + + pwsi = &pt->ah_wait_list; + + /* oh there is nobody on the waiting list... leave the ah unattached */ + if (!*pwsi) + goto nobody_usable_waiting; + + /* + * at least one wsi on the same tsi is waiting, give it to oldest guy + * who is allowed to take it (if any) + */ + lwsl_info("pt wait list %p\n", *pwsi); + wsi = NULL; + pwsi_eligible = NULL; + + while (*pwsi) { +#if defined(LWS_WITH_PEER_LIMITS) + /* are we willing to give this guy an ah? */ + if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer)) +#endif + { + wsi = *pwsi; + pwsi_eligible = pwsi; + } +#if defined(LWS_WITH_PEER_LIMITS) + else + if (!(*pwsi)->u.hdr.ah_wait_list) + lws_stats_atomic_bump(context, pt, + LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1); +#endif + pwsi = &(*pwsi)->u.hdr.ah_wait_list; + } + + if (!wsi) /* everybody waiting already has too many ah... */ + goto nobody_usable_waiting; + + lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi); + + wsi->u.hdr.ah = ah; + ah->wsi = wsi; /* new owner */ + + /* and reset the rx state */ + ah->rxpos = 0; + ah->rxlen = 0; + lws_header_table_reset(wsi, autoservice); +#if defined(LWS_WITH_PEER_LIMITS) + if (wsi->peer) + wsi->peer->count_ah++; +#endif + + /* clients acquire the ah and then insert themselves in fds table... */ + if (wsi->position_in_fds_table != -1) { + lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi); + + /* he has been stuck waiting for an ah, but now his wait is + * over, let him progress */ + + _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa); + } + + /* point prev guy to next guy in list instead */ + *pwsi_eligible = wsi->u.hdr.ah_wait_list; + /* the guy who got one is out of the list */ + wsi->u.hdr.ah_wait_list = NULL; + pt->ah_wait_list_length--; + +#ifndef LWS_NO_CLIENT + if (wsi->state == LWSS_CLIENT_UNCONNECTED) { + lws_pt_unlock(pt); + + if (!lws_client_connect_via_info2(wsi)) { + /* our client connect has failed, the wsi + * has been closed + */ + + return -1; + } + return 0; + } +#endif + + assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list); +bail: + lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__, + (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use); + + lws_pt_unlock(pt); + + return 0; + +nobody_usable_waiting: + lwsl_info("%s: nobody usable waiting\n", __func__); + _lws_destroy_ah(pt, ah); + pt->ah_count_in_use--; + + goto bail; +} + +LWS_VISIBLE int +lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx) +{ + int n; + + if (!wsi->u.hdr.ah) + return 0; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + if (!frag_idx) + return wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (frag_idx-- && n); + + return 0; +} + +LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + int len = 0; + + if (!wsi->u.hdr.ah) + return 0; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + do { + len += wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + + return len; +} + +LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h, int frag_idx) +{ + int n = 0; + int f; + + if (!wsi->u.hdr.ah) + return -1; + + f = wsi->u.hdr.ah->frag_index[h]; + + if (!f) + return -1; + + while (n < frag_idx) { + f = wsi->u.hdr.ah->frags[f].nfrag; + if (!f) + return -1; + n++; + } + + if (wsi->u.hdr.ah->frags[f].len >= len) + return -1; + + memcpy(dst, wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[f].offset, + wsi->u.hdr.ah->frags[f].len); + dst[wsi->u.hdr.ah->frags[f].len] = '\0'; + + return wsi->u.hdr.ah->frags[f].len; +} + +LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len, + enum lws_token_indexes h) +{ + int toklen = lws_hdr_total_length(wsi, h); + int n; + + if (toklen >= len) + return -1; + + if (!wsi->u.hdr.ah) + return -1; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return 0; + + do { + if (wsi->u.hdr.ah->frags[n].len >= len) + return -1; + strncpy(dst, &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset], + wsi->u.hdr.ah->frags[n].len); + dst += wsi->u.hdr.ah->frags[n].len; + len -= wsi->u.hdr.ah->frags[n].len; + n = wsi->u.hdr.ah->frags[n].nfrag; + } while (n); + *dst = '\0'; + + return toklen; +} + +char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h) +{ + int n; + + n = wsi->u.hdr.ah->frag_index[h]; + if (!n) + return NULL; + + return wsi->u.hdr.ah->data + wsi->u.hdr.ah->frags[n].offset; +} + +int LWS_WARN_UNUSED_RESULT +lws_pos_in_bounds(struct lws *wsi) +{ + if (wsi->u.hdr.ah->pos < + (unsigned int)wsi->context->max_http_header_data) + return 0; + + if (wsi->u.hdr.ah->pos == wsi->context->max_http_header_data) { + lwsl_err("Ran out of header data space\n"); + return 1; + } + + /* + * with these tests everywhere, it should never be able to exceed + * the limit, only meet it + */ + lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->u.hdr.ah->pos, + wsi->context->max_http_header_data); + assert(0); + + return 1; +} + +int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s) +{ + wsi->u.hdr.ah->nfrag++; + if (wsi->u.hdr.ah->nfrag == ARRAY_SIZE(wsi->u.hdr.ah->frags)) { + lwsl_warn("More hdr frags than we can deal with, dropping\n"); + return -1; + } + + wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->nfrag; + + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].offset = wsi->u.hdr.ah->pos; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len = 0; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].nfrag = 0; + + do { + if (lws_pos_in_bounds(wsi)) + return -1; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s; + if (*s) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + } while (*s++); + + return 0; +} + +signed char char_to_hex(const char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int LWS_WARN_UNUSED_RESULT +issue_char(struct lws *wsi, unsigned char c) +{ + unsigned short frag_len; + + if (lws_pos_in_bounds(wsi)) + return -1; + + frag_len = wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len; + /* + * If we haven't hit the token limit, just copy the character into + * the header + */ + if (frag_len < wsi->u.hdr.current_token_limit) { + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; + if (c) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->nfrag].len++; + return 0; + } + + /* Insert a null character when we *hit* the limit: */ + if (frag_len == wsi->u.hdr.current_token_limit) { + if (lws_pos_in_bounds(wsi)) + return -1; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0'; + lwsl_warn("header %i exceeds limit %d\n", + wsi->u.hdr.parser_state, + wsi->u.hdr.current_token_limit); + } + + return 1; +} + +int +lws_parse_urldecode(struct lws *wsi, uint8_t *_c) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + unsigned int enc = 0; + uint8_t c = *_c; + + /* + * PRIORITY 1 + * special URI processing... convert %xx + */ + switch (wsi->u.hdr.ues) { + case URIES_IDLE: + if (c == '%') { + wsi->u.hdr.ues = URIES_SEEN_PERCENT; + goto swallow; + } + break; + case URIES_SEEN_PERCENT: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + wsi->u.hdr.esc_stash = c; + wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1; + goto swallow; + + case URIES_SEEN_PERCENT_H1: + if (char_to_hex(c) < 0) + /* illegal post-% char */ + goto forbid; + + *_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) | + char_to_hex(c); + c = *_c; + enc = 1; + wsi->u.hdr.ues = URIES_IDLE; + break; + } + + /* + * PRIORITY 2 + * special URI processing... + * convert /.. or /... or /../ etc to / + * convert /./ to / + * convert // or /// etc to / + * leave /.dir or whatever alone + */ + + switch (wsi->u.hdr.ups) { + case URIPS_IDLE: + if (!c) + return -1; + /* genuine delimiter */ + if ((c == '&' || c == ';') && !enc) { + if (issue_char(wsi, c) < 0) + return -1; + /* swallow the terminator */ + ah->frags[ah->nfrag].len--; + /* link to next fragment */ + ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + /* start next fragment after the & */ + wsi->u.hdr.post_literal_equal = 0; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + goto swallow; + } + /* uriencoded = in the name part, disallow */ + if (c == '=' && enc && + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] && + !wsi->u.hdr.post_literal_equal) { + c = '_'; + *_c =c; + } + + /* after the real =, we don't care how many = */ + if (c == '=' && !enc) + wsi->u.hdr.post_literal_equal = 1; + + /* + to space */ + if (c == '+' && !enc) { + c = ' '; + *_c = c; + } + /* issue the first / always */ + if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_SEEN_SLASH: + /* swallow subsequent slashes */ + if (c == '/') + goto swallow; + /* track and swallow the first . after / */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT; + goto swallow; + } + wsi->u.hdr.ups = URIPS_IDLE; + break; + case URIPS_SEEN_SLASH_DOT: + /* swallow second . */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT; + goto swallow; + } + /* change /./ to / */ + if (c == '/') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + goto swallow; + } + /* it was like /.dir ... regurgitate the . */ + wsi->u.hdr.ups = URIPS_IDLE; + if (issue_char(wsi, '.') < 0) + return -1; + break; + + case URIPS_SEEN_SLASH_DOT_DOT: + + /* /../ or /..[End of URI] --> backup to last / */ + if (c == '/' || c == '?') { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + if (ah->frags[ah->nfrag].len > 1) + break; + goto swallow; + } + + /* /..[^/] ... regurgitate and allow */ + + if (issue_char(wsi, '.') < 0) + return -1; + if (issue_char(wsi, '.') < 0) + return -1; + wsi->u.hdr.ups = URIPS_IDLE; + break; + } + + if (c == '?' && !enc && + !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */ + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + /* seal off uri header */ + if (issue_char(wsi, '\0') < 0) + return -1; + + /* move to using WSI_TOKEN_HTTP_URI_ARGS */ + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + + wsi->u.hdr.post_literal_equal = 0; + ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag; + wsi->u.hdr.ups = URIPS_IDLE; + goto swallow; + } + + return LPUR_CONTINUE; + +swallow: + return LPUR_SWALLOW; + +forbid: + return LPUR_FORBID; + +excessive: + return LPUR_EXCESSIVE; +} + +static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + WSI_TOKEN_CONNECT, + WSI_TOKEN_HEAD_URI, +}; + +int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char c) +{ + struct allocated_headers *ah = wsi->u.hdr.ah; + struct lws_context *context = wsi->context; + unsigned int n, m; + int r; + + assert(wsi->u.hdr.ah); + + switch (wsi->u.hdr.parser_state) { + default: + + lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c); + + /* collect into malloc'd buffers */ + /* optional initial space swallow */ + if (!ah->frags[ah->frag_index[wsi->u.hdr.parser_state]].len && + c == ' ') + break; + + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (wsi->u.hdr.parser_state == methods[m]) + break; + if (m == ARRAY_SIZE(methods)) + /* it was not any of the methods */ + goto check_eol; + + /* special URI processing... end at space */ + + if (c == ' ') { + /* enforce starting with / */ + if (!ah->frags[ah->nfrag].len) + if (issue_char(wsi, '/') < 0) + return -1; + + if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (ah->frags[ah->nfrag].len > 2) { + ah->pos--; + ah->frags[ah->nfrag].len--; + do { + ah->pos--; + ah->frags[ah->nfrag].len--; + } while (ah->frags[ah->nfrag].len > 1 && + ah->data[ah->pos] != '/'); + } + } + + /* begin parsing HTTP version: */ + if (issue_char(wsi, '\0') < 0) + return -1; + wsi->u.hdr.parser_state = WSI_TOKEN_HTTP; + goto start_fragment; + } + + r = lws_parse_urldecode(wsi, &c); + switch (r) { + case LPUR_CONTINUE: + break; + case LPUR_SWALLOW: + goto swallow; + case LPUR_FORBID: + goto forbid; + case LPUR_EXCESSIVE: + goto excessive; + default: + return -1; + } +check_eol: + /* bail at EOL */ + if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE && + c == '\x0d') { + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + + c = '\0'; + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + lwsl_parser("*\n"); + } + + n = issue_char(wsi, c); + if ((int)n < 0) + return -1; + if (n > 0) + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + +swallow: + /* per-protocol end of headers management */ + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + break; + + /* collecting and checking a name part */ + case WSI_TOKEN_NAME_PART: + lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos); + + wsi->u.hdr.lextable_pos = + lextable_decode(wsi->u.hdr.lextable_pos, c); + /* + * Server needs to look out for unknown methods... + */ + if (wsi->u.hdr.lextable_pos < 0 && + (wsi->mode == LWSCM_HTTP_SERVING)) { + /* this is not a header we know about */ + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (ah->frag_index[methods[m]]) { + /* + * already had the method, no idea what + * this crap from the client is, ignore + */ + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + /* + * hm it's an unknown http method from a client in fact, + * it cannot be valid http + */ + if (m == ARRAY_SIZE(methods)) { + /* + * are we set up to accept raw in these cases? + */ + if (lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_FALLBACK_TO_RAW)) + return 2; /* transition to raw */ + + lwsl_info("Unknown method - dropping\n"); + goto forbid; + } + break; + } + /* + * ...otherwise for a client, let him ignore unknown headers + * coming from the server + */ + if (wsi->u.hdr.lextable_pos < 0) { + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + } + + if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) { + /* terminal state */ + + n = ((unsigned int)lextable[wsi->u.hdr.lextable_pos] << 8) | + lextable[wsi->u.hdr.lextable_pos + 1]; + + lwsl_parser("known hdr %d\n", n); + for (m = 0; m < ARRAY_SIZE(methods); m++) + if (n == methods[m] && + ah->frag_index[methods[m]]) { + lwsl_warn("Duplicated method\n"); + return -1; + } + + /* + * WSORIGIN is protocol equiv to ORIGIN, + * JWebSocket likes to send it, map to ORIGIN + */ + if (n == WSI_TOKEN_SWORIGIN) + n = WSI_TOKEN_ORIGIN; + + wsi->u.hdr.parser_state = (enum lws_token_indexes) + (WSI_TOKEN_GET_URI + n); + + if (context->token_limits) + wsi->u.hdr.current_token_limit = + context->token_limits->token_limit[ + wsi->u.hdr.parser_state]; + else + wsi->u.hdr.current_token_limit = + wsi->context->max_http_header_data; + + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) + goto set_parsing_complete; + + goto start_fragment; + } + break; + +start_fragment: + ah->nfrag++; +excessive: + if (ah->nfrag == ARRAY_SIZE(ah->frags)) { + lwsl_warn("More hdr frags than we can deal with\n"); + return -1; + } + + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + ah->frags[ah->nfrag].flags = 2; + + n = ah->frag_index[wsi->u.hdr.parser_state]; + if (!n) { /* first fragment */ + ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag; + ah->hdr_token_idx = wsi->u.hdr.parser_state; + break; + } + /* continuation */ + while (ah->frags[n].nfrag) + n = ah->frags[n].nfrag; + ah->frags[n].nfrag = ah->nfrag; + + if (issue_char(wsi, ' ') < 0) + return -1; + break; + + /* skipping arg part of a name we didn't recognize */ + case WSI_TOKEN_SKIPPING: + lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c); + + if (c == '\x0d') + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + break; + + case WSI_TOKEN_SKIPPING_SAW_CR: + lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (c == '\x0a') { + wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; + wsi->u.hdr.lextable_pos = 0; + } else + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + break; + /* we're done, ignore anything else */ + + case WSI_PARSING_COMPLETE: + lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c); + break; + } + + return 0; + +set_parsing_complete: + if (wsi->u.hdr.ues != URIES_IDLE) + goto forbid; + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION)) + wsi->ietf_spec_revision = + atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION)); + + lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision); + } + wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE; + wsi->hdr_parsing_completed = 1; + + return 0; + +forbid: + lwsl_notice(" forbidding on uri sanitation\n"); + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + + return -1; +} + +LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi) +{ + return wsi->u.ws.frame_is_binary; +} + +void +lws_add_wsi_to_draining_ext_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + + if (wsi->u.ws.rx_draining_ext) + return; + + lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__); + + wsi->u.ws.rx_draining_ext = 1; + wsi->u.ws.rx_draining_ext_list = pt->rx_draining_ext_list; + pt->rx_draining_ext_list = wsi; +} + +void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + struct lws **w = &pt->rx_draining_ext_list; + + if (!wsi->u.ws.rx_draining_ext) + return; + + lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__); + + wsi->u.ws.rx_draining_ext = 0; + + /* remove us from context draining ext list */ + while (*w) { + if (*w == wsi) { + /* if us, point it instead to who we were pointing to */ + *w = wsi->u.ws.rx_draining_ext_list; + break; + } + w = &((*w)->u.ws.rx_draining_ext_list); + } + wsi->u.ws.rx_draining_ext_list = NULL; +} + +/* + * client-parser.c: lws_client_rx_sm() needs to be roughly kept in + * sync with changes here, esp related to ext draining + */ + +int +lws_rx_sm(struct lws *wsi, unsigned char c) +{ + int callback_action = LWS_CALLBACK_RECEIVE; + int ret = 0, n, rx_draining_ext = 0; + struct lws_tokens eff_buf; + + eff_buf.token = NULL; + eff_buf.token_len = 0; + if (wsi->socket_is_permanently_unusable) + return -1; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + if (wsi->u.ws.rx_draining_ext) { + eff_buf.token = NULL; + eff_buf.token_len = 0; + lws_remove_wsi_from_draining_ext_list(wsi); + rx_draining_ext = 1; + lwsl_debug("%s: doing draining flow\n", __func__); + + goto drain_extension; + } + switch (wsi->ietf_spec_revision) { + case 13: + /* + * no prepended frame key any more + */ + wsi->u.ws.all_zero_nonce = 1; + goto handle_first; + + default: + lwsl_warn("lws_rx_sm: unknown spec version %d\n", + wsi->ietf_spec_revision); + break; + } + break; + case LWS_RXPS_04_mask_1: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2; + break; + case LWS_RXPS_04_mask_2: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3; + break; + case LWS_RXPS_04_mask_3: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + + /* + * start from the zero'th byte in the XOR key buffer since + * this is the start of a frame with a new key + */ + + wsi->u.ws.mask_idx = 0; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1; + break; + + /* + * 04 logical framing from the spec (all this is masked when incoming + * and has to be unmasked) + * + * We ignore the possibility of extension data because we don't + * negotiate any extensions at the moment. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|R| Payload len | Extended payload length | + * |I|S|S|S| (4) |S| (7) | (16/63) | + * |N|V|V|V| |V| | (if payload len==126/127) | + * | |1|2|3| |4| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | | Extension data | + * +-------------------------------+ - - - - - - - - - - - - - - - + + * : : + * +---------------------------------------------------------------+ + * : Application data : + * +---------------------------------------------------------------+ + * + * We pass payload through to userland as soon as we get it, ignoring + * FIN. It's up to userland to buffer it up if it wants to see a + * whole unfragmented block of the original size (which may be up to + * 2^63 long!) + */ + + case LWS_RXPS_04_FRAME_HDR_1: +handle_first: + + wsi->u.ws.opcode = c & 0xf; + wsi->u.ws.rsv = c & 0x70; + wsi->u.ws.final = !!((c >> 7) & 1); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + wsi->u.ws.rsv_first_msg = (c & 0x70); + wsi->u.ws.frame_is_binary = + wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME; + wsi->u.ws.first_fragment = 1; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + lwsl_info("illegal opcode\n"); + return -1; + } + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN: + + wsi->u.ws.this_frame_masked = !!(c & 0x80); + + switch (c & 0x7f) { + case 126: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; + break; + case 127: + /* control frames are not allowed to have big lengths */ + if (wsi->u.ws.opcode & 8) + goto illegal_ctl_length; + + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; + break; + default: + wsi->u.ws.rx_packet_length = c & 0x7f; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + } + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_2: + wsi->u.ws.rx_packet_length = c << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN16_1: + wsi->u.ws.rx_packet_length |= c; + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_8: + if (c & 0x80) { + lwsl_warn("b63 of length must be zero\n"); + /* kill the connection */ + return -1; + } +#if defined __LP64__ + wsi->u.ws.rx_packet_length = ((size_t)c) << 56; +#else + wsi->u.ws.rx_packet_length = 0; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_7: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_6: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_5: +#if defined __LP64__ + wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; +#endif + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_4: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_3: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_2: + wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; + wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; + break; + + case LWS_RXPS_04_FRAME_HDR_LEN64_1: + wsi->u.ws.rx_packet_length |= ((size_t)c); + if (wsi->u.ws.this_frame_masked) + wsi->lws_rx_parse_state = + LWS_RXPS_07_COLLECT_FRAME_KEY_1; + else + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_1: + wsi->u.ws.mask[0] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_2: + wsi->u.ws.mask[1] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_3: + wsi->u.ws.mask[2] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; + break; + + case LWS_RXPS_07_COLLECT_FRAME_KEY_4: + wsi->u.ws.mask[3] = c; + if (c) + wsi->u.ws.all_zero_nonce = 0; + wsi->lws_rx_parse_state = + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + wsi->u.ws.mask_idx = 0; + if (wsi->u.ws.rx_packet_length == 0) { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + break; + + + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + assert(wsi->u.ws.rx_ubuf); + + if (wsi->u.ws.rx_draining_ext) + goto drain_extension; + + if (wsi->u.ws.rx_ubuf_head + LWS_PRE >= + wsi->u.ws.rx_ubuf_alloc) { + lwsl_err("Attempted overflow \n"); + return -1; + } + if (wsi->u.ws.all_zero_nonce) + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = c; + else + wsi->u.ws.rx_ubuf[LWS_PRE + + (wsi->u.ws.rx_ubuf_head++)] = + c ^ wsi->u.ws.mask[ + (wsi->u.ws.mask_idx++) & 3]; + + if (--wsi->u.ws.rx_packet_length == 0) { + /* spill because we have the whole frame */ + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } + + /* + * if there's no protocol max frame size given, we are + * supposed to default to context->pt_serv_buf_size + */ + if (!wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) + break; + + if (wsi->protocol->rx_buffer_size && + wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) + break; + + /* spill because we filled our rx buffer */ +spill: + /* + * is this frame a control packet we should take care of at this + * layer? If so service it and hide it from the user callback + */ + + lwsl_parser("spill on %s\n", wsi->protocol->name); + + switch (wsi->u.ws.opcode) { + case LWSWSOPC_CLOSE: + + /* is this an acknowledgement of our close? */ + if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * fine he has told us he is closing too, let's + * finish our close + */ + lwsl_parser("seen client close ack\n"); + return -1; + } + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + /* if he sends us 2 CLOSE, kill him */ + return -1; + + if (lws_partial_buffered(wsi)) { + /* + * if we're in the middle of something, + * we can't do a normal close response and + * have to just close our end. + */ + wsi->socket_is_permanently_unusable = 1; + lwsl_parser("Closing on peer close due to Pending tx\n"); + return -1; + } + + if (user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, + wsi->user_space, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head)) + return -1; + + lwsl_parser("server sees client close packet\n"); + wsi->state = LWSS_RETURNED_CLOSE_ALREADY; + /* deal with the close packet contents as a PONG */ + wsi->u.ws.payload_is_close = 1; + goto process_as_ping; + + case LWSWSOPC_PING: + lwsl_info("received %d byte ping, sending pong\n", + wsi->u.ws.rx_ubuf_head); + + if (wsi->u.ws.ping_pending_flag) { + /* + * there is already a pending ping payload + * we should just log and drop + */ + lwsl_parser("DROP PING since one pending\n"); + goto ping_drop; + } +process_as_ping: + /* control packets can only be < 128 bytes long */ + if (wsi->u.ws.rx_ubuf_head > 128 - 3) { + lwsl_parser("DROP PING payload too large\n"); + goto ping_drop; + } + + /* stash the pong payload */ + memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, + &wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; + wsi->u.ws.ping_pending_flag = 1; + + /* get it sent as soon as possible */ + lws_callback_on_writable(wsi); +ping_drop: + wsi->u.ws.rx_ubuf_head = 0; + return 0; + + case LWSWSOPC_PONG: + lwsl_info("received pong\n"); + lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], + wsi->u.ws.rx_ubuf_head); + + if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { + lwsl_info("received expected PONG on wsi %p\n", wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + } + + /* issue it */ + callback_action = LWS_CALLBACK_RECEIVE_PONG; + break; + + case LWSWSOPC_TEXT_FRAME: + case LWSWSOPC_BINARY_FRAME: + case LWSWSOPC_CONTINUATION: + break; + + default: + lwsl_parser("passing opc %x up to exts\n", + wsi->u.ws.opcode); + /* + * It's something special we can't understand here. + * Pass the payload up to the extension's parsing + * state machine. + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) + /* not handle or fail */ + lwsl_ext("ext opc opcode 0x%x unknown\n", + wsi->u.ws.opcode); + + wsi->u.ws.rx_ubuf_head = 0; + return 0; + } + + /* + * No it's real payload, pass it up to the user callback. + * It's nicely buffered with the pre-padding taken care of + * so it can be sent straight out again using lws_write + */ + + eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; + eff_buf.token_len = wsi->u.ws.rx_ubuf_head; + + if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) + goto already_done; + +drain_extension: + lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_AWAITING_CLOSE_ACK) + goto already_done; + + n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); + /* + * eff_buf may be pointing somewhere completely different now, + * it's the output + */ + wsi->u.ws.first_fragment = 0; + if (n < 0) { + /* + * we may rely on this to get RX, just drop connection + */ + wsi->socket_is_permanently_unusable = 1; + return -1; + } + + if (rx_draining_ext && eff_buf.token_len == 0) + goto already_done; + + if (n && eff_buf.token_len) + /* extension had more... main loop will come back */ + lws_add_wsi_to_draining_ext_list(wsi); + else + lws_remove_wsi_from_draining_ext_list(wsi); + + if (eff_buf.token_len > 0 || + callback_action == LWS_CALLBACK_RECEIVE_PONG) { + eff_buf.token[eff_buf.token_len] = '\0'; + + if (wsi->protocol->callback) { + + if (callback_action == LWS_CALLBACK_RECEIVE_PONG) + lwsl_info("Doing pong callback\n"); + + ret = user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, + (enum lws_callback_reasons)callback_action, + wsi->user_space, + eff_buf.token, + eff_buf.token_len); + } + else + lwsl_err("No callback on payload spill!\n"); + } + +already_done: + wsi->u.ws.rx_ubuf_head = 0; + break; + } + + return ret; + +illegal_ctl_length: + + lwsl_warn("Control frame with xtended length is illegal\n"); + /* kill the connection */ + return -1; +} + +LWS_VISIBLE size_t +lws_remaining_packet_payload(struct lws *wsi) +{ + return wsi->u.ws.rx_packet_length; +} + +/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much + * to expect in that state and can deal with it in bulk more efficiently. + */ + +int +lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, + size_t *len) +{ + unsigned char *buffer = *buf, mask[4]; + int buffer_size, n; + unsigned int avail; + char *rx_ubuf; + + if (wsi->protocol->rx_buffer_size) + buffer_size = wsi->protocol->rx_buffer_size; + else + buffer_size = wsi->context->pt_serv_buf_size; + avail = buffer_size - wsi->u.ws.rx_ubuf_head; + + /* do not consume more than we should */ + if (avail > wsi->u.ws.rx_packet_length) + avail = wsi->u.ws.rx_packet_length; + + /* do not consume more than what is in the buffer */ + if (avail > *len) + avail = *len; + + /* we want to leave 1 byte for the parser to handle properly */ + if (avail <= 1) + return 0; + + avail--; + rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head; + if (wsi->u.ws.all_zero_nonce) + memcpy(rx_ubuf, buffer, avail); + else { + + for (n = 0; n < 4; n++) + mask[n] = wsi->u.ws.mask[(wsi->u.ws.mask_idx + n) & 3]; + + /* deal with 4-byte chunks using unwrapped loop */ + n = avail >> 2; + while (n--) { + *(rx_ubuf++) = *(buffer++) ^ mask[0]; + *(rx_ubuf++) = *(buffer++) ^ mask[1]; + *(rx_ubuf++) = *(buffer++) ^ mask[2]; + *(rx_ubuf++) = *(buffer++) ^ mask[3]; + } + /* and the remaining bytes bytewise */ + for (n = 0; n < (int)(avail & 3); n++) + *(rx_ubuf++) = *(buffer++) ^ mask[n]; + + wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3; + } + + (*buf) += avail; + wsi->u.ws.rx_ubuf_head += avail; + wsi->u.ws.rx_packet_length -= avail; + *len -= avail; + + return avail; +} diff --git a/thirdparty/lws/server/ranges.c b/thirdparty/lws/server/ranges.c new file mode 100644 index 0000000000..bc1578d733 --- /dev/null +++ b/thirdparty/lws/server/ranges.c @@ -0,0 +1,214 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * RFC7233 ranges parser + * + * Copyright (C) 2016 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* + * RFC7233 examples + * + * o The first 500 bytes (byte offsets 0-499, inclusive): + * + * bytes=0-499 + * + * o The second 500 bytes (byte offsets 500-999, inclusive): + * + * bytes=500-999 + * + * o The final 500 bytes (byte offsets 9500-9999, inclusive): + * + * bytes=-500 + * + * Or: + * + * bytes=9500- + * + * o The first and last bytes only (bytes 0 and 9999): + * + * bytes=0-0,-1 + * + * o Other valid (but not canonical) specifications of the second 500 + * bytes (byte offsets 500-999, inclusive): + * + * bytes=500-600,601-999 + * bytes=500-700,601-999 + */ + +/* + * returns 1 if the range struct represents a usable range + * if no ranges header, you get one of these for the whole + * file. Otherwise you get one for each valid range in the + * header. + * + * returns 0 if no further valid range forthcoming; rp->state + * may be LWSRS_SYNTAX or LWSRS_COMPLETED + */ + +int +lws_ranges_next(struct lws_range_parsing *rp) +{ + static const char * const beq = "bytes="; + char c; + + while (1) { + + c = rp->buf[rp->pos]; + + switch (rp->state) { + case LWSRS_SYNTAX: + case LWSRS_COMPLETED: + return 0; + + case LWSRS_NO_ACTIVE_RANGE: + rp->state = LWSRS_COMPLETED; + return 0; + + case LWSRS_BYTES_EQ: // looking for "bytes=" + if (c != beq[rp->pos]) { + rp->state = LWSRS_SYNTAX; + return -1; + } + if (rp->pos == 5) + rp->state = LWSRS_FIRST; + break; + + case LWSRS_FIRST: + rp->start = 0; + rp->end = 0; + rp->start_valid = 0; + rp->end_valid = 0; + + rp->state = LWSRS_STARTING; + + // fallthru + + case LWSRS_STARTING: + if (c == '-') { + rp->state = LWSRS_ENDING; + break; + } + + if (!(c >= '0' && c <= '9')) { + rp->state = LWSRS_SYNTAX; + return 0; + } + rp->start = (rp->start * 10) + (c - '0'); + rp->start_valid = 1; + break; + + case LWSRS_ENDING: + if (c == ',' || c == '\0') { + rp->state = LWSRS_FIRST; + if (c == ',') + rp->pos++; + + /* + * By the end of this, start and end are + * always valid if the range still is + */ + + if (!rp->start_valid) { /* eg, -500 */ + if (rp->end > rp->extent) + rp->end = rp->extent; + + rp->start = rp->extent - rp->end; + rp->end = rp->extent - 1; + } else + if (!rp->end_valid) + rp->end = rp->extent - 1; + + rp->did_try = 1; + + /* end must be >= start or ignore it */ + if (rp->end < rp->start) { + if (c == ',') + break; + rp->state = LWSRS_COMPLETED; + return 0; + } + + return 1; /* issue range */ + } + + if (!(c >= '0' && c <= '9')) { + rp->state = LWSRS_SYNTAX; + return 0; + } + rp->end = (rp->end * 10) + (c - '0'); + rp->end_valid = 1; + break; + } + + rp->pos++; + } +} + +void +lws_ranges_reset(struct lws_range_parsing *rp) +{ + rp->pos = 0; + rp->ctr = 0; + rp->start = 0; + rp->end = 0; + rp->start_valid = 0; + rp->end_valid = 0; + rp->state = LWSRS_BYTES_EQ; +} + +/* + * returns count of valid ranges + */ +int +lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, + unsigned long long extent) +{ + rp->agg = 0; + rp->send_ctr = 0; + rp->inside = 0; + rp->count_ranges = 0; + rp->did_try = 0; + lws_ranges_reset(rp); + rp->state = LWSRS_COMPLETED; + + rp->extent = extent; + + if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf), + WSI_TOKEN_HTTP_RANGE) <= 0) + return 0; + + rp->state = LWSRS_BYTES_EQ; + + while (lws_ranges_next(rp)) { + rp->count_ranges++; + rp->agg += rp->end - rp->start + 1; + } + + lwsl_debug("%s: count %d\n", __func__, rp->count_ranges); + lws_ranges_reset(rp); + + if (rp->did_try && !rp->count_ranges) + return -1; /* "not satisfiable */ + + lws_ranges_next(rp); + + return rp->count_ranges; +} diff --git a/thirdparty/lws/server/server-handshake.c b/thirdparty/lws/server/server-handshake.c new file mode 100644 index 0000000000..3d319c35d6 --- /dev/null +++ b/thirdparty/lws/server/server-handshake.c @@ -0,0 +1,360 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } + +#ifndef LWS_NO_EXTENSIONS +static int +lws_extension_server_handshake(struct lws *wsi, char **p, int budget) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char ext_name[64], *args, *end = (*p) + budget - 1; + const struct lws_ext_options *opts, *po; + const struct lws_extension *ext; + struct lws_ext_option_arg oa; + int n, m, more = 1; + int ext_count = 0; + char ignore; + char *c; + + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) + return 0; + + /* + * break down the list of client extensions + * and go through them + */ + + if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size, + WSI_TOKEN_EXTENSIONS) < 0) + return 1; + + c = (char *)pt->serv_buf; + lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); + wsi->count_act_ext = 0; + ignore = 0; + n = 0; + args = NULL; + + /* + * We may get a simple request + * + * Sec-WebSocket-Extensions: permessage-deflate + * + * or an elaborated one with requested options + * + * Sec-WebSocket-Extensions: permessage-deflate; \ + * server_no_context_takeover; \ + * client_no_context_takeover + */ + + while (more) { + + if (*c && (*c != ',' && *c != '\t')) { + if (*c == ';') { + ignore = 1; + args = c + 1; + } + if (ignore || *c == ' ') { + c++; + continue; + } + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + + ignore = 0; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + while (args && *args && *args == ' ') + args++; + + /* check a client's extension against our support */ + + ext = wsi->vhost->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + /* + * oh, we do support this one he asked for... but let's + * confirm he only gave it once + */ + for (m = 0; m < wsi->count_act_ext; m++) + if (wsi->active_extensions[m] == ext) { + lwsl_info("extension mentioned twice\n"); + return 1; /* shenanigans */ + } + + /* + * ask user code if it's OK to apply it on this + * particular connection + protocol + */ + m = (wsi->protocol->callback)(wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means go ahead and allow + * the extension, it's what we get if the callback is + * unhandled + */ + if (m) { + ext++; + continue; + } + + /* apply it */ + + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions[wsi->count_act_ext] = ext; + + /* allow him to construct his context */ + + if (ext->callback(lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_CONSTRUCT, + (void *)&wsi->act_ext_user[ + wsi->count_act_ext], + (void *)&opts, 0)) { + lwsl_info("ext %s failed construction\n", + ext_name); + ext_count--; + ext++; + + continue; + } + + if (ext_count > 1) + *(*p)++ = ','; + else + LWS_CPYAPP(*p, + "\x0d\x0aSec-WebSocket-Extensions: "); + *p += lws_snprintf(*p, (end - *p), "%s", ext_name); + + /* + * go through the options trying to apply the + * recognized ones + */ + + lwsl_debug("ext args %s", args); + + while (args && *args && *args != ',') { + while (*args == ' ') + args++; + po = opts; + while (po->name) { + lwsl_debug("'%s' '%s'\n", po->name, args); + /* only support arg-less options... */ + if (po->type == EXTARG_NONE && + !strncmp(args, po->name, + strlen(po->name))) { + oa.option_name = NULL; + oa.option_index = po - opts; + oa.start = NULL; + lwsl_debug("setting %s\n", po->name); + if (!ext->callback( + lws_get_context(wsi), ext, wsi, + LWS_EXT_CB_OPTION_SET, + wsi->act_ext_user[ + wsi->count_act_ext], + &oa, (end - *p))) { + + *p += lws_snprintf(*p, (end - *p), "; %s", po->name); + lwsl_debug("adding option %s\n", po->name); + } + } + po++; + } + while (*args && *args != ',' && *args != ';') + args++; + } + + wsi->count_act_ext++; + lwsl_parser("count_act_ext <- %d\n", + wsi->count_act_ext); + + ext++; + } + + n = 0; + args = NULL; + } + + return 0; +} +#endif +int +handshake_0405(struct lws_context *context, struct lws *wsi) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_process_html_args args; + unsigned char hash[20]; + int n, accept_len; + char *response; + char *p; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || + !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { + lwsl_parser("handshake_04 missing pieces\n"); + /* completed header processing, but missing some bits */ + goto bail; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) { + lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN); + goto bail; + } + + /* + * since key length is restricted above (currently 128), cannot + * overflow + */ + n = sprintf((char *)pt->serv_buf, + "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); + + lws_SHA1(pt->serv_buf, n, hash); + + accept_len = lws_b64_encode_string((char *)hash, 20, + (char *)pt->serv_buf, context->pt_serv_buf_size); + if (accept_len < 0) { + lwsl_warn("Base64 encoded hash too long\n"); + goto bail; + } + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) + goto bail; + + /* create the response packet */ + + /* make a buffer big enough for everything */ + + response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE; + p = response; + LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Upgrade: WebSocket\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Accept: "); + strcpy(p, (char *)pt->serv_buf); + p += accept_len; + + /* we can only return the protocol header if: + * - one came in, and ... */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) && + /* - it is not an empty string */ + wsi->protocol->name && + wsi->protocol->name[0]) { + LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: "); + p += lws_snprintf(p, 128, "%s", wsi->protocol->name); + } + +#ifndef LWS_NO_EXTENSIONS + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list. + * + * Give him a limited write bugdet + */ + if (lws_extension_server_handshake(wsi, &p, 192)) + goto bail; +#endif + LWS_CPYAPP(p, "\x0d\x0a"); + + args.p = p; + args.max_len = ((char *)pt->serv_buf + context->pt_serv_buf_size) - p; + if (user_callback_handle_rxflow(wsi->protocol->callback, wsi, + LWS_CALLBACK_ADD_HEADERS, + wsi->user_space, &args, 0)) + goto bail; + + p = args.p; + + /* end of response packet */ + + LWS_CPYAPP(p, "\x0d\x0a"); + + if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX, + response, p - response)) { + + /* okay send the handshake response accepting the connection */ + + lwsl_parser("issuing resp pkt %d len\n", (int)(p - response)); +#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266) + fwrite(response, 1, p - response, stderr); +#endif + n = lws_write(wsi, (unsigned char *)response, + p - response, LWS_WRITE_HTTP_HEADERS); + if (n != (p - response)) { + lwsl_debug("handshake_0405: ERROR writing to socket\n"); + goto bail; + } + + } + + /* alright clean up and set ourselves into established state */ + + wsi->state = LWSS_ESTABLISHED; + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + + { + const char * uri_ptr = + lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); + int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); + const struct lws_http_mount *hit = + lws_find_mount(wsi, uri_ptr, uri_len); + if (hit && hit->cgienv && + wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + } + + return 0; + + +bail: + /* caller will free up his parsing allocations */ + return -1; +} + diff --git a/thirdparty/lws/server/server.c b/thirdparty/lws/server/server.c new file mode 100644 index 0000000000..db05954257 --- /dev/null +++ b/thirdparty/lws/server/server.c @@ -0,0 +1,3025 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +const char * const method_names[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD", +#ifdef LWS_WITH_HTTP2 + ":path", +#endif + }; + +#if defined (LWS_WITH_ESP8266) +#undef memcpy +void *memcpy(void *dest, const void *src, size_t n) +{ + return ets_memcpy(dest, src, n); +} +#endif + +int +lws_context_init_server(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if LWS_POSIX + int n, opt = 1, limit = 1; +#endif + lws_sockfd_type sockfd; + struct lws_vhost *vh; + struct lws *wsi; + int m = 0; + + (void)method_names; + (void)opt; + /* set up our external listening socket we serve on */ + + if (info->port == CONTEXT_PORT_NO_LISTEN || + info->port == CONTEXT_PORT_NO_LISTEN_SERVER) + return 0; + + vh = vhost->context->vhost_list; + while (vh) { + if (vh->listen_port == info->port) { + if ((!info->iface && !vh->iface) || + (info->iface && vh->iface && + !strcmp(info->iface, vh->iface))) { + vhost->listen_port = info->port; + vhost->iface = info->iface; + lwsl_notice(" using listen skt from vhost %s\n", + vh->name); + return 0; + } + } + vh = vh->vhost_next; + } + +#if LWS_POSIX + (void)n; +#if defined(__linux__) + limit = vhost->context->count_threads; +#endif + + for (m = 0; m < limit; m++) { +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + else +#endif +#ifdef LWS_WITH_IPV6 + if (LWS_IPV6_ENABLED(vhost)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd == -1) { +#else +#if defined(LWS_WITH_ESP8266) + sockfd = esp8266_create_tcp_listen_socket(vhost); + if (!lws_sockfd_valid(sockfd)) { +#endif +#endif + lwsl_err("ERROR opening socket\n"); + return 1; + } +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + +#if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE) + /* + * only accept that we are the only listener on the port + * https://msdn.microsoft.com/zh-tw/library/ + * windows/desktop/ms740621(v=vs.85).aspx + * + * for lws, to match Linux, we default to exclusive listen + */ + if (!lws_check_opt(vhost->options, + LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) { + if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (const void *)&opt, sizeof(opt)) < 0) { + lwsl_err("reuseaddr failed\n"); + compatible_close(sockfd); + return 1; + } + } else +#endif + + /* + * allow us to restart even if old sockets in TIME_WAIT + */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&opt, sizeof(opt)) < 0) { + lwsl_err("reuseaddr failed\n"); + compatible_close(sockfd); + return 1; + } + +#if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY) + if (LWS_IPV6_ENABLED(vhost)) { + if (vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) { + int value = (vhost->options & + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, + (const void*)&value, sizeof(value)) < 0) { + compatible_close(sockfd); + return 1; + } + } + } +#endif + +#if defined(__linux__) && defined(SO_REUSEPORT) + n = lws_check_opt(vhost->options, LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE); +#if LWS_MAX_SMP > 1 + n = 1; +#endif + + if (n) + if (vhost->context->count_threads > 1) + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, + (const void *)&opt, sizeof(opt)) < 0) { + compatible_close(sockfd); + return 1; + } +#endif +#endif + lws_plat_set_socket_options(vhost, sockfd); + +#if LWS_POSIX + n = lws_socket_bind(vhost, sockfd, info->port, info->iface); + if (n < 0) + goto bail; + info->port = n; +#endif + vhost->listen_port = info->port; + vhost->iface = info->iface; + + wsi = lws_zalloc(sizeof(struct lws), "listen wsi"); + if (wsi == NULL) { + lwsl_err("Out of mem\n"); + goto bail; + } + wsi->context = vhost->context; + wsi->desc.sockfd = sockfd; + wsi->mode = LWSCM_SERVER_LISTENER; + wsi->protocol = vhost->protocols; + wsi->tsi = m; + wsi->vhost = vhost; + wsi->listener = 1; + +#ifdef LWS_WITH_LIBUV + if (LWS_LIBUV_ENABLED(vhost->context)) + lws_uv_initvhost(vhost, wsi); +#endif + + if (insert_wsi_socket_into_fds(vhost->context, wsi)) + goto bail; + + vhost->context->count_wsi_allocated++; + vhost->lserv_wsi = wsi; + +#if LWS_POSIX + n = listen(wsi->desc.sockfd, LWS_SOMAXCONN); + if (n < 0) { + lwsl_err("listen failed with error %d\n", LWS_ERRNO); + vhost->lserv_wsi = NULL; + vhost->context->count_wsi_allocated--; + remove_wsi_socket_from_fds(wsi); + goto bail; + } + } /* for each thread able to independently listen */ +#else +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_bind(wsi->desc.sockfd, info->port, wsi); +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vhost)) + lwsl_info(" Listening on \"%s\"\n", info->iface); + else +#endif + lwsl_info(" Listening on port %d\n", info->port); + } + + return 0; + +bail: + compatible_close(sockfd); + + return 1; +} + +#if defined(LWS_WITH_ESP8266) +#undef strchr +#define strchr ets_strchr +#endif + +struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername) +{ + struct lws_vhost *vhost = context->vhost_list; + const char *p; + int n, m, colon; + + n = strlen(servername); + colon = n; + p = strchr(servername, ':'); + if (p) + colon = p - servername; + + /* Priotity 1: first try exact matches */ + + while (vhost) { + if (port == vhost->listen_port && + !strncmp(vhost->name, servername, colon)) { + lwsl_info("SNI: Found: %s\n", servername); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* + * Priority 2: if no exact matches, try matching *.vhost-name + * unintentional matches are possible but resolve to x.com for *.x.com + * which is reasonable. If exact match exists we already chose it and + * never reach here. SSL will still fail it if the cert doesn't allow + * *.x.com. + */ + vhost = context->vhost_list; + while (vhost) { + m = strlen(vhost->name); + if (port == vhost->listen_port && + m <= (colon - 2) && + servername[colon - m - 1] == '.' && + !strncmp(vhost->name, servername + colon - m, m)) { + lwsl_info("SNI: Found %s on wildcard: %s\n", + servername, vhost->name); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* Priority 3: match the first vhost on our port */ + + vhost = context->vhost_list; + while (vhost) { + if (port == vhost->listen_port) { + lwsl_info("vhost match to %s based on port %d\n", + vhost->name, port); + return vhost; + } + vhost = vhost->vhost_next; + } + + /* no match */ + + return NULL; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_get_mimetype(const char *file, const struct lws_http_mount *m) +{ + int n = strlen(file); + const struct lws_protocol_vhost_options *pvo = NULL; + + if (m) + pvo = m->extra_mimetypes; + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".gif")) + return "image/gif"; + + if (!strcmp(&file[n - 3], ".js")) + return "text/javascript"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 4], ".jpg")) + return "image/jpeg"; + + if (!strcmp(&file[n - 3], ".gz")) + return "application/gzip"; + + if (!strcmp(&file[n - 4], ".JPG")) + return "image/jpeg"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + if (!strcmp(&file[n - 4], ".css")) + return "text/css"; + + if (!strcmp(&file[n - 4], ".txt")) + return "text/plain"; + + if (!strcmp(&file[n - 4], ".svg")) + return "image/svg+xml"; + + if (!strcmp(&file[n - 4], ".ttf")) + return "application/x-font-ttf"; + + if (!strcmp(&file[n - 4], ".otf")) + return "application/font-woff"; + + if (!strcmp(&file[n - 5], ".woff")) + return "application/font-woff"; + + if (!strcmp(&file[n - 4], ".xml")) + return "application/xml"; + + while (pvo) { + if (pvo->name[0] == '*') /* ie, match anything */ + return pvo->value; + + if (!strcmp(&file[n - strlen(pvo->name)], pvo->name)) + return pvo->value; + + pvo = pvo->next; + } + + return NULL; +} +static lws_fop_flags_t +lws_vfs_prepare_flags(struct lws *wsi) +{ + lws_fop_flags_t f = 0; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) + return f; + + if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING), + "gzip")) { + lwsl_info("client indicates GZIP is acceptable\n"); + f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP; + } + + return f; +} + +static int +lws_http_serve(struct lws *wsi, char *uri, const char *origin, + const struct lws_http_mount *m) +{ + const struct lws_protocol_vhost_options *pvo = m->interpret; + struct lws_process_html_args args; + const char *mimetype; +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + const struct lws_plat_file_ops *fops; + const char *vpath; + lws_fop_flags_t fflags = LWS_O_RDONLY; +#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) + struct _stat32i64 st; +#else + struct stat st; +#endif + int spin = 0; +#endif + char path[256], sym[512]; + unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p; + unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE; +#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32) + size_t len; +#endif + int n; + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri); + +#if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP8266) + + fflags |= lws_vfs_prepare_flags(wsi); + + do { + spin++; + fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath); + + if (wsi->u.http.fop_fd) + lws_vfs_file_close(&wsi->u.http.fop_fd); + + wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, + path, vpath, &fflags); + if (!wsi->u.http.fop_fd) { + lwsl_err("Unable to open '%s'\n", path); + + return -1; + } + + /* if it can't be statted, don't try */ + if (fflags & LWS_FOP_FLAG_VIRTUAL) + break; +#if defined(LWS_WITH_ESP32) + break; +#endif +#if !defined(WIN32) + if (fstat(wsi->u.http.fop_fd->fd, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#else +#if defined(LWS_HAVE__STAT32I64) + if (_stat32i64(path, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#else + if (stat(path, &st)) { + lwsl_info("unable to stat %s\n", path); + goto bail; + } +#endif +#endif + + wsi->u.http.fop_fd->mod_time = (uint32_t)st.st_mtime; + fflags |= LWS_FOP_FLAG_MOD_TIME_VALID; + +#if !defined(WIN32) && LWS_POSIX && !defined(LWS_WITH_ESP32) + if ((S_IFMT & st.st_mode) == S_IFLNK) { + len = readlink(path, sym, sizeof(sym) - 1); + if (len) { + lwsl_err("Failed to read link %s\n", path); + goto bail; + } + sym[len] = '\0'; + lwsl_debug("symlink %s -> %s\n", path, sym); + lws_snprintf(path, sizeof(path) - 1, "%s", sym); + } +#endif + if ((S_IFMT & st.st_mode) == S_IFDIR) { + lwsl_debug("default filename append to dir\n"); + lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html", + origin, uri); + } + + } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5); + + if (spin == 5) + lwsl_err("symlink loop %s \n", path); + + n = sprintf(sym, "%08llX%08lX", + (unsigned long long)lws_vfs_get_length(wsi->u.http.fop_fd), + (unsigned long)lws_vfs_get_mod_time(wsi->u.http.fop_fd)); + + /* disable ranges if IF_RANGE token invalid */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE)) + if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE))) + /* differs - defeat Range: */ + wsi->u.http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) { + /* + * he thinks he has some version of it already, + * check if the tag matches + */ + if (!strcmp(sym, lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_IF_NONE_MATCH))) { + + lwsl_debug("%s: ETAG match %s %s\n", __func__, + uri, origin); + + /* we don't need to send the payload */ + if (lws_add_http_header_status(wsi, + HTTP_STATUS_NOT_MODIFIED, &p, end)) + return -1; + + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + n = lws_write(wsi, start, p - start, + LWS_WRITE_HTTP_HEADERS | + LWS_WRITE_H2_STREAM_END); + if (n != (p - start)) { + lwsl_err("_write returned %d from %ld\n", n, + (long)(p - start)); + return -1; + } + + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return lws_http_transaction_completed(wsi); + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG, + (unsigned char *)sym, n, &p, end)) + return -1; +#endif + + mimetype = lws_get_mimetype(path, m); + if (!mimetype) { + lwsl_err("unknown mimetype for %s\n", path); + goto bail; + } + if (!mimetype[0]) + lwsl_debug("sending no mimetype for %s\n", path); + + wsi->sending_chunked = 0; + + /* + * check if this is in the list of file suffixes to be interpreted by + * a protocol + */ + while (pvo) { + n = strlen(path); + if (n > (int)strlen(pvo->name) && + !strcmp(&path[n - strlen(pvo->name)], pvo->name)) { + wsi->sending_chunked = 1; + wsi->protocol_interpret_idx = + (char)(lws_intptr_t)pvo->value; + lwsl_info("want %s interpreted by %s\n", path, + wsi->vhost->protocols[ + (int)(lws_intptr_t)(pvo->value)].name); + wsi->protocol = &wsi->vhost->protocols[ + (int)(lws_intptr_t)(pvo->value)]; + if (lws_ensure_user_space(wsi)) + return -1; + break; + } + pvo = pvo->next; + } + + if (m->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, m->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + args.p = (char *)p; + args.max_len = end - p; + if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS, + wsi->user_space, &args, 0)) + return -1; + p = (unsigned char *)args.p; + } + + n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start); + + if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) + return -1; /* error or can't reuse connection: close the socket */ + + return 0; +bail: + + return -1; +} + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len) +{ + const struct lws_http_mount *hm, *hit = NULL; + int best = 0; + + hm = wsi->vhost->mount_list; + while (hm) { + if (uri_len >= hm->mountpoint_len && + !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) && + (uri_ptr[hm->mountpoint_len] == '\0' || + uri_ptr[hm->mountpoint_len] == '/' || + hm->mountpoint_len == 1) + ) { + if (hm->origin_protocol == LWSMPRO_CALLBACK || + ((hm->origin_protocol == LWSMPRO_CGI || + lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || + (wsi->http2_substream && + lws_hdr_total_length(wsi, + WSI_TOKEN_HTTP_COLON_PATH)) || + hm->protocol) && + hm->mountpoint_len > best)) { + best = hm->mountpoint_len; + hit = hm; + } + } + hm = hm->mount_next; + } + + return hit; +} + +#if LWS_POSIX + +static int +lws_find_string_in_file(const char *filename, const char *string, int stringlen) +{ + char buf[128]; + int fd, match = 0, pos = 0, n = 0, hit = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + lwsl_err("can't open auth file: %s\n", filename); + return 1; + } + + while (1) { + if (pos == n) { + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (match == stringlen) + hit = 1; + break; + } + pos = 0; + } + + if (match == stringlen) { + if (buf[pos] == '\r' || buf[pos] == '\n') { + hit = 1; + break; + } + match = 0; + } + + if (buf[pos] == string[match]) + match++; + else + match = 0; + + pos++; + } + + close(fd); + + return hit; +} + +static int +lws_unauthorised_basic_auth(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + unsigned char *start = pt->serv_buf + LWS_PRE, + *p = start, *end = p + 512; + char buf[64]; + int n; + + /* no auth... tell him it is required */ + + if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end)) + return -1; + + n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\""); + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_WWW_AUTHENTICATE, + (unsigned char *)buf, n, &p, end)) + return -1; + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | + LWS_WRITE_H2_STREAM_END); + if (n < 0) + return -1; + + return lws_http_transaction_completed(wsi); + +} + +#endif + +int lws_clean_url(char *p) +{ + if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') { + p += 4; + if (*p == 's') + p++; + if (*p == ':') { + p++; + if (*p == '/') + p++; + } + } + + while (*p) { + if (p[0] == '/' && p[1] == '/') { + char *p1 = p; + while (*p1) { + *p1 = p1[1]; + p1++; + } + continue; + } + p++; + } + + return 0; +} + + +static const unsigned char methods[] = { + WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, + WSI_TOKEN_PUT_URI, + WSI_TOKEN_PATCH_URI, + WSI_TOKEN_DELETE_URI, + WSI_TOKEN_CONNECT, + WSI_TOKEN_HEAD_URI, +#ifdef LWS_WITH_HTTP2 + WSI_TOKEN_HTTP_COLON_PATH, +#endif +}; + +static int +lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len) +{ + int n, count = 0; + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) + count++; + if (!count) { + lwsl_warn("Missing URI in HTTP request\n"); + return -1; + } + + if (count != 1 && + !(wsi->http2_substream && + lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) { + lwsl_warn("multiple methods?\n"); + return -1; + } + + for (n = 0; n < ARRAY_SIZE(methods); n++) + if (lws_hdr_total_length(wsi, methods[n])) { + *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]); + *puri_len = lws_hdr_total_length(wsi, methods[n]); + return n; + } + + return -1; +} + +int +lws_http_action(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + enum http_connection_type connection_type; + enum http_version request_version; + char content_length_str[32]; + struct lws_process_html_args args; + const struct lws_http_mount *hit = NULL; + unsigned int n; + char http_version_str[10]; + char http_conn_str[20]; + int http_version_len; + char *uri_ptr = NULL, *s; + int uri_len = 0, meth; + static const char * const oprot[] = { + "http://", "https://" + }; + + meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); + if (meth < 0 || meth >= ARRAY_SIZE(method_names)) + goto bail_nuke_ah; + + /* we insist on absolute paths */ + + if (!uri_ptr || uri_ptr[0] != '/') { + lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); + + goto bail_nuke_ah; + } + + lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth], + meth, uri_ptr); + + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* HTTP header had a content length? */ + + wsi->u.http.rx_content_length = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)) + wsi->u.http.rx_content_length = 100 * 1024 * 1024; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + lws_hdr_copy(wsi, content_length_str, + sizeof(content_length_str) - 1, + WSI_TOKEN_HTTP_CONTENT_LENGTH); + wsi->u.http.rx_content_length = atoll(content_length_str); + } + + if (wsi->http2_substream) { + wsi->u.http.request_version = HTTP_VERSION_2; + } else { + /* http_version? Default to 1.0, override with token: */ + request_version = HTTP_VERSION_1_0; + + /* Works for single digit HTTP versions. : */ + http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP); + if (http_version_len > 7) { + lws_hdr_copy(wsi, http_version_str, + sizeof(http_version_str) - 1, + WSI_TOKEN_HTTP); + if (http_version_str[5] == '1' && + http_version_str[7] == '1') + request_version = HTTP_VERSION_1_1; + } + wsi->u.http.request_version = request_version; + + /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */ + if (request_version == HTTP_VERSION_1_1) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + connection_type = HTTP_CONNECTION_CLOSE; + + /* Override default if http "Connection:" header: */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { + lws_hdr_copy(wsi, http_conn_str, + sizeof(http_conn_str) - 1, + WSI_TOKEN_CONNECTION); + http_conn_str[sizeof(http_conn_str) - 1] = '\0'; + if (!strcasecmp(http_conn_str, "keep-alive")) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + if (!strcasecmp(http_conn_str, "close")) + connection_type = HTTP_CONNECTION_CLOSE; + } + wsi->u.http.connection_type = connection_type; + } + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION, + wsi->user_space, uri_ptr, uri_len); + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + /* + * if there is content supposed to be coming, + * put a timeout on it having arrived + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, + wsi->context->timeout_secs); +#ifdef LWS_OPENSSL_SUPPORT + if (wsi->redirect_to_https) { + /* + * we accepted http:// only so we could redirect to + * https://, so issue the redirect. Create the redirection + * URI from the host: header and ignore the path part + */ + unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, + *end = p + 512; + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) + goto bail_nuke_ah; + + n = sprintf((char *)end, "https://%s/", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } +#endif + +#ifdef LWS_WITH_ACCESS_LOG + lws_prepare_access_log_info(wsi, uri_ptr, meth); +#endif + + /* can we serve it from the mount list? */ + + hit = lws_find_mount(wsi, uri_ptr, uri_len); + if (!hit) { + /* deferred cleanup and reset to protocols[0] */ + + lwsl_info("no hit\n"); + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + + goto after; + } + + s = uri_ptr + hit->mountpoint_len; + + /* + * if we have a mountpoint like https://xxx.com/yyy + * there is an implied / at the end for our purposes since + * we can only mount on a "directory". + * + * But if we just go with that, the browser cannot understand + * that he is actually looking down one "directory level", so + * even though we give him /yyy/abc.html he acts like the + * current directory level is /. So relative urls like "x.png" + * wrongly look outside the mountpoint. + * + * Therefore if we didn't come in on a url with an explicit + * / at the end, we must redirect to add it so the browser + * understands he is one "directory level" down. + */ + if ((hit->mountpoint_len > 1 || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (*s != '/' || + (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) && + (hit->origin_protocol != LWSMPRO_CGI && + hit->origin_protocol != LWSMPRO_CALLBACK)) { + unsigned char *start = pt->serv_buf + LWS_PRE, + *p = start, *end = p + 512; + + lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin); + + /* > at start indicates deal with by redirect */ + if (hit->origin_protocol == LWSMPRO_REDIR_HTTP || + hit->origin_protocol == LWSMPRO_REDIR_HTTPS) + n = lws_snprintf((char *)end, 256, "%s%s", + oprot[hit->origin_protocol & 1], + hit->origin); + else { + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + if (!lws_hdr_total_length(wsi, + WSI_TOKEN_HTTP_COLON_AUTHORITY)) + goto bail_nuke_ah; + n = lws_snprintf((char *)end, 256, + "%s%s%s/", oprot[!!lws_is_ssl(wsi)], + lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_COLON_AUTHORITY), + uri_ptr); + } else + n = lws_snprintf((char *)end, 256, + "%s%s%s/", oprot[!!lws_is_ssl(wsi)], + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), + uri_ptr); + } + + lws_clean_url((char *)end); + n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, + end, n, &p, end); + if ((int)n < 0) + goto bail_nuke_ah; + + return lws_http_transaction_completed(wsi); + } + +#if LWS_POSIX + /* basic auth? */ + + if (hit->basic_auth_login_file) { + char b64[160], plain[(sizeof(b64) * 3) / 4]; + int m; + + /* Did he send auth? */ + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION)) + return lws_unauthorised_basic_auth(wsi); + + n = HTTP_STATUS_FORBIDDEN; + + m = lws_hdr_copy(wsi, b64, sizeof(b64), + WSI_TOKEN_HTTP_AUTHORIZATION); + if (m < 7) { + lwsl_err("b64 auth too long\n"); + goto transaction_result_n; + } + + b64[5] = '\0'; + if (strcasecmp(b64, "Basic")) { + lwsl_err("auth missing basic: %s\n", b64); + goto transaction_result_n; + } + + /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */ + + m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain)); + if (m < 0) { + lwsl_err("plain auth too long\n"); + goto transaction_result_n; + } + + if (!lws_find_string_in_file(hit->basic_auth_login_file, + plain, m)) { + lwsl_err("basic auth lookup failed\n"); + return lws_unauthorised_basic_auth(wsi); + } + + lwsl_notice("basic auth accepted\n"); + + /* accept the auth */ + } +#endif + +#if defined(LWS_WITH_HTTP_PROXY) + /* + * The mount is a reverse proxy? + */ + + if (hit->origin_protocol == LWSMPRO_HTTPS || + hit->origin_protocol == LWSMPRO_HTTP) { + struct lws_client_connect_info i; + char ads[96], rpath[256], *pcolon, *pslash, *p; + int n, na; + + memset(&i, 0, sizeof(i)); + i.context = lws_get_context(wsi); + + pcolon = strchr(hit->origin, ':'); + pslash = strchr(hit->origin, '/'); + if (!pslash) { + lwsl_err("Proxy mount origin '%s' must have /\n", + hit->origin); + return -1; + } + if (pcolon > pslash) + pcolon = NULL; + + if (pcolon) + n = pcolon - hit->origin; + else + n = pslash - hit->origin; + + if (n >= sizeof(ads) - 2) + n = sizeof(ads) - 2; + + memcpy(ads, hit->origin, n); + ads[n] = '\0'; + + i.address = ads; + i.port = 80; + if (hit->origin_protocol == LWSMPRO_HTTPS) { + i.port = 443; + i.ssl_connection = 1; + } + if (pcolon) + i.port = atoi(pcolon + 1); + + lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1, + uri_ptr + hit->mountpoint_len); + lws_clean_url(rpath); + na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS); + if (na) { + p = rpath + strlen(rpath); + *p++ = '?'; + lws_hdr_copy(wsi, p, &rpath[sizeof(rpath) - 1] - p, + WSI_TOKEN_HTTP_URI_ARGS); + while (--na) { + if (*p == '\0') + *p = '&'; + p++; + } + } + + + i.path = rpath; + i.host = i.address; + i.origin = NULL; + i.method = "GET"; + i.parent_wsi = wsi; + i.uri_replace_from = hit->origin; + i.uri_replace_to = hit->mountpoint; + + lwsl_notice("proxying to %s port %d url %s, ssl %d, from %s, to %s\n", + i.address, i.port, i.path, i.ssl_connection, + i.uri_replace_from, i.uri_replace_to); + + if (!lws_client_connect_via_info(&i)) { + lwsl_err("proxy connect fail\n"); + return 1; + } + + return 0; + } +#endif + + /* + * A particular protocol callback is mounted here? + * + * For the duration of this http transaction, bind us to the + * associated protocol + */ + if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) { + const struct lws_protocols *pp; + const char *name = hit->origin; + if (hit->protocol) + name = hit->protocol; + + pp = lws_vhost_name_to_protocol(wsi->vhost, name); + if (!pp) { + n = -1; + lwsl_err("Unable to find plugin '%s'\n", + hit->origin); + return 1; + } + + if (lws_bind_protocol(wsi, pp)) + return 1; + + args.p = uri_ptr; + args.len = uri_len; + args.max_len = hit->auth_mask; + args.final = 0; /* used to signal callback dealt with it */ + + n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS, + wsi->user_space, &args, 0); + if (n) { + lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED, + NULL); + goto bail_nuke_ah; + } + if (args.final) /* callback completely handled it well */ + return 0; + + if (hit->cgienv && wsi->protocol->callback(wsi, + LWS_CALLBACK_HTTP_PMO, + wsi->user_space, (void *)hit->cgienv, 0)) + return 1; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + goto after; + } + } + +#ifdef LWS_WITH_CGI + /* did we hit something with a cgi:// origin? */ + if (hit->origin_protocol == LWSMPRO_CGI) { + const char *cmd[] = { + NULL, /* replace with cgi path */ + NULL + }; + + lwsl_debug("%s: cgi\n", __func__); + cmd[0] = hit->origin; + + n = 5; + if (hit->cgi_timeout) + n = hit->cgi_timeout; + + n = lws_cgi(wsi, cmd, hit->mountpoint_len, n, + hit->cgienv); + if (n) { + lwsl_err("%s: cgi failed\n", __func__); + return -1; + } + + goto deal_body; + } +#endif + + n = strlen(s); + if (s[0] == '\0' || (n == 1 && s[n - 1] == '/')) + s = (char *)hit->def; + if (!s) + s = "index.html"; + + wsi->cache_secs = hit->cache_max_age; + wsi->cache_reuse = hit->cache_reusable; + wsi->cache_revalidate = hit->cache_revalidate; + wsi->cache_intermediaries = hit->cache_intermediaries; + + n = lws_http_serve(wsi, s, hit->origin, hit); + if (n) { + /* + * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + */ + if (hit->protocol) { + const struct lws_protocols *pp = lws_vhost_name_to_protocol( + wsi->vhost, hit->protocol); + + if (lws_bind_protocol(wsi, pp)) + return 1; + + n = pp->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, + uri_ptr + hit->mountpoint_len, + uri_len - hit->mountpoint_len); + } else + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + } + +after: + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + + return 1; + } + +#ifdef LWS_WITH_CGI +deal_body: +#endif + /* + * If we're not issuing a file, check for content_length or + * HTTP keep-alive. No keep-alive header allocation for + * ISSUING_FILE, as this uses HTTP/1.0. + * + * In any case, return 0 and let lws_read decide how to + * proceed based on state + */ + if (wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* Prepare to read body if we have a content length: */ + lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n", + (long long)wsi->u.http.rx_content_length, + wsi->upgraded_to_http2, wsi->http2_substream); + if (wsi->u.http.rx_content_length > 0) { + lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n", + __func__, wsi); + wsi->state = LWSS_HTTP_BODY; + wsi->u.http.rx_content_remain = + wsi->u.http.rx_content_length; + } + } + + return 0; + +bail_nuke_ah: + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + + return 1; + +#if LWS_POSIX +transaction_result_n: + lws_return_http_status(wsi, n, NULL); + + return lws_http_transaction_completed(wsi); +#endif +} + +static int +lws_server_init_wsi_for_ws(struct lws *wsi) +{ + int n; + + wsi->state = LWSS_ESTABLISHED; + lws_restart_ws_ping_pong_timer(wsi); + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, use + * a big default for compatibility + */ + + n = wsi->protocol->rx_buffer_size; + if (!n) + n = wsi->context->pt_serv_buf_size; + n += LWS_PRE; + wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf"); + if (!wsi->u.ws.rx_ubuf) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + return 1; + } + wsi->u.ws.rx_ubuf_alloc = n; + lwsl_debug("Allocating RX buffer %d\n", n); + +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + if (!wsi->parent_carries_io) + if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, + (const char *)&n, sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + return 1; + } +#endif + + /* notify user code that we're ready to roll */ + + if (wsi->protocol->callback) + if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, + wsi->user_space, +#ifdef LWS_OPENSSL_SUPPORT + wsi->ssl, +#else + NULL, +#endif + 0)) + return 1; + + return 0; +} + +int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) +{ + int protocol_len, n = 0, hit, non_space_char_found = 0, m; + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct _lws_header_related hdr; + struct allocated_headers *ah; + unsigned char *obuf = *buf; + char protocol_list[128]; + char protocol_name[64]; + size_t olen = len; + char *p; + + if (len >= 10000000) { + lwsl_err("%s: assert: len %ld\n", __func__, (long)len); + assert(0); + } + + if (!wsi->u.hdr.ah) { + lwsl_err("%s: assert: NULL ah\n", __func__); + assert(0); + } + + lwsl_hexdump(*buf, len); + + while (len--) { + wsi->more_rx_waiting = !!len; + + if (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP2_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) { + lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode); + goto bail_nuke_ah; + } + + m = lws_parse(wsi, *(*buf)++); + if (m) { + if (m == 2) { + /* + * we are transitioning from http with + * an AH, to raw. Drop the ah and set + * the mode. + */ +raw_transition: + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lws_bind_protocol(wsi, &wsi->vhost->protocols[ + wsi->vhost-> + raw_protocol_index]); + lwsl_info("transition to raw vh %s prot %d\n", + wsi->vhost->name, + wsi->vhost->raw_protocol_index); + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_ADOPT, + wsi->user_space, NULL, 0)) + goto bail_nuke_ah; + + lws_header_table_force_to_detachable_state(wsi); + lws_union_transition(wsi, LWSCM_RAW); + lws_header_table_detach(wsi, 1); + + if (m == 2 && (wsi->protocol->callback)(wsi, + LWS_CALLBACK_RAW_RX, + wsi->user_space, obuf, olen)) + return 1; + + return 0; + } + lwsl_info("lws_parse failed\n"); + goto bail_nuke_ah; + } + + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + continue; + + lwsl_parser("%s: lws_parse sees parsing complete\n", __func__); + lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + /* select vhost */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + struct lws_vhost *vhost = lws_select_vhost( + context, wsi->vhost->listen_port, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + if (vhost) + wsi->vhost = vhost; + } else + lwsl_info("no host\n"); + + if (wsi->mode != LWSCM_HTTP2_SERVING) { + wsi->vhost->conn_stats.h1_trans++; + if (!wsi->conn_stat_done) { + wsi->vhost->conn_stats.h1_conn++; + wsi->conn_stat_done = 1; + } + } + + /* check for unwelcome guests */ + + if (wsi->context->reject_service_keywords) { + const struct lws_protocol_vhost_options *rej = + wsi->context->reject_service_keywords; + char ua[384], *msg = NULL; + + if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1, + WSI_TOKEN_HTTP_USER_AGENT) > 0) { + ua[sizeof(ua) - 1] = '\0'; + while (rej) { + if (strstr(ua, rej->name)) { +#ifdef LWS_WITH_ACCESS_LOG + char *uri_ptr = NULL; + int meth, uri_len; +#endif + + msg = strchr(rej->value, ' '); + if (msg) + msg++; + lws_return_http_status(wsi, + atoi(rej->value), msg); +#ifdef LWS_WITH_ACCESS_LOG + meth = lws_http_get_uri_and_method(wsi, + &uri_ptr, &uri_len); + if (meth >= 0) + lws_prepare_access_log_info(wsi, + uri_ptr, meth); + + /* wsi close will do the log */ +#endif + wsi->vhost->conn_stats.rejected++; + /* + * We don't want anything from + * this rejected guy. Follow + * the close flow, not the + * transaction complete flow. + */ + goto bail_nuke_ah; + } + rej = rej->next; + } + } + } + + + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) { + lwsl_info("Changing to RAW mode\n"); + m = 0; + goto raw_transition; + } + + wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT; + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* is this websocket protocol or normal http 1.0? */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "websocket")) { + wsi->vhost->conn_stats.ws_upg++; + lwsl_info("Upgrade to ws\n"); + goto upgrade_ws; + } +#ifdef LWS_WITH_HTTP2 + if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), + "h2c")) { + wsi->vhost->conn_stats.h2_upg++; + lwsl_info("Upgrade to h2c\n"); + goto upgrade_h2c; + } +#endif + lwsl_info("Unknown upgrade\n"); + /* dunno what he wanted to upgrade to */ + goto bail_nuke_ah; + } + + /* no upgrade ack... he remained as HTTP */ + + lwsl_info("No upgrade\n"); + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED); + wsi->state = LWSS_HTTP; + wsi->u.http.fop_fd = NULL; + + /* expose it at the same offset as u.hdr */ + wsi->u.http.ah = ah; + lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, + (void *)wsi->u.hdr.ah); + + n = lws_http_action(wsi); + + return n; + +#ifdef LWS_WITH_HTTP2 +upgrade_h2c: + if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) { + lwsl_info("missing http2_settings\n"); + goto bail_nuke_ah; + } + + lwsl_info("h2c upgrade...\n"); + + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS); + /* convert the peer's HTTP-Settings */ + n = lws_b64_decode_string(p, protocol_list, + sizeof(protocol_list)); + if (n < 0) { + lwsl_parser("HTTP2_SETTINGS too long\n"); + return 1; + } + + /* adopt the header info */ + + ah = wsi->u.hdr.ah; + + lws_union_transition(wsi, LWSCM_HTTP2_SERVING); + + /* http2 union member has http union struct at start */ + wsi->u.http.ah = ah; + + if (!wsi->u.h2.h2n) { + wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n"); + if (!wsi->u.h2.h2n) + return 1; + } + + lws_h2_init(wsi); + + /* HTTP2 union */ + + lws_h2_settings(wsi, &wsi->u.h2.h2n->set, + (unsigned char *)protocol_list, n); + + lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[ + H2SET_HEADER_TABLE_SIZE]); + + strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a" + "Connection: Upgrade\x0d\x0a" + "Upgrade: h2c\x0d\x0a\x0d\x0a"); + n = lws_issue_raw(wsi, (unsigned char *)protocol_list, + strlen(protocol_list)); + if (n != strlen(protocol_list)) { + lwsl_debug("http2 switch: ERROR writing to socket\n"); + return 1; + } + + wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE; + + return 0; +#endif + +upgrade_ws: + if (!wsi->protocol) + lwsl_err("NULL protocol at lws_read\n"); + + /* + * It's websocket + * + * Select the first protocol we support from the list + * the client sent us. + * + * Copy it to remove header fragmentation + */ + + if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1, + WSI_TOKEN_PROTOCOL) < 0) { + lwsl_err("protocol list too long"); + goto bail_nuke_ah; + } + + protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + protocol_list[protocol_len] = '\0'; + p = protocol_list; + hit = 0; + + while (*p && !hit) { + n = 0; + non_space_char_found = 0; + while (n < sizeof(protocol_name) - 1 && + *p && *p != ',') { + /* ignore leading spaces */ + if (!non_space_char_found && *p == ' ') { + n++; + continue; + } + non_space_char_found = 1; + protocol_name[n++] = *p++; + } + protocol_name[n] = '\0'; + if (*p) + p++; + + lwsl_info("checking %s\n", protocol_name); + + n = 0; + while (wsi->vhost->protocols[n].callback) { + lwsl_info("try %s\n", + wsi->vhost->protocols[n].name); + + if (wsi->vhost->protocols[n].name && + !strcmp(wsi->vhost->protocols[n].name, + protocol_name)) { + wsi->protocol = &wsi->vhost->protocols[n]; + hit = 1; + break; + } + + n++; + } + } + + /* we didn't find a protocol he wanted? */ + + if (!hit) { + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) { + lwsl_info("No protocol from \"%s\" supported\n", + protocol_list); + goto bail_nuke_ah; + } + /* + * some clients only have one protocol and + * do not send the protocol list header... + * allow it and match to the vhost's default + * protocol (which itself defaults to zero) + */ + lwsl_info("defaulting to prot handler %d\n", + wsi->vhost->default_protocol_index); + n = wsi->vhost->default_protocol_index; + wsi->protocol = &wsi->vhost->protocols[ + (int)wsi->vhost->default_protocol_index]; + } + + /* allocate wsi->user storage */ + if (lws_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* + * Give the user code a chance to study the request and + * have the opportunity to deny it + */ + if ((wsi->protocol->callback)(wsi, + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, + wsi->user_space, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { + lwsl_warn("User code denied connection\n"); + goto bail_nuke_ah; + } + + /* + * Perform the handshake according to the protocol version the + * client announced + */ + + switch (wsi->ietf_spec_revision) { + case 13: + lwsl_parser("lws_parse calling handshake_04\n"); + if (handshake_0405(context, wsi)) { + lwsl_info("hs0405 has failed the connection\n"); + goto bail_nuke_ah; + } + break; + + default: + lwsl_info("Unknown client spec version %d\n", + wsi->ietf_spec_revision); + goto bail_nuke_ah; + } + + lws_same_vh_protocol_insert(wsi, n); + + /* we are upgrading to ws, so http/1.1 and keepalive + + * pipelined header considerations about keeping the ah around + * no longer apply. However it's common for the first ws + * protocol data to have been coalesced with the browser + * upgrade request and to already be in the ah rx buffer. + */ + + lwsl_info("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n", + __func__, wsi, wsi->u.hdr.ah->rxpos, + wsi->u.hdr.ah->rxlen); + lws_pt_lock(pt); + hdr = wsi->u.hdr; + + lws_union_transition(wsi, LWSCM_WS_SERVING); + /* + * first service is WS mode will notice this, use the RX and + * then detach the ah (caution: we are not in u.hdr union + * mode any more then... ah_temp member is at start the same + * though) + * + * Because rxpos/rxlen shows something in the ah, we will get + * service guaranteed next time around the event loop + * + * All union members begin with hdr, so we can use it even + * though we transitioned to ws union mode (the ah detach + * code uses it anyway). + */ + wsi->u.hdr = hdr; + lws_pt_unlock(pt); + + lws_server_init_wsi_for_ws(wsi); + lwsl_parser("accepted v%02d connection\n", + wsi->ietf_spec_revision); + + /* !!! drop ah unreservedly after ESTABLISHED */ + if (!wsi->more_rx_waiting) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + } + + return 0; + } /* while all chars are handled */ + + return 0; + +bail_nuke_ah: + /* drop the header info */ + /* we're closing, losing some rx is OK */ + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); + + return 1; +} + + +static int +lws_get_idlest_tsi(struct lws_context *context) +{ + unsigned int lowest = ~0; + int n = 0, hit = -1; + + for (; n < context->count_threads; n++) { + if ((unsigned int)context->pt[n].fds_count != + context->fd_limit_per_thread - 1 && + (unsigned int)context->pt[n].fds_count < lowest) { + lowest = context->pt[n].fds_count; + hit = n; + } + } + + return hit; +} + +struct lws * +lws_create_new_server_wsi(struct lws_vhost *vhost) +{ + struct lws *new_wsi; + int n = lws_get_idlest_tsi(vhost->context); + + if (n < 0) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = n; + lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, + vhost->name, new_wsi->tsi); + + new_wsi->vhost = vhost; + new_wsi->context = vhost->context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + new_wsi->state = LWSS_HTTP; + new_wsi->mode = LWSCM_HTTP_SERVING; + new_wsi->hdr_parsing_completed = 0; + +#ifdef LWS_OPENSSL_SUPPORT + new_wsi->use_ssl = LWS_SSL_ENABLED(vhost); +#endif + + /* + * these can only be set once the protocol is known + * we set an un-established connection's protocol pointer + * to the start of the supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = vhost->protocols; + new_wsi->user_space = NULL; + new_wsi->ietf_spec_revision = 0; + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + new_wsi->position_in_fds_table = -1; + + vhost->context->count_wsi_allocated++; + + /* + * outermost create notification for wsi + * no user_space because no protocol selection + */ + vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, + NULL, NULL, 0); + + return new_wsi; +} + +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed(struct lws *wsi) +{ + int n = NO_PENDING_TIMEOUT; + + lwsl_info("%s: wsi %p\n", __func__, wsi); + + lws_access_log(wsi); + + if (!wsi->hdr_parsing_completed) { + lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__); + return 0; + } + + lwsl_debug("%s: wsi %p\n", __func__, wsi); + /* if we can't go back to accept new headers, drop the connection */ + if (wsi->http2_substream) + return 0; + + if (wsi->seen_zero_length_recv) + return 1; + + if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_info("%s: %p: close connection\n", __func__, wsi); + return 1; + } + + if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0])) + return 1; + + /* otherwise set ourselves up ready to go again */ + wsi->state = LWSS_HTTP; + wsi->mode = LWSCM_HTTP_SERVING; + wsi->u.http.tx_content_length = 0; + wsi->u.http.tx_content_remain = 0; + wsi->hdr_parsing_completed = 0; +#ifdef LWS_WITH_ACCESS_LOG + wsi->access_log.sent = 0; +#endif + + if (wsi->vhost->keepalive_timeout) + n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE; + lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout); + + /* + * We already know we are on http1.1 / keepalive and the next thing + * coming will be another header set. + * + * If there is no pending rx and we still have the ah, drop it and + * reacquire a new ah when the new headers start to arrive. (Otherwise + * we needlessly hog an ah indefinitely.) + * + * However if there is pending rx and we know from the keepalive state + * that is already at least the start of another header set, simply + * reset the existing header table and keep it. + */ + if (wsi->u.hdr.ah) { + lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, + wsi->more_rx_waiting); + + if (!wsi->more_rx_waiting) { + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 1); +#ifdef LWS_OPENSSL_SUPPORT + /* + * additionally... if we are hogging an SSL instance + * with no pending pipelined headers (or ah now), and + * SSL is scarce, drop this connection without waiting + */ + + if (wsi->vhost->use_ssl && + wsi->context->simultaneous_ssl_restriction && + wsi->context->simultaneous_ssl == + wsi->context->simultaneous_ssl_restriction) { + lwsl_info("%s: simultaneous_ssl_restriction\n", + __func__); + return 1; + } +#endif + } else { + lws_header_table_reset(wsi, 1); + /* + * If we kept the ah, we should restrict the amount + * of time we are willing to keep it. Otherwise it + * will be bound the whole time the connection remains + * open. + */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, + wsi->vhost->keepalive_timeout); + } + } + + /* If we're (re)starting on headers, need other implied init */ + wsi->u.hdr.ues = URIES_IDLE; + + lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); + + return 0; +} + +/* if not a socket, it's a raw, non-ssl file descriptor */ + +LWS_VISIBLE struct lws * +lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, + lws_sock_file_fd_type fd, const char *vh_prot_name, + struct lws *parent) +{ + struct lws_context *context = vh->context; + struct lws *new_wsi; + struct lws_context_per_thread *pt; + int n, ssl = 0; + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer = NULL; + + if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) { + peer = lws_get_or_create_peer(vh, fd.sockfd); + + if (!peer) { + lwsl_err("OOM creating peer\n"); + return NULL; + } + if (context->ip_limit_wsi && + peer->count_wsi >= context->ip_limit_wsi) { + lwsl_notice("Peer reached wsi limit %d\n", + context->ip_limit_wsi); + lws_stats_atomic_bump(context, &context->pt[0], + LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1); + return NULL; + } + } +#endif + + new_wsi = lws_create_new_server_wsi(vh); + if (!new_wsi) { + if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) + compatible_close(fd.sockfd); + return NULL; + } +#if defined(LWS_WITH_PEER_LIMITS) + if (peer) + lws_peer_add_wsi(context, peer, new_wsi); +#endif + pt = &context->pt[(int)new_wsi->tsi]; + lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1); + + if (parent) { + new_wsi->parent = parent; + new_wsi->sibling_list = parent->child_list; + parent->child_list = new_wsi; + + if (type & LWS_ADOPT_WS_PARENTIO) + new_wsi->parent_carries_io = 1; + } + + new_wsi->desc = fd; + + if (vh_prot_name) { + new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, + vh_prot_name); + if (!new_wsi->protocol) { + lwsl_err("Protocol %s not enabled on vhost %s\n", + vh_prot_name, new_wsi->vhost->name); + goto bail; + } + if (lws_ensure_user_space(new_wsi)) { + lwsl_notice("OOM trying to get user_space\n"); + goto bail; + } + if (type & LWS_ADOPT_WS_PARENTIO) { + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + lwsl_debug("binding to %s\n", new_wsi->protocol->name); + lws_bind_protocol(new_wsi, new_wsi->protocol); + lws_union_transition(new_wsi, LWSCM_WS_SERVING); + lws_server_init_wsi_for_ws(new_wsi); + + return new_wsi; + } + } else + if (type & LWS_ADOPT_HTTP) /* he will transition later */ + new_wsi->protocol = + &vh->protocols[vh->default_protocol_index]; + else { /* this is the only time he will transition */ + lws_bind_protocol(new_wsi, + &vh->protocols[vh->raw_protocol_index]); + lws_union_transition(new_wsi, LWSCM_RAW); + } + + if (type & LWS_ADOPT_SOCKET) { /* socket desc */ + lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi, + (int)(lws_intptr_t)fd.sockfd); + + if (type & LWS_ADOPT_HTTP) + /* the transport is accepted... + * give him time to negotiate */ + lws_set_timeout(new_wsi, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + +#if LWS_POSIX == 0 +#if defined(LWS_WITH_ESP8266) + esp8266_tcp_stream_accept(accept_fd, new_wsi); +#endif +#endif + } else /* file desc */ + lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi, + (int)(lws_intptr_t)fd.filefd); + + /* + * A new connection was accepted. Give the user a chance to + * set properties of the newly created wsi. There's no protocol + * selected yet so we issue this to the vhosts's default protocol, + * itself by default protocols[0] + */ + n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; + if (!(type & LWS_ADOPT_HTTP)) { + if (!(type & LWS_ADOPT_SOCKET)) + n = LWS_CALLBACK_RAW_ADOPT_FILE; + else + n = LWS_CALLBACK_RAW_ADOPT; + } + + if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) || + !(type & LWS_ADOPT_SOCKET)) { + /* non-SSL */ + if (!(type & LWS_ADOPT_HTTP)) { + if (!(type & LWS_ADOPT_SOCKET)) + new_wsi->mode = LWSCM_RAW_FILEDESC; + else + new_wsi->mode = LWSCM_RAW; + } + } else { + /* SSL */ + if (!(type & LWS_ADOPT_HTTP)) + new_wsi->mode = LWSCM_SSL_INIT_RAW; + else + new_wsi->mode = LWSCM_SSL_INIT; + + ssl = 1; + } + + lws_libev_accept(new_wsi, new_wsi->desc); + lws_libuv_accept(new_wsi, new_wsi->desc); + lws_libevent_accept(new_wsi, new_wsi->desc); + + if (!ssl) { + if (insert_wsi_socket_into_fds(context, new_wsi)) { + lwsl_err("%s: fail inserting socket\n", __func__); + goto fail; + } + } else + if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { + lwsl_info("%s: fail ssl negotiation\n", __func__); + goto fail; + } + + /* + * by deferring callback to this point, after insertion to fds, + * lws_callback_on_writable() can work from the callback + */ + if ((new_wsi->protocol->callback)( + new_wsi, n, new_wsi->user_space, NULL, 0)) + goto fail; + + if (type & LWS_ADOPT_HTTP) { + if (!lws_header_table_attach(new_wsi, 0)) + lwsl_debug("Attached ah immediately\n"); + else + lwsl_info("%s: waiting for ah\n", __func__); + } + + return new_wsi; + +fail: + if (type & LWS_ADOPT_SOCKET) + lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; + +bail: + lwsl_notice("%s: exiting on bail\n", __func__); + if (parent) + parent->child_list = new_wsi->sibling_list; + if (new_wsi->user_space) + lws_free(new_wsi->user_space); + lws_free(new_wsi); + compatible_close(fd.sockfd); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) +{ + lws_sock_file_fd_type fd; + + fd.sockfd = accept_fd; + return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | + LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) +{ + return lws_adopt_socket_vhost(context->vhost_list, accept_fd); +} + +/* Common read-buffer adoption for lws_adopt_*_readbuf */ +static struct lws* +adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) +{ + struct lws_context_per_thread *pt; + struct allocated_headers *ah; + struct lws_pollfd *pfd; + + if (!wsi) + return NULL; + + if (!readbuf || len == 0) + return wsi; + + if (len > sizeof(ah->rx)) { + lwsl_err("%s: rx in too big\n", __func__); + goto bail; + } + + /* + * we can't process the initial read data until we can attach an ah. + * + * if one is available, get it and place the data in his ah rxbuf... + * wsi with ah that have pending rxbuf get auto-POLLIN service. + * + * no autoservice because we didn't get a chance to attach the + * readbuf data to wsi or ah yet, and we will do it next if we get + * the ah. + */ + if (wsi->u.hdr.ah || !lws_header_table_attach(wsi, 0)) { + ah = wsi->u.hdr.ah; + memcpy(ah->rx, readbuf, len); + ah->rxpos = 0; + ah->rxlen = (int16_t)len; + + lwsl_notice("%s: calling service on readbuf ah\n", __func__); + pt = &wsi->context->pt[(int)wsi->tsi]; + + /* unlike a normal connect, we have the headers already + * (or the first part of them anyway). + * libuv won't come back and service us without a network + * event, so we need to do the header service right here. + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) + /* service closed us */ + return NULL; + + return wsi; + } + lwsl_err("%s: deferring handling ah\n", __func__); + /* + * hum if no ah came, we are on the wait list and must defer + * dealing with this until the ah arrives. + * + * later successful lws_header_table_attach() will apply the + * below to the rx buffer (via lws_header_table_reset()). + */ + wsi->u.hdr.preamble_rx = lws_malloc(len, "preamble_rx"); + if (!wsi->u.hdr.preamble_rx) { + lwsl_err("OOM\n"); + goto bail; + } + memcpy(wsi->u.hdr.preamble_rx, readbuf, len); + wsi->u.hdr.preamble_rx_len = len; + + return wsi; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), + readbuf, len); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, + lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), + readbuf, len); +} + +LWS_VISIBLE int +lws_server_socket_service(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + lws_sockfd_type accept_fd = LWS_SOCK_INVALID; + struct allocated_headers *ah; + lws_sock_file_fd_type fd; + int opts = LWS_ADOPT_SOCKET | LWS_ADOPT_ALLOW_SSL; +#if LWS_POSIX + struct sockaddr_storage cli_addr; + socklen_t clilen; +#endif + int n, len; + + switch (wsi->mode) { + + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_HTTP2_SERVING: + case LWSCM_RAW: + + /* handle http headers coming in */ + + /* pending truncated sends have uber priority */ + + if (wsi->trunc_len) { + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + if (lws_issue_raw(wsi, wsi->trunc_alloc + + wsi->trunc_offset, + wsi->trunc_len) < 0) + goto fail; + /* + * we can't afford to allow input processing to send + * something new, so spin around he event loop until + * he doesn't have any partials + */ + break; + } + + /* any incoming data ready? */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + goto try_pollout; + + /* + * If we previously just did POLLIN when IN and OUT were + * signalled (because POLLIN processing may have used up + * the POLLOUT), don't let that happen twice in a row... + * next time we see the situation favour POLLOUT + */ +#if !defined(LWS_WITH_ESP8266) + if (wsi->favoured_pollin && + (pollfd->revents & pollfd->events & LWS_POLLOUT)) { + lwsl_notice("favouring pollout\n"); + wsi->favoured_pollin = 0; + goto try_pollout; + } +#endif + + /* these states imply we MUST have an ah attached */ + + if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP || + wsi->state == LWSS_HTTP_ISSUING_FILE || + wsi->state == LWSS_HTTP_HEADERS)) { + if (!wsi->u.hdr.ah) { + /* no autoservice beacuse we will do it next */ + if (lws_header_table_attach(wsi, 0)) { + lwsl_info("wsi %p: ah get fail\n", wsi); + goto try_pollout; + } + } + ah = wsi->u.hdr.ah; + + /* if nothing in ah rx buffer, get some fresh rx */ + if (ah->rxpos == ah->rxlen) { + ah->rxlen = lws_ssl_capable_read(wsi, ah->rx, + sizeof(ah->rx)); + ah->rxpos = 0; + switch (ah->rxlen) { + case 0: + lwsl_info("%s: read 0 len a\n", __func__); + wsi->seen_zero_length_recv = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + goto try_pollout; + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + ah->rxlen = ah->rxpos = 0; + goto try_pollout; + } + + /* + * make sure ah does not get detached if we + * have live data in the rx + */ + if (ah->rxlen) + wsi->more_rx_waiting = 1; + } + + if (!(ah->rxpos != ah->rxlen && ah->rxlen)) { + lwsl_err("%s: assert: rxpos %d, rxlen %d\n", + __func__, ah->rxpos, ah->rxlen); + + assert(0); + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* + * otherwise give it to whoever wants it + * according to the connection state + */ + + n = lws_read(wsi, ah->rx + ah->rxpos, + ah->rxlen - ah->rxpos); + if (n < 0) /* we closed wsi */ + return 1; + + if (!wsi->u.hdr.ah) + break; + if ( wsi->u.hdr.ah->rxlen) + wsi->u.hdr.ah->rxpos += n; + + lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n", + __func__, wsi, wsi->u.hdr.ah->rxpos, + wsi->u.hdr.ah->rxlen); + + if (lws_header_table_is_in_detachable_state(wsi) && + (wsi->mode != LWSCM_HTTP_SERVING && + wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED && + wsi->mode != LWSCM_HTTP2_SERVING)) + lws_header_table_detach(wsi, 1); + + break; + } + + goto try_pollout; + } + + len = lws_ssl_capable_read(wsi, pt->serv_buf, + context->pt_serv_buf_size); + lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len); + switch (len) { + case 0: + lwsl_info("%s: read 0 len b\n", __func__); + + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + goto try_pollout; + } + + if (len < 0) /* coverity */ + goto fail; + + if (wsi->mode == LWSCM_RAW) { + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RAW_RX, + wsi->user_space, pt->serv_buf, len); + if (n < 0) { + lwsl_info("LWS_CALLBACK_RAW_RX_fail\n"); + goto fail; + } + goto try_pollout; + } + + /* just ignore incoming if waiting for close */ + if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE && + wsi->state != LWSS_HTTP_ISSUING_FILE) { + /* + * this may want to send + * (via HTTP callback for example) + */ + n = lws_read(wsi, pt->serv_buf, len); + if (n < 0) /* we closed wsi */ + return 1; + /* + * he may have used up the + * writability above, if we will defer POLLOUT + * processing in favour of POLLIN, note it + */ + if (pollfd->revents & LWS_POLLOUT) + wsi->favoured_pollin = 1; + break; + } + /* + * he may have used up the + * writability above, if we will defer POLLOUT + * processing in favour of POLLIN, note it + */ + if (pollfd->revents & LWS_POLLOUT) + wsi->favoured_pollin = 1; + +try_pollout: + + /* this handles POLLOUT for http serving fragments */ + + if (!(pollfd->revents & LWS_POLLOUT)) + break; + + /* one shot */ + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_notice("%s a\n", __func__); + goto fail; + } + + if (wsi->mode == LWSCM_RAW) { + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RAW_WRITEABLE, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("writeable_fail\n"); + goto fail; + } + break; + } + + if (!wsi->hdr_parsing_completed) + break; + + if (wsi->state != LWSS_HTTP_ISSUING_FILE) { + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_HTTP_WRITEABLE, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("writeable_fail\n"); + goto fail; + } + break; + } + + /* >0 == completion, <0 == error + * + * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done. That's the case even if we just completed the + * send, so wait for that. + */ + n = lws_serve_http_file_fragment(wsi); + if (n < 0) + goto fail; + + break; + + case LWSCM_SERVER_LISTENER: + +#if LWS_POSIX + /* pollin means a client has connected to us then */ + + do { + if (!(pollfd->revents & LWS_POLLIN) || + !(pollfd->events & LWS_POLLIN)) + break; + +#ifdef LWS_OPENSSL_SUPPORT + /* + * can we really accept it, with regards to SSL limit? + * another vhost may also have had POLLIN on his listener this + * round and used it up already + */ + + if (wsi->vhost->use_ssl && + context->simultaneous_ssl_restriction && + context->simultaneous_ssl == + context->simultaneous_ssl_restriction) + /* no... ignore it, he won't come again until we are + * below the simultaneous_ssl_restriction limit and + * POLLIN is enabled on him again + */ + break; +#endif + /* listen socket got an unencrypted connection... */ + + clilen = sizeof(cli_addr); + lws_latency_pre(context, wsi); + + /* + * We cannot identify the peer who is in the listen + * socket connect queue before we accept it; even if + * we could, not accepting it due to PEER_LIMITS would + * block the connect queue for other legit peers. + */ + accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, + &clilen); + lws_latency(context, wsi, "listener accept", accept_fd, + accept_fd >= 0); + if (accept_fd < 0) { + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK) { + break; + } + lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO)); + break; + } + + lws_plat_set_socket_options(wsi->vhost, accept_fd); + +#if defined(LWS_WITH_IPV6) + lwsl_debug("accepted new conn port %u on fd=%d\n", + ((cli_addr.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *) &cli_addr)->sin6_port) : + ntohs(((struct sockaddr_in *) &cli_addr)->sin_port)), + accept_fd); +#else + lwsl_debug("accepted new conn port %u on fd=%d\n", + ntohs(((struct sockaddr_in *) &cli_addr)->sin_port), + accept_fd); +#endif + +#else + /* not very beautiful... */ + accept_fd = (lws_sockfd_type)pollfd; +#endif + /* + * look at who we connected to and give user code a chance + * to reject based on client IP. There's no protocol selected + * yet so we issue this to protocols[0] + */ + if ((wsi->vhost->protocols[0].callback)(wsi, + LWS_CALLBACK_FILTER_NETWORK_CONNECTION, + NULL, (void *)(lws_intptr_t)accept_fd, 0)) { + lwsl_debug("Callback denied network connection\n"); + compatible_close(accept_fd); + break; + } + + if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW)) + opts |= LWS_ADOPT_HTTP; + else + opts = LWS_ADOPT_SOCKET; + + fd.sockfd = accept_fd; + if (!lws_adopt_descriptor_vhost(wsi->vhost, opts, fd, + NULL, NULL)) + /* already closed cleanly as necessary */ + return 1; + +#if LWS_POSIX + } while (pt->fds_count < context->fd_limit_per_thread - 1 && + lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0); +#endif + return 0; + + default: + break; + } + + if (!lws_server_socket_service_ssl(wsi, accept_fd)) + return 0; + +fail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; +} + +LWS_VISIBLE int +lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, + const char *other_headers, int other_headers_len) +{ + static const char * const intermediates[] = { "private", "public" }; + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; +#if defined(LWS_WITH_RANGES) + struct lws_range_parsing *rp = &wsi->u.http.range; +#endif + char cache_control[50], *cc = "no-store"; + unsigned char *response = pt->serv_buf + LWS_PRE; + unsigned char *p = response; + unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE; + lws_filepos_t computed_total_content_length; + int ret = 0, cclen = 8, n = HTTP_STATUS_OK; + lws_fop_flags_t fflags = LWS_O_RDONLY; +#if defined(LWS_WITH_RANGES) + int ranges; +#endif + const struct lws_plat_file_ops *fops; + const char *vpath; + + /* + * We either call the platform fops .open with first arg platform fops, + * or we call fops_zip .open with first arg platform fops, and fops_zip + * open will decide whether to switch to fops_zip or stay with fops_def. + * + * If wsi->u.http.fop_fd is already set, the caller already opened it + */ + if (!wsi->u.http.fop_fd) { + fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath); + fflags |= lws_vfs_prepare_flags(wsi); + wsi->u.http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops, + file, vpath, &fflags); + if (!wsi->u.http.fop_fd) { + lwsl_err("Unable to open '%s'\n", file); + + return -1; + } + } + wsi->u.http.filelen = lws_vfs_get_length(wsi->u.http.fop_fd); + computed_total_content_length = wsi->u.http.filelen; + +#if defined(LWS_WITH_RANGES) + ranges = lws_ranges_init(wsi, rp, wsi->u.http.filelen); + + lwsl_debug("Range count %d\n", ranges); + /* + * no ranges -> 200; + * 1 range -> 206 + Content-Type: normal; Content-Range; + * more -> 206 + Content-Type: multipart/byteranges + * Repeat the true Content-Type in each multipart header + * along with Content-Range + */ + if (ranges < 0) { + /* it means he expressed a range in Range:, but it was illegal */ + lws_return_http_status(wsi, HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, + NULL); + if (lws_http_transaction_completed(wsi)) + return -1; /* <0 means just hang up */ + + lws_vfs_file_close(&wsi->u.http.fop_fd); + + return 0; /* == 0 means we dealt with the transaction complete */ + } + if (ranges) + n = HTTP_STATUS_PARTIAL_CONTENT; +#endif + + if (lws_add_http_header_status(wsi, n, &p, end)) + return -1; + + if ((wsi->u.http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | + LWS_FOP_FLAG_COMPR_IS_GZIP)) == + (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) { + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_ENCODING, + (unsigned char *)"gzip", 4, &p, end)) + return -1; + lwsl_info("file is being provided in gzip\n"); + } + + if ( +#if defined(LWS_WITH_RANGES) + ranges < 2 && +#endif + content_type && content_type[0]) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)content_type, + strlen(content_type), &p, end)) + return -1; + +#if defined(LWS_WITH_RANGES) + if (ranges >= 2) { /* multipart byteranges */ + strncpy(wsi->u.http.multipart_content_type, content_type, + sizeof(wsi->u.http.multipart_content_type) - 1); + wsi->u.http.multipart_content_type[ + sizeof(wsi->u.http.multipart_content_type) - 1] = '\0'; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, + (unsigned char *)"multipart/byteranges; boundary=_lws", + 20, &p, end)) + return -1; + + /* + * our overall content length has to include + * + * - (n + 1) x "_lws\r\n" + * - n x Content-Type: xxx/xxx\r\n + * - n x Content-Range: bytes xxx-yyy/zzz\r\n + * - n x /r/n + * - the actual payloads (aggregated in rp->agg) + * + * Precompute it for the main response header + */ + + computed_total_content_length = (lws_filepos_t)rp->agg + + 6 /* final _lws\r\n */; + + lws_ranges_reset(rp); + while (lws_ranges_next(rp)) { + n = lws_snprintf(cache_control, sizeof(cache_control), + "bytes %llu-%llu/%llu", + rp->start, rp->end, rp->extent); + + computed_total_content_length += + 6 /* header _lws\r\n */ + + /* Content-Type: xxx/xxx\r\n */ + 14 + strlen(content_type) + 2 + + /* Content-Range: xxxx\r\n */ + 15 + n + 2 + + 2; /* /r/n */ + } + + lws_ranges_reset(rp); + lws_ranges_next(rp); + } + + if (ranges == 1) { + computed_total_content_length = (lws_filepos_t)rp->agg; + n = lws_snprintf(cache_control, sizeof(cache_control), + "bytes %llu-%llu/%llu", + rp->start, rp->end, rp->extent); + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_RANGE, + (unsigned char *)cache_control, + n, &p, end)) + return -1; + } + + wsi->u.http.range.inside = 0; + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES, + (unsigned char *)"bytes", 5, &p, end)) + return -1; +#endif + + if (!wsi->sending_chunked) { + if (lws_add_http_header_content_length(wsi, + computed_total_content_length, + &p, end)) + return -1; + } else { + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", + 7, &p, end)) + return -1; + } + + if (wsi->cache_secs && wsi->cache_reuse) { + if (wsi->cache_revalidate) { + cc = cache_control; + cclen = sprintf(cache_control, "%s max-age: %u", + intermediates[wsi->cache_intermediaries], + wsi->cache_secs); + } else { + cc = "no-cache"; + cclen = 8; + } + } + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL, + (unsigned char *)cc, cclen, &p, end)) + return -1; + + if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE) + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, + (unsigned char *)"keep-alive", 10, &p, end)) + return -1; + + if (other_headers) { + if ((end - p) < other_headers_len) + return -1; + memcpy(p, other_headers, other_headers_len); + p += other_headers_len; + } + + if (lws_finalize_http_header(wsi, &p, end)) + return -1; + + ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); + if (ret != (p - response)) { + lwsl_err("_write returned %d from %ld\n", ret, + (long)(p - response)); + return -1; + } + + wsi->u.http.filepos = 0; + wsi->state = LWSS_HTTP_ISSUING_FILE; + + lws_callback_on_writable(wsi); + + return 0; +} + +int +lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len) +{ + int m; + + lwsl_parser("%s: received %d byte packet\n", __func__, (int)len); +#if 0 + lwsl_hexdump(*buf, len); +#endif + + /* let the rx protocol state machine have as much as it needs */ + + while (len) { + /* + * we were accepting input but now we stopped doing so + */ + if (wsi->rxflow_bitmap) { + lws_rxflow_cache(wsi, *buf, 0, len); + lwsl_parser("%s: cached %ld\n", __func__, (long)len); + return 1; + } + + if (wsi->u.ws.rx_draining_ext) { + m = lws_rx_sm(wsi, 0); + if (m < 0) + return -1; + continue; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->rxflow_buffer) { + wsi->rxflow_pos++; + if (wsi->rxflow_pos > wsi->rxflow_len) { + lwsl_err("bumped rxflow buffer too far (%d / %d)", wsi->rxflow_pos, wsi->rxflow_len); + assert(0); + } + } + + /* consume payload bytes efficiently */ + if (wsi->lws_rx_parse_state == + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) { + m = lws_payload_until_length_exhausted(wsi, buf, &len); + if (wsi->rxflow_buffer) + wsi->rxflow_pos += m; + } + + if (wsi->rxflow_buffer && wsi->rxflow_pos == wsi->rxflow_len) { + lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi); + lws_free_set_NULL(wsi->rxflow_buffer); + /* having drained the rxflow buffer, can rearm POLLIN */ +#ifdef LWS_NO_SERVER + m = +#endif + _lws_rx_flow_control(wsi); + /* m ignored, needed for NO_SERVER case */ + } + + /* process the byte */ + m = lws_rx_sm(wsi, *(*buf)++); + if (m < 0) + return -1; + len--; + } + + lwsl_parser("%s: exit with %d unused\n", __func__, (int)len); + + return 0; +} + +LWS_VISIBLE void +lws_server_get_canonical_hostname(struct lws_context *context, + struct lws_context_creation_info *info) +{ + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) + return; +#if LWS_POSIX && !defined(LWS_WITH_ESP32) + /* find canonical hostname */ + gethostname((char *)context->canonical_hostname, + sizeof(context->canonical_hostname) - 1); + + lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname); +#else + (void)context; +#endif +} + + +LWS_VISIBLE LWS_EXTERN int +lws_chunked_html_process(struct lws_process_html_args *args, + struct lws_process_html_state *s) +{ + char *sp, buffer[32]; + const char *pc; + int old_len, n; + + /* do replacements */ + sp = args->p; + old_len = args->len; + args->len = 0; + s->start = sp; + while (sp < args->p + old_len) { + + if (args->len + 7 >= args->max_len) { + lwsl_err("Used up interpret padding\n"); + return -1; + } + + if ((!s->pos && *sp == '$') || s->pos) { + int hits = 0, hit = 0; + + if (!s->pos) + s->start = sp; + s->swallow[s->pos++] = *sp; + if (s->pos == sizeof(s->swallow) - 1) + goto skip; + for (n = 0; n < s->count_vars; n++) + if (!strncmp(s->swallow, s->vars[n], s->pos)) { + hits++; + hit = n; + } + if (!hits) { +skip: + s->swallow[s->pos] = '\0'; + memcpy(s->start, s->swallow, s->pos); + args->len++; + s->pos = 0; + sp = s->start + 1; + continue; + } + if (hits == 1 && s->pos == strlen(s->vars[hit])) { + pc = s->replace(s->data, hit); + if (!pc) + pc = "NULL"; + n = strlen(pc); + s->swallow[s->pos] = '\0'; + if (n != s->pos) { + memmove(s->start + n, + s->start + s->pos, + old_len - (sp - args->p)); + old_len += (n - s->pos) + 1; + } + memcpy(s->start, pc, n); + args->len++; + sp = s->start + 1; + + s->pos = 0; + } + sp++; + continue; + } + + args->len++; + sp++; + } + + /* no space left for final chunk trailer */ + if (args->final && args->len + 7 >= args->max_len) + return -1; + + n = sprintf(buffer, "%X\x0d\x0a", args->len); + + args->p -= n; + memcpy(args->p, buffer, n); + args->len += n; + + if (args->final) { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '0'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 7; + } else { + sp = args->p + args->len; + *sp++ = '\x0d'; + *sp++ = '\x0a'; + args->len += 2; + } + + return 0; +} diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c new file mode 100644 index 0000000000..a9516f2239 --- /dev/null +++ b/thirdparty/lws/server/ssl-server.c @@ -0,0 +1,477 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +extern int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +extern void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); + +#if !defined(LWS_WITH_MBEDTLS) +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct lws *wsi; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + return !n; +} +#endif + +static int +lws_context_ssl_init_ecdh(struct lws_vhost *vhost) +{ +#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT + EC_KEY *EC_key = NULL; + EVP_PKEY *pkey; + int KeyType; + X509 *x; + + if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) + return 0; + + lwsl_notice(" Using ECDH certificate support\n"); + + /* Get X509 certificate from ssl context */ + x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0); + if (!x) { + lwsl_err("%s: x is NULL\n", __func__); + return 1; + } + /* Get the public key from certificate */ + pkey = X509_get_pubkey(x); + if (!pkey) { + lwsl_err("%s: pkey is NULL\n", __func__); + + return 1; + } + /* Get the key type */ + KeyType = EVP_PKEY_type(pkey->type); + + if (EVP_PKEY_EC != KeyType) { + lwsl_notice("Key type is not EC\n"); + return 0; + } + /* Get the key */ + EC_key = EVP_PKEY_get1_EC_KEY(pkey); + /* Set ECDH parameter */ + if (!EC_key) { + lwsl_err("%s: ECDH key is NULL \n", __func__); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key); + EC_KEY_free(EC_key); +#endif + return 0; +} + +static int +lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ +#if defined(LWS_HAVE_OPENSSL_ECDH_H) && !defined(LWS_WITH_MBEDTLS) + EC_KEY *ecdh; + int ecdh_nid; + const char *ecdh_curve = "prime256v1"; + + if (info->ecdh_curve) + ecdh_curve = info->ecdh_curve; + + ecdh_nid = OBJ_sn2nid(ecdh_curve); + if (NID_undef == ecdh_nid) { + lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); + return 1; + } + + ecdh = EC_KEY_new_by_curve_name(ecdh_nid); + if (NULL == ecdh) { + lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); + return 1; + } + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + + lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); +#else +#if !defined(LWS_WITH_MBEDTLS) + lwsl_notice(" OpenSSL doesn't support ECDH\n"); +#endif +#endif + return 0; +} + +#if !defined(LWS_WITH_MBEDTLS) && defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) +static int +lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) +{ + struct lws_context *context = (struct lws_context *)arg; + struct lws_vhost *vhost, *vh; + const char *servername; + + if (!ssl) + return SSL_TLSEXT_ERR_NOACK; + + /* + * We can only get ssl accepted connections by using a vhost's ssl_ctx + * find out which listening one took us and only match vhosts on the + * same port. + */ + vh = context->vhost_list; + while (vh) { + if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + break; + vh = vh->vhost_next; + } + + if (!vh) { + assert(vh); /* can't match the incoming vh? */ + return SSL_TLSEXT_ERR_OK; + } + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) { + /* the client doesn't know what hostname it wants */ + lwsl_info("SNI: Unknown ServerName: %s\n", servername); + + return SSL_TLSEXT_ERR_OK; + } + + vhost = lws_select_vhost(context, vh->listen_port, servername); + if (!vhost) { + lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); + + return SSL_TLSEXT_ERR_OK; + } + + lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); + + /* select the ssl ctx from the selected vhost for this conn */ + SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); + + return SSL_TLSEXT_ERR_OK; +} +#endif + +LWS_VISIBLE int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct lws_vhost *vhost) +{ + struct lws_context *context = vhost->context; + struct lws wsi; + unsigned long error; + int n; + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + vhost->use_ssl = 0; + return 0; + } + + /* + * If he is giving a cert filepath, take it as a sign he wants to use + * it on this vhost. User code can leave the cert filepath NULL and + * set the LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX option itself, in + * which case he's expected to set up the cert himself at + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which + * provides the vhost SSL_CTX * in the user parameter. + */ + if (info->ssl_cert_filepath) + info->options |= LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + + vhost->use_ssl = lws_check_opt(info->options, + LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX); + + if (vhost->use_ssl && info->ssl_cipher_list) + lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list); + + if (vhost->use_ssl) + lwsl_notice(" Using SSL mode\n"); + else + lwsl_notice(" Using non-SSL mode\n"); + } + + /* + * give him a fake wsi with context + vhost set, so he can use + * lws_get_context() in the callback + */ + memset(&wsi, 0, sizeof(wsi)); + wsi.vhost = vhost; + wsi.context = context; + + (void)n; + (void)error; + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + * + * SSLv23_server_method() is the openssl method for "allow all TLS + * versions", compared to e.g. TLSv1_2_server_method() which only allows + * tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options() + */ +#if !defined(LWS_WITH_MBEDTLS) + { + SSL_METHOD *method; + + method = (SSL_METHOD *)SSLv23_server_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + } +#else + { + const SSL_METHOD *method = TLSv1_2_server_method(); + + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { + lwsl_err("problem creating ssl context\n"); + return 1; + } + + } +#endif +#if !defined(LWS_WITH_MBEDTLS) + + /* associate the lws context with the SSL_CTX */ + + SSL_CTX_set_ex_data(vhost->ssl_ctx, + openssl_SSL_CTX_private_data_index, (char *)vhost->context); + /* Disable SSLv2 and SSLv3 */ + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(vhost->ssl_ctx, + info->ssl_cipher_list); +#endif + + /* as a server, are we requiring clients to identify themselves? */ + + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) { + int verify_options = SSL_VERIFY_PEER; + + if (!lws_check_opt(info->options, + LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) + verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + +#if !defined(LWS_WITH_MBEDTLS) + SSL_CTX_set_session_id_context(vhost->ssl_ctx, + (unsigned char *)context, sizeof(void *)); + + /* absolutely require the client cert */ + + SSL_CTX_set_verify(vhost->ssl_ctx, + verify_options, OpenSSL_verify_callback); +#endif + } + +#if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT) + SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx, + lws_ssl_server_name_cb); + SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context); +#endif + + /* + * give user code a chance to load certs into the server + * allowing it to verify incoming client certs + */ +#if !defined(LWS_WITH_MBEDTLS) + if (info->ssl_ca_filepath && + !SSL_CTX_load_verify_locations(vhost->ssl_ctx, + info->ssl_ca_filepath, NULL)) { + lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__); + } +#endif + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh_curve(info, vhost)) + return -1; + + vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + vhost->ssl_ctx, NULL, 0); + } + + if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT)) + /* Normally SSL listener rejects non-ssl, optionally allow */ + vhost->allow_non_ssl_on_ssl_port = 1; + + if (info->ssl_options_set) + SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set); + +/* SSL_clear_options introduced in 0.9.8m */ +#if !defined(LWS_WITH_MBEDTLS) +#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) + if (info->ssl_options_clear) + SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear); +#endif +#endif + + lwsl_info(" SSL options 0x%lX\n", SSL_CTX_get_options(vhost->ssl_ctx)); + + if (vhost->use_ssl && info->ssl_cert_filepath) { + /* + * The user code can choose to either pass the cert and + * key filepaths using the info members like this, or it can + * leave them NULL; force the vhost SSL_CTX init using the info + * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and + * set up the cert himself using the user callback + * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which + * happened just above and has the vhost SSL_CTX * in the user + * parameter. + */ +#if !defined(LWS_WITH_MBEDTLS) + /* set the local certificate from CertFile */ + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx, + info->ssl_cert_filepath); + if (n != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } + lws_ssl_bind_passphrase(vhost->ssl_ctx, info); +#else + uint8_t *p; + lws_filepos_t flen; + int err; + + if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p, + &flen)) { + lwsl_err("couldn't find cert file %s\n", + info->ssl_cert_filepath); + + return 1; + } + err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p); + if (!err) { + lwsl_err("Problem loading cert\n"); + return 1; + } +#if !defined(LWS_WITH_ESP32) + free(p); + p = NULL; +#endif + + if (info->ssl_private_key_filepath) { + if (alloc_pem_to_der_file(vhost->context, + info->ssl_private_key_filepath, &p, &flen)) { + lwsl_err("couldn't find cert file %s\n", + info->ssl_cert_filepath); + + return 1; + } + err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen); + if (!err) { + lwsl_err("Problem loading key\n"); + + return 1; + } + } + +#if !defined(LWS_WITH_ESP32) + free(p); + p = NULL; +#endif +#endif + if (info->ssl_private_key_filepath != NULL) { +#if !defined(LWS_WITH_MBEDTLS) + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + info->ssl_private_key_filepath, error, + ERR_error_string(error, + (char *)context->pt[0].serv_buf)); + return 1; + } +#endif + } else + if (vhost->protocols[0].callback(&wsi, + LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, + vhost->ssl_ctx, NULL, 0)) { + lwsl_err("ssl private key not set\n"); + + return 1; + } +#if !defined(LWS_WITH_MBEDTLS) + /* verify private key */ + if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } +#endif + } + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh(vhost)) + return 1; + + /* + * SSL is happy and has a cert it's content with + * If we're supporting HTTP2, initialize that + */ + lws_context_init_http2_ssl(vhost); + } + + return 0; +} + diff --git a/thirdparty/lws/service.c b/thirdparty/lws/service.c new file mode 100644 index 0000000000..6748e30bd4 --- /dev/null +++ b/thirdparty/lws/service.c @@ -0,0 +1,1699 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +static int +lws_calllback_as_writeable(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1); +#if defined(LWS_WITH_STATS) + if (wsi->active_writable_req_us) { + uint64_t ul = time_in_microseconds() - + wsi->active_writable_req_us; + + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_WRITABLE_DELAY, ul); + lws_stats_atomic_max(wsi->context, pt, + LWSSTATS_MS_WORST_WRITABLE_DELAY, ul); + wsi->active_writable_req_us = 0; + } +#endif + + switch (wsi->mode) { + case LWSCM_RAW: + n = LWS_CALLBACK_RAW_WRITEABLE; + break; + case LWSCM_RAW_FILEDESC: + n = LWS_CALLBACK_RAW_WRITEABLE_FILE; + break; + case LWSCM_WS_CLIENT: + n = LWS_CALLBACK_CLIENT_WRITEABLE; + break; + case LWSCM_WSCL_ISSUE_HTTP_BODY: + n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE; + break; + case LWSCM_WS_SERVING: + n = LWS_CALLBACK_SERVER_WRITEABLE; + break; + default: + n = LWS_CALLBACK_HTTP_WRITEABLE; + break; + } + + return user_callback_handle_rxflow(wsi->protocol->callback, + wsi, (enum lws_callback_reasons) n, + wsi->user_space, NULL, 0); +} + +LWS_VISIBLE int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) +{ + int write_type = LWS_WRITE_PONG; + struct lws_tokens eff_buf; +#ifdef LWS_WITH_HTTP2 + struct lws **wsi2, *wsi2a; +#endif + int ret, m, n; + + wsi->leave_pollout_active = 0; + wsi->handling_pollout = 1; + /* + * if another thread wants POLLOUT on us, from here on while + * handling_pollout is set, he will only set leave_pollout_active. + * If we are going to disable POLLOUT, we will check that first. + */ + + /* + * user callback is lowest priority to get these notifications + * actually, since other pending things cannot be disordered + */ + + /* Priority 1: pending truncated sends are incomplete ws fragments + * If anything else sent first the protocol would be + * corrupted. + */ + if (wsi->trunc_len) { + //lwsl_notice("%s: completing partial\n", __func__); + if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s signalling to close\n", __func__); + goto bail_die; + } + /* leave POLLOUT active either way */ + goto bail_ok; + } else + if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + wsi->socket_is_permanently_unusable = 1; + goto bail_die; /* retry closing now */ + } + + if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY) + goto user_service; + +#ifdef LWS_WITH_HTTP2 + /* + * Priority 2: protocol packets + */ + if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) { + lwsl_info("servicing pps\n"); + if (lws_h2_do_pps_send(wsi)) { + wsi->socket_is_permanently_unusable = 1; + goto bail_die; + } + if (wsi->u.h2.h2n->pps) + goto bail_ok; + + /* we can resume whatever we were doing */ + lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE | + LWS_RXFLOW_REASON_H2_PPS_PENDING); + + goto bail_ok; /* leave POLLOUT active */ + } +#endif + +#ifdef LWS_WITH_CGI + if (wsi->cgi) { + /* also one shot */ + if (pollfd) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + goto user_service_go_again; + } +#endif + + /* Priority 3: pending control packets (pong or close) + * + * 3a: close notification packet requested from close api + */ + + if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) { + lwsl_debug("sending close packet\n"); + wsi->waiting_to_send_close_frame = 0; + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.close_in_ping_buffer_len, + LWS_WRITE_CLOSE); + if (n >= 0) { + wsi->state = LWSS_AWAITING_CLOSE_ACK; + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1); + lwsl_debug("sent close indication, awaiting ack\n"); + + goto bail_ok; + } + + goto bail_die; + } + + /* else, the send failed and we should just hang up */ + + if ((wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.ping_pending_flag) || + (wsi->state == LWSS_RETURNED_CLOSE_ALREADY && + wsi->u.ws.payload_is_close)) { + + if (wsi->u.ws.payload_is_close) + write_type = LWS_WRITE_CLOSE; + + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + wsi->u.ws.ping_payload_len, write_type); + if (n < 0) + goto bail_die; + + /* well he is sent, mark him done */ + wsi->u.ws.ping_pending_flag = 0; + if (wsi->u.ws.payload_is_close) + /* oh... a close frame was it... then we are done */ + goto bail_die; + + /* otherwise for PING, leave POLLOUT active either way */ + goto bail_ok; + } + + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + wsi->u.ws.send_check_ping) { + + lwsl_info("issuing ping on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 0; + n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE], + 0, LWS_WRITE_PING); + if (n < 0) + goto bail_die; + + /* + * we apparently were able to send the PING in a reasonable time + * now reset the clock on our peer to be able to send the + * PONG in a reasonable time. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG, + wsi->context->timeout_secs); + + goto bail_ok; + } + + /* Priority 4: if we are closing, not allowed to send more data frags + * which means user callback or tx ext flush banned now + */ + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + goto user_service; + + /* Priority 5: Tx path extension with more to send + * + * These are handled as new fragments each time around + * So while we must block new writeable callback to enforce + * payload ordering, but since they are always complete + * fragments control packets can interleave OK. + */ + if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) { + lwsl_ext("SERVICING TX EXT DRAINING\n"); + if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) + goto bail_die; + /* leave POLLOUT active */ + goto bail_ok; + } + + /* Priority 6: user can get the callback + */ + m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0); + if (m) + goto bail_die; +#ifndef LWS_NO_EXTENSIONS + if (!wsi->extension_data_pending) + goto user_service; +#endif + /* + * check in on the active extensions, see if they + * had pending stuff to spill... they need to get the + * first look-in otherwise sequence will be disordered + * + * NULL, zero-length eff_buf means just spill pending + */ + + ret = 1; + if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC) + ret = 0; + + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* give every extension a chance to spill */ + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND, + &eff_buf, 0); + if (m < 0) { + lwsl_err("ext reports fatal error\n"); + goto bail_die; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they gave us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from POLLOUT spill\n"); + goto bail_die; + } + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %d\n", + eff_buf.token_len, n); + goto bail_die; + } + } else + continue; + + /* no extension has more to spill */ + + if (!ret) + continue; + + /* + * There's more to spill from an extension, but we just sent + * something... did that leave the pipe choked? + */ + + if (!lws_send_pipe_choked(wsi)) + /* no we could add more */ + continue; + + lwsl_info("choked in POLLOUT service\n"); + + /* + * Yes, he's choked. Leave the POLLOUT masked on so we will + * come back here when he is unchoked. Don't call the user + * callback to enforce ordering of spilling, he'll get called + * when we come back here and there's nothing more to spill. + */ + + goto bail_ok; + } +#ifndef LWS_NO_EXTENSIONS + wsi->extension_data_pending = 0; +#endif +user_service: + /* one shot */ + + if (wsi->parent_carries_io) { + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return lws_calllback_as_writeable(wsi); + } + + if (pollfd) { + int eff = wsi->leave_pollout_active; + + if (!eff) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + goto bail_die; + } + + wsi->handling_pollout = 0; + + /* cannot get leave_pollout_active set after the above */ + if (!eff && wsi->leave_pollout_active) + /* got set inbetween sampling eff and clearing + * handling_pollout, force POLLOUT on */ + lws_calllback_as_writeable(wsi); + + wsi->leave_pollout_active = 0; + } + + if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY && + !wsi->hdr_parsing_completed) + goto bail_ok; + + +#ifdef LWS_WITH_CGI +user_service_go_again: +#endif + +#ifdef LWS_WITH_HTTP2 + /* + * we are the 'network wsi' for potentially many muxed child wsi with + * no network connection of their own, who have to use us for all their + * network actions. So we use a round-robin scheme to share out the + * POLLOUT notifications to our children. + * + * But because any child could exhaust the socket's ability to take + * writes, we can only let one child get notified each time. + * + * In addition children may be closed / deleted / added between POLLOUT + * notifications, so we can't hold pointers + */ + + if (wsi->mode != LWSCM_HTTP2_SERVING) { + lwsl_info("%s: non http2\n", __func__); + goto notify; + } + + wsi->u.h2.requested_POLLOUT = 0; + if (!wsi->u.h2.initialized) { + lwsl_info("pollout on uninitialized http2 conn\n"); + goto bail_ok; + } + +// if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) { +// lws_callback_on_writable(wsi); +// goto bail_ok; +// } + + lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi); + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) + lwsl_debug(" * %p\n", wsi2a); + else + lwsl_debug(" %p\n", wsi2a); + + wsi2a = wsi2a->u.h2.sibling_list; + } + + wsi2 = &wsi->u.h2.child_list; + if (!*wsi2) + goto bail_ok; + + do { + struct lws *w, **wa; + + wa = &(*wsi2)->u.h2.sibling_list; + if (!(*wsi2)->u.h2.requested_POLLOUT) { + lwsl_debug(" child %p doesn't want POLLOUT\n", *wsi2); + goto next_child; + } + + /* + * we're going to do writable callback for this child. + * move him to be the last child + */ + + lwsl_debug("servicing child %p\n", *wsi2); + + w = *wsi2; + while (w) { + if (!w->u.h2.sibling_list) { /* w is the current last */ + lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2); + if (w == *wsi2) /* we are already last */ + break; + w->u.h2.sibling_list = *wsi2; /* last points to us as new last */ + *wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */ + w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */ + w = w->u.h2.sibling_list; /* w becomes us */ + break; + } + w = w->u.h2.sibling_list; + } + + w->u.h2.requested_POLLOUT = 0; + lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state); + + if (w->u.h2.pending_status_body) { + w->u.h2.send_END_STREAM = 1; + n = lws_write(w, + (uint8_t *)w->u.h2.pending_status_body + LWS_PRE, + strlen(w->u.h2.pending_status_body + LWS_PRE), + LWS_WRITE_HTTP_FINAL); + lws_free_set_NULL(w->u.h2.pending_status_body); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + goto next_child; + } + + if (w->state == LWSS_HTTP_ISSUING_FILE) { + + w->leave_pollout_active = 0; + + /* >0 == completion, <0 == error + * + * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done. That's the case even if we just completed the + * send, so wait for that. + */ + n = lws_serve_http_file_fragment(w); + lwsl_debug("lws_serve_http_file_fragment says %d\n", n); + + /* + * We will often hear about out having sent the final + * DATA here... if so close the actual wsi + */ + if (n < 0 || w->u.h2.send_END_STREAM) { + lwsl_debug("Closing POLLOUT child %p\n", w); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + goto next_child; + } + if (n > 0) + if (lws_http_transaction_completed(w)) + goto bail_die; + if (!n) { + lws_callback_on_writable(w); + (w)->u.h2.requested_POLLOUT = 1; + } + + goto next_child; + } + + if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) { + lwsl_debug("Closing POLLOUT child\n"); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS); + wa = &wsi->u.h2.child_list; + } + +next_child: + wsi2 = wa; + } while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi)); + + lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list); + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) + lwsl_debug(" * %p\n", wsi2a); + else + lwsl_debug(" %p\n", wsi2a); + + wsi2a = wsi2a->u.h2.sibling_list; + } + + + wsi2a = wsi->u.h2.child_list; + while (wsi2a) { + if (wsi2a->u.h2.requested_POLLOUT) { + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + break; + } + wsi2a = wsi2a->u.h2.sibling_list; + } + + goto bail_ok; + + +notify: +#endif + wsi->leave_pollout_active = 0; + + n = lws_calllback_as_writeable(wsi); + wsi->handling_pollout = 0; + + if (wsi->leave_pollout_active) + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + + return n; + + /* + * since these don't disable the POLLOUT, they are always doing the + * right thing for leave_pollout_active whether it was set or not. + */ + +bail_ok: + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return 0; + +bail_die: + wsi->handling_pollout = 0; + wsi->leave_pollout_active = 0; + + return -1; +} + +int +lws_service_timeout_check(struct lws *wsi, unsigned int sec) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int n = 0; + + (void)n; + + /* + * if extensions want in on it (eg, we are a mux parent) + * give them a chance to service child timeouts + */ + if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0) + return 0; + + if (!wsi->pending_timeout) + return 0; + + /* + * if we went beyond the allowed time, kill the + * connection + */ + if ((time_t)sec > wsi->pending_timeout_limit) { + + if (wsi->desc.sockfd != LWS_SOCK_INVALID && + wsi->position_in_fds_table >= 0) + n = pt->fds[wsi->position_in_fds_table].events; + + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1); + + /* no need to log normal idle keepalive timeout */ + if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE) + lwsl_info("wsi %p: TIMEDOUT WAITING on %d " + "(did hdr %d, ah %p, wl %d, pfd " + "events %d) %llu vs %llu\n", + (void *)wsi, wsi->pending_timeout, + wsi->hdr_parsing_completed, wsi->u.hdr.ah, + pt->ah_wait_list_length, n, + (unsigned long long)sec, + (unsigned long long)wsi->pending_timeout_limit); + + /* + * Since he failed a timeout, he already had a chance to do + * something and was unable to... that includes situations like + * half closed connections. So process this "failed timeout" + * close as a violent death and don't try to do protocol + * cleanup like flush partials. + */ + wsi->socket_is_permanently_unusable = 1; + if (wsi->mode == LWSCM_WSCL_WAITING_SSL) + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, + (void *)"Timed out waiting SSL", 21); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return 1; + } + + return 0; +} + +int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len) +{ +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + + assert(h2n->rx_scratch); + buf += n; + len -= n; + assert ((char *)buf >= (char *)h2n->rx_scratch && + (char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]); + + h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch); + h2n->rx_scratch_len = len; + + lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi); + + return 0; + } +#endif + /* his RX is flowcontrolled, don't send remaining now */ + if (wsi->rxflow_buffer) { + if (buf >= wsi->rxflow_buffer && + &buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) { + /* rxflow while we were spilling prev rxflow */ + lwsl_info("%s: staying in rxflow buf\n", __func__); + return 1; + } else { + lwsl_err("%s: conflicting rxflow buf, " + "current %p len %d, new %p len %d\n", __func__, + wsi->rxflow_buffer, wsi->rxflow_len, buf, len); + assert(0); + return 1; + } + } + + /* a new rxflow, buffer it and warn caller */ + lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n); + wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf"); + if (!wsi->rxflow_buffer) + return -1; + + wsi->rxflow_len = len - n; + wsi->rxflow_pos = 0; + memcpy(wsi->rxflow_buffer, buf + n, len - n); + + return 0; +} + +/* this is used by the platform service code to stop us waiting for network + * activity in poll() when we have something that already needs service + */ + +LWS_VISIBLE LWS_EXTERN int +lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct allocated_headers *ah; + + /* Figure out if we really want to wait in poll() + * We only need to wait if really nothing already to do and we have + * to wait for something from network + */ + + /* 1) if we know we are draining rx ext, do not wait in poll */ + if (pt->rx_draining_ext_list) + return 0; + +#ifdef LWS_OPENSSL_SUPPORT + /* 2) if we know we have non-network pending data, do not wait in poll */ + if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) { + lwsl_info("ssl buffered read\n"); + return 0; + } +#endif + + /* 3) if any ah has pending rx, do not wait in poll */ + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen) { + if (!ah->wsi) { + assert(0); + } + return 0; + } + ah = ah->next; + } + + return timeout_ms; +} + +/* + * guys that need POLLIN service again without waiting for network action + * can force POLLIN here if not flowcontrolled, so they will get service. + * + * Return nonzero if anybody got their POLLIN faked + */ +int +lws_service_flag_pending(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct allocated_headers *ah; +#ifdef LWS_OPENSSL_SUPPORT + struct lws *wsi_next; +#endif + struct lws *wsi; + int forced = 0; + + /* POLLIN faking */ + + /* + * 1) For all guys with already-available ext data to drain, if they are + * not flowcontrolled, fake their POLLIN status + */ + wsi = pt->rx_draining_ext_list; + while (wsi) { + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + break; + } + wsi = wsi->u.ws.rx_draining_ext_list; + } + +#ifdef LWS_OPENSSL_SUPPORT + /* + * 2) For all guys with buffered SSL read data already saved up, if they + * are not flowcontrolled, fake their POLLIN status so they'll get + * service to use up the buffered incoming data, even though their + * network socket may have nothing + */ + wsi = pt->pending_read_list; + while (wsi) { + wsi_next = wsi->pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) { + forced = 1; + /* + * he's going to get serviced now, take him off the + * list of guys with buffered SSL. If he still has some + * at the end of the service, he'll get put back on the + * list then. + */ + lws_ssl_remove_wsi_from_buffered_list(wsi); + } + + wsi = wsi_next; + } +#endif + /* + * 3) For any wsi who have an ah with pending RX who did not + * complete their current headers, and are not flowcontrolled, + * fake their POLLIN status so they will be able to drain the + * rx buffered in the ah + */ + ah = pt->ah_list; + while (ah) { + if (ah->rxpos != ah->rxlen && !ah->wsi->hdr_parsing_completed) { + pt->fds[ah->wsi->position_in_fds_table].revents |= + pt->fds[ah->wsi->position_in_fds_table].events & + LWS_POLLIN; + if (pt->fds[ah->wsi->position_in_fds_table].revents & + LWS_POLLIN) { + forced = 1; + break; + } + } + ah = ah->next; + } + + return forced; +} + +#ifndef LWS_NO_CLIENT + +LWS_VISIBLE int +lws_http_client_read(struct lws *wsi, char **buf, int *len) +{ + int rlen, n; + + rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len); + *len = 0; + + /* allow the source to signal he has data again next time */ + lws_change_pollfd(wsi, 0, LWS_POLLIN); + + if (rlen == LWS_SSL_CAPABLE_ERROR) { + lwsl_notice("%s: SSL capable error\n", __func__); + return -1; + } + + if (rlen == 0) + return -1; + + if (rlen < 0) + return 0; + + *len = rlen; + wsi->client_rx_avail = 0; + + /* + * server may insist on transfer-encoding: chunked, + * so http client must deal with it + */ +spin_chunks: + while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) { + switch (wsi->chunk_parser) { + case ELCP_HEX: + if ((*buf)[0] == '\x0d') { + wsi->chunk_parser = ELCP_CR; + break; + } + n = char_to_hex((*buf)[0]); + if (n < 0) { + lwsl_debug("chunking failure\n"); + return -1; + } + wsi->chunk_remaining <<= 4; + wsi->chunk_remaining |= n; + break; + case ELCP_CR: + if ((*buf)[0] != '\x0a') { + lwsl_debug("chunking failure\n"); + return -1; + } + wsi->chunk_parser = ELCP_CONTENT; + lwsl_info("chunk %d\n", wsi->chunk_remaining); + if (wsi->chunk_remaining) + break; + lwsl_info("final chunk\n"); + goto completed; + + case ELCP_CONTENT: + break; + + case ELCP_POST_CR: + if ((*buf)[0] != '\x0d') { + lwsl_debug("chunking failure\n"); + + return -1; + } + + wsi->chunk_parser = ELCP_POST_LF; + break; + + case ELCP_POST_LF: + if ((*buf)[0] != '\x0a') + return -1; + + wsi->chunk_parser = ELCP_HEX; + wsi->chunk_remaining = 0; + break; + } + (*buf)++; + (*len)--; + } + + if (wsi->chunked && !wsi->chunk_remaining) + return 0; + + if (wsi->u.http.rx_content_remain && + wsi->u.http.rx_content_remain < *len) + n = (int)wsi->u.http.rx_content_remain; + else + n = *len; + + if (wsi->chunked && wsi->chunk_remaining && + wsi->chunk_remaining < n) + n = wsi->chunk_remaining; + +#ifdef LWS_WITH_HTTP_PROXY + /* hubbub */ + if (wsi->perform_rewrite) + lws_rewrite_parse(wsi->rw, (unsigned char *)*buf, n); + else +#endif + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + wsi->user_space, *buf, n)) { + lwsl_debug("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ returned -1\n", __func__); + + return -1; + } + + if (wsi->chunked && wsi->chunk_remaining) { + (*buf) += n; + wsi->chunk_remaining -= n; + *len -= n; + } + + if (wsi->chunked && !wsi->chunk_remaining) + wsi->chunk_parser = ELCP_POST_CR; + + if (wsi->chunked && *len) + goto spin_chunks; + + if (wsi->chunked) + return 0; + + /* if we know the content length, decrement the content remaining */ + if (wsi->u.http.rx_content_length > 0) + wsi->u.http.rx_content_remain -= n; + + if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length) + return 0; + +completed: + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + lwsl_debug("Completed call returned -1\n"); + return -1; + } + + if (lws_http_transaction_completed_client(wsi)) { + lwsl_notice("%s: transaction completed says -1\n", __func__); + return -1; + } + + return 0; +} +#endif + +static int +lws_is_ws_with_ext(struct lws *wsi) +{ +#if defined(LWS_NO_EXTENSIONS) + return 0; +#else + return wsi->state == LWSS_ESTABLISHED && + !!wsi->count_act_ext; +#endif +} + +LWS_VISIBLE int +lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + lws_sockfd_type our_fd = 0, tmp_fd; + struct allocated_headers *ah; + struct lws_tokens eff_buf; + unsigned int pending = 0; + struct lws *wsi, *wsi1; + char draining_flow = 0; + int timed_out = 0; + time_t now; + int n = 0, m; + int more; + + if (!context->protocol_init_done) + lws_protocol_init(context); + + time(&now); + + /* + * handle case that system time was uninitialized when lws started + * at boot, and got initialized a little later + */ + if (context->time_up < 1464083026 && now > 1464083026) + context->time_up = now; + + /* TODO: if using libev, we should probably use timeout watchers... */ + if (context->last_timeout_check_s != now) { + context->last_timeout_check_s = now; + +#if defined(LWS_WITH_STATS) + if (!tsi && now - context->last_dump > 10) { + lws_stats_log_dump(context); + context->last_dump = now; + } +#endif + + lws_plat_service_periodic(context); + + lws_check_deferred_free(context, 0); + +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_cull_peer_wait_list(context); +#endif + + /* retire unused deprecated context */ +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32) +#if LWS_POSIX && !defined(_WIN32) + if (context->deprecated && !context->count_wsi_allocated) { + lwsl_notice("%s: ending deprecated context\n", __func__); + kill(getpid(), SIGINT); + return 0; + } +#endif +#endif + /* global timeout check once per second */ + + if (pollfd) + our_fd = pollfd->fd; + + /* + * Phase 1: check every wsi on the timeout check list + */ + + wsi = context->pt[tsi].timeout_list; + while (wsi) { + /* we have to take copies, because he may be deleted */ + wsi1 = wsi->timeout_list; + tmp_fd = wsi->desc.sockfd; + if (lws_service_timeout_check(wsi, (unsigned int)now)) { + /* he did time out... */ + if (tmp_fd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + /* he's gone, no need to mark as handled */ + } + wsi = wsi1; + } + + /* + * Phase 2: double-check active ah timeouts independent of wsi + * timeout status + */ + + ah = pt->ah_list; + while (ah) { + int len; + char buf[256]; + const unsigned char *c; + + if (!ah->in_use || !ah->wsi || !ah->assigned || + (ah->wsi->vhost && now - ah->assigned < + ah->wsi->vhost->timeout_secs_ah_idle + 60)) { + ah = ah->next; + continue; + } + + /* + * a single ah session somehow got held for + * an unreasonable amount of time. + * + * Dump info on the connection... + */ + wsi = ah->wsi; + buf[0] = '\0'; + lws_get_peer_simple(wsi, buf, sizeof(buf)); + lwsl_notice("ah excessive hold: wsi %p\n" + " peer address: %s\n" + " ah rxpos %u, rxlen %u, pos %u\n", + wsi, buf, ah->rxpos, ah->rxlen, + ah->pos); + buf[0] = '\0'; + m = 0; + do { + c = lws_token_to_string(m); + if (!c) + break; + + len = lws_hdr_total_length(wsi, m); + if (!len || len > sizeof(buf) - 1) { + m++; + continue; + } + + if (lws_hdr_copy(wsi, buf, + sizeof buf, m) > 0) { + buf[sizeof(buf) - 1] = '\0'; + + lwsl_notice(" %s = %s\n", + (const char *)c, buf); + } + m++; + } while (1); + + /* ... and then drop the connection */ + + if (wsi->desc.sockfd == our_fd) + /* it was the guy we came to service! */ + timed_out = 1; + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + ah = ah->next; + } + +#ifdef LWS_WITH_CGI + /* + * Phase 3: handle cgi timeouts + */ + lws_cgi_kill_terminated(pt); +#endif +#if 0 + { + char s[300], *p = s; + + for (n = 0; n < context->count_threads; n++) + p += sprintf(p, " %7lu (%5d), ", + context->pt[n].count_conns, + context->pt[n].fds_count); + + lwsl_notice("load: %s\n", s); + } +#endif + } + + /* + * at intervals, check for ws connections needing ping-pong checks + */ + + if (context->ws_ping_pong_interval && + context->last_ws_ping_pong_check_s < now + 10) { + struct lws_vhost *vh = context->vhost_list; + context->last_ws_ping_pong_check_s = now; + + while (vh) { + for (n = 0; n < vh->count_protocols; n++) { + wsi = vh->same_vh_protocol_list[n]; + + while (wsi) { + if (wsi->state == LWSS_ESTABLISHED && + !wsi->socket_is_permanently_unusable && + !wsi->u.ws.send_check_ping && + wsi->u.ws.time_next_ping_check && + wsi->u.ws.time_next_ping_check < now) { + + lwsl_info("requesting ping-pong on wsi %p\n", wsi); + wsi->u.ws.send_check_ping = 1; + lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING, + context->timeout_secs); + lws_callback_on_writable(wsi); + wsi->u.ws.time_next_ping_check = now + + wsi->context->ws_ping_pong_interval; + } + wsi = wsi->same_vh_protocol_next; + } + } + vh = vh->vhost_next; + } + } + + + /* the socket we came to service timed out, nothing to do */ + if (timed_out) + return 0; + + /* just here for timeout management? */ + if (!pollfd) + return 0; + + /* no, here to service a socket descriptor */ + wsi = wsi_from_fd(context, pollfd->fd); + if (!wsi) + /* not lws connection ... leave revents alone and return */ + return 0; + + /* + * so that caller can tell we handled, past here we need to + * zero down pollfd->revents after handling + */ + +#if LWS_POSIX + /* handle session socket closed */ + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + wsi->socket_is_permanently_unusable = 1; + lwsl_debug("Session Socket %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto close_and_handled; + } + +#ifdef _WIN32 + if (pollfd->revents & LWS_POLLOUT) + wsi->sock_send_blocking = FALSE; +#endif + +#endif + + if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + lwsl_debug("pollhup\n"); + wsi->socket_is_permanently_unusable = 1; + goto close_and_handled; + } + +#ifdef LWS_OPENSSL_SUPPORT + if ((wsi->state == LWSS_SHUTDOWN) && lws_is_ssl(wsi) && wsi->ssl) { + n = SSL_shutdown(wsi->ssl); + lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd); + switch (n) { + case 1: + n = shutdown(wsi->desc.sockfd, SHUT_WR); + goto close_and_handled; + + case 0: + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + goto handled; + + default: + n = SSL_get_error(wsi->ssl, n); + if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) { + if (SSL_want_read(wsi->ssl)) { + lwsl_debug("(wants read)\n"); + lws_change_pollfd(wsi, 0, LWS_POLLIN); + n = 0; + goto handled; + } + if (SSL_want_write(wsi->ssl)) { + lwsl_debug("(wants write)\n"); + lws_change_pollfd(wsi, 0, LWS_POLLOUT); + n = 0; + goto handled; + } + } + + /* actual error occurred, just close the connection */ + n = shutdown(wsi->desc.sockfd, SHUT_WR); + goto close_and_handled; + } + } +#endif + + /* okay, what we came here to do... */ + + switch (wsi->mode) { + case LWSCM_HTTP_SERVING: + case LWSCM_HTTP_CLIENT: + case LWSCM_HTTP_SERVING_ACCEPTED: + case LWSCM_SERVER_LISTENER: + case LWSCM_SSL_ACK_PENDING: + case LWSCM_SSL_ACK_PENDING_RAW: + if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) + goto handled; + +#ifdef LWS_WITH_CGI + if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) { + n = lws_handle_POLLOUT_event(wsi, pollfd); + if (n) + goto close_and_handled; + goto handled; + } +#endif + /* fallthru */ + case LWSCM_RAW: + n = lws_server_socket_service(context, wsi, pollfd); + if (n) /* closed by above */ + return 1; + goto handled; + + case LWSCM_RAW_FILEDESC: + + if (pollfd->revents & LWS_POLLOUT) { + n = lws_calllback_as_writeable(wsi); + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + if (n) + goto close_and_handled; + } + n = LWS_CALLBACK_RAW_RX; + if (wsi->mode == LWSCM_RAW_FILEDESC) + n = LWS_CALLBACK_RAW_RX_FILE; + + if (pollfd->revents & LWS_POLLIN) { + if (user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, n, + wsi->user_space, NULL, 0)) { + lwsl_debug("raw rx callback closed it\n"); + goto close_and_handled; + } + } + + if (pollfd->revents & LWS_POLLHUP) + goto close_and_handled; + n = 0; + goto handled; + + case LWSCM_WS_SERVING: + case LWSCM_WS_CLIENT: + case LWSCM_HTTP2_SERVING: + case LWSCM_HTTP_CLIENT_ACCEPTED: + + /* 1: something requested a callback when it was OK to write */ + + if ((pollfd->revents & LWS_POLLOUT) && + ((wsi->state == LWSS_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED || + wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS || + wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)) && + lws_handle_POLLOUT_event(wsi, pollfd)) { + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) + wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE; + lwsl_info("lws_service_fd: closing\n"); + goto close_and_handled; + } + + if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || + wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || + wsi->state == LWSS_AWAITING_CLOSE_ACK) { + /* + * we stopped caring about anything except control + * packets. Force flow control off, defeat tx + * draining. + */ + lws_rx_flow_control(wsi, 1); + wsi->u.ws.tx_draining_ext = 0; + } + + if (wsi->u.ws.tx_draining_ext) + /* we cannot deal with new RX until the TX ext + * path has been drained. It's because new + * rx will, eg, crap on the wsi rx buf that + * may be needed to retain state. + * + * TX ext drain path MUST go through event loop + * to avoid blocking. + */ + break; + + if (lws_is_flowcontrolled(wsi)) + /* We cannot deal with any kind of new RX + * because we are RX-flowcontrolled. + */ + break; + +#if defined(LWS_WITH_HTTP2) + if (wsi->http2_substream || wsi->upgraded_to_http2) { + wsi1 = lws_get_network_wsi(wsi); + if (wsi1 && wsi1->trunc_len) + /* We cannot deal with any kind of new RX + * because we are dealing with a partial send + * (new RX may trigger new http_action() that + * expect to be able to send) + */ + break; + } +#endif + + /* 2: RX Extension needs to be drained + */ + + if (wsi->state == LWSS_ESTABLISHED && + wsi->u.ws.rx_draining_ext) { + + lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__); +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_WS_CLIENT) { + n = lws_client_rx_sm(wsi, 0); + if (n < 0) + /* we closed wsi */ + n = 0; + } else +#endif + n = lws_rx_sm(wsi, 0); + + goto handled; + } + + if (wsi->u.ws.rx_draining_ext) + /* + * We have RX EXT content to drain, but can't do it + * right now. That means we cannot do anything lower + * priority either. + */ + break; + + /* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained + */ + + if (wsi->rxflow_buffer) { + lwsl_info("draining rxflow (len %d)\n", + wsi->rxflow_len - wsi->rxflow_pos); + assert(wsi->rxflow_pos < wsi->rxflow_len); + /* well, drain it */ + eff_buf.token = (char *)wsi->rxflow_buffer + + wsi->rxflow_pos; + eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos; + draining_flow = 1; + goto drain; + } + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + struct lws_h2_netconn *h2n = wsi->u.h2.h2n; + + if (h2n->rx_scratch_len) { + lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n", + __func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len); + eff_buf.token = (char *)h2n->rx_scratch + + h2n->rx_scratch_pos; + eff_buf.token_len = h2n->rx_scratch_len; + + h2n->rx_scratch_len = 0; + goto drain; + } + } +#endif + + /* 4: any incoming (or ah-stashed incoming rx) data ready? + * notice if rx flow going off raced poll(), rx flow wins + */ + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; +read: + if (lws_is_flowcontrolled(wsi)) { + lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n", + __func__, wsi, wsi->rxflow_bitmap); + break; + } + + /* all the union members start with hdr, so even in ws mode + * we can deal with the ah via u.hdr + */ + if (wsi->u.hdr.ah) { + lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi); + eff_buf.token_len = wsi->u.hdr.ah->rxlen - + wsi->u.hdr.ah->rxpos; + eff_buf.token = (char *)wsi->u.hdr.ah->rx + + wsi->u.hdr.ah->rxpos; + } else { + if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) { + /* + * extension may not consume everything (eg, pmd may be constrained + * as to what it can output...) has to go in per-wsi rx buf area. + * Otherwise in large temp serv_buf area. + */ + +#if defined(LWS_WITH_HTTP2) + if (wsi->upgraded_to_http2) { + if (!wsi->u.h2.h2n->rx_scratch) { + wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch"); + if (!wsi->u.h2.h2n->rx_scratch) + goto close_and_handled; + } + eff_buf.token = wsi->u.h2.h2n->rx_scratch; + eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE; + } else +#endif + { + eff_buf.token = (char *)pt->serv_buf; + if (lws_is_ws_with_ext(wsi)) { + eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc; + } else { + eff_buf.token_len = context->pt_serv_buf_size; + } + + if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size) + eff_buf.token_len = context->pt_serv_buf_size; + } + + if ((int)pending > eff_buf.token_len) + pending = eff_buf.token_len; + + eff_buf.token_len = lws_ssl_capable_read(wsi, + (unsigned char *)eff_buf.token, pending ? pending : + eff_buf.token_len); + switch (eff_buf.token_len) { + case 0: + lwsl_info("%s: zero length read\n", __func__); + goto close_and_handled; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lwsl_info("SSL Capable more service\n"); + n = 0; + goto handled; + case LWS_SSL_CAPABLE_ERROR: + lwsl_info("Closing when error\n"); + goto close_and_handled; + } + // lwsl_notice("Actual RX %d\n", eff_buf.token_len); + } + } + +drain: +#ifndef LWS_NO_CLIENT + if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED && + !wsi->told_user_closed) { + + /* + * In SSL mode we get POLLIN notification about + * encrypted data in. + * + * But that is not necessarily related to decrypted + * data out becoming available; in may need to perform + * other in or out before that happens. + * + * simply mark ourselves as having readable data + * and turn off our POLLIN + */ + wsi->client_rx_avail = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + + /* let user code know, he'll usually ask for writeable + * callback and drain / re-enable it there + */ + if (user_callback_handle_rxflow( + wsi->protocol->callback, + wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP, + wsi->user_space, NULL, 0)) { + lwsl_info("RECEIVE_CLIENT_HTTP closed it\n"); + goto close_and_handled; + } + + n = 0; + goto handled; + } +#endif + /* + * give any active extensions a chance to munge the buffer + * before parse. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + */ + do { + more = 0; + + m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE, + &eff_buf, 0); + if (m < 0) + goto close_and_handled; + if (m) + more = 1; + + /* service incoming data */ + + if (eff_buf.token_len) { + /* + * if draining from rxflow buffer, not + * critical to track what was used since at the + * use it bumps wsi->rxflow_pos. If we come + * around again it will pick up from where it + * left off. + */ + n = lws_read(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + /* we closed wsi */ + n = 0; + goto handled; + } + } + + eff_buf.token = NULL; + eff_buf.token_len = 0; + } while (more); + + if (wsi->u.hdr.ah) { + lwsl_debug("%s: %p: detaching\n", __func__, wsi); + lws_header_table_force_to_detachable_state(wsi); + /* we can run the normal ah detach flow despite + * being in ws union mode, since all union members + * start with hdr */ + lws_header_table_detach(wsi, 0); + } + + pending = lws_ssl_pending(wsi); + if (pending) { + if (lws_is_ws_with_ext(wsi)) + pending = pending > wsi->u.ws.rx_ubuf_alloc ? + wsi->u.ws.rx_ubuf_alloc : pending; + else + pending = pending > context->pt_serv_buf_size ? + context->pt_serv_buf_size : pending; + goto read; + } + + if (draining_flow && wsi->rxflow_buffer && + wsi->rxflow_pos == wsi->rxflow_len) { + lwsl_info("%s: %p flow buf: drained\n", __func__, wsi); + lws_free_set_NULL(wsi->rxflow_buffer); + /* having drained the rxflow buffer, can rearm POLLIN */ +#ifdef LWS_NO_SERVER + n = +#endif + _lws_rx_flow_control(wsi); + /* n ignored, needed for NO_SERVER case */ + } + + break; +#ifdef LWS_WITH_CGI + case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data... + * do the callback on our master wsi + */ + { + struct lws_cgi_args args; + + if (wsi->cgi_channel >= LWS_STDOUT && + !(pollfd->revents & pollfd->events & LWS_POLLIN)) + break; + if (wsi->cgi_channel == LWS_STDIN && + !(pollfd->revents & pollfd->events & LWS_POLLOUT)) + break; + + if (wsi->cgi_channel == LWS_STDIN) + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return 1; + } + + args.ch = wsi->cgi_channel; + args.stdwsi = &wsi->parent->cgi->stdwsi[0]; + args.hdr_state = wsi->hdr_state; + + lwsl_debug("CGI LWS_STDOUT %p mode %d state %d\n", + wsi->parent, wsi->parent->mode, + wsi->parent->state); + + if (user_callback_handle_rxflow( + wsi->parent->protocol->callback, + wsi->parent, LWS_CALLBACK_CGI, + wsi->parent->user_space, + (void *)&args, 0)) + return 1; + + break; + } +#endif + default: +#ifdef LWS_NO_CLIENT + break; +#else + if ((pollfd->revents & LWS_POLLOUT) && + lws_handle_POLLOUT_event(wsi, pollfd)) { + lwsl_debug("POLLOUT event closed it\n"); + goto close_and_handled; + } + + n = lws_client_socket_service(context, wsi, pollfd); + if (n) + return 1; + goto handled; +#endif + } + + n = 0; + goto handled; + +close_and_handled: + lwsl_debug("%p: Close and handled\n", wsi); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + /* + * pollfd may point to something else after the close + * due to pollfd swapping scheme on delete on some platforms + * we can't clear revents now because it'd be the wrong guy's revents + */ + return 1; + +handled: + pollfd->revents = 0; + return n; +} + +LWS_VISIBLE int +lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd) +{ + return lws_service_fd_tsi(context, pollfd, 0); +} + +LWS_VISIBLE int +lws_service(struct lws_context *context, int timeout_ms) +{ + return lws_plat_service(context, timeout_ms); +} + +LWS_VISIBLE int +lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi) +{ + return _lws_plat_service_tsi(context, timeout_ms, tsi); +} + diff --git a/thirdparty/lws/ssl.c b/thirdparty/lws/ssl.c new file mode 100644 index 0000000000..0a647b469c --- /dev/null +++ b/thirdparty/lws/ssl.c @@ -0,0 +1,976 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +/* workaround for mingw */ +#if !defined(ECONNABORTED) +#define ECONNABORTED 103 +#endif + +int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + lws_filepos_t len; + lws_fop_flags_t flags = LWS_O_RDONLY; + lws_fop_fd_t fops_fd = lws_vfs_file_open( + lws_get_fops(context), filename, &flags); + int ret = 1; + + if (!fops_fd) + return 1; + + len = lws_vfs_get_length(fops_fd); + + *buf = lws_malloc((size_t)len, "lws_alloc_vfs_file"); + if (!*buf) + goto bail; + + if (lws_vfs_file_read(fops_fd, amount, *buf, len)) + goto bail; + + ret = 0; +bail: + lws_vfs_file_close(&fops_fd); + + return ret; +} + +#if defined(LWS_WITH_MBEDTLS) +#if defined(LWS_WITH_ESP32) +int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + nvs_handle nvh; + size_t s; + int n = 0; + + ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); + if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) { + n = 1; + goto bail; + } + *buf = lws_malloc(s, "alloc_file"); + if (!*buf) { + n = 2; + goto bail; + } + if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) { + lws_free(*buf); + n = 1; + goto bail; + } + + *amount = s; + +bail: + nvs_close(nvh); + + return n; +} +#else +int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + FILE *f; + size_t s; + int n = 0; + + f = fopen(filename, "rb"); + if (f == NULL) { + n = 1; + goto bail; + } + + if (fseek(f, 0, SEEK_END) != 0) { + n = 1; + goto bail; + } + + s = ftell(f); + if (s == -1) { + n = 1; + goto bail; + } + + if (fseek(f, 0, SEEK_SET) != 0) { + n = 1; + goto bail; + } + + *buf = lws_malloc(s, "alloc_file"); + if (!*buf) { + n = 2; + goto bail; + } + + if (fread(*buf, s, 1, f) != 1) { + lws_free(*buf); + n = 1; + goto bail; + } + + *amount = s; + +bail: + if (f) + fclose(f); + + return n; + +} +#endif +int alloc_pem_to_der_file(struct lws_context *context, const char *filename, uint8_t **buf, + lws_filepos_t *amount) +{ + uint8_t *pem, *p, *q, *end; + lws_filepos_t len; + int n; + + n = alloc_file(context, filename, &pem, &len); + if (n) + return n; + + /* trim the first line */ + + p = pem; + end = p + len; + if (strncmp((char *)p, "-----", 5)) + goto bail; + p += 5; + while (p < end && *p != '\n' && *p != '-') + p++; + + if (*p != '-') + goto bail; + + while (p < end && *p != '\n') + p++; + + if (p >= end) + goto bail; + + p++; + + /* trim the last line */ + + q = end - 2; + + while (q > pem && *q != '\n') + q--; + + if (*q != '\n') + goto bail; + + *q = '\0'; + + *amount = lws_b64_decode_string((char *)p, (char *)pem, len); + *buf = pem; + + return 0; + +bail: + lws_free(pem); + + return 4; +} +#endif + +int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; + +int lws_ssl_get_error(struct lws *wsi, int n) +{ + int m; + + if (!wsi->ssl) + return 99; + + m = SSL_get_error(wsi->ssl, n); + lwsl_debug("%s: %p %d -> %d\n", __func__, wsi->ssl, n, m); + + return m; +} + +/* Copies a string describing the code returned by lws_ssl_get_error(), + * which may also contain system error information in the case of SSL_ERROR_SYSCALL, + * into buf up to len. + * Returns a pointer to buf. + * + * Note: the lws_ssl_get_error() code is *not* an error code that can be passed + * to ERR_error_string(), + * + * ret is the return value originally passed to lws_ssl_get_error(), needed to disambiguate + * SYS_ERROR_SYSCALL. + * + * See man page for SSL_get_error(). + * + * Not thread safe, uses strerror() + */ +char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) { + switch (status) { + case SSL_ERROR_NONE: return strncpy(buf, "SSL_ERROR_NONE", len); + case SSL_ERROR_ZERO_RETURN: return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len); + case SSL_ERROR_WANT_READ: return strncpy(buf, "SSL_ERROR_WANT_READ", len); + case SSL_ERROR_WANT_WRITE: return strncpy(buf, "SSL_ERROR_WANT_WRITE", len); + case SSL_ERROR_WANT_CONNECT: return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len); + case SSL_ERROR_WANT_ACCEPT: return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len); + case SSL_ERROR_WANT_X509_LOOKUP: return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len); + case SSL_ERROR_SYSCALL: + switch (ret) { + case 0: + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: EOF"); + return buf; + case -1: +#ifndef LWS_PLAT_OPTEE + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %s", strerror(errno)); +#else + lws_snprintf(buf, len, "SSL_ERROR_SYSCALL: %d", errno); +#endif + return buf; + default: + return strncpy(buf, "SSL_ERROR_SYSCALL", len); + } + case SSL_ERROR_SSL: return "SSL_ERROR_SSL"; + default: return "SSL_ERROR_UNKNOWN"; + } +} + +void +lws_ssl_elaborate_error(void) +{ +#if defined(LWS_WITH_MBEDTLS) +#else + char buf[256]; + u_long err; + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, buf, sizeof(buf)); + lwsl_info("*** %s\n", buf); + } +#endif +} + +#if !defined(LWS_WITH_MBEDTLS) + +static int +lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata) +{ + struct lws_context_creation_info * info = + (struct lws_context_creation_info *)userdata; + + strncpy(buf, info->ssl_private_key_password, size); + buf[size - 1] = '\0'; + + return strlen(buf); +} + +void +lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info) +{ + if (!info->ssl_private_key_password) + return; + /* + * password provided, set ssl callback and user data + * for checking password which will be trigered during + * SSL_CTX_use_PrivateKey_file function + */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info); + SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb); +} +#endif + +int +lws_context_init_ssl_library(struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_info(" Compiled with CyaSSL support\n"); +#else + lwsl_info(" Compiled with wolfSSL support\n"); +#endif +#else +#if defined(LWS_WITH_BORINGSSL) + lwsl_info(" Compiled with BoringSSL support\n"); +#else +#if defined(LWS_WITH_MBEDTLS) + lwsl_info(" Compiled with MbedTLS support\n"); +#else + lwsl_info(" Compiled with OpenSSL support\n"); +#endif +#endif +#endif + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* basic openssl init */ + + lwsl_info("Doing SSL library init\n"); + +#if !defined(LWS_WITH_MBEDTLS) + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); +#endif + + return 0; +} + +LWS_VISIBLE void +lws_ssl_destroy(struct lws_vhost *vhost) +{ + if (!lws_check_opt(vhost->context->options, + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + return; + + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); + +#if defined(LWS_WITH_MBEDTLS) + if (vhost->x509_client_CA) + X509_free(vhost->x509_client_CA); +#else +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + // after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +} + +int +lws_ssl_anybody_has_buffered_read_tsi(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws *wsi, *wsi_next; + + wsi = pt->pending_read_list; + while (wsi) { + wsi_next = wsi->pending_read_list_next; + pt->fds[wsi->position_in_fds_table].revents |= + pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN; + if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) + return 1; + + wsi = wsi_next; + } + + return 0; +} + +LWS_VISIBLE void +lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + + if (!wsi->pending_read_list_prev && + !wsi->pending_read_list_next && + pt->pending_read_list != wsi) + /* we are not on the list */ + return; + + /* point previous guy's next to our next */ + if (!wsi->pending_read_list_prev) + pt->pending_read_list = wsi->pending_read_list_next; + else + wsi->pending_read_list_prev->pending_read_list_next = + wsi->pending_read_list_next; + + /* point next guy's previous to our previous */ + if (wsi->pending_read_list_next) + wsi->pending_read_list_next->pending_read_list_prev = + wsi->pending_read_list_prev; + + wsi->pending_read_list_prev = NULL; + wsi->pending_read_list_next = NULL; +} + +LWS_VISIBLE int +lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n = 0, m; + + if (!wsi->ssl) + return lws_ssl_capable_read_no_ssl(wsi, buf, len); + + lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1); + + errno = 0; + n = SSL_read(wsi->ssl, buf, len); +#if defined(LWS_WITH_ESP32) + if (!n && errno == ENOTCONN) { + lwsl_debug("%p: SSL_read ENOTCONN\n", wsi); + return LWS_SSL_CAPABLE_ERROR; + } +#endif +#if defined(LWS_WITH_STATS) + if (!wsi->seen_rx) { + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_MS_SSL_RX_DELAY, + time_in_microseconds() - wsi->accept_start_us); + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNS_HAD_RX, 1); + wsi->seen_rx = 1; + } +#endif + + + lwsl_debug("%p: SSL_read says %d\n", wsi, n); + /* manpage: returning 0 means connection shut down */ + if (!n) { + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; + } + + if (n < 0) { + m = lws_ssl_get_error(wsi, n); + lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno); + if (m == SSL_ERROR_ZERO_RETURN || + m == SSL_ERROR_SYSCALL) + return LWS_SSL_CAPABLE_ERROR; + + if (SSL_want_read(wsi->ssl)) { + lwsl_debug("%s: WANT_READ\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + if (SSL_want_write(wsi->ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; + } + + lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); + + if (wsi->vhost) + wsi->vhost->conn_stats.rx += n; + + lws_restart_ws_ping_pong_timer(wsi); + + /* + * if it was our buffer that limited what we read, + * check if SSL has additional data pending inside SSL buffers. + * + * Because these won't signal at the network layer with POLLIN + * and if we don't realize, this data will sit there forever + */ + if (n != len) + goto bail; + if (!wsi->ssl) + goto bail; + + if (!SSL_pending(wsi->ssl)) + goto bail; + + if (wsi->pending_read_list_next) + return n; + if (wsi->pending_read_list_prev) + return n; + if (pt->pending_read_list == wsi) + return n; + + /* add us to the linked list of guys with pending ssl */ + if (pt->pending_read_list) + pt->pending_read_list->pending_read_list_prev = wsi; + + wsi->pending_read_list_next = pt->pending_read_list; + wsi->pending_read_list_prev = NULL; + pt->pending_read_list = wsi; + + return n; +bail: + lws_ssl_remove_wsi_from_buffered_list(wsi); + + return n; +} + +LWS_VISIBLE int +lws_ssl_pending(struct lws *wsi) +{ + if (!wsi->ssl) + return 0; + + return SSL_pending(wsi->ssl); +} + +LWS_VISIBLE int +lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len) +{ + int n, m; + + if (!wsi->ssl) + return lws_ssl_capable_write_no_ssl(wsi, buf, len); + + n = SSL_write(wsi->ssl, buf, len); + if (n > 0) + return n; + + m = lws_ssl_get_error(wsi, n); + if (m != SSL_ERROR_SYSCALL) { + + if (SSL_want_read(wsi->ssl)) { + lwsl_notice("%s: want read\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + if (SSL_want_write(wsi->ssl)) { + lws_set_blocking_send(wsi); + + lwsl_notice("%s: want write\n", __func__); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + } + + lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL)); + lws_ssl_elaborate_error(); + + wsi->socket_is_permanently_unusable = 1; + + return LWS_SSL_CAPABLE_ERROR; +} + +static int +lws_gate_accepts(struct lws_context *context, int on) +{ + struct lws_vhost *v = context->vhost_list; + + lwsl_info("gating accepts %d\n", on); + context->ssl_gate_accepts = !on; +#if defined(LWS_WITH_STATS) + context->updated = 1; +#endif + + while (v) { + if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */ + if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, + (LWS_POLLIN) * on)) + lwsl_info("Unable to set accept POLLIN %d\n", on); + + v = v->vhost_next; + } + + return 0; +} + +void +lws_ssl_info_callback(const SSL *ssl, int where, int ret) +{ + struct lws *wsi; + struct lws_context *context; + struct lws_ssl_info si; + + context = (struct lws_context *)SSL_CTX_get_ex_data( + SSL_get_SSL_CTX(ssl), + openssl_SSL_CTX_private_data_index); + if (!context) + return; + wsi = wsi_from_fd(context, SSL_get_fd(ssl)); + if (!wsi) + return; + + if (!(where & wsi->vhost->ssl_info_event_mask)) + return; + + si.where = where; + si.ret = ret; + + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_SSL_INFO, + wsi->user_space, &si, 0)) + lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1); +} + + +LWS_VISIBLE int +lws_ssl_close(struct lws *wsi) +{ + lws_sockfd_type n; + + if (!wsi->ssl) + return 0; /* not handled */ + +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + /* kill ssl callbacks, becausse we will remove the fd from the + * table linking it to the wsi + */ + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, NULL); +#endif + + n = SSL_get_fd(wsi->ssl); + if (!wsi->socket_is_permanently_unusable) + SSL_shutdown(wsi->ssl); + compatible_close(n); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + + if (wsi->context->simultaneous_ssl_restriction && + wsi->context->simultaneous_ssl-- == + wsi->context->simultaneous_ssl_restriction) + /* we made space and can do an accept */ + lws_gate_accepts(wsi->context, 1); +#if defined(LWS_WITH_STATS) + wsi->context->updated = 1; +#endif + + return 1; /* handled */ +} + +/* leave all wsi close processing to the caller */ + +LWS_VISIBLE int +lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) +{ + struct lws_context *context = wsi->context; + struct lws_vhost *vh; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + int n, m; +#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) + BIO *bio; +#endif + char buf[256]; + + (void)buf; + + if (!LWS_SSL_ENABLED(wsi->vhost)) + return 0; + + switch (wsi->mode) { + case LWSCM_SSL_INIT: + case LWSCM_SSL_INIT_RAW: + if (wsi->ssl) + lwsl_err("%s: leaking ssl\n", __func__); + if (accept_fd == LWS_SOCK_INVALID) + assert(0); + if (context->simultaneous_ssl_restriction && + context->simultaneous_ssl >= context->simultaneous_ssl_restriction) { + lwsl_notice("unable to deal with SSL connection\n"); + return 1; + } + errno = 0; + wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); + if (wsi->ssl == NULL) { + lwsl_err("SSL_new failed: %d (errno %d)\n", + lws_ssl_get_error(wsi, 0), errno); + + lws_ssl_elaborate_error(); + if (accept_fd != LWS_SOCK_INVALID) + compatible_close(accept_fd); + goto fail; + } +#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) + if (wsi->vhost->ssl_info_event_mask) + SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); +#endif + if (context->simultaneous_ssl_restriction && + ++context->simultaneous_ssl == context->simultaneous_ssl_restriction) + /* that was the last allowed SSL connection */ + lws_gate_accepts(context, 0); +#if defined(LWS_WITH_STATS) + context->updated = 1; +#endif + +#if !defined(LWS_WITH_MBEDTLS) + SSL_set_ex_data(wsi->ssl, + openssl_websocket_private_data_index, wsi); +#endif + SSL_set_fd(wsi->ssl, accept_fd); + +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + CyaSSL_set_using_nonblock(wsi->ssl, 1); +#else + wolfSSL_set_using_nonblock(wsi->ssl, 1); +#endif +#else +#if defined(LWS_WITH_MBEDTLS) + lws_plat_set_socket_options(wsi->vhost, accept_fd); +#else + SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + bio = SSL_get_rbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); + bio = SSL_get_wbio(wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); +#endif +#endif + + /* + * we are not accepted yet, but we need to enter ourselves + * as a live connection. That way we can retry when more + * pieces come if we're not sorted yet + */ + + if (wsi->mode == LWSCM_SSL_INIT) + wsi->mode = LWSCM_SSL_ACK_PENDING; + else + wsi->mode = LWSCM_SSL_ACK_PENDING_RAW; + + if (insert_wsi_socket_into_fds(context, wsi)) { + lwsl_err("%s: failed to insert into fds\n", __func__); + goto fail; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, + context->timeout_secs); + + lwsl_debug("inserted SSL accept into fds, trying SSL_accept\n"); + + /* fallthru */ + + case LWSCM_SSL_ACK_PENDING: + case LWSCM_SSL_ACK_PENDING_RAW: + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_err("%s: lws_change_pollfd failed\n", __func__); + goto fail; + } + + lws_latency_pre(context, wsi); + + if (wsi->vhost->allow_non_ssl_on_ssl_port) { + + n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, + context->pt_serv_buf_size, MSG_PEEK); + + /* + * optionally allow non-SSL connect on SSL listening socket + * This is disabled by default, if enabled it goes around any + * SSL-level access control (eg, client-side certs) so leave + * it disabled unless you know it's not a problem for you + */ + + if (n >= 1 && pt->serv_buf[0] >= ' ') { + /* + * TLS content-type for Handshake is 0x16, and + * for ChangeCipherSpec Record, it's 0x14 + * + * A non-ssl session will start with the HTTP + * method in ASCII. If we see it's not a legit + * SSL handshake kill the SSL for this + * connection and try to handle as a HTTP + * connection upgrade directly. + */ + wsi->use_ssl = 0; + + SSL_shutdown(wsi->ssl); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) + wsi->redirect_to_https = 1; + goto accepted; + } + if (!n) /* + * connection is gone, or nothing to read + * if it's gone, we will timeout on + * PENDING_TIMEOUT_SSL_ACCEPT + */ + break; + if (n < 0 && (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK)) { + /* + * well, we get no way to know ssl or not + * so go around again waiting for something + * to come and give us a hint, or timeout the + * connection. + */ + m = SSL_ERROR_WANT_READ; + goto go_again; + } + } + + /* normal SSL connection processing path */ + +#if defined(LWS_WITH_STATS) + if (!wsi->accept_start_us) + wsi->accept_start_us = time_in_microseconds(); +#endif + errno = 0; + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1); + n = SSL_accept(wsi->ssl); + lws_latency(context, wsi, + "SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1); + lwsl_info("SSL_accept says %d\n", n); + if (n == 1) + goto accepted; + + m = lws_ssl_get_error(wsi, n); + +#if defined(LWS_WITH_MBEDTLS) + if (m == SSL_ERROR_SYSCALL && errno == 11) + m = SSL_ERROR_WANT_READ; +#endif + if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) + goto failed; + +go_again: + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__); + goto fail; + } + + lwsl_info("SSL_ERROR_WANT_READ\n"); + break; + } + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) { + lwsl_debug("%s: WANT_WRITE\n", __func__); + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__); + goto fail; + } + + break; + } +failed: + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1); + lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd, + lws_ssl_get_error_string(m, n, buf, sizeof(buf))); + lws_ssl_elaborate_error(); + goto fail; + +accepted: + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1); +#if defined(LWS_WITH_STATS) + lws_stats_atomic_bump(wsi->context, pt, + LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, + time_in_microseconds() - wsi->accept_start_us); + wsi->accept_start_us = time_in_microseconds(); +#endif + + /* adapt our vhost to match the SNI SSL_CTX that was chosen */ + vh = context->vhost_list; + while (vh) { + if (!vh->being_destroyed && + vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) { + lwsl_info("setting wsi to vh %s\n", vh->name); + wsi->vhost = vh; + break; + } + vh = vh->vhost_next; + } + + /* OK, we are accepted... give him some time to negotiate */ + lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + context->timeout_secs); + + if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW) + wsi->mode = LWSCM_RAW; + else + wsi->mode = LWSCM_HTTP_SERVING; +#if defined(LWS_WITH_HTTP2) + if (lws_h2_configure_if_upgraded(wsi)) + goto fail; +#endif + lwsl_debug("accepted new SSL conn\n"); + break; + } + + return 0; + +fail: + return 1; +} + +void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) +{ + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); +} + +void +lws_ssl_context_destroy(struct lws_context *context) +{ + +#if !defined(LWS_WITH_MBEDTLS) + +// after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER < 0x10100000) +// <= 1.0.1f = old api, 1.0.1g+ = new api +#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL) + ERR_remove_state(0); +#else +#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + ERR_remove_thread_state(); +#else + ERR_remove_thread_state(NULL); +#endif +#endif + // after 1.1.0 no need +#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000) + SSL_COMP_free_compression_methods(); +#endif + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif +#endif +} diff --git a/thirdparty/lws/win32helpers/getopt.c b/thirdparty/lws/win32helpers/getopt.c new file mode 100644 index 0000000000..3bb21f6f28 --- /dev/null +++ b/thirdparty/lws/win32helpers/getopt.c @@ -0,0 +1,153 @@ +/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#define __P(x) x +#define _DIAGASSERT(x) assert(x) + +#ifdef __weak_alias +__weak_alias(getopt,_getopt); +#endif + + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +static char * _progname __P((char *)); +int getopt_internal __P((int, char * const *, const char *)); + +static char * +_progname(nargv0) + char * nargv0; +{ + char * tmp; + + _DIAGASSERT(nargv0 != NULL); + + tmp = strrchr(nargv0, '/'); + if (tmp) + tmp++; + else + tmp = nargv0; + return(tmp); +} + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const nargv[]; + const char *ostr; +{ + static char *__progname = 0; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + __progname = __progname?__progname:_progname(*nargv); + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(ostr != NULL); + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-' /* found "--" */ + && place[1] == '\0') { + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + diff --git a/thirdparty/lws/win32helpers/getopt.h b/thirdparty/lws/win32helpers/getopt.h new file mode 100644 index 0000000000..7137f0379c --- /dev/null +++ b/thirdparty/lws/win32helpers/getopt.h @@ -0,0 +1,33 @@ +#ifndef __GETOPT_H__ +#define __GETOPT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern int optopt; /* character checked for validity */ +extern int optreset; /* reset getopt */ +extern char *optarg; /* argument associated with option */ + +struct option +{ + const char *name; + int has_arg; + int *flag; + int val; +}; + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +int getopt(int, char**, char*); +int getopt_long(int, char**, char*, struct option*, int*); + +#ifdef __cplusplus +} +#endif + +#endif /* __GETOPT_H__ */ diff --git a/thirdparty/lws/win32helpers/getopt_long.c b/thirdparty/lws/win32helpers/getopt_long.c new file mode 100644 index 0000000000..5bcf40060f --- /dev/null +++ b/thirdparty/lws/win32helpers/getopt_long.c @@ -0,0 +1,237 @@ + +/* + * Copyright (c) 1987, 1993, 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "getopt.h" + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern int optopt; /* character checked for validity */ +extern int optreset; /* reset getopt */ +extern char *optarg; /* argument associated with option */ + +#define __P(x) x +#define _DIAGASSERT(x) assert(x) + +static char * __progname __P((char *)); +int getopt_internal __P((int, char * const *, const char *)); + +static char * +__progname(nargv0) + char * nargv0; +{ + char * tmp; + + _DIAGASSERT(nargv0 != NULL); + + tmp = strrchr(nargv0, '/'); + if (tmp) + tmp++; + else + tmp = nargv0; + return(tmp); +} + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt_internal(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(ostr != NULL); + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + /* ++optind; */ + place = EMSG; + return (-2); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname(nargv[0]), optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if ((opterr) && (*ostr != ':')) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname(nargv[0]), optopt); + return (BADARG); + } else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + +#if 0 +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt2(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + int retval; + + if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) { + retval = -1; + ++optind; + } + return(retval); +} +#endif + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(nargc, nargv, options, long_options, index) + int nargc; + char ** nargv; + char * options; + struct option * long_options; + int * index; +{ + int retval; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + _DIAGASSERT(long_options != NULL); + /* index may be NULL */ + + if ((retval = getopt_internal(nargc, nargv, options)) == -2) { + char *current_argv = nargv[optind++] + 2, *has_equal; + int i, current_argv_len, match = -1; + + if (*current_argv == '\0') { + return(-1); + } + if ((has_equal = strchr(current_argv, '=')) != NULL) { + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + if (strncmp(current_argv, long_options[i].name, current_argv_len)) + continue; + + if (strlen(long_options[i].name) == (unsigned)current_argv_len) { + match = i; + break; + } + if (match == -1) + match = i; + } + if (match != -1) { + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else + optarg = nargv[optind++]; + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument, leading : + * indicates no error should be generated + */ + if ((opterr) && (*options != ':')) + (void)fprintf(stderr, + "%s: option requires an argument -- %s\n", + __progname(nargv[0]), current_argv); + return (BADARG); + } + } else { /* No matching argument */ + if ((opterr) && (*options != ':')) + (void)fprintf(stderr, + "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv); + return (BADCH); + } + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + retval = 0; + } else + retval = long_options[match].val; + if (index) + *index = match; + } + return(retval); +} diff --git a/thirdparty/lws/win32helpers/gettimeofday.c b/thirdparty/lws/win32helpers/gettimeofday.c new file mode 100644 index 0000000000..35dd73531d --- /dev/null +++ b/thirdparty/lws/win32helpers/gettimeofday.c @@ -0,0 +1,36 @@ +#include <time.h> +#include <windows.h> //I've omitted context line + +#include "gettimeofday.h" + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} diff --git a/thirdparty/lws/win32helpers/gettimeofday.h b/thirdparty/lws/win32helpers/gettimeofday.h new file mode 100644 index 0000000000..33e7a750fe --- /dev/null +++ b/thirdparty/lws/win32helpers/gettimeofday.h @@ -0,0 +1,27 @@ +#ifndef _GET_TIME_OF_DAY_H +#define _GET_TIME_OF_DAY_H + +#include <time.h> + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +#ifdef LWS_MINGW_SUPPORT + #include <winsock2.h> +#endif + +#ifndef _TIMEZONE_DEFINED +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +#endif + +int gettimeofday(struct timeval *tv, struct timezone *tz); + +#endif diff --git a/version.py b/version.py index 82a60f3bce..0eff47acdc 100644 --- a/version.py +++ b/version.py @@ -1,6 +1,6 @@ short_name = "godot" name = "Godot Engine" major = 3 -minor = 0 -status = "rc2" +minor = 1 +status = "dev" module_config = "" |