diff --git a/Sming/Services/WebHelpers/escape.cpp b/Sming/Services/WebHelpers/escape.cpp index 0bdfeb87c7..05371dcfb8 100644 --- a/Sming/Services/WebHelpers/escape.cpp +++ b/Sming/Services/WebHelpers/escape.cpp @@ -101,7 +101,8 @@ char *uri_escape(char *dest, size_t dest_len, const char *src, int src_len) /* escape these values ~!#$%^&(){}[]=:,;?'"\ * make sure there is room in dest for a '\0' */ for(;src_len>0 && dest_len>1;src++,src_len--) { - if(must_escape(*src)) { + char c = *src; + if(must_escape(c)) { /* check that there is room for "%XX\0" in dest */ if(dest_len<=3) { if(ret_is_allocated) @@ -113,6 +114,9 @@ char *uri_escape(char *dest, size_t dest_len, const char *src, int src_len) dest[2] = hexchar(*src & 0x0f); dest+=3; dest_len-=3; + } else if (c == ' ') { + *dest++ = '+'; + dest_len--; } else { *(dest++)=*src; dest_len--; @@ -232,23 +236,34 @@ void html_escape(char *dest, size_t len, const char *s) { String uri_escape(const char *src, int src_len) { - char* p = uri_escape(nullptr, 0, src, src_len); - String s(p); - if (p) - free(p); + String s; + if (src && src_len) { + unsigned dst_len = uri_escape_len(src, src_len); + if (s.setLength(dst_len)) + uri_escape(s.begin(), s.length() + 1, src, src_len); // +1 for nul terminator + } return s; } -int uri_unescape_inplace(String& s) +char* uri_unescape_inplace(char *str) { - // If string is invalid, ensure result remains invalid - if (!s) - return 0; + if (str) { + auto len = strlen(str); + uri_unescape(str, len + 1, str, len); // +1 for nul terminator + } + return str; +} - char* p = s.begin(); - uri_unescape(p, s.length(), p, s.length()); - s.setLength(strlen(p)); - return s.length(); +String& uri_unescape_inplace(String& str) +{ + if (str) { + char* p = str.begin(); + uri_unescape(p, str.length() + 1, p, str.length()); // +1 for nul terminator + auto len = strlen(p); + assert(len <= str.length()); + str.setLength(len); + } + return str; } diff --git a/Sming/Services/WebHelpers/escape.h b/Sming/Services/WebHelpers/escape.h index 782a33980f..7025832a8f 100644 --- a/Sming/Services/WebHelpers/escape.h +++ b/Sming/Services/WebHelpers/escape.h @@ -4,12 +4,50 @@ #include #include "WString.h" +/** @brief Obtain number of characters required to escape the given text + * @param s source text + * @param len Number of characters in source text + * @retval unsigned number of characters for output, NOT including nul terminator + */ unsigned uri_escape_len(const char *s, size_t len); + +static inline unsigned uri_escape_len(const String& str) +{ + return uri_escape_len(str.c_str(), str.length()); +} + +/** @brief Escape text + * @param dest buffer to store result + * @param dest_len available space in dest; requires +1 extra character for nul terminator, + * so at least uri_escape_len() + 1 + * @param src source text to escape + * @param src_len number of characters in source + * @retval char* points to start of dest + * @note destination and source MUST be different buffers + */ char *uri_escape(char *dest, size_t dest_len, const char *src, int src_len); + +/** @brief unescape text + * @param dest buffer to store result + * @param dest_len available space in dest; requires +1 extra character for nul terminator + * @param src source text to un-escape + * @param src_len number of characters in source + * @retval char* points to start of dest + * @note destination and source may be the same buffer + */ char *uri_unescape(char *dest, size_t dest_len, const char *src, int src_len); + unsigned html_escape_len(const char *s, size_t len); void html_escape(char *dest, size_t len, const char *s); + +/** @brief Replace a nul-terminated string with its unescaped version + * @param str the string to un-escape + * @retval char* the result, a copy of str + * @note unescaped string is never longer than escaped version + */ +char* uri_unescape_inplace(char *str); + /** @brief escape the given URI string * @param src * @param src_len @@ -24,18 +62,19 @@ static inline String uri_escape(const String& src) } -/** @brief replace the given uri by its unescaped version - * @retval int length of result +/** @brief replace the given text by its unescaped version + * @param str the string to unescape + * @retval reference to str, unescaped * @note unescaped string is never longer than escaped version */ -int uri_unescape_inplace(String& s); +String& uri_unescape_inplace(String& str); /** @brief return the unescaped version of a string * @retval String unescaped string */ -static inline String uri_unescape(const String& s) +static inline String uri_unescape(const String& str) { - String ret = s; + String ret = str; uri_unescape_inplace(ret); return ret; } diff --git a/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.cpp b/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.cpp index 07ddc5b282..c39fc9ae23 100644 --- a/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.cpp +++ b/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.cpp @@ -13,16 +13,10 @@ /* * @todo Revise this so stream produces encoded output line-by-line, rather than all at once. + * Can use StreamTransformer to do this. */ UrlencodedOutputStream::UrlencodedOutputStream(const HttpParams& params) { - for(unsigned i = 0; i < params.count(); i++) { - if(i > 0) - stream.write('&'); - - stream.print(uri_escape(params.keyAt(i))); - stream.print('='); - stream.print(uri_escape(params.valueAt(i))); - } + stream.print(params); } diff --git a/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.h b/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.h index 993ca45723..8699d9576b 100644 --- a/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.h +++ b/Sming/SmingCore/Data/Stream/UrlencodedOutputStream.h @@ -12,16 +12,14 @@ #define _SMING_CORE_DATA_URL_ENCODDED_OUTPUT_STREAM_H_ #include "MemoryDataStream.h" -#include "WHashMap.h" +#include "Network/Http/HttpParams.h" /** * @brief UrlEncoded Stream * @ingroup stream data * * @{ -*/ - -typedef HashMap HttpParams; + */ class UrlencodedOutputStream : public ReadWriteStream { diff --git a/Sming/SmingCore/Data/Structures.h b/Sming/SmingCore/Data/Structures.h index 6883e13f21..4e46ad1381 100644 --- a/Sming/SmingCore/Data/Structures.h +++ b/Sming/SmingCore/Data/Structures.h @@ -47,6 +47,4 @@ template class SimpleConcurrentQueue : public FIFO HttpParams; - #endif /* _SMING_CORE_DATA_STRUCTURES_H_ */ diff --git a/Sming/SmingCore/Network/Http/HttpParams.cpp b/Sming/SmingCore/Network/Http/HttpParams.cpp new file mode 100644 index 0000000000..e3d1e37fcc --- /dev/null +++ b/Sming/SmingCore/Network/Http/HttpParams.cpp @@ -0,0 +1,27 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * @author: 2018 - Mikee47 + * + ****/ + +#include "HttpParams.h" +#include "../Services/WebHelpers/escape.h" +#include "Print.h" + +size_t HttpParams::printTo(Print& p) const +{ + size_t charsPrinted = 0; + for(unsigned i = 0; i < count(); i++) { + if(i > 0) + charsPrinted += p.print('&'); + charsPrinted += p.print(uri_escape(keyAt(i))); + charsPrinted += p.print('='); + charsPrinted += p.print(uri_escape(valueAt(i))); + } + + return charsPrinted; +} diff --git a/Sming/SmingCore/Network/Http/HttpParams.h b/Sming/SmingCore/Network/Http/HttpParams.h new file mode 100644 index 0000000000..e76918eabd --- /dev/null +++ b/Sming/SmingCore/Network/Http/HttpParams.h @@ -0,0 +1,37 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * @author: 2018 - Mikee47 + * + * Class to manage HTTP URI query parameters + * + * The HttpParams class was an empty HashMap class living in 'Structures.h'. + * It has been expanded to incorporate escaping and unescaping. + * Custom URL parsing code has been replaced with the yuarel library https://github.com/jacketizer/libyuarel + * + ****/ + +#ifndef _SMINGCORE_HTTP_HTTP_PARAMS_H_ +#define _SMINGCORE_HTTP_HTTP_PARAMS_H_ + +#include "WString.h" +#include "WHashMap.h" +#include "Printable.h" + +/** @brief + * + * @todo values stored in escaped form, unescape return value and escape provided values. + * Revise HttpBodyParser.cpp as it will no longer do this job. + * + */ +class HttpParams : public HashMap, public Printable +{ +public: + // Printable + virtual size_t printTo(Print& p) const; +}; + +#endif // _SMINGCORE_HTTP_HTTP_PARAMS_H_ diff --git a/Sming/SmingCore/Network/Http/HttpRequest.h b/Sming/SmingCore/Network/Http/HttpRequest.h index 4b481a42c3..24a76b8476 100644 --- a/Sming/SmingCore/Network/Http/HttpRequest.h +++ b/Sming/SmingCore/Network/Http/HttpRequest.h @@ -21,6 +21,7 @@ #include "Data/Stream/DataSourceStream.h" #include "Data/Stream/MultipartStream.h" #include "Network/Http/HttpHeaders.h" +#include "HttpParams.h" class HttpClient; class HttpServerConnection;