1 /** \file HttpPostSocket.cpp
3 ** \author grymse@alhem.net
6 Copyright (C) 2004-2007 Anders Hedstrom
8 This library is made available under the terms of the GNU GPL.
10 If you would like to use this library in a closed-source application,
11 a separate license agreement is available. For information about
12 the closed-source license agreement for the C++ sockets library,
13 please visit http://www.alhem.net/Sockets/license.html and/or
14 email license@alhem.net.
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU General Public License
18 as published by the Free Software Foundation; either version 2
19 of the License, or (at your option) any later version.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #pragma warning(disable:4786)
33 #pragma warning(disable:4503)
39 #include "ISocketHandler.h"
40 #include <sys/types.h>
46 #include "HttpPostSocket.h"
49 #ifdef SOCKETS_NAMESPACE
50 namespace SOCKETS_NAMESPACE
{
54 int HttpPostSocket::m_boundary_count
= 0;
55 Mutex
HttpPostSocket::m_boundary_mutex
;
58 HttpPostSocket::HttpPostSocket(ISocketHandler
& h
) : HttpClientSocket(h
)
64 HttpPostSocket::HttpPostSocket(ISocketHandler
& h
,const std::string
& url_in
) : HttpClientSocket(h
, url_in
)
67 Lock
lock(m_boundary_mutex
);
70 for (int i
= 0; i
< 12; i
++)
72 char c
= m_boundary_count
++ % 128;
74 c
= m_boundary_count
++ % 128;
77 m_boundary
+= "__" + Utility::l2string(m_boundary_count
++);
81 HttpPostSocket::~HttpPostSocket()
86 void HttpPostSocket::AddField(const std::string
& name
,const std::string
& value
)
88 std::list
<std::string
> vec
;
90 AddMultilineField(name
, vec
);
94 void HttpPostSocket::AddMultilineField(const std::string
& name
,std::list
<std::string
>& values
)
96 m_fields
[name
] = values
;
100 void HttpPostSocket::AddFile(const std::string
& name
,const std::string
& filename
,const std::string
& type
)
103 if (!stat(filename
.c_str(), &st
))
105 m_files
[name
] = filename
;
106 m_content_length
[filename
] = st
.st_size
;
107 m_content_type
[filename
] = type
;
112 Handler().LogError(this, "AddFile", Errno
, StrError(Errno
), LOG_LEVEL_FATAL
);
118 void HttpPostSocket::Open()
120 // why do I have to specify TcpSocket:: to get to the Open() method??
121 TcpSocket::Open(GetUrlHost(), GetUrlPort());
125 void HttpPostSocket::OnConnect()
135 // only fields, no files, add urlencoding
136 for (std::map
<std::string
,std::list
<std::string
> >::iterator it
= m_fields
.begin(); it
!= m_fields
.end(); it
++)
138 std::string name
= (*it
).first
;
139 std::list
<std::string
>& ref
= (*it
).second
;
146 for (std::list
<std::string
>::iterator it
= ref
.begin(); it
!= ref
.end(); it
++)
148 std::string value
= *it
;
151 body
+= "%0d%0a"; // CRLF
153 body
+= Utility::rfc1738_encode(value
);
158 // build header, send body
160 SetHttpVersion( "HTTP/1.1" );
161 AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding..
162 AddResponseHeader( "User-agent", MyUseragent());
163 AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" );
164 AddResponseHeader( "Connection", "close" );
165 AddResponseHeader( "Content-type", "application/x-www-form-urlencoded" );
166 AddResponseHeader( "Content-length", Utility::l2string((long)body
.size()) );
175 void HttpPostSocket::DoMultipartPost()
177 long length
= 0; // calculate content_length of our post body
182 for (std::map
<std::string
,std::list
<std::string
> >::iterator it
= m_fields
.begin(); it
!= m_fields
.end(); it
++)
184 std::string name
= (*it
).first
;
185 std::list
<std::string
>& ref
= (*it
).second
;
186 tmp
= "--" + m_boundary
+ "\r\n"
187 "content-disposition: form-data; name=\"" + name
+ "\"\r\n"
189 for (std::list
<std::string
>::iterator it
= ref
.begin(); it
!= ref
.end(); it
++)
191 std::string value
= *it
;
192 tmp
+= value
+ "\r\n";
194 length
+= (long)tmp
.size();
200 for (std::map
<std::string
,std::string
>::iterator it
= m_files
.begin(); it
!= m_files
.end(); it
++)
202 std::string name
= (*it
).first
;
203 std::string filename
= (*it
).second
;
204 long content_length
= m_content_length
[filename
];
205 std::string content_type
= m_content_type
[filename
];
206 tmp
= "--" + m_boundary
+ "\r\n"
207 "content-disposition: form-data; name=\"" + name
+ "\"; filename=\"" + filename
+ "\"\r\n"
208 "content-type: " + content_type
+ "\r\n"
210 length
+= (long)tmp
.size();
211 length
+= content_length
;
212 length
+= 2; // crlf after file
217 tmp
= "--" + m_boundary
+ "--\r\n";
218 length
+= (long)tmp
.size();
220 // build header, send body
222 SetHttpVersion( "HTTP/1.1" );
223 AddResponseHeader( "Host", GetUrlHost() ); // oops - this is actually a request header that we're adding..
224 AddResponseHeader( "User-agent", MyUseragent());
225 AddResponseHeader( "Accept", "text/html, text/plain, */*;q=0.01" );
226 AddResponseHeader( "Connection", "close" );
227 AddResponseHeader( "Content-type", "multipart/form-data; boundary=" + m_boundary
);
228 AddResponseHeader( "Content-length", Utility::l2string(length
) );
234 for (std::map
<std::string
,std::list
<std::string
> >::iterator it
= m_fields
.begin(); it
!= m_fields
.end(); it
++)
236 std::string name
= (*it
).first
;
237 std::list
<std::string
>& ref
= (*it
).second
;
238 tmp
= "--" + m_boundary
+ "\r\n"
239 "content-disposition: form-data; name=\"" + name
+ "\"\r\n"
241 for (std::list
<std::string
>::iterator it
= ref
.begin(); it
!= ref
.end(); it
++)
243 std::string value
= *it
;
244 tmp
+= value
+ "\r\n";
252 for (std::map
<std::string
,std::string
>::iterator it
= m_files
.begin(); it
!= m_files
.end(); it
++)
254 std::string name
= (*it
).first
;
255 std::string filename
= (*it
).second
;
256 std::string content_type
= m_content_type
[filename
];
257 tmp
= "--" + m_boundary
+ "\r\n"
258 "content-disposition: form-data; name=\"" + name
+ "\"; filename=\"" + filename
+ "\"\r\n"
259 "content-type: " + content_type
+ "\r\n"
263 FILE *fil
= fopen(filename
.c_str(),"rb");
266 char slask
[2000]; // for fread
268 while ((n
= fread(slask
, 1, 2000, fil
)) > 0)
280 Send("--" + m_boundary
+ "--\r\n");
284 void HttpPostSocket::SetMultipart()
290 #ifdef SOCKETS_NAMESPACE