]> git.saurik.com Git - wxWidgets.git/blame - src/common/url.cpp
Added wxDC::GetPartialTextExtents
[wxWidgets.git] / src / common / url.cpp
CommitLineData
f4ada568
GL
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
55d99c7a 9// Licence: wxWindows licence
f4ada568
GL
10/////////////////////////////////////////////////////////////////////////////
11
14f355c2 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
f4ada568
GL
13#pragma implementation "url.h"
14#endif
fcc6dddd
JS
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
a5d46b73 23#if wxUSE_URL
f4ada568 24
3096bd2f
VZ
25#include "wx/string.h"
26#include "wx/list.h"
27#include "wx/utils.h"
c092213d 28#include "wx/module.h"
3096bd2f 29#include "wx/url.h"
f4ada568 30
a5d46b73
VZ
31#include <string.h>
32#include <ctype.h>
33
f4ada568
GL
34IMPLEMENT_CLASS(wxProtoInfo, wxObject)
35IMPLEMENT_CLASS(wxURL, wxObject)
f4ada568
GL
36
37// Protocols list
b2b35524 38wxProtoInfo *wxURL::ms_protocols = NULL;
8a4df159 39
f92f546c 40// Enforce linking of protocol classes:
f92f546c
VS
41USE_PROTOCOL(wxFileProto)
42
8a4df159 43#if wxUSE_SOCKETS
f80eabe5
JS
44USE_PROTOCOL(wxHTTP)
45USE_PROTOCOL(wxFTP)
46
b2b35524
VZ
47 wxHTTP *wxURL::ms_proxyDefault = NULL;
48 bool wxURL::ms_useDefaultProxy = FALSE;
8a4df159 49#endif
f4ada568 50
fae05df5
GL
51// --------------------------------------------------------------
52// wxURL
53// --------------------------------------------------------------
f4ada568 54
fae05df5
GL
55// --------------------------------------------------------------
56// --------- wxURL CONSTRUCTOR DESTRUCTOR -----------------------
57// --------------------------------------------------------------
f4ada568
GL
58
59wxURL::wxURL(const wxString& url)
60{
b2b35524
VZ
61 m_protocol = NULL;
62 m_error = wxURL_NOERR;
63 m_url = url;
25959b95
VZ
64#if wxUSE_URL_NATIVE
65 m_nativeImp = CreateNativeImpObject();
66#endif
b2b35524 67
8a4df159 68#if wxUSE_SOCKETS
b2b35524
VZ
69 if ( ms_useDefaultProxy && !ms_proxyDefault )
70 {
2b5f62a0 71 SetDefaultProxy( wxGetenv(wxT("HTTP_PROXY")) );
b2b35524
VZ
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();
f4ada568
GL
85}
86
87bool wxURL::ParseURL()
88{
89 wxString last_url = m_url;
90
f6bcfd97
BP
91 // If the URL was already parsed (m_protocol != NULL), pass this section.
92 if (!m_protocol)
93 {
f61815af
GL
94 // Clean up
95 CleanData();
f4ada568 96
f61815af 97 // Extract protocol name
f6bcfd97
BP
98 if (!PrepProto(last_url))
99 {
f4ada568
GL
100 m_error = wxURL_SNTXERR;
101 return FALSE;
102 }
f61815af
GL
103
104 // Find and create the protocol object
f6bcfd97
BP
105 if (!FetchProtocol())
106 {
f61815af
GL
107 m_error = wxURL_NOPROTO;
108 return FALSE;
109 }
110
111 // Do we need a host name ?
f6bcfd97
BP
112 if (m_protoinfo->m_needhost)
113 {
f61815af 114 // Extract it
f6bcfd97
BP
115 if (!PrepHost(last_url))
116 {
f61815af
GL
117 m_error = wxURL_SNTXERR;
118 return FALSE;
119 }
120 }
121
122 // Extract full path
f6bcfd97
BP
123 if (!PrepPath(last_url))
124 {
f61815af
GL
125 m_error = wxURL_NOPATH;
126 return FALSE;
127 }
f4ada568 128 }
f61815af 129 // URL parse finished.
f4ada568 130
8a4df159 131#if wxUSE_SOCKETS
f6bcfd97
BP
132 if (m_useProxy)
133 {
f61815af
GL
134 // We destroy the newly created protocol.
135 CleanData();
136
137 // Third, we rebuild the URL.
223d09f6 138 m_url = m_protoname + wxT(":");
f61815af 139 if (m_protoinfo->m_needhost)
223d09f6 140 m_url = m_url + wxT("//") + m_hostname;
f61815af
GL
141
142 m_url += m_path;
143
144 // We initialize specific variables.
145 m_protocol = m_proxy; // FIXME: we should clone the protocol
f4ada568 146 }
8a4df159 147#endif
f4ada568
GL
148
149 m_error = wxURL_NOERR;
150 return TRUE;
151}
152
153void wxURL::CleanData()
154{
8a4df159 155#if wxUSE_SOCKETS
f61815af 156 if (!m_useProxy)
8a4df159 157#endif
f4ada568
GL
158 delete m_protocol;
159}
160
161wxURL::~wxURL()
162{
25959b95 163 CleanData();
8a4df159 164#if wxUSE_SOCKETS
25959b95
VZ
165 if (m_proxy && m_proxy != ms_proxyDefault)
166 delete m_proxy;
167#endif
168#if wxUSE_URL_NATIVE
169 delete m_nativeImp;
8a4df159 170#endif
f4ada568
GL
171}
172
fae05df5
GL
173// --------------------------------------------------------------
174// --------- wxURL urls decoders --------------------------------
175// --------------------------------------------------------------
176
f4ada568
GL
177bool wxURL::PrepProto(wxString& url)
178{
179 int pos;
180
181 // Find end
223d09f6 182 pos = url.Find(wxT(':'));
f4ada568
GL
183 if (pos == -1)
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{
856d2e52 195 wxString temp_url;
f4ada568
GL
196 int pos, pos2;
197
58c837a4 198 if ((url.GetChar(0) != wxT('/')) || (url.GetChar(1) != wxT('/')))
f4ada568
GL
199 return FALSE;
200
201 url = url(2, url.Length());
202
223d09f6 203 pos = url.Find(wxT('/'));
f4ada568 204 if (pos == -1)
b7db6f0b 205 pos = url.Length();
f4ada568 206
856d2e52
GL
207 if (pos == 0)
208 return FALSE;
209
210 temp_url = url(0, pos);
223d09f6 211 url = url(url.Find(wxT('/')), url.Length());
856d2e52
GL
212
213 // Retrieve service number
223d09f6 214 pos2 = temp_url.Find(wxT(':'), TRUE);
f6bcfd97
BP
215 if (pos2 != -1 && pos2 < pos)
216 {
375abe3d 217 m_servname = temp_url(pos2+1, pos);
f4ada568
GL
218 if (!m_servname.IsNumber())
219 return FALSE;
856d2e52 220 temp_url = temp_url(0, pos2);
f4ada568
GL
221 }
222
856d2e52 223 // Retrieve user and password.
223d09f6 224 pos2 = temp_url.Find(wxT('@'));
856d2e52
GL
225 // Even if pos2 equals -1, this code is right.
226 m_hostname = temp_url(pos2+1, temp_url.Length());
f4ada568 227
223d09f6
KB
228 m_user = wxT("");
229 m_password = wxT("");
856d2e52
GL
230
231 if (pos2 == -1)
232 return TRUE;
233
234 temp_url = temp_url(0, pos2);
223d09f6 235 pos2 = temp_url.Find(wxT(':'));
856d2e52
GL
236
237 if (pos2 == -1)
238 return FALSE;
239
240 m_user = temp_url(0, pos2);
241 m_password = temp_url(pos2+1, url.Length());
f4ada568
GL
242
243 return TRUE;
244}
245
246bool wxURL::PrepPath(wxString& url)
247{
248 if (url.Length() != 0)
f61815af 249 m_path = ConvertToValidURI(url);
f4ada568 250 else
223d09f6 251 m_path = wxT("/");
f4ada568
GL
252 return TRUE;
253}
254
255bool wxURL::FetchProtocol()
256{
b2b35524 257 wxProtoInfo *info = ms_protocols;
f4ada568 258
f6bcfd97
BP
259 while (info)
260 {
261 if (m_protoname == info->m_protoname)
262 {
f4ada568
GL
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();
f4ada568
GL
268 return TRUE;
269 }
270 info = info->next;
271 }
272 return FALSE;
273}
274
fae05df5
GL
275// --------------------------------------------------------------
276// --------- wxURL get ------------------------------------------
277// --------------------------------------------------------------
278
58c837a4 279wxInputStream *wxURL::GetInputStream()
f4ada568 280{
f6bcfd97
BP
281 if (!m_protocol)
282 {
f4ada568
GL
283 m_error = wxURL_NOPROTO;
284 return NULL;
285 }
286
287 m_error = wxURL_NOERR;
f6bcfd97
BP
288 if (m_user != wxT(""))
289 {
856d2e52
GL
290 m_protocol->SetUser(m_user);
291 m_protocol->SetPassword(m_password);
292 }
293
25959b95
VZ
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
8a4df159 307#if wxUSE_SOCKETS
19e0e04b
RD
308 wxIPV4address addr;
309
f61815af 310 // m_protoinfo is NULL when we use a proxy
f6bcfd97
BP
311 if (!m_useProxy && m_protoinfo->m_needhost)
312 {
313 if (!addr.Hostname(m_hostname))
314 {
f4ada568
GL
315 m_error = wxURL_NOHOST;
316 return NULL;
317 }
318
319 addr.Service(m_servname);
320
8a2c6ef8
JS
321 if (!m_protocol->Connect(addr, TRUE)) // Watcom needs the 2nd arg for some reason
322 {
f4ada568
GL
323 m_error = wxURL_CONNERR;
324 return NULL;
325 }
326 }
8a4df159 327#endif
f4ada568 328
f61815af 329 // When we use a proxy, we have to pass the whole URL to it.
999836aa
VZ
330 wxInputStream *the_i_stream =
331 (m_useProxy) ? m_protocol->GetInputStream(m_url) :
332 m_protocol->GetInputStream(m_path);
f61815af 333
f6bcfd97
BP
334 if (!the_i_stream)
335 {
f4ada568
GL
336 m_error = wxURL_PROTOERR;
337 return NULL;
338 }
339
340 return the_i_stream;
341}
342
8a4df159 343#if wxUSE_SOCKETS
f4ada568
GL
344void wxURL::SetDefaultProxy(const wxString& url_proxy)
345{
b2b35524
VZ
346 if ( !url_proxy )
347 {
348 if ( ms_proxyDefault )
349 {
350 ms_proxyDefault->Close();
351 delete ms_proxyDefault;
352 ms_proxyDefault = NULL;
353 }
f61815af 354 }
f61815af 355 else
b2b35524
VZ
356 {
357 wxString tmp_str = url_proxy;
358 int pos = tmp_str.Find(wxT(':'));
359 if (pos == -1)
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 }
f4ada568
GL
378}
379
380void wxURL::SetProxy(const wxString& url_proxy)
381{
b2b35524
VZ
382 if ( !url_proxy )
383 {
384 if ( m_proxy && m_proxy != ms_proxyDefault )
385 {
386 m_proxy->Close();
387 delete m_proxy;
388 }
f4ada568 389
b2b35524
VZ
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 == -1)
403 return;
404
405 hostname = tmp_str(0, pos);
bb80bb5b 406 port = tmp_str(pos+1, tmp_str.Length()-pos);
b2b35524
VZ
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 }
f4ada568 422}
b2b35524 423#endif // wxUSE_SOCKETS
35a4dab7 424
f6bcfd97 425wxString wxURL::ConvertToValidURI(const wxString& uri, const wxChar* delims)
14906731 426{
fae05df5
GL
427 wxString out_str;
428 wxString hexa_code;
429 size_t i;
430
f6bcfd97
BP
431 for (i = 0; i < uri.Len(); i++)
432 {
fae05df5
GL
433 wxChar c = uri.GetChar(i);
434
223d09f6 435 if (c == wxT(' '))
f6bcfd97
BP
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 {
223d09f6 457 hexa_code.Printf(wxT("%%%02X"), c);
5a96d2f4 458 out_str += hexa_code;
f6bcfd97
BP
459 }
460 else
461 {
5a96d2f4 462 out_str += c;
f6bcfd97 463 }
5a96d2f4 464 }
fae05df5 465 }
19e0e04b 466
fae05df5 467 return out_str;
14906731
GL
468}
469
aa6d9706
GL
470wxString wxURL::ConvertFromURI(const wxString& uri)
471{
aa6d9706
GL
472 wxString new_uri;
473
74b31181 474 size_t i = 0;
f6bcfd97
BP
475 while (i < uri.Len())
476 {
74b31181 477 int code;
f6bcfd97
BP
478 if (uri[i] == wxT('%'))
479 {
aa6d9706 480 i++;
223d09f6
KB
481 if (uri[i] >= wxT('A') && uri[i] <= wxT('F'))
482 code = (uri[i] - wxT('A') + 10) * 16;
a03c55c1
VZ
483 else if (uri[i] >= wxT('a') && uri[i] <= wxT('f'))
484 code = (uri[i] - wxT('a') + 10) * 16;
aa6d9706 485 else
223d09f6 486 code = (uri[i] - wxT('0')) * 16;
f6bcfd97 487
aa6d9706 488 i++;
223d09f6
KB
489 if (uri[i] >= wxT('A') && uri[i] <= wxT('F'))
490 code += (uri[i] - wxT('A')) + 10;
a03c55c1
VZ
491 else if (uri[i] >= wxT('a') && uri[i] <= wxT('f'))
492 code += (uri[i] - wxT('a')) + 10;
aa6d9706 493 else
223d09f6 494 code += (uri[i] - wxT('0'));
f6bcfd97 495
aa6d9706
GL
496 i++;
497 new_uri += (wxChar)code;
498 continue;
499 }
500 new_uri += uri[i];
501 i++;
502 }
503 return new_uri;
504}
b2b35524
VZ
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)
f6bcfd97 530
b2b35524
VZ
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
a5d46b73
VZ
546
547#endif // wxUSE_URL
548