You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/apps/dsm/mods/mod_curl/ModCurl.cpp

420 lines
12 KiB

/*
* Copyright (C) 2009 TelTech Systems Inc.
* Copyright (C) 2011 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ModCurl.h"
#include "log.h"
#include "AmUtils.h"
#include "DSMSession.h"
#include "AmSession.h"
#include <curl/curl.h>
#include <sstream>
#include "AmConfigReader.h"
bool CurlModule::curl_initialized = false;
SC_EXPORT(CurlModule);
CurlModule::CurlModule() {
if (!curl_initialized) {
curl_initialized = true;
if (curl_global_init(CURL_GLOBAL_ALL)) {
ERROR("Initializing libcurl\n");
throw std::runtime_error("Initializing libcurl");
}
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
if (data && 0 != data->version) {
DBG("using libcurl version '%s'\n", data->version);
if (data->features & CURL_VERSION_SSL) {
DBG("libcurl with SSL version '%s'\n", data->ssl_version);
} else {
DBG("libcurl without SSL support\n");
}
}
}
}
CurlModule::~CurlModule() {
}
DSMAction* CurlModule::getAction(const string& from_str) {
string cmd;
string params;
splitCmd(from_str, cmd, params);
DEF_CMD("curl.get", SCJCurlGetAction);
DEF_CMD("curl.getDiscardResult", SCJCurlGetNoresultAction);
DEF_CMD("curl.getFile", SCJCurlGetFileAction);
DEF_CMD("curl.getForm", SCJCurlGetFormAction);
DEF_CMD("curl.post", SCJCurlPOSTGetResultAction);
DEF_CMD("curl.postDiscardResult", SCJCurlPOSTAction);
return NULL;
}
DSMCondition* CurlModule::getCondition(const string& from_str) {
return NULL;
}
size_t debug_output_func(void *ptr, size_t size, size_t
nmemb, void *stream) {
string data((char*)ptr, size*nmemb);
DBG("server out: <<%s>>\n", data.c_str());
return size*nmemb;
}
/** append output to $curl.out */
size_t var_output_func(void *ptr, size_t size, size_t
nmemb, void *stream) {
if (NULL == stream)
return size*nmemb;
string data((char*)ptr, size*nmemb);
DBG("server out: <<%s>>\n", data.c_str());
DSMSession* sc_sess = reinterpret_cast<DSMSession*>(stream);
if (sc_sess) {
sc_sess->var["curl.out"]+=data;
}
return size*nmemb;
}
bool curl_run_get(DSMSession* sc_sess, const string& url,
bool get_result) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return false;
}
char* enc_url = curl_easy_escape(m_curl_handle, url.c_str(), url.length());
if (NULL == enc_url) {
ERROR("URL-encoding url '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2int(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
}
}
if (!get_result) {
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func)
!= CURLE_OK) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func)
!= CURLE_OK)||
(curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess)
!= CURLE_OK)) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting curl error buffer\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
if (rescode) {
DBG("Error while trying to retrieve '%s': '%s'\n",
url.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
}
curl_easy_cleanup(m_curl_handle);
free(enc_url);
return false;
}
EXEC_ACTION_START(SCJCurlGetAction) {
sc_sess->var.erase("curl.out");
return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), true);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCJCurlGetNoresultAction) {
return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCJCurlGetFormAction, ',', true);
EXEC_ACTION_START(SCJCurlGetFormAction) {
sc_sess->var.erase("curl.out");
string form_url = resolveVars(par1, sess, sc_sess, event_params);;
bool url_has_qmark = form_url.find('?')!=string::npos;
vector<string> p_vars=explode(par2, ";");
for (vector<string>::iterator it=
p_vars.begin();it != p_vars.end();it++) {
string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it);
DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
if (!url_has_qmark && it == p_vars.begin())
form_url+= "?";
else
form_url+= "&";
form_url += varname + "=" + sc_sess->var[varname];
}
return curl_run_get(sc_sess, form_url, true);
} EXEC_ACTION_END;
void curl_run_getfile(DSMSession* sc_sess, const string& url, const string& outfile) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2int(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return;
}
}
}
FILE* f = fopen(outfile.c_str(), "wb");
if (NULL == f) {
DBG("Error opening file '%s' for writing\n", outfile.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return;
}
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, f)
!= CURLE_OK) {
ERROR("setting curl data file\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
fclose(f);
return;
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting URL '%s'\n", url.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
fclose(f);
return;
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
if (rescode) {
DBG("Error while trying to retrieve '%s' to '%s': '%s'\n",
url.c_str(), outfile.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
}else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
}
fclose(f);
curl_easy_cleanup(m_curl_handle);
}
CONST_ACTION_2P(SCJCurlGetFileAction, ',', true);
EXEC_ACTION_START(SCJCurlGetFileAction) {
curl_run_getfile(sc_sess,
resolveVars(par1, sess, sc_sess, event_params),
resolveVars(par2, sess, sc_sess, event_params));
} EXEC_ACTION_END;
bool curl_run_post(DSMSession* sc_sess, const string& par1, const string& par2,
bool get_result) {
CURL* m_curl_handle = curl_easy_init();
if (!m_curl_handle) {
ERROR("getting curl handle\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
return false;
}
curl_mime *multipart = curl_mime_init(m_curl_handle);
curl_mimepart *part = curl_mime_addpart(multipart);
if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, par1.c_str())
!= CURLE_OK) {
ERROR("setting URL '%s'\n", par1.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
if (!sc_sess->var["curl.timeout"].empty()) {
unsigned int curl_timeout = 0;
if (str2int(sc_sess->var["curl.timeout"], curl_timeout)) {
WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str());
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) ||
(curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) {
ERROR("setting timeout '%u'\n", curl_timeout);
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
curl_easy_cleanup(m_curl_handle);
return false;
}
}
}
if (!get_result) {
if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func)
!= CURLE_OK) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
return false;
}
} else {
if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func)
!= CURLE_OK)||
(curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess)
!= CURLE_OK)) {
ERROR("setting curl write function\n");
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
curl_easy_cleanup(m_curl_handle);
return false;
}
}
char curl_err[CURL_ERROR_SIZE];
curl_err[0]='\0';
if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err)
!= CURLE_OK) {
ERROR("setting curl error buffer\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
curl_easy_cleanup(m_curl_handle);
return false;
}
string post_vars;
vector<string> p_vars=explode(par2, ";");
for (vector<string>::iterator it=
p_vars.begin();it != p_vars.end();it++) {
string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it);
DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
curl_mime_name(part, varname.c_str());
curl_mime_data(part, sc_sess->var[varname].c_str(), CURL_ZERO_TERMINATED);
curl_easy_setopt(m_curl_handle, CURLOPT_MIMEPOST, multipart);
}
CURLcode rescode = curl_easy_perform(m_curl_handle);
bool res = false;
if (rescode) {
DBG("Error while trying to POST to '%s': '%s'\n",
par1.c_str(), curl_err);
sc_sess->var["curl.err"] = string(curl_err);
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
res = true;
}
curl_mime_free(multipart);
curl_easy_cleanup(m_curl_handle);
return false;
}
CONST_ACTION_2P(SCJCurlPOSTAction, ',', true);
EXEC_ACTION_START(SCJCurlPOSTAction) {
curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params),
par2, false);
return false;
} EXEC_ACTION_END;
CONST_ACTION_2P(SCJCurlPOSTGetResultAction, ',', true);
EXEC_ACTION_START(SCJCurlPOSTGetResultAction) {
sc_sess->var.erase("curl.out");
curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params),
par2, true);
return false;
} EXEC_ACTION_END;