mirror of https://github.com/sipwise/sems.git
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.
420 lines
12 KiB
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;
|