/** \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 #include #endif #include "ISocketHandler.h" #include #include #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 vec; vec.push_back(value); AddMultilineField(name, vec); } void HttpPostSocket::AddMultilineField(const std::string& name,std::list& 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 >::iterator it = m_fields.begin(); it != m_fields.end(); it++) { std::string name = (*it).first; std::list& ref = (*it).second; if (body.size()) { body += '&'; } body += name + "="; bool first = true; for (std::list::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 >::iterator it = m_fields.begin(); it != m_fields.end(); it++) { std::string name = (*it).first; std::list& ref = (*it).second; tmp = "--" + m_boundary + "\r\n" "content-disposition: form-data; name=\"" + name + "\"\r\n" "\r\n"; for (std::list::iterator it = ref.begin(); it != ref.end(); it++) { std::string value = *it; tmp += value + "\r\n"; } length += (long)tmp.size(); } } // files { for (std::map::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 >::iterator it = m_fields.begin(); it != m_fields.end(); it++) { std::string name = (*it).first; std::list& ref = (*it).second; tmp = "--" + m_boundary + "\r\n" "content-disposition: form-data; name=\"" + name + "\"\r\n" "\r\n"; for (std::list::iterator it = ref.begin(); it != ref.end(); it++) { std::string value = *it; tmp += value + "\r\n"; } Send( tmp ); } } // send files { for (std::map::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