/** ** File ......... HttpPostSocket.cpp ** Published .... 2004-10-30 ** Author ....... grymse@alhem.net **/ /* Copyright (C) 2004,2005 Anders Hedstrom 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. */ //#include #ifdef _WIN32 #pragma warning(disable:4786) #else #include #include #endif #include "SocketHandler.h" #include #include #include "Parse.h" #include "Utility.h" #include "HttpPostSocket.h" HttpPostSocket::HttpPostSocket(SocketHandler& h,const std::string& url) :HTTPSocket(h) ,m_port(80) ,m_bMultipart(false) { std::string host; { Parse pa(url,"/"); pa.getword(); // 'http:' host = pa.getword(); SetUrl( "/" + pa.getrest() ); } { Parse pa(host,":"); m_host = pa.getword(); m_port = (port_t)pa.getvalue(); if (!m_port) { m_port = 80; } } std::string m_boundary = "----"; for (int i = 0; i < 12; i++) { char c = 0; while (!isalnum(c)) { c = rand() % 96 + 32; } m_boundary += c; } } 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(m_host, m_port); } 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", m_host ); // 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", m_host ); // 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]; 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::OnFirst() { int status = atoi(GetStatus().c_str()); printf("Response status %d: %s\n", status, GetStatusText().c_str()); } void HttpPostSocket::OnHeader(const std::string& ,const std::string& ) { } void HttpPostSocket::OnHeaderComplete() { SetCloseAndDelete(); } void HttpPostSocket::OnData(const char *,size_t) { } void HttpPostSocket::SetMultipart() { m_bMultipart = true; }