diff --git a/include/eepp/audio/music.hpp b/include/eepp/audio/music.hpp index 584f39538..e3970aacd 100644 --- a/include/eepp/audio/music.hpp +++ b/include/eepp/audio/music.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -239,7 +239,7 @@ class EE_API Music : public SoundStream std::vector mSamples; ///< Temporary buffer of samples Mutex mMutex; ///< Mutex protecting the data Span mLoopSpan; ///< Loop Range Specifier - SafeDataPointer mData; + ScopedBuffer mData; }; }} diff --git a/include/eepp/graphics/fonttruetype.hpp b/include/eepp/graphics/fonttruetype.hpp index 471c201c0..4d7690740 100644 --- a/include/eepp/graphics/fonttruetype.hpp +++ b/include/eepp/graphics/fonttruetype.hpp @@ -86,7 +86,7 @@ class EE_API FontTrueType : public Font { void* mStreamRec; ///< Pointer to the stream rec instance (it is typeless to avoid exposing implementation details) void* mStroker; ///< Pointer to the stroker (it is typeless to avoid exposing implementation details) int* mRefCount; ///< Reference counter used by implicit sharing - SafeDataPointer mMemCopy; + mutable ScopedBuffer mMemCopy; ///< If loaded from memory, this is the file copy in memory Font::Info mInfo; ///< Information about the font mutable PageTable mPages; ///< Table containing the glyphs pages by character size mutable std::vector mPixelBuffer; ///< Pixel buffer holding a glyph's pixels before being written to the texture diff --git a/include/eepp/math/base.hpp b/include/eepp/math/base.hpp deleted file mode 100644 index e734cfde3..000000000 --- a/include/eepp/math/base.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef EE_MATH_BASE -#define EE_MATH_BASE - -#include -#include - -#endif diff --git a/include/eepp/math/interpolation1d.hpp b/include/eepp/math/interpolation1d.hpp index 41d90a36b..fa5e7e9b4 100644 --- a/include/eepp/math/interpolation1d.hpp +++ b/include/eepp/math/interpolation1d.hpp @@ -1,7 +1,8 @@ #ifndef EE_MATHCINTERPOLATION_H #define EE_MATHCINTERPOLATION_H -#include +#include +#include #include #include diff --git a/include/eepp/math/interpolation2d.hpp b/include/eepp/math/interpolation2d.hpp index bb4034aa2..f3b34286f 100755 --- a/include/eepp/math/interpolation2d.hpp +++ b/include/eepp/math/interpolation2d.hpp @@ -1,7 +1,8 @@ #ifndef EE_MATHCWAYPOINTS_H #define EE_MATHCWAYPOINTS_H -#include +#include +#include #include #include #include diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index ac834fd9f..9ffa6d7f9 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -26,6 +26,121 @@ namespace EE { namespace Network { /** @brief A HTTP client */ class EE_API Http : NonCopyable { public : + /** @brief Define a HTTP response */ + class EE_API Response { + public: + + // Types + typedef std::map FieldTable; + + /** @brief Enumerate all the valid status codes for a response */ + enum Status { + // 2xx: success + Ok = 200, ///< Most common code returned when operation was successful + Created = 201, ///< The resource has successfully been created + Accepted = 202, ///< The request has been accepted, but will be processed later by the server + NoContent = 204, ///< The server didn't send any data in return + ResetContent = 205, ///< The server informs the client that it should clear the view (form) that caused the request to be sent + PartialContent = 206, ///< The server has sent a part of the resource, as a response to a partial GET request + + // 3xx: redirection + MultipleChoices = 300, ///< The requested page can be accessed from several locations + MovedPermanently = 301, ///< The requested page has permanently moved to a new location + MovedTemporarily = 302, ///< The requested page has temporarily moved to a new location + NotModified = 304, ///< For conditionnal requests, means the requested page hasn't changed and doesn't need to be refreshed + + // 4xx: client error + BadRequest = 400, ///< The server couldn't understand the request (syntax error) + Unauthorized = 401, ///< The requested page needs an authentification to be accessed + Forbidden = 403, ///< The requested page cannot be accessed at all, even with authentification + NotFound = 404, ///< The requested page doesn't exist + RangeNotSatisfiable = 407, ///< The server can't satisfy the partial GET request (with a "Range" header field) + + // 5xx: server error + InternalServerError = 500, ///< The server encountered an unexpected error + NotImplemented = 501, ///< The server doesn't implement a requested feature + BadGateway = 502, ///< The gateway server has received an error from the source server + ServiceNotAvailable = 503, ///< The server is temporarily unavailable (overloaded, in maintenance, ...) + GatewayTimeout = 504, ///< The gateway server couldn't receive a response from the source server + VersionNotSupported = 505, ///< The server doesn't support the requested HTTP version + + // 10xx: Custom codes + InvalidResponse = 1000, ///< Response is not a valid HTTP one + ConnectionFailed = 1001 ///< Connection with server failed + }; + + /** @return The status string */ + static const char * statusToString( const Status& status ); + + /** @brief Default constructor + ** Constructs an empty response. */ + Response(); + + FieldTable getHeaders(); + + /** @brief Get the value of a field + ** If the field @a field is not found in the response header, + ** the empty string is returned. This function uses + ** case-insensitive comparisons. + ** @param field Name of the field to get + ** @return Value of the field, or empty string if not found */ + const std::string& getField(const std::string& field) const; + + /** @return If the field is found in the response headers. */ + bool hasField(const std::string& field) const; + + /** @brief Get the response status code + ** The status code should be the first thing to be checked + ** after receiving a response, it defines whether it is a + ** success, a failure or anything else (see the Status + ** enumeration). + ** @return Status code of the response */ + Status getStatus() const; + + /** @brief Get the response status description */ + const char * getStatusDescription() const; + + /** @brief Get the major HTTP version number of the response + ** @return Major HTTP version number + ** @see GetMinorHttpVersion */ + unsigned int getMajorHttpVersion() const; + + /** @brief Get the minor HTTP version number of the response + ** @return Minor HTTP version number + ** @see GetMajorHttpVersion */ + unsigned int getMinorHttpVersion() const; + + /** @brief Get the body of the response + ** The body of a response may contain: + ** @li the requested page (for GET requests) + ** @li a response from the server (for POST requests) + ** @li nothing (for HEAD requests) + ** @li an error message (in case of an error) + ** @return The response body */ + const std::string& getBody() const; + private : + friend class Http; + + /** @brief Construct the header from a response string + ** This function is used by Http to build the response + ** of a request. + ** @param data Content of the response to parse */ + void parse(const std::string& data); + + /** @brief Read values passed in the answer header + ** This function is used by Http to extract values passed + ** in the response. + ** @param in String stream containing the header values */ + void parseFields(std::istream &in); + + // Member data + FieldTable mFields; ///< Fields of the header + Status mStatus; ///< Status code + unsigned int mMajorVersion; ///< Major HTTP version + unsigned int mMinorVersion; ///< Minor HTTP version + std::string mBody; ///< Body of the response + }; + /** @brief Define a HTTP request */ class EE_API Request { public : @@ -37,9 +152,24 @@ class EE_API Http : NonCopyable { Put, ///< The PUT method replaces all current representations of the target resource with the request payload. Delete, ///< The DELETE method deletes the specified resource. Options, ///< The OPTIONS method is used to describe the communication options for the target resource. - Patch ///< The PATCH method is used to apply partial modifications to a resource. + Patch, ///< The PATCH method is used to apply partial modifications to a resource. + Connect ///< The CONNECT method starts two-way communications with the requested resource. It can be used to open a tunnel. }; + /** @brief Enumerate the available states for a request */ + enum Status { + Connected, ///< Connected to server. + Sent, ///< Request sent to the server. + HeaderReceived, ///< Header received. + ContentReceived ///< Content received. + }; + + /** @return Method from a method name string. */ + static Method methodFromString( std::string methodString ); + + /** @return The method string from a method */ + static std::string methodToString( const Method& method ); + /** @brief Default constructor ** This constructor creates a GET request, with the root ** URI ("/") and an empty body. @@ -49,8 +179,9 @@ class EE_API Http : NonCopyable { ** @param validateCertificate Enables certificate validation for https request ** @param validateHostname Enables hostname validation for https request ** @param followRedirect Allow follor redirects to the request. + ** @param compressedResponse Set if the requested response should be compressed ( if available ) */ - Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true); + Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true, bool compressedResponse = false); /** @brief Set the value of a field ** The field is created if it doesn't exist. The name of @@ -62,6 +193,20 @@ class EE_API Http : NonCopyable { ** @param value Value of the field */ void setField(const std::string& field, const std::string& value); + /** @brief Check if the request defines a field + ** This function uses case-insensitive comparisons. + ** @param field Name of the field to test + ** @return True if the field exists, false otherwise */ + bool hasField(const std::string& field) const; + + /** @brief Get the value of a field + ** If the field @a field is not found in the response header, + ** the empty string is returned. This function uses + ** case-insensitive comparisons. + ** @param field Name of the field to get + ** @return Value of the field, or empty string if not found */ + const std::string& getField(const std::string& field) const; + /** @brief Set the request method ** See the Method enumeration for a complete list of all ** the availale methods. @@ -109,141 +254,82 @@ class EE_API Http : NonCopyable { /** Enables/Disables follow redirects */ void setFollowRedirect( bool follow ); - private: + + /** @return The maximun number of redirects allowd if follow redirect is enabled. */ + const unsigned int& getMaxRedirects() const; + + /** Set the maximun number of redirects allowed if follow redirect is enabled. */ + void setMaxRedirects( unsigned int maxRedirects ); + + /** Definition of the current progress callback + * @param http The http client + * @param request The http request + * @param status The status of the progress event + * @param totalBytes The total bytes of the document / files ( only available if Content-Length is returned, otherwise is 0 ) + * @param currentBytes Current received total bytes + * @return True if continue the request, false will cancel the current request. + */ + typedef std::function ProgressCallback; + + /** Sets a progress callback */ + void setProgressCallback( const ProgressCallback& progressCallback ); + + /** Get the progress callback */ + const ProgressCallback& getProgressCallback() const; + + /** Cancels the current request if being processed */ + void cancel(); + + /** @return True if the current request was cancelled */ + const bool& isCancelled() const; + + /** @return If requests a compressed response */ + const bool& isCompressedResponse() const; + + /** Set to request a compressed response from the server + ** The returned response will be automatically decompressed + ** by the client. + */ + void setCompressedResponse(const bool& compressedResponse); + + /** Resumes download if a file is already present */ + void setContinue(const bool& resume); + + /** @return If must continue a download previously started. */ + const bool& isContinue() const; + + private: friend class Http; /** @brief Prepare the final request to send to the server ** This is used internally by Http before sending the ** request to the web server. ** @return String containing the request, ready to be sent */ - std::string prepare() const; + std::string prepare(const Http& http) const; - /** @brief Check if the request defines a field - ** This function uses case-insensitive comparisons. - ** @param field Name of the field to test - ** @return True if the field exists, false otherwise */ - bool hasField(const std::string& field) const; + /** Prepares a http tunnel request */ + std::string prepareTunnel(const Http& http); // Types typedef std::map FieldTable; // Member data - FieldTable mFields; ///< Fields of the header associated to their value - Method mMethod; ///< Method to use for the request - std::string mUri; ///< Target URI of the request - unsigned int mMajorVersion; ///< Major HTTP version - unsigned int mMinorVersion; ///< Minor HTTP version - std::string mBody; ///< Body of the request - bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request - bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request - bool mFollowRedirect; ///< Follows redirect response codes - unsigned int mRedirectionCount; ///< Number of redirections followed by the request - }; - - /** @brief Define a HTTP response */ - class EE_API Response { - public: - - // Types - typedef std::map FieldTable; - - /** @brief Enumerate all the valid status codes for a response */ - enum Status { - // 2xx: success - Ok = 200, ///< Most common code returned when operation was successful - Created = 201, ///< The resource has successfully been created - Accepted = 202, ///< The request has been accepted, but will be processed later by the server - NoContent = 204, ///< The server didn't send any data in return - ResetContent = 205, ///< The server informs the client that it should clear the view (form) that caused the request to be sent - PartialContent = 206, ///< The server has sent a part of the resource, as a response to a partial GET request - - // 3xx: redirection - MultipleChoices = 300, ///< The requested page can be accessed from several locations - MovedPermanently = 301, ///< The requested page has permanently moved to a new location - MovedTemporarily = 302, ///< The requested page has temporarily moved to a new location - NotModified = 304, ///< For conditionnal requests, means the requested page hasn't changed and doesn't need to be refreshed - - // 4xx: client error - BadRequest = 400, ///< The server couldn't understand the request (syntax error) - Unauthorized = 401, ///< The requested page needs an authentification to be accessed - Forbidden = 403, ///< The requested page cannot be accessed at all, even with authentification - NotFound = 404, ///< The requested page doesn't exist - RangeNotSatisfiable = 407, ///< The server can't satisfy the partial GET request (with a "Range" header field) - - // 5xx: server error - InternalServerError = 500, ///< The server encountered an unexpected error - NotImplemented = 501, ///< The server doesn't implement a requested feature - BadGateway = 502, ///< The gateway server has received an error from the source server - ServiceNotAvailable = 503, ///< The server is temporarily unavailable (overloaded, in maintenance, ...) - GatewayTimeout = 504, ///< The gateway server couldn't receive a response from the source server - VersionNotSupported = 505, ///< The server doesn't support the requested HTTP version - - // 10xx: Custom codes - InvalidResponse = 1000, ///< Response is not a valid HTTP one - ConnectionFailed = 1001 ///< Connection with server failed - }; - - /** @brief Default constructor - ** Constructs an empty response. */ - Response(); - - FieldTable getHeaders(); - - /** @brief Get the value of a field - ** If the field @a field is not found in the response header, - ** the empty string is returned. This function uses - ** case-insensitive comparisons. - ** @param field Name of the field to get - ** @return Value of the field, or empty string if not found */ - const std::string& getField(const std::string& field) const; - - /** @brief Get the response status code - ** The status code should be the first thing to be checked - ** after receiving a response, it defines whether it is a - ** success, a failure or anything else (see the Status - ** enumeration). - ** @return Status code of the response */ - Status getStatus() const; - - /** @brief Get the major HTTP version number of the response - ** @return Major HTTP version number - ** @see GetMinorHttpVersion */ - unsigned int getMajorHttpVersion() const; - - /** @brief Get the minor HTTP version number of the response - ** @return Minor HTTP version number - ** @see GetMajorHttpVersion */ - unsigned int getMinorHttpVersion() const; - - /** @brief Get the body of the response - ** The body of a response may contain: - ** @li the requested page (for GET requests) - ** @li a response from the server (for POST requests) - ** @li nothing (for HEAD requests) - ** @li an error message (in case of an error) - ** @return The response body */ - const std::string& getBody() const; - private : - friend class Http; - - /** @brief Construct the header from a response string - ** This function is used by Http to build the response - ** of a request. - ** @param data Content of the response to parse */ - void parse(const std::string& data); - - /** @brief Read values passed in the answer header - ** This function is used by Http to extract values passed - ** in the response. - ** @param in String stream containing the header values */ - void parseFields(std::istream &in); - - // Member data - FieldTable mFields; ///< Fields of the header - Status mStatus; ///< Status code - unsigned int mMajorVersion; ///< Major HTTP version - unsigned int mMinorVersion; ///< Minor HTTP version - std::string mBody; ///< Body of the response + FieldTable mFields; ///< Fields of the header associated to their value + Method mMethod; ///< Method to use for the request + std::string mUri; ///< Target URI of the request + unsigned int mMajorVersion; ///< Major HTTP version + unsigned int mMinorVersion; ///< Minor HTTP version + std::string mBody; ///< Body of the request + bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request + bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request + bool mFollowRedirect; ///< Follows redirect response codes + bool mCompressedResponse; ///< Request comrpessed response + bool mContinue; ///< Resume download + mutable bool mCancel; ///< Cancel state of current request + ProgressCallback mProgressCallback; ///< Progress callback + unsigned int mMaxRedirections; ///< Maximun number of redirections allowed + mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request + URI mProxy; ///< Proxy information }; /** @brief Default constructor */ @@ -258,8 +344,10 @@ class EE_API Http : NonCopyable { ** than the standard one, or use an unknown protocol. ** @param host Web server to connect to ** @param port Port to use for connection - ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. */ - Http(const std::string& host, unsigned short port = 0, bool useSSL = false); + ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. + ** @param proxy Set an http proxy for the host connection + */ + Http(const std::string& host, unsigned short port = 0, bool useSSL = false, URI proxy = URI()); ~Http(); @@ -273,8 +361,10 @@ class EE_API Http : NonCopyable { ** than the standard one, or use an unknown protocol. ** @param host Web server to connect to ** @param port Port to use for connection - ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. */ - void setHost(const std::string& host, unsigned short port = 0, bool useSSL = false); + ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. + ** @param proxy Set an http proxy for the host connection + */ + void setHost(const std::string& host, unsigned short port = 0, bool useSSL = false, URI proxy = URI()); /** @brief Send a HTTP request and return the server's response. ** You must have a valid host before sending a request (see setHost). @@ -344,6 +434,21 @@ class EE_API Http : NonCopyable { /** @return The host port */ const unsigned short& getPort() const; + + /** @return If the HTTP client uses SSL/TLS */ + const bool& isSSL() const; + + /** @return The URI from the schema + hostname + port */ + URI getURI() const; + + /** Sets the request proxy */ + void setProxy( const URI& uri ); + + /** @return The request proxy */ + const URI& getProxy() const; + + /** @return Is a proxy is need to be used */ + bool isProxied() const; private: class AsyncRequest : public Thread { public: @@ -367,14 +472,53 @@ class EE_API Http : NonCopyable { bool mStreamOwned; IOStream * mStream; }; + + class HttpConnection { + public: + HttpConnection(); + + HttpConnection( TcpSocket * socket ); + + ~HttpConnection(); + + void setSocket( TcpSocket * socket ); + + TcpSocket * getSocket() const; + + void disconnect(); + + const bool& isConnected() const; + + void setConnected( const bool& connected ); + + const bool& isTunneled() const; + + void setTunneled( const bool& tunneled ); + + const bool& isSSL() const; + + void setSSL( const bool& ssl ); + + const bool& isKeepAlive() const; + + void setKeepAlive( const bool& isKeepAlive ); + protected: + TcpSocket * mSocket; + bool mIsConnected; + bool mIsTunneled; + bool mIsSSL; + bool mIsKeepAlive; + }; + friend class AsyncRequest; - ThreadLocalPtr mConnection; ///< Connection to the host + ThreadLocalPtr mConnection; ///< Connection to the host IpAddress mHost; ///< Web host address std::string mHostName; ///< Web host name unsigned short mPort; ///< Port used for connection with host std::list mThreads; Mutex mThreadsMutex; bool mIsSSL; + URI mProxy; void removeOldThreads(); diff --git a/include/eepp/network/socket.hpp b/include/eepp/network/socket.hpp index 23b16c85f..884a95120 100644 --- a/include/eepp/network/socket.hpp +++ b/include/eepp/network/socket.hpp @@ -48,8 +48,7 @@ class EE_API Socket : NonCopyable { bool isBlocking() const; protected : /** @brief Types of protocols that the socket can use */ - enum Type - { + enum Type { Tcp, ///< TCP protocol Udp ///< UDP protocol }; diff --git a/include/eepp/network/ssl/sslsocket.hpp b/include/eepp/network/ssl/sslsocket.hpp index 1161d65eb..0f1829a52 100644 --- a/include/eepp/network/ssl/sslsocket.hpp +++ b/include/eepp/network/ssl/sslsocket.hpp @@ -36,15 +36,22 @@ class EE_API SSLSocket : public TcpSocket { Status receive(Packet& packet); + Status sslConnect(const IpAddress& remoteAddress, unsigned short remotePort, Time timeout = Time::Zero); + + void sslDisconnect(); + + Status tcpConnect(const IpAddress& remoteAddress, unsigned short remotePort, Time timeout = Time::Zero); + + void tcpDisconnect(); + + Status tcpReceive(void* data, std::size_t size, std::size_t& received); + + Status tcpSend(const void* data, std::size_t size, std::size_t& sent); protected: friend class SSLSocketImpl; friend class OpenSSLSocket; friend class MbedTLSSocket; - Status tcp_receive(void* data, std::size_t size, std::size_t& received); - - Status tcp_send(const void* data, std::size_t size, std::size_t& sent); - SSLSocketImpl * mImpl; std::string mHostName; bool mValidateCertificate; diff --git a/include/eepp/network/tcpsocket.hpp b/include/eepp/network/tcpsocket.hpp index fc16f9f04..e201d9353 100644 --- a/include/eepp/network/tcpsocket.hpp +++ b/include/eepp/network/tcpsocket.hpp @@ -113,6 +113,14 @@ class EE_API TcpSocket : public Socket { ** @see Send */ virtual Status receive(Packet& packet); + /** Set the send timeout. Only callable after connect ( after the socket + ** has been initialized ). */ + void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout Only callable after connect ( after the socket + ** has been initialized ). */ + void setReceiveTimeout(SocketHandle sock, const Time& timeout); + private: friend class TcpListener; diff --git a/include/eepp/network/udpsocket.hpp b/include/eepp/network/udpsocket.hpp index 5cf175ab0..27840baba 100644 --- a/include/eepp/network/udpsocket.hpp +++ b/include/eepp/network/udpsocket.hpp @@ -98,6 +98,14 @@ class EE_API UdpSocket : public Socket { ** @return Status code ** @see Send */ Status receive(Packet& packet, IpAddress& remoteAddress, unsigned short& remotePort); + + /** Set the send timeout. Only callable after bind ( after the socket + ** has been initialized ). */ + void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout Only callable after bind ( after the socket + ** has been initialized ). */ + void setReceiveTimeout(SocketHandle sock, const Time& timeout); private: // Member data std::vector mBuffer; ///< Temporary buffer holding the received data in Receive(Packet) diff --git a/include/eepp/network/uri.hpp b/include/eepp/network/uri.hpp index 9cce38f06..33f04fac1 100644 --- a/include/eepp/network/uri.hpp +++ b/include/eepp/network/uri.hpp @@ -213,6 +213,9 @@ class EE_API URI { /** Places the single path segments (delimited by slashes) into the given vector. */ void getPathSegments(std::vector& segments); + /** @return The last path segment if any */ + std::string getLastPathSegment(); + /** URI-encodes the given string by escaping reserved and non-ASCII * characters. The encoded string is appended to encodedStr. */ static void encode(const std::string& str, const std::string& reserved, std::string& encodedStr); diff --git a/include/eepp/system.hpp b/include/eepp/system.hpp index 47d94a3db..cf7c5d490 100644 --- a/include/eepp/system.hpp +++ b/include/eepp/system.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,9 @@ #include #include #include +#include +#include +#include #include #endif diff --git a/include/eepp/system/compression.hpp b/include/eepp/system/compression.hpp new file mode 100644 index 000000000..3f77e9eb7 --- /dev/null +++ b/include/eepp/system/compression.hpp @@ -0,0 +1,55 @@ +#ifndef EE_SYSTEM_COMPRESSION_HPP +#define EE_SYSTEM_COMPRESSION_HPP + +#include +#include + +namespace EE { namespace System { + +class Compression { + public: + enum Mode { + MODE_DEFLATE, + MODE_GZIP + }; + + enum Status { + OK = 0, + ERRNO = -1, + STREAM_ERROR = -2, + DATA_ERROR = -3, + MEM_ERROR = -4, + BUF_ERROR = -5, + VERSION_ERROR = -6 + }; + + struct ZlibConfig { + int level= -1; + }; + + struct GzipConfig { + int level = -1; + }; + + struct Config { + Config() {} + ZlibConfig zlib; + GzipConfig gzip; + }; + + static Status compress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static Status compress(IOStream& dst, IOStream& src, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static int getMaxCompressedBufferSize(Uint64 srcSize, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static Status decompress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode = MODE_DEFLATE); + + static Status decompress(IOStream& dst, IOStream& src, Mode mode = MODE_DEFLATE); + + static std::size_t getModeDefaultChunkSize( const Mode& mode ); +}; + +}} + +#endif // EE_SYSTEM_COMPRESSION_HPP diff --git a/include/eepp/system/directorypack.hpp b/include/eepp/system/directorypack.hpp index 55cc5ec2c..53a861957 100644 --- a/include/eepp/system/directorypack.hpp +++ b/include/eepp/system/directorypack.hpp @@ -52,7 +52,7 @@ class EE_API DirectoryPack : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index 36f799d1b..709817987 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace EE { namespace System { @@ -25,10 +25,10 @@ class EE_API FileSystem { /** Copy a file to memory * @param path The file path - * @param data A SafeDataPointer to allocate the data to memory - * @return True if returned the file to the SafeDataPointer + * @param data A ScopedBuffer to allocate the data to memory + * @return True if returned the file to the ScopedBuffer */ - static bool fileGet( const std::string& path, SafeDataPointer& data ); + static bool fileGet( const std::string& path, ScopedBuffer& data ); /** Copy a file to location. * @param src Source File Path @@ -110,6 +110,13 @@ class EE_API FileSystem { /** @return Returns free disk space for a given path in bytes */ static Int64 getDiskFreeSpace(const std::string& path); + + /** Creates a file name available for the directory path. + * @example For file name "file-name-" will search the first available name + * in the file system starting from file-name-1, file-name-2, and so on. + * @return The file name found, otherwise empty string if error. + */ + static std::string fileGetNumberedFileNameFromPath( std::string directoryPath, const std::string& fileName, const std::string& separator = ".", const std::string& fileExtension = "" ); }; }} diff --git a/include/eepp/system/iostreamdeflate.hpp b/include/eepp/system/iostreamdeflate.hpp new file mode 100644 index 000000000..eab4223bc --- /dev/null +++ b/include/eepp/system/iostreamdeflate.hpp @@ -0,0 +1,49 @@ +#ifndef EE_SYSTEM_IOSTREAMDEFLATE_HPP +#define EE_SYSTEM_IOSTREAMDEFLATE_HPP + +#include +#include +#include + +namespace EE { namespace System { + +struct LocalStreamData; + +/** @brief Implementation of a deflating stream */ +class EE_API IOStreamDeflate : public IOStream { + public: + static IOStreamDeflate * New( IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config = Compression::Config() ); + + /** @brief Use a stream as a input or output buffer + ** @param inOutStream Stream where the results will ve loaded or saved. + ** It must be used only for reading or writing, can't mix both calls. + ** @param mode Compression method used + ** @param config Compression configuration + */ + IOStreamDeflate( IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config = Compression::Config() ); + + virtual ~IOStreamDeflate(); + + virtual ios_size read( char * data, ios_size size ); + + virtual ios_size write( const char * data, ios_size size ); + + virtual ios_size seek( ios_size position ); + + virtual ios_size tell(); + + virtual ios_size getSize(); + + virtual bool isOpen(); + + const Compression::Mode& getMode() const; + protected: + IOStream& mStream; + Compression::Mode mMode; + ScopedBuffer mBuffer; + LocalStreamData * mLocalStream; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMDEFLATE_HPP diff --git a/include/eepp/system/iostreamfile.hpp b/include/eepp/system/iostreamfile.hpp index be0bf99ca..a7b96c371 100644 --- a/include/eepp/system/iostreamfile.hpp +++ b/include/eepp/system/iostreamfile.hpp @@ -33,6 +33,8 @@ class EE_API IOStreamFile : public IOStream { /** @brief Synchronizes the buffer associated with the stream to its controlled output sequence. ** This effectively means that all unwritten characters in the buffer are written to its controlled output sequence as soon as possible. */ void flush(); + + void close(); protected: std::FILE* mFS; ios_size mSize; diff --git a/include/eepp/system/iostreaminflate.hpp b/include/eepp/system/iostreaminflate.hpp new file mode 100644 index 000000000..8eaacfae3 --- /dev/null +++ b/include/eepp/system/iostreaminflate.hpp @@ -0,0 +1,48 @@ +#ifndef EE_SYSTEM_IOSTREAMINFLATE_HPP +#define EE_SYSTEM_IOSTREAMINFLATE_HPP + +#include +#include +#include + +namespace EE { namespace System { + +struct LocalStreamData; + +/** @brief Implementation of a inflating stream */ +class EE_API IOStreamInflate : public IOStream { + public: + static IOStreamInflate * New( IOStream& inOutStream, Compression::Mode mode ); + + /** @brief Use a stream as a input or output buffer + ** @param inOutStream Stream where the results will ve loaded or saved. + ** It must be used only for reading or writing, can't mix both calls. + ** @param mode Compression/Decompression method used + */ + IOStreamInflate( IOStream& inOutStream, Compression::Mode mode ); + + virtual ~IOStreamInflate(); + + virtual ios_size read( char * data, ios_size size ); + + virtual ios_size write( const char * data, ios_size size ); + + virtual ios_size seek( ios_size position ); + + virtual ios_size tell(); + + virtual ios_size getSize(); + + virtual bool isOpen(); + + const Compression::Mode& getMode() const; + protected: + IOStream& mStream; + Compression::Mode mMode; + ScopedBuffer mBuffer; + LocalStreamData * mLocalStream; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMINFLATE_HPP diff --git a/include/eepp/system/iostreamstring.hpp b/include/eepp/system/iostreamstring.hpp new file mode 100644 index 000000000..0866a33ef --- /dev/null +++ b/include/eepp/system/iostreamstring.hpp @@ -0,0 +1,44 @@ +#ifndef EE_SYSTEM_IOSTREAMSTRING_HPP +#define EE_SYSTEM_IOSTREAMSTRING_HPP + +#include +#include + +namespace EE { namespace System { + +/** @brief Implementation of a memory stream file using an std::string as a container */ +class EE_API IOStreamString : public IOStream { + public: + IOStreamString(); + + virtual ios_size read( char * data, ios_size size ); + + virtual ios_size write( const char * data, ios_size size ); + + virtual ios_size write( const std::string& string ); + + virtual ios_size seek( ios_size position ); + + virtual ios_size tell(); + + virtual ios_size getSize(); + + virtual bool isOpen(); + + void clear(); + + /** @return Pointer to the current position in the stream */ + const char * getPositionPointer(); + + /** @return The pointer to the beggining of the stream */ + const char * getStreamPointer() const; + + const std::string& getStream() const; + protected: + std::string mStream; + ios_size mPos; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMSTRING_HPP diff --git a/include/eepp/system/pack.hpp b/include/eepp/system/pack.hpp index bb371436f..5bdb9469c 100755 --- a/include/eepp/system/pack.hpp +++ b/include/eepp/system/pack.hpp @@ -2,7 +2,7 @@ #define EE_SYSTEMCPACK_HPP #include -#include +#include #include namespace EE { namespace System { @@ -52,7 +52,7 @@ class EE_API Pack : protected Mutex { virtual bool extractFileToMemory( const std::string& path, std::vector& data ) = 0; /** Extract a file to memory from the pack file */ - virtual bool extractFileToMemory( const std::string& path, SafeDataPointer& data ) = 0; + virtual bool extractFileToMemory( const std::string& path, ScopedBuffer& data ) = 0; /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ virtual Int32 exists( const std::string& path ) = 0; diff --git a/include/eepp/system/pak.hpp b/include/eepp/system/pak.hpp index 952bf6808..75668f444 100755 --- a/include/eepp/system/pak.hpp +++ b/include/eepp/system/pak.hpp @@ -53,7 +53,7 @@ class EE_API Pak : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pakFile and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/include/eepp/system/safedatapointer.hpp b/include/eepp/system/safedatapointer.hpp deleted file mode 100644 index 0f572d1d2..000000000 --- a/include/eepp/system/safedatapointer.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef EE_SYSTEM_SAFEDATAPOINTER -#define EE_SYSTEM_SAFEDATAPOINTER - -#include -#include -#include - -namespace EE { namespace System { - -/** @brief Keep a pointer and release it in the SafeDataPointer destructor */ -template -class TSafeDataPointer { - public: - TSafeDataPointer(); - - TSafeDataPointer( Uint32 size ); - - TSafeDataPointer( T * data, Uint32 size ); - - /** @brief The destructor deletes the buffer */ - ~TSafeDataPointer(); - - void clear(); - - /** Pointer to the buffer */ - T * data; - - /** Buffer size */ - Uint32 size; -}; - - -template -TSafeDataPointer::TSafeDataPointer() : - data( NULL ), - size( 0 ) -{ -} - -template -TSafeDataPointer::TSafeDataPointer( Uint32 size ) : - data( eeNewArray( T, size ) ), - size( size ) -{ -} - -template -TSafeDataPointer::TSafeDataPointer( T * data, Uint32 size ) : - data( data ), - size( size ) -{ -} - -template -TSafeDataPointer::~TSafeDataPointer() { - clear(); -} - -template -void TSafeDataPointer::clear() { - eeSAFE_DELETE_ARRAY( data ); -} - -typedef TSafeDataPointer SafeDataPointer; - -}} - -#endif diff --git a/include/eepp/system/scopedbuffer.hpp b/include/eepp/system/scopedbuffer.hpp new file mode 100644 index 000000000..a6d401924 --- /dev/null +++ b/include/eepp/system/scopedbuffer.hpp @@ -0,0 +1,153 @@ +#ifndef EE_SYSTEM_SCOPEDBUFFER +#define EE_SYSTEM_SCOPEDBUFFER + +#include +#include +#include +#include +#include + +namespace EE { namespace System { + +/** @brief Keep a pointer to a buffer and release it when the ScopedBuffer goes out of scope. + +The TScopedBuffer class template stores a pointer to a dynamically allocated array. +(Dynamically allocated arrays are allocated with the C++ new[] expression.) +The array pointed to is guaranteed to be deleted, either on destruction of the TScopedBuffer, +or via an explicit reset. + +The TScopedBuffer template is a simple solution for simple needs. +It supplies a basic "resource acquisition is initialization" facility, +without shared-ownership or transfer-of-ownership semantics. +Both its name and enforcement of semantics (by being NonCopyable) signal its +intent to retain ownership solely within the current scope. +*/ +template +class TScopedBuffer : NonCopyable { + public: + TScopedBuffer(); + + TScopedBuffer( std::size_t length ); + + TScopedBuffer( T * data, std::size_t length ); + + /** @brief The destructor deletes the buffer */ + ~TScopedBuffer(); + + void clear(); + + T& operator[]( std::size_t i ) const; + + bool operator()() const; + + T * get() const; + + void swap(TScopedBuffer& b); + + void reset(T * p = 0, const std::size_t& size = 0); + + void reset(const std::size_t& size = 0); + + bool isEmpty() const; + + const std::size_t& size() const; + + const std::size_t& length() const; + + private: + /** Pointer to the buffer */ + T * mData; + + /** Buffer size */ + std::size_t mSize; +}; + +template +TScopedBuffer::TScopedBuffer() : + mData( NULL ), + mSize( 0 ) +{ +} + +template +TScopedBuffer::TScopedBuffer( std::size_t length ) : + mData( eeNewArray( T, length ) ), + mSize( length ) +{ +} + +template +TScopedBuffer::TScopedBuffer( T * data, std::size_t length ) : + mData( data ), + mSize( length ) +{ +} + +template +TScopedBuffer::~TScopedBuffer() { + clear(); +} + +template +void TScopedBuffer::clear() { + eeSAFE_DELETE_ARRAY( mData ); + mSize = 0; +} + +template +T& TScopedBuffer::operator[]( std::size_t i ) const { + eeASSERT( i < mSize && NULL != mData ); + return mData[i]; +} + +template +bool TScopedBuffer::operator()() const { + return NULL != mData; +} + +template +T * TScopedBuffer::get() const { + return mData; +} + +template +const std::size_t& TScopedBuffer::size() const { + return mSize; +} + +template +const std::size_t& TScopedBuffer::length() const { + return mSize; +} + +template +bool TScopedBuffer::isEmpty() const { + return mData == NULL; +} + +template +void TScopedBuffer::swap(TScopedBuffer& b) { + std::swap(mData, b.mData); + std::swap(mSize, b.mSize); +} + +template +void TScopedBuffer::reset(T * p, const std::size_t& size) { + eeASSERT( p == 0 || p != mData ); + clear(); + mData = p; + mSize = size; +} + +template +void TScopedBuffer::reset(const std::size_t& size) { + clear(); + mData = eeNewArray( T, ( size ) ); + mSize = size; +} + +typedef TScopedBuffer ScopedBuffer; + +}} + +#endif diff --git a/include/eepp/system/zip.hpp b/include/eepp/system/zip.hpp index 9eadaf3cd..f64228ef8 100644 --- a/include/eepp/system/zip.hpp +++ b/include/eepp/system/zip.hpp @@ -51,7 +51,7 @@ class EE_API Zip : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/premake4.lua b/premake4.lua index 76b77feca..0e7cc96c4 100644 --- a/premake4.lua +++ b/premake4.lua @@ -145,8 +145,8 @@ newoption { trigger = "with-static-backend", description = "It will try to compi newoption { trigger = "with-gles2", description = "Compile with GLES2 support" } newoption { trigger = "with-gles1", description = "Compile with GLES1 support" } newoption { trigger = "use-frameworks", description = "In Mac OS X it will try to link the external libraries from its frameworks. For example, instead of linking against SDL2 it will link agains SDL2.framework." } -newoption { - trigger = "with-backend", +newoption { + trigger = "with-backend", description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.", allowed = { { "SDL2", "SDL2 (default and recommended)" }, @@ -167,21 +167,21 @@ function explode(div,str) end function os.get_real() - if _OPTIONS.platform == "ios-arm7" or + if _OPTIONS.platform == "ios-arm7" or _OPTIONS.platform == "ios-x86" or _OPTIONS.platform == "ios-cross-arm7" or _OPTIONS.platform == "ios-cross-x86" then return "ios" end - + if _OPTIONS.platform == "android-arm7" then return "android" end - + if _OPTIONS.platform == "mingw32" then return _OPTIONS.platform end - + if _OPTIONS.platform == "emscripten" then return _OPTIONS.platform end @@ -229,7 +229,7 @@ end function os_findlib( name ) if os.is_real("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then local path = "/Library/Frameworks/" .. name .. ".framework" - + if os.isdir( path ) then return path end @@ -241,12 +241,12 @@ end function get_backend_link_name( name ) if os.is_real("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then local fname = name .. ".framework" - + if os_findlib( name ) then -- Search for the framework return fname end end - + return name end @@ -254,7 +254,7 @@ function string.starts(String,Start) if ( _ACTION ) then return string.sub(String,1,string.len(Start))==Start end - + return false end @@ -315,7 +315,7 @@ function build_base_cpp_configuration( package_name ) if not os.is("windows") then buildoptions{ "-fPIC" } end - + set_ios_config() set_xcode_config() @@ -364,7 +364,7 @@ function build_link_configuration( package_name, use_ee_icon ) includedirs { "include" } local extension = ""; - + if package_name == "eepp" then defines { "EE_EXPORTS" } elseif package_name == "eepp-static" then @@ -384,13 +384,13 @@ function build_link_configuration( package_name, use_ee_icon ) add_static_links() links { link_list } end - - if os.is("windows") and not is_vs() then + + if os.is("windows") and not is_vs() then if ( true == use_ee_icon ) then linkoptions { "../../bin/assets/icon/ee.res" } end end - + if os.is_real("emscripten") then extension = ".html" @@ -403,11 +403,11 @@ function build_link_configuration( package_name, use_ee_icon ) linkoptions { "--preload-file assets/" } end end - + if _OPTIONS.platform == "ios-cross-arm7" then extension = ".ios" end - + if _OPTIONS.platform == "ios-cross-x86" then extension = ".x86.ios" end @@ -440,10 +440,10 @@ function build_link_configuration( package_name, use_ee_icon ) fix_shared_lib_linking_path( package_name, "libeepp" ) targetname ( package_name .. extension ) - + configuration "windows" add_cross_config_links() - + configuration "emscripten" linkoptions{ "-O2 -s TOTAL_MEMORY=67108864 -s ASM_JS=1 -s VERBOSE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s FULL_ES3=1 -s \"BINARYEN_TRAP_MODE='clamp'\"" } buildoptions { "-fno-strict-aliasing -O2 -s USE_SDL=2 -s PRECISE_F32=1" } @@ -463,7 +463,7 @@ end function generate_os_links() if os.is_real("linux") then multiple_insert( os_links, { "rt", "pthread", "X11", "openal", "GL", "Xcursor" } ) - + if _OPTIONS["with-static-eepp"] then table.insert( os_links, "dl" ) end @@ -488,28 +488,28 @@ function parse_args() if _OPTIONS["with-gles2"] then defines { "EE_GLES2", "SOIL_GLES2" } end - + if _OPTIONS["with-gles1"] then defines { "EE_GLES1", "SOIL_GLES1" } - end + end end function add_static_links() -- The linking order DOES matter -- Expose the symbols that need one static library AFTER adding that static lib - + -- Add static backends if next(static_backends) ~= nil then for _, value in pairs( static_backends ) do linkoptions { value } end end - + if _OPTIONS["with-static-freetype"] or not os_findlib("freetype") then print("Enabled static freetype") links { "freetype-static" } end - + links { "SOIL2-static", "chipmunk-static", "libzip-static", @@ -523,7 +523,7 @@ function add_static_links() if _OPTIONS["with-ssl"] and not _OPTIONS["with-openssl"] then links { "mbedtls-static" } end - + if not os.is_real("haiku") and not os.is_real("ios") and not os.is_real("android") and not os.is_real("emscripten") then links{ "glew-static" } end @@ -544,7 +544,7 @@ function add_sdl2() print("Using SDL2 backend"); files { "src/eepp/window/backend/SDL2/*.cpp" } defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" } - + if not can_add_static_backend("SDL2") then table.insert( link_list, get_backend_link_name( "SDL2" ) ) else @@ -556,7 +556,7 @@ function add_sfml() print("Using SFML backend"); files { "src/eepp/window/backend/SFML/*.cpp" } defines { "EE_BACKEND_SFML_ACTIVE" } - + if not can_add_static_backend("SFML") then table.insert( link_list, get_backend_link_name( "sfml-system" ) ) table.insert( link_list, get_backend_link_name( "sfml-window" ) ) @@ -577,13 +577,13 @@ end function set_ios_config() if _OPTIONS.platform == "ios-arm7" or _OPTIONS.platform == "ios-x86" then local err = false - + if nil == os.getenv("TOOLCHAINPATH") then print("You must set TOOLCHAINPATH enviroment variable.") print("\tExample: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/") err = true end - + if nil == os.getenv("SYSROOTPATH") then print("You must set SYSROOTPATH enviroment variable.") print("\tExample: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk") @@ -595,7 +595,7 @@ function set_ios_config() print("\tExample: 5.0") err = true end - + if err then os.exit(1) end @@ -604,14 +604,14 @@ function set_ios_config() local framework_path = sysroot_path .. "/System/Library/Frameworks" local framework_libs_path = framework_path .. "/usr/lib" local sysroot_ver = " -miphoneos-version-min=" .. os.getenv("IOSVERSION") .. " -isysroot " .. sysroot_path - + buildoptions { sysroot_ver .. " -I" .. sysroot_path .. "/usr/include" } linkoptions { sysroot_ver } libdirs { framework_libs_path } linkoptions { " -F" .. framework_path .. " -L" .. framework_libs_path .. " -isysroot " .. sysroot_path } includedirs { "src/thirdparty/SDL2/include" } end - + if _OPTIONS.platform == "ios-cross-arm7" or _OPTIONS.platform == "ios-cross-x86" then includedirs { "src/thirdparty/SDL2/include" } end @@ -625,7 +625,7 @@ function backend_is( name, libname ) if next(backends) == nil then backends = string.explode(_OPTIONS["with-backend"],",") end - + local backend_sel = table.contains( backends, name ) local ret_val = os_findlib( libname ) and backend_sel @@ -641,7 +641,7 @@ function backend_is( name, libname ) return ret_val end -function select_backend() +function select_backend() if backend_is("SDL2", "SDL2") then print("Selected SDL2") add_sdl2() @@ -693,10 +693,10 @@ end function build_eepp( build_name ) includedirs { "include", "src", "src/thirdparty", "include/eepp/thirdparty", "src/thirdparty/freetype2/include", "src/thirdparty/zlib", "src/thirdparty/libogg/include", "src/thirdparty/libvorbis/include", "src/thirdparty/mbedtls/include" } - + set_ios_config() set_xcode_config() - + add_static_links() if is_vs() then @@ -725,6 +725,7 @@ function build_eepp( build_name ) "src/eepp/window/platform/null/*.cpp", "src/eepp/network/*.cpp", "src/eepp/network/ssl/*.cpp", + "src/eepp/network/http/*.cpp", "src/eepp/scene/*.cpp", "src/eepp/scene/actions/*.cpp", "src/eepp/ui/*.cpp", @@ -736,11 +737,11 @@ function build_eepp( build_name ) "src/eepp/maps/*.cpp", "src/eepp/maps/mapeditor/*.cpp" } - + check_ssl_support() - + select_backend() - + if not _OPTIONS["with-static-freetype"] and os_findlib("freetype") then table.insert( link_list, get_backend_link_name( "freetype" ) ) end @@ -748,19 +749,19 @@ function build_eepp( build_name ) multiple_insert( link_list, os_links ) links { link_list } - + build_link_configuration( build_name ) configuration "windows" files { "src/eepp/window/platform/win/*.cpp" } add_cross_config_links() - + configuration "linux" files { "src/eepp/window/platform/x11/*.cpp" } - + configuration "macosx" files { "src/eepp/window/platform/osx/*.cpp" } - + configuration "emscripten" if _OPTIONS["force-gles1"] then defines{ "EE_GLES1_DEFAULT" } @@ -776,7 +777,7 @@ function set_targetdir( dir ) end solution "eepp" - + targetdir("./bin/") configurations { "debug", "release" } @@ -865,7 +866,7 @@ solution "eepp" files { "src/thirdparty/freetype2/src/**.c" } includedirs { "src/thirdparty/freetype2/include" } build_base_configuration( "freetype" ) - + project "chipmunk-static" kind "StaticLib" @@ -900,15 +901,15 @@ solution "eepp" language "C++" set_targetdir("libs/" .. os.get_real() .. "/thirdparty/") includedirs { "src/thirdparty/efsw/include", "src/thirdparty/efsw/src" } - + if os.is("windows") then osfiles = "src/thirdparty/efsw/src/efsw/platform/win/*.cpp" else osfiles = "src/thirdparty/efsw/src/efsw/platform/posix/*.cpp" end - + files { "src/thirdparty/efsw/src/efsw/*.cpp", osfiles } - + if os.is("windows") then excludes { "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" } elseif os.is("linux") then @@ -918,7 +919,7 @@ solution "eepp" elseif os.is("freebsd") then excludes { "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" } end - + build_base_cpp_configuration( "efsw" ) project "eepp-main" @@ -932,7 +933,7 @@ solution "eepp" language "C++" set_targetdir("libs/" .. os.get_real() .. "/") build_eepp( "eepp-static" ) - + project "eepp-shared" kind "SharedLib" language "C++" @@ -1007,20 +1008,20 @@ solution "eepp" language "C++" files { "src/tools/mapeditor/*.cpp" } build_link_configuration( "eepp-MapEditor", true ) - + project "eepp-uieditor" set_kind() language "C++" includedirs { "src/thirdparty/efsw/include", "src/thirdparty" } - + if not os.is("windows") and not os.is("haiku") then links { "pthread" } end - + links { "efsw-static", "pugixml-static" } files { "src/tools/uieditor/*.cpp" } build_link_configuration( "eepp-UIEditor", true ) - + if os.isfile("external_projects.lua") then dofile("external_projects.lua") end diff --git a/projects/linux/ee.config b/projects/linux/ee.config index c69ad180d..8aaaa23a8 100644 --- a/projects/linux/ee.config +++ b/projects/linux/ee.config @@ -1,7 +1,6 @@ #define EE_SDL_VERSION_2 #define EE_X11_PLATFORM #define EE_DEBUG -#define EE_LIBSNDFILE_ENABLED #define EE_MEMORY_MANAGER #define EE_SHADERS_SUPPORTED #define EE_GLEW_AVAILABLE @@ -10,7 +9,6 @@ #define EE_BACKEND_SFML_ACTIVE #define EE_BACKEND_SDL2 #define EE_GL3_ENABLED -#define EE_SHADERS_SUPPORTED #define EE_BACKEND_SDL_ACTIVE #define EE_MBEDTLS #define DR_MP3_IMPLEMENTATION diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 1880d817f..ef9e03f61 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -143,7 +143,6 @@ ../../include/eepp/maps/mapobjectlayer.hpp ../../include/eepp/maps/tilemap.hpp ../../include/eepp/maps/tilemaplayer.hpp -../../include/eepp/math/base.hpp ../../include/eepp/math/ease.hpp ../../include/eepp/math/easing.hpp ../../include/eepp/math.hpp @@ -242,16 +241,20 @@ ../../include/eepp/system/bitop.hpp ../../include/eepp/system/clock.hpp ../../include/eepp/system/color.hpp +../../include/eepp/system/compression.hpp ../../include/eepp/system/condition.hpp ../../include/eepp/system/container.hpp ../../include/eepp/system/directorypack.hpp ../../include/eepp/system/filesystem.hpp ../../include/eepp/system.hpp ../../include/eepp/system/inifile.hpp +../../include/eepp/system/iostreamdeflate.hpp ../../include/eepp/system/iostreamfile.hpp ../../include/eepp/system/iostream.hpp +../../include/eepp/system/iostreaminflate.hpp ../../include/eepp/system/iostreammemory.hpp ../../include/eepp/system/iostreampak.hpp +../../include/eepp/system/iostreamstring.hpp ../../include/eepp/system/iostreamzip.hpp ../../include/eepp/system/lock.hpp ../../include/eepp/system/log.hpp @@ -264,7 +267,7 @@ ../../include/eepp/system/rc4.hpp ../../include/eepp/system/resourceloader.hpp ../../include/eepp/system/resourcemanager.hpp -../../include/eepp/system/safedatapointer.hpp +../../include/eepp/system/scopedbuffer.hpp ../../include/eepp/system/singleton.hpp ../../include/eepp/system/sys.hpp ../../include/eepp/system/thread.hpp @@ -588,6 +591,8 @@ ../../src/eepp/math/transform.cpp ../../src/eepp/network/ftp.cpp ../../src/eepp/network/http.cpp +../../src/eepp/network/http/httpstreamchunked.cpp +../../src/eepp/network/http/httpstreamchunked.hpp ../../src/eepp/network/ipaddress.cpp ../../src/eepp/network/packet.cpp ../../src/eepp/network/platform/platformimpl.hpp @@ -667,13 +672,17 @@ ../../src/eepp/system/base64.cpp ../../src/eepp/system/clock.cpp ../../src/eepp/system/color.cpp +../../src/eepp/system/compression.cpp ../../src/eepp/system/condition.cpp ../../src/eepp/system/directorypack.cpp ../../src/eepp/system/filesystem.cpp ../../src/eepp/system/inifile.cpp +../../src/eepp/system/iostreamdeflate.cpp ../../src/eepp/system/iostreamfile.cpp +../../src/eepp/system/iostreaminflate.cpp ../../src/eepp/system/iostreammemory.cpp ../../src/eepp/system/iostreampak.cpp +../../src/eepp/system/iostreamstring.cpp ../../src/eepp/system/iostreamzip.cpp ../../src/eepp/system/lock.cpp ../../src/eepp/system/log.cpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index cdfe74fdb..5d55259ce 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -4,10 +4,4 @@ ../../include/eepp/thirdparty ../../src/thirdparty/efsw/include ../../src/thirdparty/libvorbis/include -../../src/eepp/audio -../../include/eepp/audio /usr/include/freetype2/ - -../../include/eepp/ui -../../src/eepp/ui -../../bin/assets/layouts diff --git a/src/eepp/audio/music.cpp b/src/eepp/audio/music.cpp index cf93d2944..cf72b7de9 100644 --- a/src/eepp/audio/music.cpp +++ b/src/eepp/audio/music.cpp @@ -81,7 +81,7 @@ bool Music::openFromStream(IOStream& stream) { bool Music::openFromPack(Pack * pack, const std::string & filePackPath) { if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, mData ) ) - return openFromMemory( reinterpret_cast ( mData.data ), mData.size ); + return openFromMemory( reinterpret_cast ( mData.get() ), mData.length() ); return false; } diff --git a/src/eepp/audio/soundbuffer.cpp b/src/eepp/audio/soundbuffer.cpp index 90dab69fa..6daf7a815 100644 --- a/src/eepp/audio/soundbuffer.cpp +++ b/src/eepp/audio/soundbuffer.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -105,10 +105,10 @@ bool SoundBuffer::loadFromSamples(const Int16* samples, Uint64 sampleCount, unsi bool SoundBuffer::loadFromPack(Pack * pack, std::string filePackPath) { bool Ret = false; - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, PData ) ) - Ret = loadFromMemory( reinterpret_cast ( PData.data ), PData.size ); + if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, buffer ) ) + Ret = loadFromMemory( reinterpret_cast ( buffer.get() ), buffer.length() ); return Ret; } diff --git a/src/eepp/audio/soundfilereadermp3.cpp b/src/eepp/audio/soundfilereadermp3.cpp index 203fce15b..9bc8e942b 100644 --- a/src/eepp/audio/soundfilereadermp3.cpp +++ b/src/eepp/audio/soundfilereadermp3.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include static size_t drmp3_func_read(void* data, void* ptr, size_t size) { IOStream* stream = static_cast(data); @@ -73,15 +73,15 @@ Uint64 SoundFileReaderMp3::read(Int16* samples, Uint64 maxCount) { while (count < maxCount) { const int samplesToRead = static_cast(maxCount - count); int frames = samplesToRead / mChannelCount; - TSafeDataPointer rSamples( samplesToRead ); + TScopedBuffer rSamples( samplesToRead ); - long framesRead = drmp3_read_pcm_frames_f32( mMp3, frames, rSamples.data ); + long framesRead = drmp3_read_pcm_frames_f32( mMp3, frames, rSamples.get() ); if (framesRead > 0) { long samplesRead = framesRead * mChannelCount; for ( int i = 0; i < samplesRead; i++ ) - samples[i] = rSamples.data[i] * 32768.f; + samples[i] = rSamples[i] * 32768.f; count += samplesRead; samples += samplesRead; diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 2c3f936fc..8792a8de2 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -242,7 +242,7 @@ bool FontTrueType::loadFromPack( Pack * pack, std::string filePackPath ) { mMemCopy.clear(); if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, mMemCopy ) ) { - Ret = loadFromMemory( mMemCopy.data, mMemCopy.size ); + Ret = loadFromMemory( mMemCopy.get(), mMemCopy.length() ); } return Ret; @@ -367,8 +367,9 @@ Texture* FontTrueType::getTexture(unsigned int characterSize) const { } FontTrueType& FontTrueType::operator =(const FontTrueType& right) { - FontTrueType temp(right); + FontTrueType temp(right.getName()); + temp.mMemCopy.swap(right.mMemCopy); std::swap(mLibrary, temp.mLibrary); std::swap(mFace, temp.mFace); std::swap(mStreamRec, temp.mStreamRec); diff --git a/src/eepp/graphics/image.cpp b/src/eepp/graphics/image.cpp index 142086d49..e87d04eb0 100644 --- a/src/eepp/graphics/image.cpp +++ b/src/eepp/graphics/image.cpp @@ -284,18 +284,18 @@ bool Image::getInfo( const std::string& path, int * width, int * height, int * c Pack * tPack = PackManager::instance()->exists( npath ); if ( NULL != tPack ) { - SafeDataPointer PData; + ScopedBuffer buffer; - tPack->extractFileToMemory( npath, PData ); + tPack->extractFileToMemory( npath, buffer ); - res = 0 != stbi_info_from_memory( PData.data, PData.size, width, height, channels ); + res = 0 != stbi_info_from_memory( buffer.get(), buffer.length(), width, height, channels ); - if ( !res && svg_test_from_memory( PData.data, PData.size ) ) { - SafeDataPointer data( PData.size + 1 ); - memcpy( data.data, PData.data, PData.size ); - data.data[PData.size] = '\0'; + if ( !res && svg_test_from_memory( buffer.get(), buffer.length() ) ) { + ScopedBuffer data( buffer.length() + 1 ); + memcpy( data.get(), buffer.get(), buffer.length() ); + data[buffer.length()] = '\0'; - NSVGimage * image = nsvgParse( (char*)data.data, "px", 96.0f ); + NSVGimage * image = nsvgParse( (char*)data.get(), "px", 96.0f ); if ( NULL != image ) { *width = image->width * imageFormatConfiguration.svgScale(); @@ -467,10 +467,10 @@ Image::Image( const Uint8 * imageData, const unsigned int & imageDataSize, const mLoadedFromStbi = true; } else if ( svg_test_from_memory( imageData, imageDataSize ) ) { - SafeDataPointer data( imageDataSize + 1 ); - memcpy( data.data, imageData, imageDataSize ); - data.data[imageDataSize] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + ScopedBuffer data( imageDataSize + 1 ); + memcpy( data.get(), imageData, imageDataSize ); + data[imageDataSize] = '\0'; + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { std::string reason = "."; @@ -526,14 +526,14 @@ Image::Image( IOStream & stream, const unsigned int& forceChannels, const Format mLoadedFromStbi = true; } else if ( svg_test_from_stream( stream ) ) { - SafeDataPointer data( stream.getSize() + 1 ); + ScopedBuffer data( stream.getSize() + 1 ); stream.seek( 0 ); - stream.read( (char*)data.data, data.size - 1 ); + stream.read( (char*)data.get(), data.length() - 1 ); - data.data[data.size - 1] = '\0'; + data[data.length() - 1] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { eePRINTL( "Failed to load image. Reason: %s", stbi_failure_reason() ); } @@ -580,12 +580,12 @@ void Image::svgLoad( NSVGimage * image ) { void Image::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); int w, h, c; - Uint8 * data = stbi_load_from_memory( PData.data, PData.size, &w, &h, &c, mChannels ); + Uint8 * data = stbi_load_from_memory( buffer.get(), buffer.length(), &w, &h, &c, mChannels ); if ( NULL != data ) { mPixels = data; @@ -598,11 +598,11 @@ void Image::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { mSize = mWidth * mHeight * mChannels; mLoadedFromStbi = true; - } else if ( svg_test_from_memory( PData.data, PData.size ) ) { - SafeDataPointer data( PData.size + 1 ); - memcpy( data.data, PData.data, PData.size ); - data.data[PData.size] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + } else if ( svg_test_from_memory( buffer.get(), buffer.length() ) ) { + ScopedBuffer data( buffer.length() + 1 ); + memcpy( data.get(), buffer.get(), buffer.length() ); + data[buffer.length()] = '\0'; + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { eePRINTL( "Failed to load image %s. Reason: %s", FilePackPath.c_str(), stbi_failure_reason() ); } diff --git a/src/eepp/graphics/shader.cpp b/src/eepp/graphics/shader.cpp index 43599342e..cb3b083f7 100644 --- a/src/eepp/graphics/shader.cpp +++ b/src/eepp/graphics/shader.cpp @@ -30,21 +30,21 @@ Shader::Shader( const Uint32& Type, const std::string& Filename ) { mFilename = FileSystem::fileNameFromPath( Filename ); if ( FileSystem::fileExists( Filename ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - FileSystem::fileGet( Filename, PData ); + FileSystem::fileGet( Filename, buffer ); - setSource( (const char*)PData.data, PData.size ); + setSource( (const char*)buffer.get(), buffer.length() ); } else { std::string tPath = Filename; Pack * tPack = NULL; if ( PackManager::instance()->isFallbackToPacksActive() && NULL != ( tPack = PackManager::instance()->exists( tPath ) ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - tPack->extractFileToMemory( tPath, PData ); + tPack->extractFileToMemory( tPath, buffer ); - setSource( reinterpret_cast ( PData.data ), PData.size ); + setSource( reinterpret_cast ( buffer.get() ), buffer.length() ); } else { eePRINTL( "Couldn't open shader object: %s", Filename.c_str() ); } @@ -62,16 +62,16 @@ Shader::Shader( const Uint32& Type, const char * Data, const Uint32& DataSize ) } Shader::Shader( const Uint32& Type, Pack * Pack, const std::string& Filename ) { - SafeDataPointer PData; + ScopedBuffer buffer; Init( Type ); mFilename = FileSystem::fileNameFromPath( Filename ); if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( Filename ) ) { - Pack->extractFileToMemory( Filename, PData ); + Pack->extractFileToMemory( Filename, buffer ); - setSource( reinterpret_cast ( PData.data ), PData.size ); + setSource( reinterpret_cast ( buffer.get() ), buffer.length() ); } compile(); diff --git a/src/eepp/graphics/textureatlasloader.cpp b/src/eepp/graphics/textureatlasloader.cpp index db6717475..ca53bad06 100644 --- a/src/eepp/graphics/textureatlasloader.cpp +++ b/src/eepp/graphics/textureatlasloader.cpp @@ -193,11 +193,11 @@ void TextureAtlasLoader::loadFromPack( Pack * Pack, const std::string& FilePackP if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { mPack = Pack; - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); - loadFromMemory( reinterpret_cast ( PData.data ), PData.size, FilePackPath ); + loadFromMemory( buffer.get(), buffer.length(), FilePackPath ); } } diff --git a/src/eepp/graphics/textureloader.cpp b/src/eepp/graphics/textureloader.cpp index d8caa35db..dc2e00012 100644 --- a/src/eepp/graphics/textureloader.cpp +++ b/src/eepp/graphics/textureloader.cpp @@ -278,11 +278,11 @@ void TextureLoader::loadFromFile() { } void TextureLoader::loadFromPack() { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( NULL != mPack && mPack->isOpen() && mPack->extractFileToMemory( mFilepath, PData ) ) { - mImagePtr = PData.data; - mSize = PData.size; + if ( NULL != mPack && mPack->isOpen() && mPack->extractFileToMemory( mFilepath, buffer ) ) { + mImagePtr = buffer.get(); + mSize = buffer.length(); loadFromMemory(); } diff --git a/src/eepp/maps/tilemap.cpp b/src/eepp/maps/tilemap.cpp index 695a96917..9efd6ffff 100644 --- a/src/eepp/maps/tilemap.cpp +++ b/src/eepp/maps/tilemap.cpp @@ -1072,13 +1072,13 @@ bool TileMap::loadFromFile( const std::string& path ) { bool TileMap::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); mPath = FilePackPath; - return loadFromMemory( reinterpret_cast ( PData.data ), PData.size ); + return loadFromMemory( reinterpret_cast ( buffer.get() ), buffer.length() ); } return false; diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index a2f6fea38..e39cef95d 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -10,18 +14,52 @@ #include using namespace EE::Network::SSL; +using namespace EE::Network::Private; namespace EE { namespace Network { -Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect) : +#define PACKET_BUFFER_SIZE (16384) + +Http::Request::Method Http::Request::methodFromString( std::string methodString ) { + String::toLowerInPlace(methodString); + if ( "get" == methodString ) return Method::Get; + else if ( "head" == methodString ) return Method::Head; + else if ( "post" == methodString ) return Method::Post; + else if ( "put" == methodString ) return Method::Put; + else if ( "delete" == methodString ) return Method::Delete; + else if ( "options" == methodString ) return Method::Options; + else if ( "patch" == methodString ) return Method::Patch; + else if ( "connect" == methodString ) return Method::Connect; + else return Method::Get; +} + +std::string Http::Request::methodToString(const Http::Request::Method& method) { + switch (method) { + default : + case Get: return "GET"; + case Head: return "HEAD"; + case Post: return "POST"; + case Put: return "PUT"; + case Delete: return "DELETE"; + case Options: return "OPTIONS"; + case Patch: return "PATCH"; + case Connect: return "CONNECT"; + } +} + +Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect, bool compressedResponse) : mValidateCertificate( validateCertificate ), mValidateHostname( validateHostname ), mFollowRedirect( followRedirect ), + mCompressedResponse( compressedResponse ), + mContinue( false ), + mCancel( false ), + mMaxRedirections( 10 ), mRedirectionCount( 0 ) { setMethod(method); setUri(uri); - setHttpVersion(1, 0); + setHttpVersion(1, 1); setBody(body); } @@ -78,24 +116,83 @@ void Http::Request::setFollowRedirect(bool follow) { mFollowRedirect = follow; } -std::string Http::Request::prepare() const { +const unsigned int& Http::Request::getMaxRedirects() const { + return mMaxRedirections; +} + +void Http::Request::setMaxRedirects(unsigned int maxRedirects) { + mMaxRedirections = maxRedirects; +} + +void Http::Request::setProgressCallback(const Http::Request::ProgressCallback& progressCallback) { + mProgressCallback = progressCallback; +} + +const Http::Request::ProgressCallback& Http::Request::getProgressCallback() const { + return mProgressCallback; +} + +void Http::Request::cancel() { + mCancel = true; +} + +const bool &Http::Request::isCancelled() const { + return mCancel; +} + +std::string Http::Request::prepareTunnel(const Http& http) { + std::ostringstream out; + + setMethod( Connect ); + + std::string method = methodToString( mMethod ); + + out << method << " " << http.getHostName() << ":" << http.getPort() << " "; + out << "HTTP/" << mMajorVersion << "." << mMinorVersion << "\r\n"; + + setField( "Host", String::format( "%s:%d", http.getHostName().c_str(), http.getPort() ) ); + setField( "Proxy-Connection", "Keep-Alive" ); + setField( "User-Agent", "eepp-network" ); + + for (FieldTable::const_iterator i = mFields.begin(); i != mFields.end(); ++i) + out << i->first << ": " << i->second << "\r\n"; + + out << "\r\n"; + + return out.str(); +} + +void Http::Request::setContinue(const bool& resume) { + mContinue = resume; +} + +const bool& Http::Request::isContinue() const { + return mContinue; +} + +const bool& Http::Request::isCompressedResponse() const { + return mCompressedResponse; +} + +void Http::Request::setCompressedResponse(const bool& compressedResponse) { + mCompressedResponse = compressedResponse; +} + +std::string Http::Request::prepare(const Http& http) const { std::ostringstream out; // Convert the method to its string representation - std::string method; - switch (mMethod) { - default : - case Get: method = "GET"; break; - case Head: method = "HEAD"; break; - case Post: method = "POST"; break; - case Put: method = "PUT"; break; - case Delete: method = "DELETE"; break; - case Options: method = "OPTIONS"; break; - case Patch: method = "PATCH"; break; - } + std::string method = methodToString( mMethod ); // Write the first line containing the request type - out << method << " " << mUri << " "; + if ( http.getProxy().empty() ) { + out << method << " " << mUri << " "; + } else { + URI uri = http.getURI(); + uri.setPathEtc( mUri ); + out << method << " " << uri.toString() << " "; + } + out << "HTTP/" << mMajorVersion << "." << mMinorVersion << "\r\n"; // Write fields @@ -116,6 +213,54 @@ bool Http::Request::hasField(const std::string& field) const { return mFields.find(String::toLower(field)) != mFields.end(); } +const std::string& Http::Request::getField(const std::string& field) const { + FieldTable::const_iterator it = mFields.find(String::toLower(field)); + if (it != mFields.end()) { + return it->second; + } else { + static const std::string empty = ""; + return empty; + } +} + +const char * Http::Response::statusToString( const Http::Response::Status& status ) { + switch ( status ) { + // 2xx: success + case Ok: return "OK"; + case Created: return "Created"; + case Accepted: return "Accepted"; + case NoContent: return "No Content"; + case ResetContent: return "Reset Content"; + case PartialContent: return "Partial Content"; + + // 3xx: redirection + case MultipleChoices: return "Multiple Choices"; + case MovedPermanently: return "Moved Permanently"; + case MovedTemporarily: return "Moved Temporarily"; + case NotModified: return "Not Modified"; + + // 4xx: client error + case BadRequest: return "BadRequest"; + case Unauthorized: return "Unauthorized"; + case Forbidden: return "Forbidden"; + case NotFound: return "Not Found"; + case RangeNotSatisfiable: return "Range Not Satisfiable"; + + // 5xx: server error + case InternalServerError: return "Internal Server Error"; + case NotImplemented: return "Not Implemented"; + case BadGateway: return "Bad Gateway"; + case ServiceNotAvailable: return "Service Not Available"; + case GatewayTimeout: return "Gateway Timeout"; + case VersionNotSupported: return "Version Not Supported"; + + // 10xx: Custom codes + case InvalidResponse: return "Invalid Response"; + case ConnectionFailed: return "Connection Failed"; + default: return ""; + } +} + Http::Response::Response() : mStatus (ConnectionFailed), mMajorVersion(0), @@ -137,10 +282,52 @@ const std::string& Http::Response::getField(const std::string& field) const { } } +bool Http::Response::hasField(const std::string & field) const { + return mFields.find(String::toLower(field)) != mFields.end(); +} + Http::Response::Status Http::Response::getStatus() const { return mStatus; } +const char * Http::Response::getStatusDescription() const { + switch ( mStatus ) { + // 2xx: success + case Ok: return "Successfull"; + case Created: return "The resource has successfully been created"; + case Accepted: return "The request has been accepted, but will be processed later by the server"; + case NoContent: return "The server didn't send any data in return"; + case ResetContent: return "The server informs the client that it should clear the view (form) that caused the request to be sent"; + case PartialContent: return "The server has sent a part of the resource, as a response to a partial GET request"; + + // 3xx: redirection + case MultipleChoices: return "The requested page can be accessed from several locations"; + case MovedPermanently: return "The requested page has permanently moved to a new location"; + case MovedTemporarily: return "The requested page has temporarily moved to a new location"; + case NotModified: return "For conditionnal requests, means the requested page hasn't changed and doesn't need to be refreshed"; + + // 4xx: client error + case BadRequest: return "The server couldn't understand the request (syntax error)"; + case Unauthorized: return "The requested page needs an authentification to be accessed"; + case Forbidden: return "The requested page cannot be accessed at all, even with authentification"; + case NotFound: return "The requested page doesn't exist"; + case RangeNotSatisfiable: return "The server can't satisfy the partial GET request (with a \"Range\" header field)"; + + // 5xx: server error + case InternalServerError: return "The server encountered an unexpected error"; + case NotImplemented: return "The server doesn't implement a requested feature"; + case BadGateway: return "The gateway server has received an error from the source server"; + case ServiceNotAvailable: return "The server is temporarily unavailable (overloaded, in maintenance, ...)"; + case GatewayTimeout: return "The gateway server couldn't receive a response from the source server"; + case VersionNotSupported: return "The server doesn't support the requested HTTP version"; + + // 10xx: Custom codes + case InvalidResponse: return "Response is not a valid HTTP one"; + case ConnectionFailed: return "Connection with server failed"; + default: return "Unknown response status"; + } +} + unsigned int Http::Response::getMajorHttpVersion() const { return mMajorVersion; } @@ -190,34 +377,7 @@ void Http::Response::parse(const std::string& data) { // Parse the other lines, which contain fields, one by one parseFields(in); - // Finally extract the body mBody.clear(); - - // Determine whether the transfer is chunked - if (String::toLower(getField("transfer-encoding")) != "chunked") { - // Not chunked - everything at once - std::copy(std::istreambuf_iterator(in), std::istreambuf_iterator(), std::back_inserter(mBody)); - } else { - // Chunked - have to read chunk by chunk - std::size_t length; - - // Read all chunks, identified by a chunk-size not being 0 - while (in >> std::hex >> length) { - // Drop the rest of the line (chunk-extension) - in.ignore(std::numeric_limits::max(), '\n'); - - // Copy the actual content data - std::istreambuf_iterator it(in); - for (std::size_t i = 0; i < length; i++) - mBody.push_back(*it++); - } - - // Drop the rest of the line (chunk-extension) - in.ignore(std::numeric_limits::max(), '\n'); - - // Read all trailers (if present) - parseFields(in); - } } void Http::Response::parseFields(std::istream &in) { @@ -248,32 +408,37 @@ Http::Http() : { } -Http::Http(const std::string& host, unsigned short port, bool useSSL) : +Http::Http(const std::string & host, unsigned short port, bool useSSL, URI proxy) : mConnection( NULL ), - mIsSSL( false ) + mHostName(host), + mPort(port), + mIsSSL( useSSL ), + mProxy(proxy) { - setHost(host, port, useSSL); + setHost(host, port, useSSL, proxy); } Http::~Http() { std::list::iterator itt; // First we wait to finish any request pending - for ( itt = mThreads.begin(); itt != mThreads.end(); ++itt ) { - (*itt)->wait(); + for ( auto&& itt : mThreads ) { + itt->wait(); } - for ( itt = mThreads.begin(); itt != mThreads.end(); ++itt ) { - eeDelete( *itt ); + for ( auto&& itt : mThreads ) { + eeDelete( itt ); } // Then we destroy the last open connection - TcpSocket * tcp = mConnection; + HttpConnection * connection = mConnection; - eeSAFE_DELETE( tcp ); + eeSAFE_DELETE( connection ); } -void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { +void Http::setHost(const std::string& host, unsigned short port, bool useSSL, URI proxy) { + mProxy = proxy; + bool sameHost( host == mHostName && port == mPort && useSSL == mIsSSL ); // Check the protocol @@ -306,79 +471,34 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { if (!mHostName.empty() && (*mHostName.rbegin() == '/')) mHostName.erase(mHostName.size() - 1); - mHost = IpAddress(mHostName); + if ( !mProxy.empty() ) { + mHost = IpAddress(mProxy.getHost()); + sameHost = false; + } else { + mHost = IpAddress(mHostName); + } // If the new host is different to the last set host // and there's an open connection to the host, we close // the old connection to prepare a new one. if ( !sameHost && NULL != mConnection ) { - TcpSocket * tcp = mConnection; - eeSAFE_DELETE( tcp ); + HttpConnection * connection = mConnection; + eeSAFE_DELETE( connection ); mConnection = NULL; } } Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { - if ( 0 == mHost.toInteger() ) { - return Response(); - } + IOStreamString stream; + Response response = downloadRequest( request, stream, timeout ); + response.mBody = std::move(stream.getStream()); + return response; +} - if ( NULL == mConnection ) { - TcpSocket * Conn = mIsSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : TcpSocket::New(); - mConnection = Conn; - } - - // First make sure that the request is valid -- add missing mandatory fields - Request toSend(prepareFields(request)); - - // Prepare the response - Response received; - - // Connect the socket to the host - if (mConnection->connect(mHost, mPort, timeout) == Socket::Done) { - // Convert the request to string and send it through the connected socket - std::string requestStr = toSend.prepare(); - - if (!requestStr.empty()) { - // Send it through the socket - if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { - // Wait for the server's response - std::string receivedStr; - std::size_t size = 0; - char buffer[1024]; - - while (mConnection->receive(buffer, sizeof(buffer), size) == Socket::Done) { - receivedStr.append(buffer, buffer + size); - } - - // Build the Response object from the received data - received.parse(receivedStr); - } - } - - // Close the connection - mConnection->disconnect(); - } - - // If a redirection is requested, and requests follows redirections, - // send a new request to the redirection location. - if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && - request.getFollowRedirect() ) { - - const_cast( request ).mRedirectionCount++; - - // Only continue redirecting if less than 10 redirections were done - if ( request.mRedirectionCount < 10 ) { - std::string location( received.getField("location") ); - URI uri( location ); - Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); - Http::Request newRequest( request ); - newRequest.setUri( uri.getPathEtc() ); - return http.sendRequest( request, timeout ); - } - } - - return received; +static bool sendProgress( const Http& http, const Http::Request& request, const Http::Response& response, const Http::Request::Status& status, const std::size_t& totalBytes, const std::size_t& currentBytes ) { + if ( request.getProgressCallback() ) + return request.getProgressCallback()( http, request, response, status, totalBytes, currentBytes ); + return true; } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { @@ -387,64 +507,179 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } if ( NULL == mConnection ) { - TcpSocket * Conn = mIsSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : TcpSocket::New(); - mConnection = Conn; + HttpConnection * connection = eeNew( HttpConnection, () ); + TcpSocket * socket = NULL; + + // If the http client is proxied and the end host use SSL + // We need to create an HTTP Tunnel against the proxy server + if ( isProxied() && mIsSSL && SSLSocket::isSupported() ) { + socket = SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ); + + connection->setSSL( true ); + } else { + bool isSSL = !isProxied() ? mIsSSL : ( SSLSocket::isSupported() && mProxy.getScheme() == "https" ); + + socket = isSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : + TcpSocket::New(); + + connection->setSSL( isSSL ); + } + + connection->setSocket( socket ); + + mConnection = connection; } + // First make sure that the request is valid -- add missing mandatory fields Request toSend(prepareFields(request)); + + // Prepare the response Response received; - if (mConnection->connect(mHost, mPort, timeout) == Socket::Done) { - std::string requestStr = toSend.prepare(); + // If not connected, try to connect to the server + if ( !mConnection->isConnected() ) { + // We need to create an HTTP Tunnel? + if ( isProxied() && mIsSSL && SSLSocket::isSupported() ) { + SSLSocket * sslSocket = reinterpret_cast( mConnection->getSocket() ); + + // For an HTTP Tunnel first we need to connect to the proxy server ( without TLS ) + if (sslSocket->tcpConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { + return received; + } else { + mConnection->setConnected(true); + } + } else { + if (mConnection->getSocket()->connect(mHost, mProxy.empty() ? mPort : mProxy.getPort(), timeout) != Socket::Done) { + return received; + } else { + mConnection->setConnected(true); + } + } + + if ( mConnection->isConnected() && !sendProgress( *this, request, received, Request::Connected, 0, 0 ) ) { + mConnection->disconnect(); + return received; + } + } + + // Connect the socket to the host + if (mConnection->isConnected()) { + // Create a HTTP Tunnel for SSL connections if not ready + if ( isProxied() && mIsSSL && !mConnection->isTunneled() ) { + // Create the HTTP Tunnel request + Request tunnelRequest; + std::string tunnelStr = tunnelRequest.prepareTunnel(*this); + + SSLSocket * sslSocket = reinterpret_cast( mConnection->getSocket() ); + std::size_t sent; + + // Send the request + if (sslSocket->tcpSend(tunnelStr.c_str(), tunnelStr.size(), sent) == Socket::Done) { + char buffer[PACKET_BUFFER_SIZE+1]; + std::size_t readed = 0; + + // Get the proxy server response + if (sslSocket->tcpReceive(buffer, PACKET_BUFFER_SIZE, readed) == Socket::Done) { + // Parse the HTTP Tunnel request response + Response tunnelResponse; + std::string header; + header.append( buffer, readed ); + tunnelResponse.parse(header); + + if ( tunnelResponse.getStatus() == Response::Ok ) { + // Stablish the SSL connection if the response is positive + if (sslSocket->sslConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { + return received; + } + } else { + return tunnelResponse; + } + } else { + return received; + } + + mConnection->setTunneled(true); + mConnection->setKeepAlive(true); + } + } + + if ( request.isContinue() ) { + std::size_t continueLength = writeTo.getSize(); + + if ( continueLength > 0 ) { + IOStreamString responseHeadBody; + Request requestHead = request; + requestHead.setContinue( false ); + requestHead.setMethod( Request::Head ); + Response responseHead = downloadRequest( requestHead, responseHeadBody ); + std::size_t contentLength = 0; + + if ( responseHead.hasField("Accept-Ranges") && + responseHead.hasField("Content-Length") && + String::fromString( contentLength, responseHead.getField("Content-Length") ) && + contentLength > 0 && + continueLength < contentLength + ) + { + writeTo.seek( continueLength ); + Request newRequest( request ); + newRequest.setContinue( false ); + newRequest.setField( "Range", String::format( "bytes=%lu-%lu", continueLength, contentLength ) ); + return downloadRequest( newRequest, writeTo, timeout ); + } + } + } + + // Convert the request to string and send it through the connected socket + std::string requestStr = toSend.prepare(*this); if (!requestStr.empty()) { - if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { - int isnheader = 0; - size_t len = 0; - char * eol; // end of line - char * bol; // beginning of line - std::size_t size = 0; - const size_t bufferSize = 1024; - char buffer[bufferSize+1]; - std::string header; + Socket::Status status; - while (mConnection->receive(buffer, bufferSize, size) == Socket::Done) { - if ( isnheader != 0 ) - writeTo.write( buffer, size ); + // Send it through the socket + if (mConnection->getSocket()->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { + if ( !sendProgress( *this, request, received, Request::Sent, 0, 0 ) ) { + request.mCancel = true; + } - if ( isnheader == 0 ) { + // Wait for the server's response + std::size_t currentTotalBytes = 0; + std::size_t len = 0; + std::size_t readed = 0; + char * eol = NULL; // end of line + char * bol = NULL; // beginning of line + char buffer[PACKET_BUFFER_SIZE+1]; + bool isnheader = false; + bool chunked = false; + bool compressed = false; + std::size_t contentLength = 0; + std::string headerBuffer; + HttpStreamChunked * chunkedStream = NULL; + IOStreamInflate * inflateStream = NULL; + IOStream * bufferStream = NULL; + + while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, PACKET_BUFFER_SIZE, readed) ) == Socket::Done) { + char * readBuffer = buffer; + + // If we didn't receive the header yet, we will try to find the end of the header + if ( !isnheader ) { // calculate combined length of unprocessed data and new data - len += size; + len += readed; // NULL terminate buffer for string functions - buffer[len] = '\0'; - - // checks if the header break happened to be the first line of the buffer - if ( !( strncmp( buffer, "\r\n", 2 ) ) ) { - if (len > 2) - writeTo.write(buffer, (len-2)); - - continue; - } - - if ( !( strncmp( buffer, "\n", 1 ) ) ) { - if ( len > 1 ) - writeTo.write(buffer, (len-1)); - - continue; - } + readBuffer[len] = '\0'; // process each line in buffer looking for header break - bol = buffer; + bol = readBuffer; - while( ( eol = strchr( bol, '\n') ) != NULL ) { + while( !isnheader && ( eol = strchr( bol, '\n') ) != NULL ) { // update bol based upon the value of eol bol = eol + 1; // test if end of headers has been reached - if ( ( !( strncmp( bol, "\r\n", 2 ) ) ) || ( ! ( strncmp( bol, "\n", 1) ) ) ) { + if ( 0 == strncmp( bol, "\r\n", 2 ) || 0 == strncmp( bol, "\n", 1) ) { // note that end of headers has been reached - isnheader = 1; + isnheader = true; // update the value of bol to reflect the beginning of the line // immediately after the headers @@ -454,63 +689,147 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri bol += 1; // calculate the amount of data remaining in the buffer - len = len - ( bol - buffer ); + len = readed - ( bol - readBuffer ); - // write remaining data to FILE stream - if ( len > 0 ) - writeTo.write( bol, len ); + // Fill the header buffer + headerBuffer.append( readBuffer, ( bol - readBuffer ) ); - header.append( buffer, ( bol - buffer ) ); + if ( !headerBuffer.empty() ) { + // Build the Response object from the received data + received.parse(headerBuffer); - // reset length of left over data to zero and continue processing - // non-header information - len = 0; + // Check if the response is chunked + chunked = received.getField("transfer-encoding") == "chunked"; + + // Check if the content is compressed + std::string encoding( received.getField("content-encoding") ); + compressed = encoding == "gzip" || encoding == "deflate"; + + if ( compressed ) { + Compression::Mode compressionMode = "gzip" == encoding ? Compression::MODE_GZIP : Compression::MODE_DEFLATE; + + inflateStream = IOStreamInflate::New( writeTo, compressionMode ); + } + + IOStream& writeToStream = compressed ? *inflateStream : writeTo; + + if ( chunked ) + chunkedStream = eeNew( HttpStreamChunked, ( writeToStream ) ); + + bufferStream = chunked ? chunkedStream : ( compressed ? inflateStream : &writeTo ); + + // Get the content length + if ( !received.getField("content-length").empty() ) { + if ( !String::fromString( contentLength, received.getField("content-length") ) ) + contentLength = 0; + } + + if ( received.getField("connection") == "closed" ) { + mConnection->setConnected(false); + mConnection->setTunneled(false); + } + + // If a redirection is requested, and requests follows redirections, + // send a new request to the redirection location. + if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && + request.getFollowRedirect() ) { + + // Only continue redirecting if less than 10 redirections were done + if ( request.mRedirectionCount < request.getMaxRedirects() ) { + std::string location( received.getField("location") ); + URI uri( location ); + Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); + Http::Request newRequest( request ); + newRequest.setUri( uri.getPathEtc() ); + + // Close the connection + if ( !mConnection->isKeepAlive() ) + mConnection->disconnect(); + + request.mRedirectionCount++; + + eeSAFE_DELETE( chunkedStream ); + eeSAFE_DELETE( inflateStream ); + return http.downloadRequest( request, writeTo, timeout ); + } + } + + if ( !sendProgress( *this, request, received, Request::HeaderReceived, contentLength, 0 ) ) { + request.mCancel = true; + } + + // Move the readBuffer to the starting point + // of the file buffer + if ( len > 0 ) { + readBuffer = bol; + readed = len; + } else { + readed = 0; + } + + headerBuffer.clear(); + } } } - if ( isnheader == 0 ) { - header.append( buffer, ( bol - buffer ) ); + if ( !isnheader ) { + headerBuffer.append( readBuffer, ( bol - readBuffer ) ); + } + } + + if ( isnheader ) { + currentTotalBytes += readed; + + if ( readed > 0 ) + bufferStream->write( readBuffer, readed ); + + if ( !sendProgress( *this, request, received, Request::ContentReceived, contentLength, currentTotalBytes ) ) { + request.mCancel = true; + break; + } + + // If the response is compressed and the stream ended means that we received + // the message. So we can skip the socket receive call. + if ( ( compressed && NULL != inflateStream && !inflateStream->isOpen() ) || + ( contentLength > 0 && contentLength == currentTotalBytes ) + ) { + break; } } } - if ( !header.empty() ) { - received.parse(header); - - // If a redirection is requested, and requests follows redirections, - // send a new request to the redirection location. - if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && - request.getFollowRedirect() ) { - - const_cast( request ).mRedirectionCount++; - - // Only continue redirecting if less than 10 redirections were done - if ( request.mRedirectionCount < 10 ) { - std::string location( received.getField("location") ); - URI uri( location ); - Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); - Http::Request newRequest( request ); - newRequest.setUri( uri.getPathEtc() ); - - // Close the connection - mConnection->disconnect(); - - return http.downloadRequest( request, writeTo, timeout ); - } - } + if ( chunked && NULL != chunkedStream && !chunkedStream->getHeaderBuffer().empty() ) { + headerBuffer.append( chunkedStream->getHeaderBuffer() ); } + + if ( !headerBuffer.empty() ) { + std::istringstream in(headerBuffer); + received.parseFields(in); + } + + if ( status == Socket::Status::Disconnected ) { + mConnection->setConnected(false); + mConnection->setTunneled(false); + } + + eeSAFE_DELETE( chunkedStream ); + eeSAFE_DELETE( inflateStream ); + } else { + mConnection->setConnected(false); + mConnection->setTunneled(false); } } // Close the connection - mConnection->disconnect(); + if ( !mConnection->isKeepAlive() ) + mConnection->disconnect(); } return received; } -Http::Response Http::downloadRequest(const Http::Request & request, std::string writePath, Time timeout) { - IOStreamFile file( writePath, "wb" ); +Http::Response Http::downloadRequest(const Http::Request& request, std::string writePath, Time timeout) { + IOStreamFile file( writePath, request.isContinue() ? "ab+" : "wb+" ); return downloadRequest( request, file, timeout ); } @@ -565,8 +884,8 @@ void Http::AsyncRequest::run() { } // The Async Request destroys the socket used to create the request - TcpSocket * tcp = mHttp->mConnection; - eeSAFE_DELETE( tcp ); + HttpConnection * connection = mHttp->mConnection; + eeSAFE_DELETE( connection ); mHttp->mConnection = NULL; mRunning = false; @@ -595,16 +914,14 @@ void Http::removeOldThreads() { } } -Http::Request Http::prepareFields(const Http::Request & request) { +Http::Request Http::prepareFields(const Http::Request& request) { Request toSend(request); - if (!toSend.hasField("User-Agent")) { + if (!toSend.hasField("User-Agent")) toSend.setField("User-Agent", "eepp-network"); - } - if (!toSend.hasField("Host")) { + if (!toSend.hasField("Host")) toSend.setField("Host", mHostName); - } if (!toSend.hasField("Content-Length")) { std::ostringstream out; @@ -612,17 +929,40 @@ Http::Request Http::prepareFields(const Http::Request & request) { toSend.setField("Content-Length", out.str()); } - if ((toSend.mMethod == Request::Post) && !toSend.hasField("Content-Type")) { + if ((toSend.mMethod == Request::Post) && !toSend.hasField("Content-Type")) toSend.setField("Content-Type", "application/x-www-form-urlencoded"); + + if ((toSend.mMajorVersion * 10 + toSend.mMinorVersion >= 11) && !toSend.hasField("Connection")) + toSend.setField("Connection", "close"); + + if (!mProxy.empty()) { + toSend.setField("Accept", "*/*"); + + if ( mIsSSL ) { + toSend.setField("Proxy-connection", "keep-alive"); + } else { + toSend.setField("Proxy-connection", "close"); + } } - if ((toSend.mMajorVersion * 10 + toSend.mMinorVersion >= 11) && !toSend.hasField("Connection")) { - toSend.setField("Connection", "close"); - } + if ( request.isCompressedResponse() ) + toSend.setField("Accept-Encoding", "gzip, deflate"); return toSend; } +void Http::setProxy(const URI& uri) { + setHost( mHostName, mPort, mIsSSL, uri ); +} + +const URI& Http::getProxy() const { + return mProxy; +} + +bool Http::isProxied() const { + return !mProxy.empty(); +} + void Http::sendAsyncRequest( AsyncResponseCallback cb, const Http::Request& request, Time timeout ) { AsyncRequest * thread = eeNew( AsyncRequest, ( this, cb, request, timeout ) ); @@ -662,11 +1002,11 @@ void Http::downloadAsyncRequest(Http::AsyncResponseCallback cb, const Http::Requ mThreads.push_back( thread ); } -const IpAddress &Http::getHost() const { +const IpAddress& Http::getHost() const { return mHost; } -const std::string &Http::getHostName() const { +const std::string& Http::getHostName() const { return mHostName; } @@ -674,4 +1014,78 @@ const unsigned short& Http::getPort() const { return mPort; } +const bool& Http::isSSL() const { + return mIsSSL; +} + +URI Http::getURI() const { + return URI( String::format( "%s://%s:%d", mIsSSL ? "https" : "http", mHostName.c_str(), mPort ) ); +} + +Http::HttpConnection::HttpConnection() : + mSocket(NULL), + mIsConnected(false), + mIsTunneled(false), + mIsSSL(false), + mIsKeepAlive(false) +{} + +Http::HttpConnection::HttpConnection(TcpSocket * socket) : + mSocket( socket ), + mIsConnected( false ), + mIsTunneled( false ), + mIsSSL( false ) +{} + +Http::HttpConnection::~HttpConnection() { + eeSAFE_DELETE(mSocket); +} + +void Http::HttpConnection::setSocket(TcpSocket * socket) { + mSocket = socket; +} + +TcpSocket *Http::HttpConnection::getSocket() const { + return mSocket; +} + +void Http::HttpConnection::disconnect() { + if ( NULL != mSocket ) + mSocket->disconnect(); + + mIsConnected = false; +} + +const bool &Http::HttpConnection::isConnected() const { + return mIsConnected; +} + +void Http::HttpConnection::setConnected(const bool & connected) { + mIsConnected = connected; +} + +const bool &Http::HttpConnection::isTunneled() const { + return mIsTunneled; +} + +void Http::HttpConnection::setTunneled(const bool & tunneled) { + mIsTunneled = tunneled; +} + +const bool &Http::HttpConnection::isSSL() const { + return mIsSSL; +} + +void Http::HttpConnection::setSSL(const bool & ssl) { + mIsSSL = ssl; +} + +const bool &Http::HttpConnection::isKeepAlive() const { + return mIsKeepAlive; +} + +void Http::HttpConnection::setKeepAlive(const bool & isKeepAlive) { + mIsKeepAlive = isKeepAlive; +} + }} diff --git a/src/eepp/network/http/httpstreamchunked.cpp b/src/eepp/network/http/httpstreamchunked.cpp new file mode 100644 index 000000000..806fa055e --- /dev/null +++ b/src/eepp/network/http/httpstreamchunked.cpp @@ -0,0 +1,112 @@ + #include + +namespace EE { namespace Network { namespace Private { + +HttpStreamChunked::HttpStreamChunked(IOStream & mWriteTo) : + mWriteTo( mWriteTo ), + mChunkNewBuffer(false), + mChunkEnded(false) +{} + +ios_size HttpStreamChunked::write(const char * data, ios_size size) { + ios_size writeTotal = 0; + + // If the chunk reading ended we just add the buffer received as a header + // Otherwise we process the buffer data as chunk + if ( !mChunkEnded ) { + // Keep a chunk buffer until the end of chunk is found + mChunkBuffer.append( data, size ); + + // If the new chunk starts with \r\n and the last removed chunk + // did not contain the trailing \r\n, we remove it to detect + // correctly the next length data + if ( mChunkNewBuffer ) { + if ( mChunkBuffer.substr( 0, 2 ) == "\r\n" ) { + mChunkBuffer = mChunkBuffer.substr( 2 ); + } + + mChunkNewBuffer = false; + } + + bool retry; + + do { + retry = false; + + // Check for the first \r\n to find the end of the length definition + std::string::size_type lenEnd = mChunkBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + std::string::size_type firstCharPos = lenEnd + 2; + unsigned long length; + + // Get the length of the chunk + bool res = String::fromString( length, mChunkBuffer.substr(0, lenEnd), std::hex ); + + // If the length is solved... + if ( res ) { + // And it's bigger than 0, means that there are more chunks + if ( length > 0 ) { + // Check if the chunk buffer size at least equals to the length reported + if ( mChunkBuffer.size() - firstCharPos >= length ) { + // In that case write the chunk to the file buffer + writeTotal = mWriteTo.write( &mChunkBuffer[firstCharPos], length ); + + // And keep the remaining not completed chunk + mChunkBuffer = mChunkBuffer.substr( firstCharPos + length ); + + // Check if already have the \r\n of the next length in the buffer + if ( !mChunkBuffer.empty() ) { + std::size_t pos = 0; + + // Remove al the \r\n remaining + while ( pos < mChunkBuffer.size() && 0 == strncmp( &mChunkBuffer[pos], "\r\n", 2 ) ) { + pos += 2; + } + + if ( pos > 0 ) { + // Remove it to be able to read the next length + mChunkBuffer = mChunkBuffer.substr( pos ); + } + + // If still the chunk is not empty it could be another chunk + // already received, so we check that retrying + if ( !mChunkBuffer.empty() ) { + std::string::size_type lenEnd = mChunkBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + bool res = String::fromString( length, mChunkBuffer.substr(0, lenEnd), std::hex ); + + if ( res && length > 0 ) { + retry = true; + } + } + } + } + + // If the next chunk received starts with \r\n it's because + // it's part of the chunk size information, so we need to flag it + // to remove it + mChunkNewBuffer = true; + } + } else { + // If the value is 0 means that the data ended + // But after this we can receive extra headers + mChunkEnded = true; + mChunkBuffer.clear(); + } + } + } + } while ( retry ); + } else { + mHeaderBuffer.append( data, size ); + } + + return writeTotal; +} + +const std::string& HttpStreamChunked::getHeaderBuffer() const { + return mHeaderBuffer; +} + +}}} diff --git a/src/eepp/network/http/httpstreamchunked.hpp b/src/eepp/network/http/httpstreamchunked.hpp new file mode 100644 index 000000000..7f4f0d122 --- /dev/null +++ b/src/eepp/network/http/httpstreamchunked.hpp @@ -0,0 +1,28 @@ +#ifndef EE_NETWORK_HTTPSTREAMCHUNKED_HPP +#define EE_NETWORK_HTTPSTREAMCHUNKED_HPP + +#include +#include + +using namespace EE::System; + +namespace EE { namespace Network { namespace Private { + +class HttpStreamChunked : public IOStreamString { + public: + HttpStreamChunked( IOStream& mWriteTo ); + + ios_size write( const char * data, ios_size size ); + + const std::string& getHeaderBuffer() const; + protected: + IOStream& mWriteTo; + std::string mChunkBuffer; + std::string mHeaderBuffer; + bool mChunkNewBuffer = false; + bool mChunkEnded = false; +}; + +}}} + +#endif // EE_NETWORK_HTTPSTREAMCHUNKED_HPP diff --git a/src/eepp/network/platform/unix/socketimpl.cpp b/src/eepp/network/platform/unix/socketimpl.cpp index 8b175ff1d..c003e0bff 100644 --- a/src/eepp/network/platform/unix/socketimpl.cpp +++ b/src/eepp/network/platform/unix/socketimpl.cpp @@ -57,6 +57,20 @@ Socket::Status SocketImpl::getErrorStatus() { } } +void SocketImpl::setSendTimeout(SocketHandle sock, const Time& timeout) { + struct timeval time; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1000000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1000000); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time, sizeof time); +} + +void SocketImpl::setReceiveTimeout(SocketHandle sock, const Time & timeout) { + struct timeval time; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1000000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1000000); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time, sizeof time); +} + }}} #endif diff --git a/src/eepp/network/platform/unix/socketimpl.hpp b/src/eepp/network/platform/unix/socketimpl.hpp index 0cc0e5f9f..3cf6dbfca 100644 --- a/src/eepp/network/platform/unix/socketimpl.hpp +++ b/src/eepp/network/platform/unix/socketimpl.hpp @@ -5,6 +5,7 @@ #if defined( EE_PLATFORM_POSIX ) +#include #include #include #include @@ -14,6 +15,8 @@ #include #include +using namespace EE::System; + namespace EE { namespace Network { namespace Private { /** @brief Helper class implementing all the non-portable socket stuff; this is the Unix version */ @@ -44,6 +47,12 @@ class SocketImpl { /** Get the last socket error status ** @return Status corresponding to the last socket error */ static Socket::Status getErrorStatus(); + + /** Set the send timeout */ + static void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout */ + static void setReceiveTimeout(SocketHandle sock, const Time& timeout); }; }}} diff --git a/src/eepp/network/platform/win/socketimpl.cpp b/src/eepp/network/platform/win/socketimpl.cpp index a773244e3..433f853f0 100644 --- a/src/eepp/network/platform/win/socketimpl.cpp +++ b/src/eepp/network/platform/win/socketimpl.cpp @@ -42,6 +42,16 @@ Socket::Status SocketImpl::getErrorStatus() { } } +void SocketImpl::setSendTimeout(SocketHandle sock, const Time& timeout) { + DWORD time = timeout.asMilliseconds(); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time, sizeof time); +} + +void SocketImpl::setReceiveTimeout(SocketHandle sock, const Time & timeout) { + DWORD time = timeout.asMilliseconds(); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time, sizeof time); +} + /** Windows needs some initialization and cleanup to get ** sockets working properly... so let's create a class that will do it automatically */ struct SocketInitializer diff --git a/src/eepp/network/platform/win/socketimpl.hpp b/src/eepp/network/platform/win/socketimpl.hpp index ea241cbcd..2a2d2d4bb 100644 --- a/src/eepp/network/platform/win/socketimpl.hpp +++ b/src/eepp/network/platform/win/socketimpl.hpp @@ -47,6 +47,12 @@ class SocketImpl { /** Get the last socket error status ** @return Status corresponding to the last socket error */ static Socket::Status getErrorStatus(); + + /** Set the send timeout */ + static void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout */ + static void setReceiveTimeout(SocketHandle sock, const Time& timeout); }; }}} diff --git a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp index cf341e472..ef6791e7c 100644 --- a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp +++ b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp @@ -14,7 +14,7 @@ bool MbedTLSSocket::init() { mbedtls_x509_crt_init(&sCACert); //! Load the certificates and config - SafeDataPointer data; + ScopedBuffer data; if ( FileSystem::fileExists( SSLSocket::CertificatesPath ) ) { FileSystem::fileGet( SSLSocket::CertificatesPath, data ); @@ -28,12 +28,12 @@ bool MbedTLSSocket::init() { } } - if ( data.size > 0 ) { - SafeDataPointer dataZeroEnded( data.size + 1 ); - memcpy( dataZeroEnded.data, data.data, data.size ); - dataZeroEnded.data[ data.size ] = '\0'; + if ( data.length() > 0 ) { + ScopedBuffer dataZeroEnded( data.length() + 1 ); + memcpy( dataZeroEnded.get(), data.get(), data.length() ); + dataZeroEnded[ data.length() ] = '\0'; - int err = mbedtls_x509_crt_parse( &sCACert, (const unsigned char*)dataZeroEnded.data, dataZeroEnded.size ); + int err = mbedtls_x509_crt_parse( &sCACert, (const unsigned char*)dataZeroEnded.get(), dataZeroEnded.length() ); if ( err != 0 ) { char errStr[ 1024 ]; @@ -72,7 +72,7 @@ int MbedTLSSocket::bio_send(void *ctx, const unsigned char *buf, size_t len) { MbedTLSSocket *sp = (MbedTLSSocket *)ctx; size_t sent; - Socket::Status err = sp->mSSLSocket->tcp_send((const void*)buf, len, sent); + Socket::Status err = sp->mSSLSocket->tcpSend((const void*)buf, len, sent); if (err != Socket::Done) { return MBEDTLS_ERR_SSL_INTERNAL_ERROR; @@ -90,7 +90,7 @@ int MbedTLSSocket::bio_recv(void *ctx, unsigned char *buf, size_t len) { MbedTLSSocket *sp = (MbedTLSSocket *)ctx; size_t got; - Socket::Status err = sp->mSSLSocket->tcp_receive(buf, len, got); + Socket::Status err = sp->mSSLSocket->tcpReceive(buf, len, got); if (err != Socket::Done) { return MBEDTLS_ERR_SSL_INTERNAL_ERROR; diff --git a/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp b/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp index 43063f4e7..994f6b482 100644 --- a/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp +++ b/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp @@ -152,13 +152,13 @@ bool OpenSSLSocket::init() { //! Load the certificates and config if ( FileSystem::fileExists( SSLSocket::CertificatesPath ) ) { - SafeDataPointer data; + ScopedBuffer data; FileSystem::fileGet( SSLSocket::CertificatesPath, data ); - if ( data.size > 0 ) { + if ( data.length() > 0 ) { BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts( mem, (const char*) data.data ); + BIO_puts( mem, (const char*) data.get() ); while( true ) { X509 * cert = PEM_read_bio_X509(mem, NULL, 0, NULL); diff --git a/src/eepp/network/ssl/sslsocket.cpp b/src/eepp/network/ssl/sslsocket.cpp index fff6a3fb2..5967b8f39 100644 --- a/src/eepp/network/ssl/sslsocket.cpp +++ b/src/eepp/network/ssl/sslsocket.cpp @@ -134,16 +134,17 @@ SSLSocket::~SSLSocket() { Socket::Status SSLSocket::connect( const IpAddress& remoteAddress, unsigned short remotePort, Time timeout ) { Status status = Socket::Disconnected; - if ( ( status = TcpSocket::connect( remoteAddress, remotePort, timeout ) ) == Socket::Done ) { - status = mImpl->connect( remoteAddress, remotePort, timeout ); + if ( ( status = tcpConnect( remoteAddress, remotePort, timeout ) ) == Socket::Done ) { + status = sslConnect( remoteAddress, remotePort, timeout ); } return status; } void SSLSocket::disconnect() { - mImpl->disconnect(); - TcpSocket::disconnect(); + sslDisconnect(); + + tcpDisconnect(); } Socket::Status SSLSocket::send(const void* data, std::size_t size) { @@ -162,11 +163,27 @@ Socket::Status SSLSocket::receive(Packet& packet) { return TcpSocket::receive( packet ); } -Socket::Status SSLSocket::tcp_receive(void * data, std::size_t size, std::size_t & received) { +Socket::Status SSLSocket::sslConnect(const IpAddress & remoteAddress, unsigned short remotePort, Time timeout) { + return mImpl->connect( remoteAddress, remotePort, timeout ); +} + +void SSLSocket::sslDisconnect() { + mImpl->disconnect(); +} + +Socket::Status SSLSocket::tcpConnect(const IpAddress & remoteAddress, unsigned short remotePort, Time timeout) { + return TcpSocket::connect( remoteAddress, remotePort, timeout ); +} + +void SSLSocket::tcpDisconnect() { + TcpSocket::disconnect(); +} + +Socket::Status SSLSocket::tcpReceive(void * data, std::size_t size, std::size_t & received) { return TcpSocket::receive( data, size, received ); } -Socket::Status SSLSocket::tcp_send(const void * data, std::size_t size, std::size_t & sent) { +Socket::Status SSLSocket::tcpSend(const void * data, std::size_t size, std::size_t & sent) { return TcpSocket::send( data, size, sent ); } diff --git a/src/eepp/network/tcpsocket.cpp b/src/eepp/network/tcpsocket.cpp index ed1e257b6..b0cfde2b8 100644 --- a/src/eepp/network/tcpsocket.cpp +++ b/src/eepp/network/tcpsocket.cpp @@ -316,6 +316,18 @@ Socket::Status TcpSocket::receive(Packet& packet) { return Done; } +void TcpSocket::setSendTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setSendTimeout(getHandle(), timeout); + } +} + +void TcpSocket::setReceiveTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setReceiveTimeout(getHandle(), timeout); + } +} + TcpSocket::PendingPacket::PendingPacket() : Size (0), SizeReceived(0), diff --git a/src/eepp/network/udpsocket.cpp b/src/eepp/network/udpsocket.cpp index 0dcf8a7ac..0f9883975 100644 --- a/src/eepp/network/udpsocket.cpp +++ b/src/eepp/network/udpsocket.cpp @@ -61,8 +61,7 @@ Socket::Status UdpSocket::send(const void* data, std::size_t size, const IpAddre create(); // Make sure that all the data will fit in one datagram - if (size > MaxDatagramSize) - { + if (size > MaxDatagramSize) { eePRINTL( "Cannot send data over the network (the number of bytes to send is greater than UdpSocket::MaxDatagramSize)" ); return Error; } @@ -143,5 +142,16 @@ Socket::Status UdpSocket::receive(Packet& packet, IpAddress& remoteAddress, unsi return status; } +void UdpSocket::setSendTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setSendTimeout(getHandle(), timeout); + } +} + +void UdpSocket::setReceiveTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setReceiveTimeout(getHandle(), timeout); + } +} }} diff --git a/src/eepp/network/uri.cpp b/src/eepp/network/uri.cpp index f3c41c154..c1f734827 100644 --- a/src/eepp/network/uri.cpp +++ b/src/eepp/network/uri.cpp @@ -411,6 +411,17 @@ void URI::getPathSegments(std::vector& segments) { getPathSegments(mPath, segments); } +std::string URI::getLastPathSegment() { + std::vector segments; + getPathSegments( segments ); + + if ( !segments.empty() ) { + return segments[ segments.size() - 1 ]; + } + + return ""; +} + void URI::getPathSegments(const std::string& path, std::vector& segments) { std::string::const_iterator it = path.begin(); std::string::const_iterator end = path.end(); diff --git a/src/eepp/system/compression.cpp b/src/eepp/system/compression.cpp new file mode 100644 index 000000000..38c056453 --- /dev/null +++ b/src/eepp/system/compression.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +#include + +#define DEFLATE_CHUNK_SIZE (16384) + +namespace EE { namespace System { + +Compression::Status Compression::compress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode, const Config& config) { + IOStreamMemory srcMem( (const char*)src, srcSize ); + IOStreamMemory dstMem( (char*)dst, dstMaxSize ); + return compress( dstMem, srcMem, mode, config ); +} + +Compression::Status Compression::compress(IOStream& dst, IOStream& src, Compression::Mode mode, const Config& config) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + int ret, flush; + ios_size have; + z_stream strm = {}; + char in[DEFLATE_CHUNK_SIZE]; + char out[DEFLATE_CHUNK_SIZE]; + int level = mode == MODE_DEFLATE ? config.zlib.level : config.gzip.level; + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + ret = deflateInit2(&strm, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) + return (Status)ret; + + src.seek(0); + + do { + strm.avail_in = src.read(in, DEFLATE_CHUNK_SIZE); + if ( strm.avail_in == 0 ) { + deflateEnd(&strm); + return Status::ERRNO; + } + + flush = src.tell() == src.getSize() ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = (unsigned char*)in; + + do { + strm.avail_out = DEFLATE_CHUNK_SIZE; + strm.next_out = (unsigned char*)out; + + ret = deflate(&strm, flush); + + if ( ret == Z_STREAM_ERROR ) + return Status::STREAM_ERROR; + + have = DEFLATE_CHUNK_SIZE - strm.avail_out; + + if ( dst.write(out, have) != have ) { + deflateEnd(&strm); + + return Status::ERRNO; + } + } while (strm.avail_out == 0); + + if (strm.avail_in != 0) + return Status::DATA_ERROR; + } while (flush != Z_FINISH); + } + } + + return Status::OK; +} + +int Compression::getMaxCompressedBufferSize(Uint64 srcSize, Mode mode, const Config&) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + int windowBits = mode == MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + z_stream strm = {}; + int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + return -1; + int aout = deflateBound(&strm, srcSize); + deflateEnd(&strm); + return aout; + } + } + + return -1; +} + +Compression::Status Compression::decompress(Uint8* dst, Uint64 dstMaxSize, const Uint8 * src, Uint64 srcSize, Mode mode) { + IOStreamMemory srcMem( (const char*)src, srcSize ); + IOStreamMemory dstMem( (char*)dst, dstMaxSize ); + return decompress( dstMem, srcMem, mode ); +} + +Compression::Status Compression::decompress(IOStream& dst, IOStream& src, Mode mode) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + ScopedBuffer buffer( DEFLATE_CHUNK_SIZE ); + ScopedBuffer bufferDst( DEFLATE_CHUNK_SIZE ); + + src.seek( 0 ); + + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + z_stream strm = {}; + strm.next_in = buffer.get(); + + int err = inflateInit2(&strm, windowBits); + if (err != Z_OK) + return (Status)err; + + int zlibStatus; + int bytesRead; + unsigned int have; + Uint32 totalSize = src.getSize(); + Uint32 totalRead = 0; + + while ( totalRead < totalSize ) { + bytesRead = src.read( (char*)buffer.get(), buffer.length() ); + + strm.avail_in = bytesRead; + strm.next_in = buffer.get(); + + do { + strm.avail_out = bufferDst.length(); + strm.next_out = bufferDst.get(); + zlibStatus = inflate(&strm, Z_NO_FLUSH); + + switch (zlibStatus) { + case Z_OK: + case Z_STREAM_END: + case Z_BUF_ERROR: + break; + default: + inflateEnd(&strm); + return (Status)zlibStatus; + } + + have = bufferDst.length()- strm.avail_out; + + dst.write( (const char*)bufferDst.get(), have ); + } while (strm.avail_out == 0); + + totalRead += bytesRead; + } + + inflateEnd(&strm); + + return Status::OK; + } + } + + return Status::ERRNO; +} + +std::size_t Compression::getModeDefaultChunkSize(const Mode&) { + return DEFLATE_CHUNK_SIZE; +} + +}} diff --git a/src/eepp/system/directorypack.cpp b/src/eepp/system/directorypack.cpp index e171877a9..2b36fa16f 100644 --- a/src/eepp/system/directorypack.cpp +++ b/src/eepp/system/directorypack.cpp @@ -88,7 +88,7 @@ bool DirectoryPack::extractFileToMemory( const std::string& path, std::vector ( data.data ), data.size ); + fs.read( reinterpret_cast ( data.get() ), data.length() ); return true; } @@ -88,10 +85,8 @@ bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) { Int64 allocate = ( size < chunksize ) ? size : chunksize; Int64 copysize = 0; - SafeDataPointer data; - data.size = (Uint32)allocate; - data.data = eeNewArray( Uint8, ( data.size ) ); - char * buff = (char*)data.data; + TScopedBuffer data( allocate ); + char * buff = data.get(); IOStreamFile in( src, "rb" ); IOStreamFile out( dst, "wb" ); @@ -105,7 +100,7 @@ bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) { } in.read ( &buff[0], copysize ); - out.write ( (const char*)&buff[0], copysize ); + out.write ( &buff[0], copysize ); size_left -= copysize; } while ( size_left > 0 ); @@ -580,4 +575,24 @@ Int64 FileSystem::getDiskFreeSpace(const std::string& path) { #endif } +std::string FileSystem::fileGetNumberedFileNameFromPath(std::string directoryPath, const std::string& fileName, const std::string& separator, const std::string& fileExtension) { + Uint32 fileNum = 1; + std::string fileNumName; + + if ( FileSystem::isDirectory( directoryPath ) ) { + dirPathAddSlashAtEnd( directoryPath ); + + while ( fileNum < 10000 ) { + fileNumName = String::format( std::string( "%s" + separator + "%d%s" ).c_str(), fileName.c_str(), fileNum, fileExtension.empty() ? "" : std::string( "." + fileExtension ).c_str() ); + + if ( !FileSystem::fileExists( directoryPath + fileNumName ) ) + return fileNumName; + + fileNum++; + } + } + + return ""; +} + }} diff --git a/src/eepp/system/inifile.cpp b/src/eepp/system/inifile.cpp index 43720618e..66f833187 100755 --- a/src/eepp/system/inifile.cpp +++ b/src/eepp/system/inifile.cpp @@ -56,11 +56,11 @@ IniFile::IniFile( IOStream& stream, const bool& shouldReadFile ) : bool IniFile::loadFromPack( Pack * Pack, std::string iniPackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( iniPackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( iniPackPath, PData ); + Pack->extractFileToMemory( iniPackPath, buffer ); - return loadFromMemory( PData.data, PData.size ); + return loadFromMemory( buffer.get(), buffer.length() ); } return false; diff --git a/src/eepp/system/iostreamdeflate.cpp b/src/eepp/system/iostreamdeflate.cpp new file mode 100644 index 000000000..f8c52afc3 --- /dev/null +++ b/src/eepp/system/iostreamdeflate.cpp @@ -0,0 +1,193 @@ +#include + +#include + +namespace EE { namespace System { + +struct LocalStreamData { + z_stream strm; + int state; + bool writedStream; +}; + +IOStreamDeflate * IOStreamDeflate::New(IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config) { + return eeNew( IOStreamDeflate, ( inOutStream, mode ) ); +} + +IOStreamDeflate::IOStreamDeflate(IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config) : + mStream(inOutStream), + mMode(mode), + mBuffer(Compression::getModeDefaultChunkSize(mode)), + mLocalStream(eeNew(LocalStreamData,())) +{ + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + int level = mode == Compression::MODE_DEFLATE ? config.zlib.level : config.gzip.level; + + mLocalStream->strm = z_stream{}; + mLocalStream->writedStream = false; + + mLocalStream->state = deflateInit2(&mLocalStream->strm, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); +} + +IOStreamDeflate::~IOStreamDeflate() { + if (mStream.isOpen() && mLocalStream->writedStream) { + z_stream& zstr = mLocalStream->strm; + + if (zstr.next_out) { + int rc = deflate(&zstr, Z_FINISH); + + if (rc != Z_OK && rc != Z_STREAM_END) return; + + mStream.write((char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); + + if (!mStream.isOpen()) return; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + + while (rc != Z_STREAM_END) { + rc = deflate(&zstr, Z_FINISH); + + if (rc != Z_OK && rc != Z_STREAM_END) return; + + mStream.write((char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); + + if (!mStream.isOpen()) return; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + } + } + } + + deflateEnd( &mLocalStream->strm ); + + eeSAFE_DELETE( mLocalStream ); +} + +ios_size IOStreamDeflate::read(char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + bool eof = false; + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if ( mStream.isOpen()) { + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.get(); + zstr.avail_in = n; + } else { + zstr.next_in = NULL; + zstr.avail_in = 0; + eof = true; + } + } + + zstr.next_out = (unsigned char*) buffer; + zstr.avail_out = length; + + for (;;) { + int rc = deflate(&zstr, eof ? Z_FINISH : Z_NO_FLUSH); + + if (eof && rc == Z_STREAM_END) { + return length - zstr.avail_out; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) + return static_cast(length); + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if (mStream.isOpen()) { + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.get(); + zstr.avail_in = n; + } else { + zstr.next_in = NULL; + zstr.avail_in = 0; + eof = true; + } + } + } +} + +ios_size IOStreamDeflate::write(const char * buffer, ios_size length) { + mLocalStream->writedStream = true; + + if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + zstr.next_in = (unsigned char*) buffer; + zstr.avail_in = length; + zstr.next_out = mBuffer.get(); + zstr.avail_out = mBuffer.length(); + + for (;;) { + int rc = deflate(&zstr, Z_NO_FLUSH); + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length()); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + } + + if (zstr.avail_in == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + + break; + } + } + + return length; +} + +ios_size IOStreamDeflate::seek(ios_size position) { + return mStream.seek( position ); +} + +ios_size IOStreamDeflate::tell() { + return mStream.tell(); +} + +ios_size IOStreamDeflate::getSize() { + return mStream.getSize(); +} + +bool IOStreamDeflate::isOpen() { + return mStream.isOpen(); +} + +const Compression::Mode& IOStreamDeflate::getMode() const { + return mMode; +} + +}} diff --git a/src/eepp/system/iostreamfile.cpp b/src/eepp/system/iostreamfile.cpp index ee6f3ebe8..770f5b76b 100644 --- a/src/eepp/system/iostreamfile.cpp +++ b/src/eepp/system/iostreamfile.cpp @@ -15,9 +15,7 @@ IOStreamFile::IOStreamFile( const std::string& path, const char * modes ) : } IOStreamFile::~IOStreamFile() { - if ( isOpen() ) { - std::fclose(mFS); - } + close(); } ios_size IOStreamFile::read( char * data, ios_size size ) { @@ -80,4 +78,12 @@ void IOStreamFile::flush() { std::fflush( mFS ); } +void IOStreamFile::close() { + if ( isOpen() ) { + std::fclose(mFS); + + mFS = NULL; + } +} + }} diff --git a/src/eepp/system/iostreaminflate.cpp b/src/eepp/system/iostreaminflate.cpp new file mode 100644 index 000000000..ee6d2f176 --- /dev/null +++ b/src/eepp/system/iostreaminflate.cpp @@ -0,0 +1,165 @@ +#include + +#include + +namespace EE { namespace System { + +struct LocalStreamData { + z_stream strm; + int state; +}; + +IOStreamInflate * IOStreamInflate::New(IOStream& inOutStream, Compression::Mode mode) { + return eeNew( IOStreamInflate, ( inOutStream, mode ) ); +} + +IOStreamInflate::IOStreamInflate(IOStream& inOutStream, Compression::Mode mode) : + mStream(inOutStream), + mMode(mode), + mBuffer(Compression::getModeDefaultChunkSize(mode)), + mLocalStream(eeNew(LocalStreamData,())) +{ + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + mLocalStream->strm = z_stream{}; + + mLocalStream->state = inflateInit2(&mLocalStream->strm, windowBits); +} + +IOStreamInflate::~IOStreamInflate() { + inflateEnd( &mLocalStream->strm ); + + eeSAFE_DELETE( mLocalStream ); +} + +ios_size IOStreamInflate::read(char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if ( mStream.isOpen()) { + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); + } + + zstr.next_in = (unsigned char*) mBuffer.get(); + zstr.avail_in = n; + } + + zstr.next_out = (unsigned char*) buffer; + zstr.avail_out = length; + + for (;;) { + int rc = inflate(&zstr, Z_NO_FLUSH); + + if (rc == Z_DATA_ERROR) { + if (zstr.avail_in == 0) { + if (mStream.isOpen()) + rc = Z_OK; + else + rc = Z_STREAM_END; + } + } + + if (rc == Z_STREAM_END) { + return length - zstr.avail_out; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) + return static_cast(length); + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if (mStream.isOpen()) { + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.get(); + zstr.avail_in = n; + } else { + return length - zstr.avail_out; + } + } + } +} + +ios_size IOStreamInflate::write(const char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + zstr.next_in = (unsigned char*) buffer; + zstr.avail_in = length; + zstr.next_out = mBuffer.get(); + zstr.avail_out = mBuffer.length(); + + for (;;) { + int rc = inflate(&zstr, Z_NO_FLUSH); + + if (rc == Z_STREAM_END) { + length = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); + + mLocalStream->state = rc; + + break; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length()); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + } + + if (zstr.avail_in == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); + + break; + } + } + + return length; +} + +ios_size IOStreamInflate::seek(ios_size position) { + return mStream.seek( position ); +} + +ios_size IOStreamInflate::tell() { + return mStream.tell(); +} + +ios_size IOStreamInflate::getSize() { + return mStream.getSize(); +} + +bool IOStreamInflate::isOpen() { + return mStream.isOpen() && mLocalStream->state != Z_STREAM_END; +} + +const Compression::Mode& IOStreamInflate::getMode() const { + return mMode; +} + +}} diff --git a/src/eepp/system/iostreamstring.cpp b/src/eepp/system/iostreamstring.cpp new file mode 100644 index 000000000..daa5cc12a --- /dev/null +++ b/src/eepp/system/iostreamstring.cpp @@ -0,0 +1,68 @@ +#include +#include + +namespace EE { namespace System { + +IOStreamString::IOStreamString() : + mPos(0) +{} + +ios_size IOStreamString::read(char * data, ios_size size) { + Int64 endPosition = mPos + size; + Int64 count = endPosition <= getSize() ? size : getSize() - mPos; + + if ( count > 0 ) { + memcpy( data, &mStream[mPos], static_cast( count ) ); + mPos += count; + } + + return count; +} + +ios_size IOStreamString::write(const char * data, ios_size size) { + mStream.insert( mPos, data, size ); + + mPos += size; + + return size; +} + +ios_size IOStreamString::write(const std::string& string) { + return write( string.c_str(), string.size() ); +} + +ios_size IOStreamString::seek(ios_size position) { + mPos = ( position < getSize() ) ? position : getSize(); + return mPos; +} + +ios_size IOStreamString::tell() { + return getSize(); +} + +ios_size IOStreamString::getSize() { + return mStream.size(); +} + +bool IOStreamString::isOpen() { + return true; +} + +void IOStreamString::clear() { + mStream.clear(); + mPos = 0; +} + +const char* IOStreamString::getPositionPointer() { + return &mStream[mPos]; +} + +const char* IOStreamString::getStreamPointer() const { + return mStream.c_str(); +} + +const std::string& IOStreamString::getStream() const { + return mStream; +} + +}} diff --git a/src/eepp/system/iostreamzip.cpp b/src/eepp/system/iostreamzip.cpp index f777e3364..5f61f2fca 100644 --- a/src/eepp/system/iostreamzip.cpp +++ b/src/eepp/system/iostreamzip.cpp @@ -58,8 +58,8 @@ ios_size IOStreamZip::seek( ios_size position ) { mFile = zip_fopen_index( mZip, zs.index, 0 ); if ( 0 != position ) { - SafeDataPointer ptr( position ); - read( (char*)ptr.data, position ); + ScopedBuffer ptr( position ); + read( (char*)ptr.get(), position ); } mPos = position; diff --git a/src/eepp/system/pak.cpp b/src/eepp/system/pak.cpp index 6b49350a5..2e1c713b2 100755 --- a/src/eepp/system/pak.cpp +++ b/src/eepp/system/pak.cpp @@ -133,10 +133,10 @@ bool Pak::extractFile( const std::string& path , const std::string& dest ) { Int32 Pos = exists( path ); if ( Pos != -1 ) { - SafeDataPointer data; + ScopedBuffer data; if ( extractFileToMemory( path, data ) ) { - FileSystem::fileWrite( path, data.data, data.size ); + FileSystem::fileWrite( path, data.get(), data.length() ); } Ret = true; @@ -173,7 +173,7 @@ bool Pak::extractFileToMemory( const std::string& path, std::vector& data return Ret; } -bool Pak::extractFileToMemory( const std::string& path, SafeDataPointer& data ) { +bool Pak::extractFileToMemory( const std::string& path, ScopedBuffer& data ) { if ( NULL == mPak.fs || !mPak.fs->isOpen() ) { return false; } @@ -185,11 +185,10 @@ bool Pak::extractFileToMemory( const std::string& path, SafeDataPointer& data ) Int32 Pos = exists( path ); if ( Pos != -1 ) { - data.size = mPakFiles[Pos].file_length; - data.data = eeNewArray( Uint8, ( data.size ) ); + data.reset( mPakFiles[Pos].file_length ); mPak.fs->seek( mPakFiles[Pos].file_position ); - mPak.fs->read( reinterpret_cast ( data.data ), mPakFiles[Pos].file_length ); + mPak.fs->read( reinterpret_cast ( data.get() ), data.length() ); Ret = true; } @@ -279,11 +278,11 @@ bool Pak::addFile( const std::string& path, const std::string& inpack ) { if ( path.size() > 56 ) return false; - SafeDataPointer file; + ScopedBuffer file; FileSystem::fileGet( path, file ); - return addFile( file.data, file.size, inpack ); + return addFile( file.get(), file.length(), inpack ); } bool Pak::addFiles( std::map paths ) { diff --git a/src/eepp/system/rc4.cpp b/src/eepp/system/rc4.cpp index 345dbb08c..200064fed 100644 --- a/src/eepp/system/rc4.cpp +++ b/src/eepp/system/rc4.cpp @@ -72,13 +72,13 @@ bool RC4::encryptFile( const std::string& SourceFile, const std::string& DestFil if ( !FileSystem::fileExists( SourceFile ) ) return false; - SafeDataPointer data; + ScopedBuffer data; FileSystem::fileGet( SourceFile, data ); - encryptByte( data.data, data.size ); + encryptByte( data.get(), data.length() ); - FileSystem::fileWrite( DestFile, data.data, data.size ); + FileSystem::fileWrite( DestFile, data.get(), data.length() ); return true; } diff --git a/src/eepp/system/translator.cpp b/src/eepp/system/translator.cpp index ceff51e46..06efba65d 100644 --- a/src/eepp/system/translator.cpp +++ b/src/eepp/system/translator.cpp @@ -114,11 +114,11 @@ void Translator::loadFromStream( IOStream& stream, std::string lang ) { return; ios_size bufferSize = stream.getSize(); - SafeDataPointer safeDataPointer( bufferSize ); - stream.read( reinterpret_cast( safeDataPointer.data ), safeDataPointer.size ); + TScopedBuffer scopedBuffer( bufferSize ); + stream.read( scopedBuffer.get(), scopedBuffer.length() ); pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_buffer( safeDataPointer.data, safeDataPointer.size ); + pugi::xml_parse_result result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length() ); if ( result ) { loadNodes( doc.first_child(), lang ); @@ -130,12 +130,12 @@ void Translator::loadFromStream( IOStream& stream, std::string lang ) { } void Translator::loadFromPack( Pack * pack, const std::string& FilePackPath, std::string lang ) { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, PData ) ) { + if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, buffer ) ) { lang = lang.size() == 2 ? lang : FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( FilePackPath ) ); - loadFromMemory( PData.data, PData.size, lang ); + loadFromMemory( buffer.get(), buffer.length(), lang ); } } diff --git a/src/eepp/system/zip.cpp b/src/eepp/system/zip.cpp index b94a01f3b..f2bc5fada 100644 --- a/src/eepp/system/zip.cpp +++ b/src/eepp/system/zip.cpp @@ -80,11 +80,11 @@ bool Zip::close() { } bool Zip::addFile( const std::string& path, const std::string& inpack ) { - SafeDataPointer file; + ScopedBuffer file; FileSystem::fileGet( path, file ); - return addFile( file.data, file.size, inpack ); + return addFile( file.get(), file.length(), inpack ); } bool Zip::addFile( const Uint8 * data, const Uint32& dataSize, const std::string& inpack ) { @@ -148,12 +148,12 @@ bool Zip::extractFile( const std::string& path , const std::string& dest ) { bool Ret; - SafeDataPointer data; + ScopedBuffer data; Ret = extractFileToMemory( path, data ); if ( Ret ) - FileSystem::fileWrite( dest, data.data, data.size ); + FileSystem::fileWrite( dest, data.get(), data.length() ); unlock(); @@ -194,7 +194,7 @@ bool Zip::extractFileToMemory( const std::string& path, std::vector& data return Ret; } -bool Zip::extractFileToMemory( const std::string& path, SafeDataPointer& data ) { +bool Zip::extractFileToMemory( const std::string& path, ScopedBuffer& data ) { lock(); bool Ret = false; @@ -210,10 +210,9 @@ bool Zip::extractFileToMemory( const std::string& path, SafeDataPointer& data ) struct zip_file * zf = zip_fopen_index( mZip, zs.index, 0 ); if ( NULL != zf ) { - data.size = (Uint32)zs.size; - data.data = eeNewArray( Uint8, ( data.size ) ); + data.reset( zs.size ); - Result = (Int32)zip_fread( zf, (void*)data.data, data.size ); + Result = (Int32)zip_fread( zf, (void*)data.get(), data.length() ); zip_fclose(zf); diff --git a/src/eepp/ui/css/stylesheetparser.cpp b/src/eepp/ui/css/stylesheetparser.cpp index 529b6a448..2c33b8831 100644 --- a/src/eepp/ui/css/stylesheetparser.cpp +++ b/src/eepp/ui/css/stylesheetparser.cpp @@ -42,10 +42,10 @@ bool StyleSheetParser::loadFromPack( Pack * pack, std::string filePackPath ) { bool Ret = false; - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, PData ) ) { - Ret = loadFromMemory( PData.data, PData.size ); + if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, buffer ) ) { + Ret = loadFromMemory( buffer.get(), buffer.length() ); } return Ret; diff --git a/src/eepp/ui/uimenu.cpp b/src/eepp/ui/uimenu.cpp index c6876a85c..e88a8c8d0 100644 --- a/src/eepp/ui/uimenu.cpp +++ b/src/eepp/ui/uimenu.cpp @@ -259,7 +259,7 @@ void UIMenu::insert( UINode * Control, const Uint32& Index ) { bool UIMenu::isSubMenu( Node * Ctrl ) { for ( Uint32 i = 0; i < mItems.size(); i++ ) { - if ( mItems[i]->isType( UI_TYPE_MENUSUBMENU ) ) { + if ( NULL != mItems[i] && mItems[i]->isType( UI_TYPE_MENUSUBMENU ) ) { UIMenuSubMenu * tMenu = reinterpret_cast ( mItems[i] ); if ( tMenu->getSubMenu() == Ctrl ) diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 74655dbce..cc454ee3c 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -209,11 +209,11 @@ UIWidget * UISceneNode::loadLayoutFromStream( IOStream& stream, Node * parent ) return NULL; ios_size bufferSize = stream.getSize(); - SafeDataPointer safeDataPointer( eeNewArray( Uint8, bufferSize ), bufferSize ); - stream.read( reinterpret_cast( safeDataPointer.data ), safeDataPointer.size ); + TScopedBuffer scopedBuffer( bufferSize ); + stream.read( scopedBuffer.get(), scopedBuffer.length() ); pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_buffer( safeDataPointer.data, safeDataPointer.size ); + pugi::xml_parse_result result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length() ); if ( result ) { return loadLayoutNodes( doc.first_child(), NULL != parent ? parent : this ); @@ -227,10 +227,10 @@ UIWidget * UISceneNode::loadLayoutFromStream( IOStream& stream, Node * parent ) } UIWidget * UISceneNode::loadLayoutFromPack( Pack * pack, const std::string& FilePackPath, Node * parent ) { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, PData ) ) { - return loadLayoutFromMemory( PData.data, PData.size, parent ); + if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, buffer ) ) { + return loadLayoutFromMemory( buffer.get(), buffer.length(), parent ); } return NULL; diff --git a/src/eepp/window/platform/x11/cursorx11.cpp b/src/eepp/window/platform/x11/cursorx11.cpp index 142cf2853..735aef5bf 100644 --- a/src/eepp/window/platform/x11/cursorx11.cpp +++ b/src/eepp/window/platform/x11/cursorx11.cpp @@ -38,7 +38,7 @@ CursorX11::CursorX11( const std::string& path, const Vector2i& hotspot, const st CursorX11::~CursorX11() { if ( None != mCursor ) - XFreeCursor( getPlatform()->GetDisplay(), mCursor ); + XFreeCursor( getPlatform()->getDisplay(), mCursor ); } void CursorX11::create() { @@ -65,11 +65,11 @@ void CursorX11::create() { image->xhot = mHotSpot.x; image->yhot = mHotSpot.y; - getPlatform()->Lock(); + getPlatform()->lock(); - mCursor = XcursorImageLoadCursor( getPlatform()->GetDisplay(), image ); + mCursor = XcursorImageLoadCursor( getPlatform()->getDisplay(), image ); - getPlatform()->Unlock(); + getPlatform()->unlock(); XcursorImageDestroy( image ); } diff --git a/src/eepp/window/platform/x11/x11impl.cpp b/src/eepp/window/platform/x11/x11impl.cpp index a4a5b8db3..1ad9a199b 100644 --- a/src/eepp/window/platform/x11/x11impl.cpp +++ b/src/eepp/window/platform/x11/x11impl.cpp @@ -40,18 +40,18 @@ X11Impl::~X11Impl() { } void X11Impl::minimizeWindow() { - Lock(); + lock(); XIconifyWindow( mDisplay, mX11Window, 0 ); XFlush( mDisplay ); - Unlock(); + unlock(); } void X11Impl::maximizeWindow() { // coded by Rafał Maj, idea from Måns Rullgård http://tinyurl.com/68mvk3 - Lock(); + lock(); XEvent xev; Atom wm_state = XAtom( "_NET_WM_STATE" ); @@ -71,11 +71,11 @@ void X11Impl::maximizeWindow() { XFlush(mDisplay); - Unlock(); + unlock(); } bool X11Impl::isWindowMaximized() { - Lock(); + lock(); //bool minimized = false; bool maximizedhorz = false; @@ -117,7 +117,7 @@ bool X11Impl::isWindowMaximized() { XFlush(mDisplay); - Unlock(); + unlock(); if( maximizedhorz && maximizedvert ) { return true; @@ -127,45 +127,45 @@ bool X11Impl::isWindowMaximized() { } void X11Impl::hideWindow() { - Lock(); + lock(); XUnmapWindow( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::raiseWindow() { - Lock(); + lock(); XRaiseWindow( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::showWindow() { - Lock(); + lock(); XMapRaised( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::moveWindow( int left, int top ) { - Lock(); + lock(); XMoveWindow( mDisplay, mX11Window, left, top ); XFlush( mDisplay ); - Unlock(); + unlock(); } void X11Impl::setContext( eeWindowContex Context ) { - Lock(); + lock(); glXMakeCurrent( mDisplay, mX11Window, Context ); - Unlock(); + unlock(); } Vector2i X11Impl::getPosition() { @@ -181,20 +181,20 @@ void X11Impl::showMouseCursor() { if ( !mCursorHidden ) return; - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); mCursorHidden = false; - Unlock(); + unlock(); } void X11Impl::hideMouseCursor() { if ( mCursorHidden ) return; - Lock(); + lock(); if ( mCursorInvisible == None ) { unsigned long gcmask; @@ -225,7 +225,7 @@ void X11Impl::hideMouseCursor() { mCursorHidden = true; - Unlock(); + unlock(); } Cursor * X11Impl::createMouseCursor( Texture * tex, const Vector2i& hotspot, const std::string& name ) { @@ -244,21 +244,21 @@ void X11Impl::setMouseCursor( Cursor * cursor ) { mCursorCurrent = reinterpret_cast( cursor )->GetCursor(); if ( !mCursorHidden ) { - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); - Unlock(); + unlock(); } } void X11Impl::restoreCursor() { if ( !mCursorHidden ) { - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); - Unlock(); + unlock(); } else { hideMouseCursor(); } @@ -287,7 +287,7 @@ void X11Impl::setSystemMouseCursor( Cursor::SysType syscursor ) { XFreeCursor( mDisplay, mCursorSystemLast ); } - Lock(); + lock(); mCursorCurrent = XCreateFontCursor( mDisplay, cursor_shape ); mCursorSystemLast = mCursorCurrent; @@ -296,19 +296,19 @@ void X11Impl::setSystemMouseCursor( Cursor::SysType syscursor ) { XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); } - Unlock(); + unlock(); } -eeWindowHandle X11Impl::GetDisplay() const { +eeWindowHandle X11Impl::getDisplay() const { return mDisplay; } -void X11Impl::Lock() { +void X11Impl::lock() { if ( NULL != mLock ) mLock(); } -void X11Impl::Unlock() { +void X11Impl::unlock() { if ( NULL != mUnlock ) mUnlock(); } diff --git a/src/eepp/window/platform/x11/x11impl.hpp b/src/eepp/window/platform/x11/x11impl.hpp index 529bc8cca..f1446c117 100644 --- a/src/eepp/window/platform/x11/x11impl.hpp +++ b/src/eepp/window/platform/x11/x11impl.hpp @@ -54,11 +54,11 @@ class X11Impl : public PlatformImpl { void restoreCursor(); - eeWindowHandle GetDisplay() const; + eeWindowHandle getDisplay() const; - void Lock(); + void lock(); - void Unlock(); + void unlock(); eeWindowContex getWindowContext(); protected: diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index f821f9647..1324d5291 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -1,26 +1,50 @@ #include #include +// Prints the response headers +void printResponseHeaders( Http::Response& response ) { + Http::Response::FieldTable headers = response.getHeaders(); + + std::cout << "HTTP/" << response.getMajorHttpVersion() << "." << response.getMinorHttpVersion() << " " << response.getStatus() << " " << Http::Response::statusToString( response.getStatus() ) << std::endl; + + for ( auto&& head : headers ) { + std::cout << head.first << ": " << head.second << std::endl; + } + + std::cout << std::endl; +} + EE_MAIN_FUNC int main (int argc, char * argv []) { args::ArgumentParser parser("HTTP request program example"); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); - args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"} ); - args::Positional url(parser, "url", "The url to request"); - args::Flag verbose(parser, "verbose", "Prints the request response headers", {'v',"verbose"} ); + args::Flag resume(parser, "continue", "Resume getting a partially-downloaded file", {'c',"continue"}); + args::Flag compressed(parser, "compressed", "Request compressed response", {"compressed"}); + args::ValueFlag postData(parser, "data", "HTTP POST data", {'d', "data"}); + args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); + args::Flag includeHead(parser, "include", "Include protocol response headers in the output", {'i',"include"}); + args::Flag insecure(parser, "insecure", "Allow insecure server connections when using SSL", {'k',"insecure"}); + args::Flag location(parser, "location", "Follow redirects", {'L',"location"}); + args::ValueFlag maxRedirs(parser, "max-redirs", "Maximum number of redirects allowed", {"max-redirs"}); + args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"}); + args::ValueFlag proxy(parser, "proxy", "[protocol://]host[:port] Use this proxy", {'x', "proxy"}); + args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"}); + args::Flag verbose(parser, "verbose", "Make the operation more talkative", {'v',"verbose"}); + args::ValueFlag requestMethod(parser, "request", "Specify request command to use", {'X', "request"}); + args::Positional url(parser, "URL", "The URL to request"); try { parser.ParseCLI(argc, argv); } catch (const args::Help&) { std::cout << parser; - return 0; + return EXIT_SUCCESS; } catch (const args::ParseError& e) { std::cerr << e.what() << std::endl; std::cerr << parser; - return 1; + return EXIT_FAILURE; } catch (args::ValidationError& e) { std::cerr << e.what() << std::endl; std::cerr << parser; - return 1; + return EXIT_FAILURE; } { @@ -36,10 +60,7 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { http.setHost("http://en.wikipedia.org"); } - // Prepare a request to get the wikipedia main page - request.setUri("/wiki/Main_Page"); - - // Creates an async http request + // Creates an async http request and set the path requested Http::Request asyncRequest( "/wiki/" + Version::getCodename() ); http.sendAsyncRequest([]( const Http& http, Http::Request& request, Http::Response& response ) { @@ -52,19 +73,83 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { } }, asyncRequest, Seconds( 5 ) ); } else { - // If the user provided the URI, creates an instance of URI to parse it. + // If the user provided the URL, creates an instance of URI to parse it. URI uri( url.Get() ); + // If no scheme provided asume HTTP if ( uri.getScheme().empty() ) { uri = URI( "http://" + url.Get() ); } + // Allow insecure connections if requested + if ( insecure ) { + request.setValidateCertificate(false); + request.setValidateHostname(false); + } + // Set the host and port from the URI http.setHost( uri.getHost(), uri.getPort() ); // Set the path and query parts for the request request.setUri( uri.getPathEtc() ); + // Set the headers + for ( const std::string& header : args::get(headers) ) { + std::string::size_type pos = header.find_first_of( ':' ); + if ( std::string::npos != pos ) { + std::string key( header.substr( 0, pos ) ); + std::string val( String::trim( header.substr( pos + 1 ) ) ); + request.setField(key, val); + } + } + + // Set the request method + if ( requestMethod ) { + request.setMethod( Http::Request::methodFromString( requestMethod.Get() ) ); + } + + // Set the post data / body + if ( postData ) { + request.setBody( postData.Get() ); + } + + // If progress requested print a progress on screen + if ( progress ) { + request.setProgressCallback( []( const Http&, const Http::Request&, const Http::Response&, const Http::Request::Status& status, size_t totalBytes, size_t currentBytes ) { + if ( status == Http::Request::ContentReceived ) { + std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; + std::cout << std::flush; + } + return true; + }); + } + + // Set follow redirect + request.setFollowRedirect(location.Get()); + + // Set the maximun number of redirects + if ( maxRedirs ) { + request.setMaxRedirects(maxRedirs.Get()); + } + + // Set the proxy for the request + char * http_proxy = getenv( "http_proxy" ); + if ( !proxy && NULL != http_proxy ) { + http.setProxy( URI( http_proxy ) ); + } else if ( proxy ) { + http.setProxy( URI( proxy.Get() ) ); + } + + // Request a compressed response + if ( compressed ) { + request.setCompressedResponse( true ); + } + + // Resume existing download + if ( resume ) { + request.setContinue( true ); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); @@ -72,25 +157,44 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { // Check the status code and display the result Http::Response::Status status = response.getStatus(); + if ( includeHead ) + printResponseHeaders(response); + if ( status == Http::Response::Ok ) { - if ( verbose ) { - Http::Response::FieldTable headers = response.getHeaders(); - - std::cout << "Headers: " << std::endl; - - for ( auto&& head : headers ) { - std::cout << "\t" << head.first << ": " << head.second << std::endl; - } - - std::cout << std::endl << "Body: " << std::endl; - } - - std::cout << response.getBody() << std::endl; + std::cout << response.getBody(); } else { - std::cout << "Error " << status << std::endl; + std::cout << "Error " << status << std::endl << response.getStatusDescription() << std::endl; + std::cout << response.getBody(); } } else { - http.downloadRequest(request, output.Get(), Seconds(5)); + std::string path( output.Get() ); + + // If output path is a directory guess a file name + if ( FileSystem::isDirectory( path ) ) { + FileSystem::dirPathAddSlashAtEnd( path ); + + std::string lastPathSegment = uri.getLastPathSegment(); + + // If there's a path end segment + if ( !lastPathSegment.empty() ) { + + // Save with the path end segment name + if ( !FileSystem::fileExists( path + lastPathSegment ) ) { + path += lastPathSegment; + } else { + path += FileSystem::fileGetNumberedFileNameFromPath( path, lastPathSegment ); + } + } else { + // Create a file name if no name found + path += FileSystem::fileGetNumberedFileNameFromPath( path, "eepp-network-file", "-" ); + } + } + + // Download the request response into a file + Http::Response response = http.downloadRequest(request, path, Seconds(5)); + + if ( includeHead ) + printResponseHeaders(response); } } }