]> git.saurik.com Git - wxWidgets.git/blob - src/common/url.cpp
as after recent change wxID_SEPARATOR is now -2, it is better for idMenuTitle to...
[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 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
34 IMPLEMENT_CLASS(wxProtoInfo, wxObject)
35 IMPLEMENT_CLASS(wxURL, wxObject)
36
37 // Protocols list
38 wxProtoInfo *wxURL::ms_protocols = NULL;
39
40 // Enforce linking of protocol classes:
41 USE_PROTOCOL(wxFileProto)
42
43 #if wxUSE_SOCKETS
44 USE_PROTOCOL(wxHTTP)
45 USE_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
59 wxURL::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
87 bool 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 // We destroy the newly created protocol.
135 CleanData();
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
153 void wxURL::CleanData()
154 {
155 #if wxUSE_SOCKETS
156 if (!m_useProxy)
157 #endif
158 delete m_protocol;
159 }
160
161 wxURL::~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
177 bool wxURL::PrepProto(wxString& url)
178 {
179 int pos;
180
181 // Find end
182 pos = url.Find(wxT(':'));
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
193 bool 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 == -1)
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 != -1 && 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 -1, 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 == -1)
232 return TRUE;
233
234 temp_url = temp_url(0, pos2);
235 pos2 = temp_url.Find(wxT(':'));
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());
242
243 return TRUE;
244 }
245
246 bool 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
255 bool 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
279 wxInputStream *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
344 void 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 == -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 }
378 }
379
380 void 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 == -1)
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
425 wxString 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
470 wxString 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
512 class wxURLModule : public wxModule
513 {
514 public:
515 virtual bool OnInit();
516 virtual void OnExit();
517
518 private:
519 DECLARE_DYNAMIC_CLASS(wxURLModule)
520 };
521
522 IMPLEMENT_DYNAMIC_CLASS(wxURLModule, wxModule)
523
524 bool 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
539 void 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