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