/**
** 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 <stdio.h>
#ifdef _WIN32
#pragma warning(disable:4786)
#else
#include <errno.h>
#include <ctype.h>
#endif
#include "SocketHandler.h"
#include <sys/types.h>
#include <sys/stat.h>
#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<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(m_host, m_port);
}
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", 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<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", 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<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];
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;
}
syntax highlighted by Code2HTML, v. 0.9.1