3f33def255962aa262e79006e0e62247b1e542ef
[bpdyndnsd] / src / httphelper.cpp
1 /** @file
2  * @brief HTTPHelper class implementation. This class represents a Helper to perform HTTP operations easily.
3  *
4  *
5  *
6  * @copyright Intra2net AG
7  * @license GPLv2
8 */
9
10 #include "httphelper.hpp"
11 #include "version_info.h"
12
13 using namespace std;
14
15
16 /**
17  * Default Constructor.
18  */
19 HTTPHelper::HTTPHelper()
20     : Log(new Logger)
21     , ProxyPort(0)
22     , CurlError(CURLE_OK)
23     , CurlInitError(CURLE_OK)
24 {
25     CurlEasyHandle = init_curl(CurlWritedataBuff, CurlErrBuff);
26 }
27
28
29 /**
30  * Constructor. Use this constructor if HTTP AUTH should be used. Username and password will then be set as HTTP auth options.
31  * @param _log Logger Object
32  * @param _proxy Proxy to use
33  * @param _proxy_port Proxy Port
34  * @param _username Username
35  * @param _password Password
36  */
37 HTTPHelper::HTTPHelper(Logger::Ptr _log, const string& _proxy, const int _proxy_port, const string& _username, const string& _password)
38     : Log(_log)
39     , Proxy(_proxy)
40     , ProxyPort(_proxy_port)
41     , CurlError(CURLE_OK)
42     , CurlInitError(CURLE_OK)
43     , Username(_username)
44     , Password(_password)
45 {
46     CurlEasyHandle = init_curl(CurlWritedataBuff, CurlErrBuff);
47     if ( CurlEasyHandle != NULL )
48     {
49         if ( (CurlInitError = set_curl_auth(Username,Password)) != CURLE_OK )
50         {
51             curl_easy_cleanup(CurlEasyHandle);
52             CurlEasyHandle = NULL;
53         }
54     }
55 }
56
57
58 /**
59  * Constructor. Use this constructor if you have to encode the username and password into the url
60  * @param _log Logger Object
61  * @param _proxy Proxy to use
62  * @param _proxy_port Proxy Port
63  */
64 HTTPHelper::HTTPHelper(Logger::Ptr _log, const string& _proxy, const int _proxy_port)
65     : Log(_log)
66     , Proxy(_proxy)
67     , ProxyPort(_proxy_port)
68     , CurlError(CURLE_OK)
69     , CurlInitError(CURLE_OK)
70 {
71     CurlEasyHandle = init_curl(CurlWritedataBuff, CurlErrBuff);
72 }
73
74
75 /**
76  * Destructor
77  */
78 HTTPHelper::~HTTPHelper()
79 {
80     // Free memory
81     if ( CurlEasyHandle != NULL )
82     {
83         curl_easy_cleanup(CurlEasyHandle);
84         CurlEasyHandle = NULL;
85     }
86 }
87
88 /**
89  * Re-Init curl
90  */
91 void HTTPHelper::re_initialize()
92 {
93     CurlEasyHandle = init_curl(CurlWritedataBuff, CurlErrBuff);
94     if ( (CurlEasyHandle != NULL) && !(Username.empty()) && !(Password.empty()) )
95     {
96         if ( (CurlInitError = set_curl_auth(Username,Password)) != CURLE_OK )
97         {
98             curl_easy_cleanup(CurlEasyHandle);
99             CurlEasyHandle = NULL;
100         }
101     }
102 }
103
104
105 /**
106  * Perform a HTTP GET operation
107  * @param url URL for HTTP GET operation
108  * @return The status code from the http operation or -1 if an curl error occurs
109  */
110 long HTTPHelper::http_get(const string& url)
111 {
112     long curl_info;
113     CurlError = CURLE_OK;
114
115     if ( CurlEasyHandle != NULL )
116     {
117         if ( (CurlInitError = set_curl_url(url)) != CURLE_OK )
118         {
119             Log->print_curl_error(url,CurlInitError,CurlErrBuff);
120             CurlWritedataBuff.clear();
121             return -1;
122         }
123         if ( (CurlError = curl_easy_perform(CurlEasyHandle) ) != CURLE_OK )
124         {
125             Log->print_curl_error(url,CurlError,CurlErrBuff);
126             CurlWritedataBuff.clear();
127             return -1;
128         }
129         if ( (CurlError = curl_easy_getinfo(CurlEasyHandle,CURLINFO_RESPONSE_CODE,&curl_info)) != CURLE_OK )
130         {
131             Log->print_curl_error(url,CurlError);
132             CurlWritedataBuff.clear();
133             return -1;
134         }
135
136         Log->print_curl_data(CurlWritedataBuff);
137
138         // Copy the received data received via curl from the curl data buffer member to the received data member. This is needed because curl appends data to the buffer rather than overrites it.
139         ReceivedCurlData = CurlWritedataBuff;
140         CurlWritedataBuff.clear();
141
142         // Operation performed without any problems so we can return the curl_info
143         return curl_info;
144     }
145     return -1;
146 }
147
148
149 /**
150  * Getter for member CurlWritedataBuff
151  * @return CurlWritedataBuff
152  */
153 string HTTPHelper::get_curl_data() const
154 {
155     return ReceivedCurlData;
156 }
157
158
159 /**
160  * Initialized curl easy handle with a few options.
161  * @param curl_writedata_buff Reference to a string wich will be filled with the curl result
162  * @param curl_err_buff A pointer to an char array which will be filled with error message.
163  * @return A pointer to the easy curl handle or NULL if something went wrong.
164  */
165 CURL* HTTPHelper::init_curl(string& curl_writedata_buff,char* curl_err_buff)
166 {
167     CurlInitError = CURLE_OK;
168     ostringstream user_agent_stream;
169     user_agent_stream << "Intra2net AG - Bullet Proof DYNDNS Daemon - " << MAJOR_VERSION << "." << MINOR_VERSION << endl;
170     string user_agent = user_agent_stream.str();
171
172     CURL *curl_easy_handle = curl_easy_init();
173     if ( curl_easy_handle == NULL )
174     {
175         // something went wrong.
176         CurlInitError = CURLE_FAILED_INIT;
177         Log->print_curl_error_init("Could not initialize CURL object.",CURLE_FAILED_INIT);
178         return NULL;
179     }
180
181     CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_NOPROGRESS,1);
182     if ( CurlInitError == CURLE_OK)
183         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_CONNECTTIMEOUT,5);
184     if ( CurlInitError == CURLE_OK)
185         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_TIMEOUT,10);
186     if ( CurlInitError == CURLE_OK)
187         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_BUFFERSIZE,1024);
188     if ( CurlInitError == CURLE_OK)
189         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_ERRORBUFFER,curl_err_buff);
190     if ( CurlInitError == CURLE_OK)
191         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEFUNCTION,http_receive);
192     if ( CurlInitError == CURLE_OK)
193         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEDATA,&curl_writedata_buff);
194     if ( CurlInitError == CURLE_OK)
195         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_USERAGENT,&user_agent);
196     if ( CurlInitError == CURLE_OK)
197         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_SSL_VERIFYHOST,0);
198     if ( CurlInitError == CURLE_OK)
199         CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_SSL_VERIFYPEER,0);
200
201     if ( !Proxy.empty() )
202     {
203         if ( CurlInitError == CURLE_OK)
204             CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_PROXY,Proxy.c_str());
205         if ( CurlInitError == CURLE_OK)
206             CurlInitError = curl_easy_setopt(curl_easy_handle,CURLOPT_PROXYPORT,ProxyPort);
207     }
208
209     if ( CurlInitError != CURLE_OK )
210     {
211         // Some options could not be set, so destroy the CURL handle.
212         Log->print_curl_error_init("Could not set CURL options properly.",CurlInitError);
213         curl_easy_cleanup(curl_easy_handle);
214         curl_easy_handle = NULL;
215     }
216
217     return curl_easy_handle;
218 }
219
220
221 /**
222  * Test if the curl handle is initialized correctly.
223  * @return True if correctly initialized, false if something went wrong during initialization.
224  */
225 bool HTTPHelper::is_initialized() const
226 {
227     if ( (CurlInitError == CURLE_OK) && (CurlEasyHandle != NULL) )
228     {
229         return true;
230     }
231     return false;
232 }
233
234
235 /**
236  * Sets a url to the easy curl handle
237  * @param url The url to set.
238  * @return CURLcode CURLE_OK if everything is right.
239  */
240 CURLcode HTTPHelper::set_curl_url(const string& url)
241 {
242     CURLcode curlError = CURLE_OK;
243     if ( (CurlEasyHandle != NULL) && (CurlInitError == CURLE_OK) )
244     {
245         curlError = curl_easy_setopt(CurlEasyHandle,CURLOPT_URL,url.c_str());
246         if ( curlError != CURLE_OK )
247         {
248             // Some options could not be set, so destroy the CURL handle.
249             Log->print_curl_error_init("Could not set CURL URL properly.",curlError);
250             curl_easy_cleanup(CurlEasyHandle);
251             CurlEasyHandle = NULL;
252         }
253     }
254     else
255     {
256         return CURLE_FAILED_INIT;
257     }
258     return curlError;
259 }
260
261
262 /**
263  * Sets HTTP AUTH parameters
264  * @param username The username for HTTP AUTH
265  * @param password The password for HTTP AUTH
266  * @return CURLcode CURLE_OK if everything is right.
267  */
268 CURLcode HTTPHelper::set_curl_auth(const string& username, const string& password)
269 {
270     CURLcode curlError = CURLE_OK;
271     if ( (CurlEasyHandle != NULL) && (CurlInitError == CURLE_OK) )
272     {
273         curlError = curl_easy_setopt(CurlEasyHandle,CURLOPT_USERNAME,username.c_str());
274         if ( curlError != CURLE_OK )
275             Log->print_curl_error_init("Could not set CURL username properly.",curlError);
276         curlError = curl_easy_setopt(CurlEasyHandle,CURLOPT_PASSWORD,password.c_str());
277         if ( curlError != CURLE_OK )
278             Log->print_curl_error_init("Could not set CURL password properly.",curlError);
279     }
280     else
281     {
282         return CURLE_FAILED_INIT;
283     }
284     return curlError;
285 }
286
287
288 /**
289  * Callback Function is called every time CURL is receiving data from HTTPS-Server and will copy all received Data to the given stream pointer
290  * @param inBuffer Pointer to input.
291  * @param size How many mem blocks are received
292  * @param nmemb size of each memblock
293  * @param outBuffer Pointer to output stream.
294  * @return The size received.
295  */
296 size_t HTTPHelper::http_receive( const char *inBuffer, size_t size, size_t nmemb, string *outBuffer )
297 {
298     outBuffer->append(inBuffer);
299     return (size*nmemb);
300 }
301