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