diff options
Diffstat (limited to 'Sockets/HttpPostSocket.cpp')
-rw-r--r-- | Sockets/HttpPostSocket.cpp | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/Sockets/HttpPostSocket.cpp b/Sockets/HttpPostSocket.cpp new file mode 100644 index 0000000..a72b8cf --- /dev/null +++ b/Sockets/HttpPostSocket.cpp @@ -0,0 +1,294 @@ +/** \file HttpPostSocket.cpp + ** \date 2004-10-30 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004-2007 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.net. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif +#else +#include <errno.h> +#include <ctype.h> +#endif +#include "ISocketHandler.h" +#include <sys/types.h> +#include <sys/stat.h> +#include "Parse.h" +#include "Utility.h" +#include "Lock.h" + +#include "HttpPostSocket.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +int HttpPostSocket::m_boundary_count = 0; +Mutex HttpPostSocket::m_boundary_mutex; + + +HttpPostSocket::HttpPostSocket(ISocketHandler& h) : HttpClientSocket(h) +,m_bMultipart(false) +{ +} + + +HttpPostSocket::HttpPostSocket(ISocketHandler& h,const std::string& url_in) : HttpClientSocket(h, url_in) +,m_bMultipart(false) +{ + Lock lock(m_boundary_mutex); + + m_boundary = "----"; + for (int i = 0; i < 12; i++) + { + char c = m_boundary_count++ % 128; + while (!isalnum(c)) + c = m_boundary_count++ % 128; + m_boundary += c; + } + m_boundary += "__" + Utility::l2string(m_boundary_count++); +} + + +HttpPostSocket::~HttpPostSocket() +{ +} + + +void HttpPostSocket::AddField(const std::string& name,const std::string& value) +{ + std::list<std::string> vec; + vec.push_back(value); + AddMultilineField(name, vec); +} + + +void HttpPostSocket::AddMultilineField(const std::string& name,std::list<std::string>& values) +{ + m_fields[name] = values; +} + + +void HttpPostSocket::AddFile(const std::string& name,const std::string& filename,const std::string& type) +{ + struct stat st; + if (!stat(filename.c_str(), &st)) + { + m_files[name] = filename; + m_content_length[filename] = st.st_size; + m_content_type[filename] = type; + m_bMultipart = true; + } + else + { + Handler().LogError(this, "AddFile", Errno, StrError(Errno), LOG_LEVEL_FATAL); + SetCloseAndDelete(); + } +} + + +void HttpPostSocket::Open() +{ + // why do I have to specify TcpSocket:: to get to the Open() method?? + TcpSocket::Open(GetUrlHost(), GetUrlPort()); +} + + +void HttpPostSocket::OnConnect() +{ + if (m_bMultipart) + { + DoMultipartPost(); + } + else + { + std::string body; + + // only fields, no files, add urlencoding + for (std::map<std::string,std::list<std::string> >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list<std::string>& ref = (*it).second; + if (body.size()) + { + body += '&'; + } + body += name + "="; + bool first = true; + for (std::list<std::string>::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + if (!first) + { + body += "%0d%0a"; // CRLF + } + body += Utility::rfc1738_encode(value); + first = false; + } + } + + // build header, send body + SetMethod("POST"); + SetHttpVersion( "HTTP/1.1" ); + AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding.. + AddResponseHeader( "User-agent", MyUseragent()); + AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" ); + AddResponseHeader( "Connection", "close" ); + AddResponseHeader( "Content-type", "application/x-www-form-urlencoded" ); + AddResponseHeader( "Content-length", Utility::l2string((long)body.size()) ); + SendRequest(); + + // send body + Send( body ); + } +} + + +void HttpPostSocket::DoMultipartPost() +{ + long length = 0; // calculate content_length of our post body + std::string tmp; + + // fields + { + for (std::map<std::string,std::list<std::string> >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list<std::string>& ref = (*it).second; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"\r\n" + "\r\n"; + for (std::list<std::string>::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + tmp += value + "\r\n"; + } + length += (long)tmp.size(); + } + } + + // files + { + for (std::map<std::string,std::string>::iterator it = m_files.begin(); it != m_files.end(); it++) + { + std::string name = (*it).first; + std::string filename = (*it).second; + long content_length = m_content_length[filename]; + std::string content_type = m_content_type[filename]; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n" + "content-type: " + content_type + "\r\n" + "\r\n"; + length += (long)tmp.size(); + length += content_length; + length += 2; // crlf after file + } + } + + // end + tmp = "--" + m_boundary + "--\r\n"; + length += (long)tmp.size(); + + // build header, send body + SetMethod("POST"); + SetHttpVersion( "HTTP/1.1" ); + AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding.. + AddResponseHeader( "User-agent", MyUseragent()); + AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" ); + AddResponseHeader( "Connection", "close" ); + AddResponseHeader( "Content-type", "multipart/form-data; boundary=" + m_boundary ); + AddResponseHeader( "Content-length", Utility::l2string(length) ); + + SendRequest(); + + // send fields + { + for (std::map<std::string,std::list<std::string> >::iterator it = m_fields.begin(); it != m_fields.end(); it++) + { + std::string name = (*it).first; + std::list<std::string>& ref = (*it).second; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"\r\n" + "\r\n"; + for (std::list<std::string>::iterator it = ref.begin(); it != ref.end(); it++) + { + std::string value = *it; + tmp += value + "\r\n"; + } + Send( tmp ); + } + } + + // send files + { + for (std::map<std::string,std::string>::iterator it = m_files.begin(); it != m_files.end(); it++) + { + std::string name = (*it).first; + std::string filename = (*it).second; + std::string content_type = m_content_type[filename]; + tmp = "--" + m_boundary + "\r\n" + "content-disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n" + "content-type: " + content_type + "\r\n" + "\r\n"; + Send( tmp ); + { + FILE *fil = fopen(filename.c_str(),"rb"); + if (fil) + { + char slask[2000]; // for fread + size_t n; + while ((n = fread(slask, 1, 2000, fil)) > 0) + { + SendBuf(slask, n); + } + fclose(fil); + } + } + Send("\r\n"); + } + } + + // end of send + Send("--" + m_boundary + "--\r\n"); +} + + +void HttpPostSocket::SetMultipart() +{ + m_bMultipart = true; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + + |