[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / filesystem / DllLibCurl.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "system.h"
23 #include "DllLibCurl.h"
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/TimeUtils.h"
27
28 #include <assert.h>
29
30 using namespace XCURL;
31
32 /* okey this is damn ugly. our dll loader doesn't allow for postload, preunload functions */
33 static long g_curlReferences = 0;
34 #if(0)
35 static unsigned int g_curlTimeout = 0;
36 #endif
37
38 bool DllLibCurlGlobal::Load()
39 {
40   CSingleLock lock(m_critSection);
41   if(g_curlReferences > 0)
42   {
43     g_curlReferences++;
44     return true;
45   }
46
47   /* we handle this ourself */
48   DllDynamic::EnableDelayedUnload(false);
49   if (!DllDynamic::Load())
50     return false;
51
52   if (global_init(CURL_GLOBAL_ALL))
53   {
54     DllDynamic::Unload();
55     CLog::Log(LOGERROR, "Error initializing libcurl");
56     return false;
57   }
58
59   /* check idle will clean up the last one */
60   g_curlReferences = 2;
61
62   return true;
63 }
64
65 void DllLibCurlGlobal::Unload()
66 {
67   CSingleLock lock(m_critSection);
68   if (--g_curlReferences == 0)
69   {
70     if (!IsLoaded())
71       return;
72
73     // close libcurl
74     global_cleanup();
75
76     DllDynamic::Unload();
77   }
78
79   /* CheckIdle will clear this one up */
80 #if(0)
81   if(g_curlReferences == 1)
82     g_curlTimeout = XbmcThreads::SystemClockMillis();
83 #endif
84 }
85
86 void DllLibCurlGlobal::CheckIdle()
87 {
88   /* avoid locking section here, to avoid stalling gfx thread on loads*/
89   if(g_curlReferences == 0)
90     return;
91
92   CSingleLock lock(m_critSection);
93   /* 20 seconds idle time before closing handle */
94   const unsigned int idletime = 30000;
95
96   VEC_CURLSESSIONS::iterator it = m_sessions.begin();
97   while(it != m_sessions.end())
98   {
99     if( !it->m_busy && (XbmcThreads::SystemClockMillis() - it->m_idletimestamp) > idletime )
100     {
101       CLog::Log(LOGINFO, "%s - Closing session to %s://%s (easy=%p, multi=%p)\n", __FUNCTION__, it->m_protocol.c_str(), it->m_hostname.c_str(), (void*)it->m_easy, (void*)it->m_multi);
102
103       // It's important to clean up multi *before* cleaning up easy, because the multi cleanup
104       // code accesses stuff in the easy's structure.
105       if(it->m_multi)
106         multi_cleanup(it->m_multi);
107       if(it->m_easy)
108         easy_cleanup(it->m_easy);
109
110       Unload();
111
112       it = m_sessions.erase(it);
113       continue;
114     }
115     it++;
116   }
117
118   /* check if we should unload the dll */
119 #if(0) // we never unload libcurl, since libssl can break when python unloads then
120   if(g_curlReferences == 1 && XbmcThreads::SystemClockMillis() - g_curlTimeout > idletime)
121     Unload();
122 #endif
123 }
124
125 void DllLibCurlGlobal::easy_aquire(const char *protocol, const char *hostname, CURL_HANDLE** easy_handle, CURLM** multi_handle)
126 {
127   assert(easy_handle != NULL);
128
129   CSingleLock lock(m_critSection);
130
131   VEC_CURLSESSIONS::iterator it;
132   for(it = m_sessions.begin(); it != m_sessions.end(); it++)
133   {
134     if( !it->m_busy )
135     {
136       /* allow reuse of requester is trying to connect to same host */
137       /* curl will take care of any differences in username/password */
138       if( it->m_protocol.compare(protocol) == 0 && it->m_hostname.compare(hostname) == 0)
139       {
140         it->m_busy = true;
141         if(easy_handle)
142         {
143           if(!it->m_easy)
144             it->m_easy = easy_init();
145
146           *easy_handle = it->m_easy;
147         }
148
149         if(multi_handle)
150         {
151           if(!it->m_multi)
152             it->m_multi = multi_init();
153
154           *multi_handle = it->m_multi;
155         }
156
157         return;
158       }
159     }
160   }
161
162   SSession session = {};
163   session.m_busy = true;
164   session.m_protocol = protocol;
165   session.m_hostname = hostname;
166
167   /* count up global interface counter */
168   Load();
169
170   if(easy_handle)
171   {
172     session.m_easy = easy_init();
173     *easy_handle = session.m_easy;
174   }
175
176   if(multi_handle)
177   {
178     session.m_multi = multi_init();
179     *multi_handle = session.m_multi;
180   }
181
182   m_sessions.push_back(session);
183
184
185   CLog::Log(LOGINFO, "%s - Created session to %s://%s\n", __FUNCTION__, protocol, hostname);
186
187   return;
188
189 }
190
191 void DllLibCurlGlobal::easy_release(CURL_HANDLE** easy_handle, CURLM** multi_handle)
192 {
193   CSingleLock lock(m_critSection);
194
195   CURL_HANDLE* easy = NULL;
196   CURLM*       multi = NULL;
197
198   if(easy_handle)
199   {
200     easy = *easy_handle;
201     *easy_handle = NULL;
202   }
203
204   if(multi_handle)
205   {
206     multi = *multi_handle;
207     *multi_handle = NULL;
208   }
209
210   VEC_CURLSESSIONS::iterator it;
211   for(it = m_sessions.begin(); it != m_sessions.end(); it++)
212   {
213     if( it->m_easy == easy && (multi == NULL || it->m_multi == multi) )
214     {
215       /* reset session so next caller doesn't reuse options, only connections */
216       /* will reset verbose too so it won't print that it closed connections on cleanup*/
217       easy_reset(easy);
218       it->m_busy = false;
219       it->m_idletimestamp = XbmcThreads::SystemClockMillis();
220       return;
221     }
222   }
223 }
224
225 CURL_HANDLE* DllLibCurlGlobal::easy_duphandle(CURL_HANDLE* easy_handle)
226 {
227   CSingleLock lock(m_critSection);
228
229   VEC_CURLSESSIONS::iterator it;
230   for(it = m_sessions.begin(); it != m_sessions.end(); it++)
231   {
232     if( it->m_easy == easy_handle )
233     {
234       SSession session = *it;
235       session.m_easy = DllLibCurl::easy_duphandle(easy_handle);
236       Load();
237       m_sessions.push_back(session);
238       return session.m_easy;
239     }
240   }
241   return DllLibCurl::easy_duphandle(easy_handle);
242 }
243
244 void DllLibCurlGlobal::easy_duplicate(CURL_HANDLE* easy, CURLM* multi, CURL_HANDLE** easy_out, CURLM** multi_out)
245 {
246   CSingleLock lock(m_critSection);
247
248   if(easy_out && easy)
249     *easy_out = DllLibCurl::easy_duphandle(easy);
250
251   if(multi_out && multi)
252     *multi_out = DllLibCurl::multi_init();
253
254   VEC_CURLSESSIONS::iterator it;
255   for(it = m_sessions.begin(); it != m_sessions.end(); it++)
256   {
257     if( it->m_easy == easy )
258     {
259       SSession session = *it;
260       if(easy_out && easy)
261         session.m_easy = *easy_out;
262       else
263         session.m_easy = NULL;
264
265       if(multi_out && multi)
266         session.m_multi = *multi_out;
267       else
268         session.m_multi = NULL;
269
270       Load();
271       m_sessions.push_back(session);
272       return;
273     }
274   }
275   return;
276 }