Added --use-stl to cnfigure, wxUSE_STL to setup0.h
[wxWidgets.git] / src / common / http.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: http.cpp
3 // Purpose: HTTP protocol
4 // Author: Guilhem Lavaux
5 // Modified by:
6 // Created: August 1997
7 // RCS-ID: $Id$
8 // Copyright: (c) 1997, 1998 Guilhem Lavaux
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "http.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_PROTOCOL_HTTP
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include "wx/string.h"
28 #include "wx/tokenzr.h"
29 #include "wx/socket.h"
30 #include "wx/protocol/protocol.h"
31 #include "wx/url.h"
32 #include "wx/protocol/http.h"
33 #include "wx/sckstrm.h"
34
35 IMPLEMENT_DYNAMIC_CLASS(wxHTTP, wxProtocol)
36 IMPLEMENT_PROTOCOL(wxHTTP, wxT("http"), wxT("80"), TRUE)
37
38 #define HTTP_BSIZE 2048
39
40 wxHTTP::wxHTTP()
41 : wxProtocol()
42 {
43 m_addr = NULL;
44 m_read = FALSE;
45 m_proxy_mode = FALSE;
46
47 SetNotify(wxSOCKET_LOST_FLAG);
48 }
49
50 wxHTTP::~wxHTTP()
51 {
52 ClearHeaders();
53
54 delete m_addr;
55 }
56
57 void wxHTTP::ClearHeaders()
58 {
59 m_headers.clear();
60 }
61
62 wxString wxHTTP::GetContentType()
63 {
64 return GetHeader(wxT("Content-Type"));
65 }
66
67 void wxHTTP::SetProxyMode(bool on)
68 {
69 m_proxy_mode = on;
70 }
71
72 void wxHTTP::SetHeader(const wxString& header, const wxString& h_data)
73 {
74 if (m_read) {
75 ClearHeaders();
76 m_read = FALSE;
77 }
78
79 wxStringToStringHashMap::iterator it = m_headers.find(header);
80 if (it != m_headers.end())
81 it->second = h_data;
82 else
83 m_headers[header.Upper()] = h_data;
84 }
85
86 wxString wxHTTP::GetHeader(const wxString& header)
87 {
88 wxStringToStringHashMap::iterator it = m_headers.find(header.Upper());
89
90 if (it == m_headers.end())
91 return wxEmptyString;
92
93 return it->second;
94 }
95
96 void wxHTTP::SendHeaders()
97 {
98 typedef wxStringToStringHashMap::iterator iterator;
99 wxString buf;
100
101 for (iterator it = m_headers.begin(), en = m_headers.end(); it != en; ++it )
102 {
103 buf.Printf(wxT("%s: %s\r\n"), it->first.c_str(), it->second.c_str());
104
105 const wxWX2MBbuf cbuf = buf.mb_str();
106 Write(cbuf, strlen(cbuf));
107 }
108 }
109
110 bool wxHTTP::ParseHeaders()
111 {
112 wxString line;
113 wxStringTokenizer tokenzr;
114
115 ClearHeaders();
116 m_read = TRUE;
117
118 #if defined(__VISAGECPP__)
119 // VA just can't stand while(1)
120 bool bOs2var = TRUE;
121 while(bOs2var) {
122 #else
123 while (1) {
124 #endif
125 m_perr = GetLine(this, line);
126 if (m_perr != wxPROTO_NOERR)
127 return FALSE;
128
129 if (line.Length() == 0)
130 break;
131
132 wxString left_str = line.BeforeFirst(':');
133 left_str.MakeUpper();
134
135 m_headers[left_str] = line.AfterFirst(':').Strip(wxString::both);
136 }
137 return TRUE;
138 }
139
140 bool wxHTTP::Connect(const wxString& host)
141 {
142 wxIPV4address *addr;
143
144 if (m_addr) {
145 delete m_addr;
146 m_addr = NULL;
147 Close();
148 }
149
150 m_addr = addr = new wxIPV4address();
151
152 if (!addr->Hostname(host)) {
153 delete m_addr;
154 m_addr = NULL;
155 m_perr = wxPROTO_NETERR;
156 return FALSE;
157 }
158
159 if (!addr->Service(wxT("http")))
160 addr->Service(80);
161
162 SetHeader(wxT("Host"), host);
163
164 return TRUE;
165 }
166
167 bool wxHTTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
168 {
169 if (m_addr) {
170 delete m_addr;
171 Close();
172 }
173
174 m_addr = addr.Clone();
175
176 wxIPV4address *ipv4addr = wxDynamicCast(&addr, wxIPV4address);
177 if (ipv4addr)
178 SetHeader(wxT("Host"), ipv4addr->OrigHostname());
179
180 return TRUE;
181 }
182
183 bool wxHTTP::BuildRequest(const wxString& path, wxHTTP_Req req)
184 {
185 const wxChar *request;
186
187 switch (req) {
188 case wxHTTP_GET:
189 request = wxT("GET");
190 break;
191 default:
192 return FALSE;
193 }
194
195 // If there is no User-Agent defined, define it.
196 if (GetHeader(wxT("User-Agent")).IsNull())
197 SetHeader(wxT("User-Agent"), wxT("wxWindows 2.x"));
198
199 SaveState();
200 SetFlags(wxSOCKET_NONE);
201 Notify(FALSE);
202
203 wxString buf;
204 buf.Printf(wxT("%s %s HTTP/1.0\r\n"), request, path.c_str());
205 const wxWX2MBbuf pathbuf = wxConvLocal.cWX2MB(buf);
206 Write(pathbuf, strlen(wxMBSTRINGCAST pathbuf));
207 SendHeaders();
208 Write("\r\n", 2);
209
210 wxString tmp_str;
211 m_perr = GetLine(this, tmp_str);
212 if (m_perr != wxPROTO_NOERR) {
213 RestoreState();
214 return FALSE;
215 }
216
217 if (!tmp_str.Contains(wxT("HTTP/"))) {
218 // TODO: support HTTP v0.9 which can have no header.
219 // FIXME: tmp_str is not put back in the in-queue of the socket.
220 SetHeader(wxT("Content-Length"), wxT("-1"));
221 SetHeader(wxT("Content-Type"), wxT("none/none"));
222 RestoreState();
223 return TRUE;
224 }
225
226 wxStringTokenizer token(tmp_str,wxT(' '));
227 wxString tmp_str2;
228 bool ret_value;
229
230 token.NextToken();
231 tmp_str2 = token.NextToken();
232
233 switch (tmp_str2[0u]) {
234 case wxT('1'):
235 /* INFORMATION / SUCCESS */
236 break;
237 case wxT('2'):
238 /* SUCCESS */
239 break;
240 case wxT('3'):
241 /* REDIRECTION */
242 break;
243 default:
244 m_perr = wxPROTO_NOFILE;
245 RestoreState();
246 return FALSE;
247 }
248
249 ret_value = ParseHeaders();
250 RestoreState();
251 return ret_value;
252 }
253
254 class wxHTTPStream : public wxSocketInputStream
255 {
256 public:
257 wxHTTP *m_http;
258 size_t m_httpsize;
259 unsigned long m_read_bytes;
260
261 wxHTTPStream(wxHTTP *http) : wxSocketInputStream(*http), m_http(http) {}
262 size_t GetSize() const { return m_httpsize; }
263 virtual ~wxHTTPStream(void) { m_http->Abort(); }
264
265 protected:
266 size_t OnSysRead(void *buffer, size_t bufsize);
267
268 DECLARE_NO_COPY_CLASS(wxHTTPStream)
269 };
270
271 size_t wxHTTPStream::OnSysRead(void *buffer, size_t bufsize)
272 {
273 if (m_httpsize > 0 && m_read_bytes >= m_httpsize)
274 {
275 m_lasterror = wxSTREAM_EOF;
276 return 0;
277 }
278
279 size_t ret = wxSocketInputStream::OnSysRead(buffer, bufsize);
280 m_read_bytes += ret;
281
282 return ret;
283 }
284
285 bool wxHTTP::Abort(void)
286 {
287 return wxSocketClient::Close();
288 }
289
290 wxInputStream *wxHTTP::GetInputStream(const wxString& path)
291 {
292 wxHTTPStream *inp_stream;
293
294 wxString new_path;
295
296 m_perr = wxPROTO_CONNERR;
297 if (!m_addr)
298 return NULL;
299
300 // We set m_connected back to FALSE so wxSocketBase will know what to do.
301 #ifdef __WXMAC__
302 wxSocketClient::Connect(*m_addr , FALSE );
303 wxSocketClient::WaitOnConnect(10);
304
305 if (!wxSocketClient::IsConnected())
306 return NULL;
307 #else
308 if (!wxProtocol::Connect(*m_addr))
309 return NULL;
310 #endif
311
312 if (!BuildRequest(path, wxHTTP_GET))
313 return NULL;
314
315 inp_stream = new wxHTTPStream(this);
316
317 if (!GetHeader(wxT("Content-Length")).IsEmpty())
318 inp_stream->m_httpsize = wxAtoi(WXSTRINGCAST GetHeader(wxT("Content-Length")));
319 else
320 inp_stream->m_httpsize = (size_t)-1;
321
322 inp_stream->m_read_bytes = 0;
323
324 Notify(FALSE);
325 SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL);
326
327 return inp_stream;
328 }
329
330 #endif // wxUSE_PROTOCOL_HTTP
331