]> git.saurik.com Git - wxWidgets.git/blob - src/common/url.cpp
Applied memory allocation patch, and worked around bug that crashes memcheck.cpp
[wxWidgets.git] / src / common / url.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: url.cpp
3 // Purpose: URL parser
4 // Author: Guilhem Lavaux
5 // Modified by:
6 // Created: 20/07/1997
7 // RCS-ID: $Id$
8 // Copyright: (c) 1997, 1998 Guilhem Lavaux
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "url.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 #include <string.h>
24 #include <ctype.h>
25
26 #include "wx/string.h"
27 #include "wx/list.h"
28 #include "wx/utils.h"
29 #include "wx/module.h"
30 #include "wx/url.h"
31
32 IMPLEMENT_CLASS(wxProtoInfo, wxObject)
33 IMPLEMENT_CLASS(wxURL, wxObject)
34
35 // Protocols list
36 wxProtoInfo *wxURL::ms_protocols = NULL;
37
38 #if wxUSE_SOCKETS
39 wxHTTP *wxURL::ms_proxyDefault = NULL;
40 bool wxURL::ms_useDefaultProxy = FALSE;
41 #endif
42
43 // --------------------------------------------------------------
44 // wxURL
45 // --------------------------------------------------------------
46
47 // --------------------------------------------------------------
48 // --------- wxURL CONSTRUCTOR DESTRUCTOR -----------------------
49 // --------------------------------------------------------------
50
51 wxURL::wxURL(const wxString& url)
52 {
53 m_protocol = NULL;
54 m_error = wxURL_NOERR;
55 m_url = url;
56
57 #if wxUSE_SOCKETS
58 if ( ms_useDefaultProxy && !ms_proxyDefault )
59 {
60 SetDefaultProxy(getenv("HTTP_PROXY"));
61
62 if ( !ms_proxyDefault )
63 {
64 // don't try again
65 ms_useDefaultProxy = FALSE;
66 }
67 }
68
69 m_useProxy = ms_proxyDefault != NULL;
70 m_proxy = ms_proxyDefault;
71 #endif // wxUSE_SOCKETS
72
73 ParseURL();
74 }
75
76 bool wxURL::ParseURL()
77 {
78 wxString last_url = m_url;
79
80 // If the URL was already parsed (m_protocol != NULL), pass this section.
81 if (!m_protocol)
82 {
83 // Clean up
84 CleanData();
85
86 // Extract protocol name
87 if (!PrepProto(last_url))
88 {
89 m_error = wxURL_SNTXERR;
90 return FALSE;
91 }
92
93 // Find and create the protocol object
94 if (!FetchProtocol())
95 {
96 m_error = wxURL_NOPROTO;
97 return FALSE;
98 }
99
100 // Do we need a host name ?
101 if (m_protoinfo->m_needhost)
102 {
103 // Extract it
104 if (!PrepHost(last_url))
105 {
106 m_error = wxURL_SNTXERR;
107 return FALSE;
108 }
109 }
110
111 // Extract full path
112 if (!PrepPath(last_url))
113 {
114 m_error = wxURL_NOPATH;
115 return FALSE;
116 }
117 }
118 // URL parse finished.
119
120 #if wxUSE_SOCKETS
121 if (m_useProxy)
122 {
123 // We destroy the newly created protocol.
124 CleanData();
125
126 // Third, we rebuild the URL.
127 m_url = m_protoname + wxT(":");
128 if (m_protoinfo->m_needhost)
129 m_url = m_url + wxT("//") + m_hostname;
130
131 m_url += m_path;
132
133 // We initialize specific variables.
134 m_protocol = m_proxy; // FIXME: we should clone the protocol
135 }
136 #endif
137
138 m_error = wxURL_NOERR;
139 return TRUE;
140 }
141
142 void wxURL::CleanData()
143 {
144 #if wxUSE_SOCKETS
145 if (!m_useProxy)
146 #endif
147 delete m_protocol;
148 }
149
150 wxURL::~wxURL()
151 {
152 CleanData();
153 #if wxUSE_SOCKETS
154 if (m_proxy && m_proxy != ms_proxyDefault)
155 delete m_proxy;
156 #endif
157 }
158
159 // --------------------------------------------------------------
160 // --------- wxURL urls decoders --------------------------------
161 // --------------------------------------------------------------
162
163 bool wxURL::PrepProto(wxString& url)
164 {
165 int pos;
166
167 // Find end
168 pos = url.Find(wxT(':'));
169 if (pos == -1)
170 return FALSE;
171
172 m_protoname = url(0, pos);
173
174 url = url(pos+1, url.Length());
175
176 return TRUE;
177 }
178
179 bool wxURL::PrepHost(wxString& url)
180 {
181 wxString temp_url;
182 int pos, pos2;
183
184 if ((url.GetChar(0) != wxT('/')) || (url.GetChar(1) != wxT('/')))
185 return FALSE;
186
187 url = url(2, url.Length());
188
189 pos = url.Find(wxT('/'));
190 if (pos == -1)
191 pos = url.Length();
192
193 if (pos == 0)
194 return FALSE;
195
196 temp_url = url(0, pos);
197 url = url(url.Find(wxT('/')), url.Length());
198
199 // Retrieve service number
200 pos2 = temp_url.Find(wxT(':'), TRUE);
201 if (pos2 != -1 && pos2 < pos)
202 {
203 m_servname = temp_url(pos2+1, pos);
204 if (!m_servname.IsNumber())
205 return FALSE;
206 temp_url = temp_url(0, pos2);
207 }
208
209 // Retrieve user and password.
210 pos2 = temp_url.Find(wxT('@'));
211 // Even if pos2 equals -1, this code is right.
212 m_hostname = temp_url(pos2+1, temp_url.Length());
213
214 m_user = wxT("");
215 m_password = wxT("");
216
217 if (pos2 == -1)
218 return TRUE;
219
220 temp_url = temp_url(0, pos2);
221 pos2 = temp_url.Find(wxT(':'));
222
223 if (pos2 == -1)
224 return FALSE;
225
226 m_user = temp_url(0, pos2);
227 m_password = temp_url(pos2+1, url.Length());
228
229 return TRUE;
230 }
231
232 bool wxURL::PrepPath(wxString& url)
233 {
234 if (url.Length() != 0)
235 m_path = ConvertToValidURI(url);
236 else
237 m_path = wxT("/");
238 return TRUE;
239 }
240
241 bool wxURL::FetchProtocol()
242 {
243 wxProtoInfo *info = ms_protocols;
244
245 while (info)
246 {
247 if (m_protoname == info->m_protoname)
248 {
249 if (m_servname.IsNull())
250 m_servname = info->m_servname;
251
252 m_protoinfo = info;
253 m_protocol = (wxProtocol *)m_protoinfo->m_cinfo->CreateObject();
254 return TRUE;
255 }
256 info = info->next;
257 }
258 return FALSE;
259 }
260
261 // --------------------------------------------------------------
262 // --------- wxURL get ------------------------------------------
263 // --------------------------------------------------------------
264
265 wxInputStream *wxURL::GetInputStream()
266 {
267 wxInputStream *the_i_stream = NULL;
268
269 if (!m_protocol)
270 {
271 m_error = wxURL_NOPROTO;
272 return NULL;
273 }
274
275 m_error = wxURL_NOERR;
276 if (m_user != wxT(""))
277 {
278 m_protocol->SetUser(m_user);
279 m_protocol->SetPassword(m_password);
280 }
281
282 #if wxUSE_SOCKETS
283 wxIPV4address addr;
284
285 // m_protoinfo is NULL when we use a proxy
286 if (!m_useProxy && m_protoinfo->m_needhost)
287 {
288 if (!addr.Hostname(m_hostname))
289 {
290 m_error = wxURL_NOHOST;
291 return NULL;
292 }
293
294 addr.Service(m_servname);
295
296 if (!m_protocol->Connect(addr, TRUE)) // Watcom needs the 2nd arg for some reason
297 {
298 m_error = wxURL_CONNERR;
299 return NULL;
300 }
301 }
302 #endif
303
304 // When we use a proxy, we have to pass the whole URL to it.
305 if (m_useProxy)
306 the_i_stream = m_protocol->GetInputStream(m_url);
307 else
308 the_i_stream = m_protocol->GetInputStream(m_path);
309
310 if (!the_i_stream)
311 {
312 m_error = wxURL_PROTOERR;
313 return NULL;
314 }
315
316 return the_i_stream;
317 }
318
319 #if wxUSE_SOCKETS
320 void wxURL::SetDefaultProxy(const wxString& url_proxy)
321 {
322 if ( !url_proxy )
323 {
324 if ( ms_proxyDefault )
325 {
326 ms_proxyDefault->Close();
327 delete ms_proxyDefault;
328 ms_proxyDefault = NULL;
329 }
330 }
331 else
332 {
333 wxString tmp_str = url_proxy;
334 int pos = tmp_str.Find(wxT(':'));
335 if (pos == -1)
336 return;
337
338 wxString hostname = tmp_str(0, pos),
339 port = tmp_str(pos+1, tmp_str.Length()-pos);
340 wxIPV4address addr;
341
342 if (!addr.Hostname(hostname))
343 return;
344 if (!addr.Service(port))
345 return;
346
347 if (ms_proxyDefault)
348 // Finally, when all is right, we connect the new proxy.
349 ms_proxyDefault->Close();
350 else
351 ms_proxyDefault = new wxHTTP();
352 ms_proxyDefault->Connect(addr, TRUE); // Watcom needs the 2nd arg for some reason
353 }
354 }
355
356 void wxURL::SetProxy(const wxString& url_proxy)
357 {
358 if ( !url_proxy )
359 {
360 if ( m_proxy && m_proxy != ms_proxyDefault )
361 {
362 m_proxy->Close();
363 delete m_proxy;
364 }
365
366 m_useProxy = FALSE;
367 }
368 else
369 {
370 wxString tmp_str;
371 wxString hostname, port;
372 int pos;
373 wxIPV4address addr;
374
375 tmp_str = url_proxy;
376 pos = tmp_str.Find(wxT(':'));
377 // This is an invalid proxy name.
378 if (pos == -1)
379 return;
380
381 hostname = tmp_str(0, pos);
382 port = tmp_str(pos, tmp_str.Length()-pos);
383
384 addr.Hostname(hostname);
385 addr.Service(port);
386
387 // Finally, create the whole stuff.
388 if (m_proxy && m_proxy != ms_proxyDefault)
389 delete m_proxy;
390 m_proxy = new wxHTTP();
391 m_proxy->Connect(addr, TRUE); // Watcom needs the 2nd arg for some reason
392
393 CleanData();
394 // Reparse url.
395 m_useProxy = TRUE;
396 ParseURL();
397 }
398 }
399 #endif // wxUSE_SOCKETS
400
401 wxString wxURL::ConvertToValidURI(const wxString& uri, const wxChar* delims)
402 {
403 wxString out_str;
404 wxString hexa_code;
405 size_t i;
406
407 for (i = 0; i < uri.Len(); i++)
408 {
409 wxChar c = uri.GetChar(i);
410
411 if (c == wxT(' '))
412 {
413 // GRG, Apr/2000: changed to "%20" instead of '+'
414
415 out_str += wxT("%20");
416 }
417 else
418 {
419 // GRG, Apr/2000: modified according to the URI definition (RFC 2396)
420 //
421 // - Alphanumeric characters are never escaped
422 // - Unreserved marks are never escaped
423 // - Delimiters must be escaped if they appear within a component
424 // but not if they are used to separate components. Here we have
425 // no clear way to distinguish between these two cases, so they
426 // are escaped unless they are passed in the 'delims' parameter
427 // (allowed delimiters).
428
429 static const wxChar marks[] = wxT("-_.!~*()'");
430
431 if ( !wxIsalnum(c) && !wxStrchr(marks, c) && !wxStrchr(delims, c) )
432 {
433 hexa_code.Printf(wxT("%%%02X"), c);
434 out_str += hexa_code;
435 }
436 else
437 {
438 out_str += c;
439 }
440 }
441 }
442
443 return out_str;
444 }
445
446 wxString wxURL::ConvertFromURI(const wxString& uri)
447 {
448 wxString new_uri;
449
450 size_t i = 0;
451 while (i < uri.Len())
452 {
453 int code;
454 if (uri[i] == wxT('%'))
455 {
456 i++;
457 if (uri[i] >= wxT('A') && uri[i] <= wxT('F'))
458 code = (uri[i] - wxT('A') + 10) * 16;
459 else
460 code = (uri[i] - wxT('0')) * 16;
461
462 i++;
463 if (uri[i] >= wxT('A') && uri[i] <= wxT('F'))
464 code += (uri[i] - wxT('A')) + 10;
465 else
466 code += (uri[i] - wxT('0'));
467
468 i++;
469 new_uri += (wxChar)code;
470 continue;
471 }
472 new_uri += uri[i];
473 i++;
474 }
475 return new_uri;
476 }
477
478 // ----------------------------------------------------------------------
479 // A module which deletes the default proxy if we created it
480 // ----------------------------------------------------------------------
481
482 #if wxUSE_SOCKETS
483
484 class wxURLModule : public wxModule
485 {
486 public:
487 virtual bool OnInit();
488 virtual void OnExit();
489
490 private:
491 DECLARE_DYNAMIC_CLASS(wxURLModule)
492 };
493
494 IMPLEMENT_DYNAMIC_CLASS(wxURLModule, wxModule)
495
496 bool wxURLModule::OnInit()
497 {
498 // env var HTTP_PROXY contains the address of the default proxy to use if
499 // set, but don't try to create this proxy right now because it will slow
500 // down the program startup (especially if there is no DNS server
501 // available, in which case it may take up to 1 minute)
502
503 if ( getenv("HTTP_PROXY") )
504 {
505 wxURL::ms_useDefaultProxy = TRUE;
506 }
507
508 return TRUE;
509 }
510
511 void wxURLModule::OnExit()
512 {
513 delete wxURL::ms_proxyDefault;
514 wxURL::ms_proxyDefault = NULL;
515 }
516
517 #endif // wxUSE_SOCKETS