1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/url.cpp
3 // Purpose:     URL parser
4 // Author:      Guilhem Lavaux
5 // Modified by:
6 // Created:     20/07/1997
7 // RCS-ID:      $Id: url.cpp 57545 2008-12-25 17:03:20Z VZ $
8 // Copyright:   (c) 1997, 1998 Guilhem Lavaux
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #if wxUSE_URL
20 
21 #include "wx/url.h"
22 
23 #ifndef WX_PRECOMP
24     #include "wx/list.h"
25     #include "wx/string.h"
26     #include "wx/utils.h"
27     #include "wx/module.h"
28 #endif
29 
30 #include <string.h>
31 #include <ctype.h>
32 
33 IMPLEMENT_CLASS(wxProtoInfo, wxObject)
34 IMPLEMENT_CLASS(wxURL, wxURI)
35 
36 // Protocols list
37 wxProtoInfo *wxURL::ms_protocols = NULL;
38 
39 // Enforce linking of protocol classes:
40 USE_PROTOCOL(wxFileProto)
41 
42 #if wxUSE_PROTOCOL_HTTP
43 USE_PROTOCOL(wxHTTP)
44 
45     wxHTTP *wxURL::ms_proxyDefault = NULL;
46     bool wxURL::ms_useDefaultProxy = false;
47 #endif
48 
49 #if wxUSE_PROTOCOL_FTP
USE_PROTOCOL(wxFTP)50 USE_PROTOCOL(wxFTP)
51 #endif
52 
53 // --------------------------------------------------------------
54 //
55 //                          wxURL
56 //
57 // --------------------------------------------------------------
58 
59 // --------------------------------------------------------------
60 // Construction
61 // --------------------------------------------------------------
62 
63 wxURL::wxURL(const wxString& url) : wxURI(url)
64 {
65     Init(url);
66     ParseURL();
67 }
68 
wxURL(const wxURI & url)69 wxURL::wxURL(const wxURI& url) : wxURI(url)
70 {
71     Init(url.BuildURI());
72     ParseURL();
73 }
74 
Init(const wxString & url)75 void wxURL::Init(const wxString& url)
76 {
77     m_protocol = NULL;
78     m_error = wxURL_NOERR;
79     m_url = url;
80 #if wxUSE_URL_NATIVE
81     m_nativeImp = CreateNativeImpObject();
82 #endif
83 
84 #if wxUSE_PROTOCOL_HTTP
85     if ( ms_useDefaultProxy && !ms_proxyDefault )
86     {
87         SetDefaultProxy( wxGetenv(wxT("HTTP_PROXY")) );
88 
89         if ( !ms_proxyDefault )
90         {
91             // don't try again
92             ms_useDefaultProxy = false;
93         }
94     }
95 
96     m_useProxy = ms_proxyDefault != NULL;
97     m_proxy = ms_proxyDefault;
98 #endif // wxUSE_PROTOCOL_HTTP
99 
100 }
101 
102 // --------------------------------------------------------------
103 // Assignment
104 // --------------------------------------------------------------
105 
operator =(const wxURI & url)106 wxURL& wxURL::operator = (const wxURI& url)
107 {
108     wxURI::operator = (url);
109     Init(url.BuildURI());
110     ParseURL();
111     return *this;
112 }
operator =(const wxString & url)113 wxURL& wxURL::operator = (const wxString& url)
114 {
115     wxURI::operator = (url);
116     Init(url);
117     ParseURL();
118     return *this;
119 }
120 
121 // --------------------------------------------------------------
122 // ParseURL
123 //
124 // Builds the URL and takes care of the old protocol stuff
125 // --------------------------------------------------------------
126 
ParseURL()127 bool wxURL::ParseURL()
128 {
129     // If the URL was already parsed (m_protocol != NULL), pass this section.
130     if (!m_protocol)
131     {
132         // Clean up
133         CleanData();
134 
135         // Make sure we have a protocol/scheme
136         if (!HasScheme())
137         {
138             m_error = wxURL_SNTXERR;
139             return false;
140         }
141 
142         // Find and create the protocol object
143         if (!FetchProtocol())
144         {
145             m_error = wxURL_NOPROTO;
146             return false;
147         }
148 
149         // Do we need a host name ?
150         if (m_protoinfo->m_needhost)
151         {
152             //  Make sure we have one, then
153             if (!HasServer())
154             {
155                 m_error = wxURL_SNTXERR;
156                 return false;
157             }
158         }
159     }
160 
161 #if wxUSE_PROTOCOL_HTTP
162     if (m_useProxy)
163     {
164         // Third, we rebuild the URL.
165         m_url = m_scheme + wxT(":");
166         if (m_protoinfo->m_needhost)
167             m_url = m_url + wxT("//") + m_server;
168 
169         // We initialize specific variables.
170         m_protocol = m_proxy; // FIXME: we should clone the protocol
171     }
172 #endif // wxUSE_PROTOCOL_HTTP
173 
174     m_error = wxURL_NOERR;
175     return true;
176 }
177 
178 // --------------------------------------------------------------
179 // Destruction/Cleanup
180 // --------------------------------------------------------------
181 
CleanData()182 void wxURL::CleanData()
183 {
184 #if wxUSE_PROTOCOL_HTTP
185     if (!m_useProxy)
186 #endif // wxUSE_PROTOCOL_HTTP
187         if (m_protocol)
188             // Need to safely delete the socket (pending events)
189             m_protocol->Destroy();
190 }
191 
~wxURL()192 wxURL::~wxURL()
193 {
194     CleanData();
195 #if wxUSE_PROTOCOL_HTTP
196     if (m_proxy && m_proxy != ms_proxyDefault)
197         delete m_proxy;
198 #endif // wxUSE_PROTOCOL_HTTP
199 #if wxUSE_URL_NATIVE
200     delete m_nativeImp;
201 #endif
202 }
203 
204 // --------------------------------------------------------------
205 // FetchProtocol
206 // --------------------------------------------------------------
207 
FetchProtocol()208 bool wxURL::FetchProtocol()
209 {
210     wxProtoInfo *info = ms_protocols;
211 
212     while (info)
213     {
214         if (m_scheme == info->m_protoname)
215         {
216             if (m_port.IsNull())
217                 m_port = info->m_servname;
218             m_protoinfo = info;
219             m_protocol = (wxProtocol *)m_protoinfo->m_cinfo->CreateObject();
220             return true;
221         }
222         info = info->next;
223     }
224     return false;
225 }
226 
227 // --------------------------------------------------------------
228 // GetInputStream
229 // --------------------------------------------------------------
230 
GetInputStream()231 wxInputStream *wxURL::GetInputStream()
232 {
233     if (!m_protocol)
234     {
235         m_error = wxURL_NOPROTO;
236         return NULL;
237     }
238 
239     m_error = wxURL_NOERR;
240     if (HasUserInfo())
241     {
242         size_t dwPasswordPos = m_userinfo.find(':');
243 
244         if (dwPasswordPos == wxString::npos)
245             m_protocol->SetUser(Unescape(m_userinfo));
246         else
247         {
248             m_protocol->SetUser(Unescape(m_userinfo(0, dwPasswordPos)));
249             m_protocol->SetPassword(Unescape(m_userinfo(dwPasswordPos+1, m_userinfo.length() + 1)));
250         }
251     }
252 
253 #if wxUSE_URL_NATIVE
254     // give the native implementation to return a better stream
255     // such as the native WinINet functionality under MS-Windows
256     if (m_nativeImp)
257     {
258         wxInputStream *rc;
259         rc = m_nativeImp->GetInputStream(this);
260         if (rc != 0)
261             return rc;
262     }
263     // else use the standard behaviour
264 #endif // wxUSE_URL_NATIVE
265 
266 #if wxUSE_SOCKETS
267     wxIPV4address addr;
268 
269     // m_protoinfo is NULL when we use a proxy
270     if (!m_useProxy && m_protoinfo->m_needhost)
271     {
272         if (!addr.Hostname(m_server))
273         {
274             m_error = wxURL_NOHOST;
275             return NULL;
276         }
277 
278         addr.Service(m_port);
279 
280         if (!m_protocol->Connect(addr, true)) // Watcom needs the 2nd arg for some reason
281         {
282             m_error = wxURL_CONNERR;
283             return NULL;
284         }
285     }
286 #endif
287 
288     wxString fullPath;
289 
290     // When we use a proxy, we have to pass the whole URL to it.
291     if (m_useProxy)
292         fullPath += m_url;
293 
294     if(m_path.empty())
295         fullPath += wxT("/");
296     else
297         fullPath += m_path;
298 
299     if (HasQuery())
300         fullPath += wxT("?") + m_query;
301 
302     if (HasFragment())
303         fullPath += wxT("#") + m_fragment;
304 
305     wxInputStream *the_i_stream = m_protocol->GetInputStream(fullPath);
306 
307     if (!the_i_stream)
308     {
309         m_error = wxURL_PROTOERR;
310         return NULL;
311     }
312 
313     return the_i_stream;
314 }
315 
316 #if wxUSE_PROTOCOL_HTTP
SetDefaultProxy(const wxString & url_proxy)317 void wxURL::SetDefaultProxy(const wxString& url_proxy)
318 {
319     if ( !url_proxy )
320     {
321         if ( ms_proxyDefault )
322         {
323             ms_proxyDefault->Close();
324             delete ms_proxyDefault;
325             ms_proxyDefault = NULL;
326         }
327     }
328     else
329     {
330         wxString tmp_str = url_proxy;
331         int pos = tmp_str.Find(wxT(':'));
332         if (pos == wxNOT_FOUND)
333             return;
334 
335         wxString hostname = tmp_str(0, pos),
336         port = tmp_str(pos+1, tmp_str.length()-pos);
337         wxIPV4address addr;
338 
339         if (!addr.Hostname(hostname))
340             return;
341         if (!addr.Service(port))
342             return;
343 
344         if (ms_proxyDefault)
345             // Finally, when all is right, we connect the new proxy.
346             ms_proxyDefault->Close();
347         else
348             ms_proxyDefault = new wxHTTP();
349         ms_proxyDefault->Connect(addr, true); // Watcom needs the 2nd arg for some reason
350     }
351 }
352 
SetProxy(const wxString & url_proxy)353 void wxURL::SetProxy(const wxString& url_proxy)
354 {
355     if ( !url_proxy )
356     {
357         if ( m_proxy && m_proxy != ms_proxyDefault )
358         {
359             m_proxy->Close();
360             delete m_proxy;
361         }
362 
363         m_useProxy = false;
364     }
365     else
366     {
367         wxString tmp_str;
368         wxString hostname, port;
369         int pos;
370         wxIPV4address addr;
371 
372         tmp_str = url_proxy;
373         pos = tmp_str.Find(wxT(':'));
374         // This is an invalid proxy name.
375         if (pos == wxNOT_FOUND)
376             return;
377 
378         hostname = tmp_str(0, pos);
379         port = tmp_str(pos+1, tmp_str.length()-pos);
380 
381         addr.Hostname(hostname);
382         addr.Service(port);
383 
384         // Finally, create the whole stuff.
385         if (m_proxy && m_proxy != ms_proxyDefault)
386             delete m_proxy;
387         m_proxy = new wxHTTP();
388         m_proxy->Connect(addr, true); // Watcom needs the 2nd arg for some reason
389 
390         CleanData();
391         // Reparse url.
392         m_useProxy = true;
393         ParseURL();
394     }
395 }
396 #endif // wxUSE_PROTOCOL_HTTP
397 
398 // ----------------------------------------------------------------------
399 // wxURLModule
400 //
401 // A module which deletes the default proxy if we created it
402 // ----------------------------------------------------------------------
403 
404 #if wxUSE_SOCKETS
405 
406 class wxURLModule : public wxModule
407 {
408 public:
409     wxURLModule();
410 
411     virtual bool OnInit();
412     virtual void OnExit();
413 
414 private:
415     DECLARE_DYNAMIC_CLASS(wxURLModule)
416 };
417 
IMPLEMENT_DYNAMIC_CLASS(wxURLModule,wxModule)418 IMPLEMENT_DYNAMIC_CLASS(wxURLModule, wxModule)
419 
420 wxURLModule::wxURLModule()
421 {
422     // we must be cleaned up before wxSocketModule as otherwise deleting
423     // ms_proxyDefault from our OnExit() won't work (and can actually crash)
424     AddDependency(wxClassInfo::FindClass(_T("wxSocketModule")));
425 }
426 
OnInit()427 bool wxURLModule::OnInit()
428 {
429 #if wxUSE_PROTOCOL_HTTP
430     // env var HTTP_PROXY contains the address of the default proxy to use if
431     // set, but don't try to create this proxy right now because it will slow
432     // down the program startup (especially if there is no DNS server
433     // available, in which case it may take up to 1 minute)
434 
435     if ( wxGetenv(_T("HTTP_PROXY")) )
436     {
437         wxURL::ms_useDefaultProxy = true;
438     }
439 #endif // wxUSE_PROTOCOL_HTTP
440     return true;
441 }
442 
OnExit()443 void wxURLModule::OnExit()
444 {
445 #if wxUSE_PROTOCOL_HTTP
446     delete wxURL::ms_proxyDefault;
447     wxURL::ms_proxyDefault = NULL;
448 #endif // wxUSE_PROTOCOL_HTTP
449 }
450 
451 #endif // wxUSE_SOCKETS
452 
453 // ---------------------------------------------------------------------------
454 //
455 //                        wxURL Compatibility
456 //
457 // ---------------------------------------------------------------------------
458 
459 #if WXWIN_COMPATIBILITY_2_4
460 
461 #include "wx/url.h"
462 
GetProtocolName() const463 wxString wxURL::GetProtocolName() const
464 {
465     return m_scheme;
466 }
467 
GetHostName() const468 wxString wxURL::GetHostName() const
469 {
470     return m_server;
471 }
472 
GetPath() const473 wxString wxURL::GetPath() const
474 {
475     return m_path;
476 }
477 
478 //Note that this old code really doesn't convert to a URI that well and looks
479 //more like a dirty hack than anything else...
480 
ConvertToValidURI(const wxString & uri,const wxChar * delims)481 wxString wxURL::ConvertToValidURI(const wxString& uri, const wxChar* delims)
482 {
483   wxString out_str;
484   wxString hexa_code;
485   size_t i;
486 
487   for (i = 0; i < uri.Len(); i++)
488   {
489     wxChar c = uri.GetChar(i);
490 
491     if (c == wxT(' '))
492     {
493       // GRG, Apr/2000: changed to "%20" instead of '+'
494 
495       out_str += wxT("%20");
496     }
497     else
498     {
499       // GRG, Apr/2000: modified according to the URI definition (RFC 2396)
500       //
501       // - Alphanumeric characters are never escaped
502       // - Unreserved marks are never escaped
503       // - Delimiters must be escaped if they appear within a component
504       //     but not if they are used to separate components. Here we have
505       //     no clear way to distinguish between these two cases, so they
506       //     are escaped unless they are passed in the 'delims' parameter
507       //     (allowed delimiters).
508 
509       static const wxChar marks[] = wxT("-_.!~*()'");
510 
511       if ( !wxIsalnum(c) && !wxStrchr(marks, c) && !wxStrchr(delims, c) )
512       {
513         hexa_code.Printf(wxT("%%%02X"), c);
514         out_str += hexa_code;
515       }
516       else
517       {
518         out_str += c;
519       }
520     }
521   }
522 
523   return out_str;
524 }
525 
ConvertFromURI(const wxString & uri)526 wxString wxURL::ConvertFromURI(const wxString& uri)
527 {
528     return wxURI::Unescape(uri);
529 }
530 
531 #endif //WXWIN_COMPATIBILITY_2_4
532 
533 #endif // wxUSE_URL
534