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