Compare commits
5 Commits
2367c48709
...
fe52dae047
Author | SHA1 | Date |
---|---|---|
ovadbar | fe52dae047 | |
Aron Podrigal | 5cb74797fe | |
ovadbar | 406bd88f1b | |
Baroukh Ovadia | bed779239b | |
Baroukh Ovadia | 81f41b28c7 |
|
@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam
|
|||
MODNAME=mod_http_cache
|
||||
|
||||
noinst_LTLIBRARIES = libhttpcachemod.la
|
||||
libhttpcachemod_la_SOURCES = mod_http_cache.c common.c aws.c azure.c
|
||||
libhttpcachemod_la_SOURCES = mod_http_cache.c common.c aws.c azure.c gcs.c
|
||||
|
||||
mod_LTLIBRARIES = mod_http_cache.la
|
||||
mod_http_cache_la_SOURCES =
|
||||
|
@ -11,12 +11,16 @@ mod_http_cache_la_CPPFLAGS = $(CURL_CFLAGS) $(AM_CPPFLAGS)
|
|||
mod_http_cache_la_LIBADD = $(switch_builddir)/libfreeswitch.la libhttpcachemod.la
|
||||
mod_http_cache_la_LDFLAGS = $(CURL_LIBS) -avoid-version -module -no-undefined -shared
|
||||
|
||||
noinst_PROGRAMS = test/test_aws
|
||||
noinst_PROGRAMS = test/test_aws test/test_gcs
|
||||
|
||||
test_test_aws_SOURCES = test/test_aws.c
|
||||
test_test_aws_CFLAGS = $(AM_CFLAGS) -I. -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
|
||||
test_test_aws_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
|
||||
test_test_aws_LDADD = libhttpcachemod.la
|
||||
|
||||
TESTS = $(noinst_PROGRAMS)
|
||||
test_test_gcs_SOURCES = test/test_gcs.c
|
||||
test_test_gcs_CFLAGS = $(AM_CFLAGS) -I. -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
|
||||
test_test_gcs_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) -lcurl
|
||||
test_test_gcs_LDADD = libhttpcachemod.la
|
||||
|
||||
TESTS = $(noinst_PROGRAMS)
|
||||
|
|
|
@ -42,8 +42,11 @@ struct http_profile {
|
|||
char *aws_s3_access_key_id;
|
||||
char *secret_access_key;
|
||||
char *base_domain;
|
||||
char *region; // AWS region. Used by AWS S3
|
||||
char *region; // AWS region. Used by AWS S3 // Used by gcs as token uri
|
||||
char *gcs_email; // GCS service account email
|
||||
char *gcs_credentials; // GCS credencial token
|
||||
switch_time_t expires; // Expiration time in seconds for URL signature. Default is 604800 seconds. Used by AWS S3
|
||||
// GCS Expiration used for when the gcs_credentials expires
|
||||
switch_size_t bytes_per_block;
|
||||
int header_count;
|
||||
char** header_names;
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* gcs.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2020
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is gcs.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* gcs.c -- Some GCS Blob Service helper functions
|
||||
*
|
||||
*/
|
||||
#include "gcs.h"
|
||||
#include <switch.h>
|
||||
#include <switch_curl.h>
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#endif
|
||||
|
||||
struct http_data {
|
||||
switch_stream_handle_t stream;
|
||||
switch_size_t bytes;
|
||||
switch_size_t max_bytes;
|
||||
int err;
|
||||
};
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
char *encoded_token(const char *token_uri, const char *client_email, const char *private_key_id, int *token_length, time_t now) {
|
||||
time_t then = now + 3600;
|
||||
int tlength = 1 + snprintf(NULL, 0, "{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"%s\"}", private_key_id);
|
||||
int payload_length = 1 + snprintf(NULL, 0, "{\"iat\":\"%ld\",\"exp\":\"%ld\",\"iss\":\"%s\",\"aud\":\"%s\",\"scope\":\"https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_only https://www.googleapis.com/auth/devstorage.read_write\"}", now, then, client_email,token_uri);
|
||||
char token[tlength];
|
||||
char payload[payload_length];
|
||||
int encoded_tlength = tlength * 4 / 3 + (tlength % 3 ? 1 : 0);
|
||||
int encoded_playload_length = payload_length * 4 / 3 + (payload_length % 3 ? 1 : 0);
|
||||
char *tokenb64 = malloc(encoded_tlength * sizeof(char));
|
||||
char *payloadb64 = malloc(encoded_playload_length* sizeof(char));
|
||||
int signee_length = encoded_tlength + encoded_playload_length;
|
||||
char *signee = malloc((signee_length) * sizeof(char));
|
||||
sprintf(token,"{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"%s\"}", private_key_id);
|
||||
sprintf(payload, "{\"iat\":\"%ld\",\"exp\":\"%ld\",\"iss\":\"%s\",\"aud\":\"%s\",\"scope\":\"https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_only https://www.googleapis.com/auth/devstorage.read_write\"}", now, then, client_email,token_uri);
|
||||
*token_length = signee_length - 1;
|
||||
switch_b64_encode((unsigned char *) token,sizeof(token), (unsigned char *) tokenb64, encoded_tlength);
|
||||
switch_b64_encode((unsigned char *) payload,sizeof(payload), (unsigned char *) payloadb64, encoded_playload_length);
|
||||
sprintf(signee, "%s.%s", tokenb64, payloadb64);
|
||||
free(tokenb64);
|
||||
free(payloadb64);
|
||||
return signee;
|
||||
}
|
||||
|
||||
void signtoken(char *token, int tokenlen,char *pkey, char *out) {
|
||||
unsigned char *sig = NULL;
|
||||
BIO *b = NULL;
|
||||
RSA *r = NULL;
|
||||
unsigned int sig_len;
|
||||
unsigned char *md = malloc(SHA256_DIGEST_LENGTH * sizeof(char));
|
||||
unsigned char *digest = SHA256((const unsigned char *) token, tokenlen, md);
|
||||
b = BIO_new_mem_buf(pkey, -1);
|
||||
r = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
|
||||
BIO_set_close(b, BIO_CLOSE);
|
||||
BIO_free(b);
|
||||
sig = malloc(RSA_size(r));
|
||||
RSA_sign(NID_sha256, digest, sizeof(char) * SHA256_DIGEST_LENGTH, sig, &sig_len, r);
|
||||
switch_b64_encode(sig,(switch_size_t) sizeof(char) * sig_len,(unsigned char *) out, 343 * sizeof(char));
|
||||
free(sig);
|
||||
free(md);
|
||||
RSA_free(r);
|
||||
}
|
||||
|
||||
char *gcs_auth_request(char *content, char *url);
|
||||
switch_status_t gcs_refresh_authorization (http_profile_t *profile)
|
||||
{
|
||||
int token_length;
|
||||
char *token = NULL;
|
||||
char *encoded = NULL;
|
||||
char *assertion = NULL;
|
||||
char *auth = NULL;
|
||||
char content[GCS_SIGNATURE_LENGTH_MAX];
|
||||
char *signature_url_encoded = NULL;
|
||||
time_t exp;
|
||||
time_t now = time(NULL);
|
||||
token = encoded_token(profile->region, profile->gcs_email, profile->aws_s3_access_key_id, &token_length, now);
|
||||
encoded = malloc(sizeof(char) * 343);
|
||||
signtoken(token, token_length, profile->secret_access_key, encoded);
|
||||
assertion = malloc(sizeof(char) * (1 + token_length + 343));
|
||||
sprintf(assertion, "%s.%s", token, encoded);
|
||||
free(token);
|
||||
signature_url_encoded = switch_string_replace(assertion, "+", "%2B");
|
||||
sprintf(content,"%s%s", "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=", signature_url_encoded);
|
||||
auth = gcs_auth_request(content, profile->region);
|
||||
profile->gcs_credentials = auth;
|
||||
exp = now + 3540;
|
||||
profile->expires = exp;
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Credentials Expries Unix Time: %ld", exp);
|
||||
switch_safe_free(assertion);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Append the specific GCS Blob Service headers
|
||||
* @param http_profile_t the provile
|
||||
* @param headers the list of headers to append to
|
||||
*/
|
||||
|
||||
switch_curl_slist_t *gcs_append_headers(http_profile_t *profile, switch_curl_slist_t *headers, const char *verb,unsigned int content_length, const char *content_type, const char *url, const unsigned int block_num, char **query_string)
|
||||
{
|
||||
char header[1024];
|
||||
#if defined(HAVE_OPENSSL)
|
||||
switch_time_t now = time(NULL);
|
||||
if (profile->expires < now) {
|
||||
gcs_refresh_authorization(profile);
|
||||
}
|
||||
switch_snprintf(header, sizeof(header), "Authorization: Bearer %s", profile->gcs_credentials);
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Credecials Token: %s", profile->gcs_credentials);
|
||||
headers = switch_curl_slist_append(headers, header);
|
||||
#endif
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the GCS Blob Service profile
|
||||
* @param name the name of the profile
|
||||
* @param xml the portion of the XML document containing the profile
|
||||
* @param access_key_id returned value of access_key_id in the configuration
|
||||
* @param secret_access_key returned value of secret_access_key in the configuration
|
||||
* @param base_domain returned value of base_domain in the configuration
|
||||
* @param bytes_per_block returned value of bytes_per_block in the configuration
|
||||
* @return SWITCH_STATUS_SUCCESS on success
|
||||
*/
|
||||
switch_status_t gcs_config_profile(switch_xml_t xml, http_profile_t *profile,switch_memory_pool_t *pool)
|
||||
{
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
#if defined(HAVE_OPENSSL)
|
||||
char *file = NULL;
|
||||
char *envfile = getenv("GOOGLE_APPLICATION_CREDENTIALS");
|
||||
switch_xml_t base_domain_xml = switch_xml_child(xml, "base-domain");
|
||||
profile->append_headers_ptr = gcs_append_headers;
|
||||
|
||||
/* check if environment variables set the keys */
|
||||
if (!zstr(envfile)) {
|
||||
file = switch_core_strdup(pool, envfile);
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
|
||||
// "Using GOOGLE_APPLICATION_CREDENTIALS environment variables for GCS access on profile \"%s\"\n", profile->name);
|
||||
} else {
|
||||
/* use configuration for keys */
|
||||
switch_xml_t creds = switch_xml_child(xml, "credential_file");
|
||||
if (creds) {
|
||||
file = switch_strip_whitespace(switch_xml_txt(creds));
|
||||
}
|
||||
}
|
||||
if (switch_file_exists(file, pool) == SWITCH_STATUS_SUCCESS) {
|
||||
char *contents = NULL;
|
||||
char *jsonstr = NULL;
|
||||
switch_file_t *fd;
|
||||
switch_status_t status;
|
||||
switch_size_t size;
|
||||
cJSON *json = {0};
|
||||
|
||||
status = switch_file_open(&fd, file, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD, pool);
|
||||
if (status != SWITCH_STATUS_SUCCESS) {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERR, "Could not open credencial file\n", profile->bytes_per_block);
|
||||
switch_safe_free(file);
|
||||
return status;
|
||||
}
|
||||
|
||||
size = switch_file_get_size(fd);
|
||||
if (size) {
|
||||
contents = malloc(size);
|
||||
switch_file_read(fd, (void *) contents, &size);
|
||||
} else {
|
||||
switch_safe_free(file);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
status = switch_file_close(fd);
|
||||
if (status != SWITCH_STATUS_SUCCESS) {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERR, "Could not close credencial file\n", profile->bytes_per_block);
|
||||
switch_safe_free(file);
|
||||
return status;
|
||||
}
|
||||
json = cJSON_Parse(contents);
|
||||
if (cJSON_GetObjectItem(json,"private_key_id") != NULL) {
|
||||
jsonstr = cJSON_GetObjectItem(json,"private_key_id")->valuestring;
|
||||
profile->aws_s3_access_key_id = malloc(sizeof(char) * (1+ strlen(jsonstr)));
|
||||
strcpy(profile->aws_s3_access_key_id, jsonstr);
|
||||
}
|
||||
if (cJSON_GetObjectItem(json,"private_key") != NULL) {
|
||||
jsonstr = cJSON_GetObjectItem(json,"private_key")->valuestring;
|
||||
profile->secret_access_key = malloc(sizeof(char) * (1+ strlen(jsonstr)));
|
||||
strcpy(profile->secret_access_key, jsonstr);
|
||||
}
|
||||
if (cJSON_GetObjectItem(json,"client_email") != NULL) {
|
||||
jsonstr = cJSON_GetObjectItem(json,"client_email")->valuestring;
|
||||
profile->gcs_email = malloc(sizeof(char) * (1+ strlen(jsonstr)));
|
||||
strcpy(profile->gcs_email, jsonstr);
|
||||
}
|
||||
if (cJSON_GetObjectItem(json,"token_uri") != NULL) {
|
||||
jsonstr = cJSON_GetObjectItem(json,"token_uri")->valuestring;
|
||||
profile->region = malloc(sizeof(char) * (1+ strlen(jsonstr)));
|
||||
strcpy(profile->region, jsonstr);
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
free(contents);
|
||||
} else {
|
||||
switch_xml_t private_key = switch_xml_child(xml, "private_key");
|
||||
switch_xml_t private_key_id = switch_xml_child(xml, "private_key_id");
|
||||
switch_xml_t client_email = switch_xml_child(xml, "client_email");
|
||||
switch_xml_t token_uri = switch_xml_child(xml, "token_uri");
|
||||
if (private_key_id) {
|
||||
profile->aws_s3_access_key_id = switch_strip_whitespace(switch_xml_txt(private_key_id));
|
||||
} else {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key private_key_id\n");
|
||||
switch_safe_free(file);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
if (private_key) {
|
||||
profile->secret_access_key = switch_xml_txt(private_key);
|
||||
} else {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key private_key\n");
|
||||
switch_safe_free(file);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
if (client_email) {
|
||||
profile->gcs_email = switch_strip_whitespace(switch_xml_txt(client_email));
|
||||
} else {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key client_email\n");
|
||||
switch_safe_free(file);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
if (token_uri) {
|
||||
profile->region = switch_strip_whitespace(switch_xml_txt(token_uri));
|
||||
} else {
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key token_uri\n");
|
||||
switch_safe_free(file);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
switch_safe_free(file);
|
||||
profile->bytes_per_block = 4e6;
|
||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set number of bytes per block to %zu\n", profile->bytes_per_block);
|
||||
status = gcs_refresh_authorization(profile);
|
||||
if (status != SWITCH_STATUS_SUCCESS){
|
||||
return status;
|
||||
}
|
||||
|
||||
if (base_domain_xml) {
|
||||
profile->base_domain = switch_strip_whitespace(switch_xml_txt(base_domain_xml));
|
||||
if (zstr(profile->base_domain)) {
|
||||
switch_safe_free(profile->base_domain);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
static size_t gcs_auth_callback(void *ptr, size_t size, size_t nmemb, void *data)
|
||||
{
|
||||
register unsigned int realsize = (unsigned int) (size * nmemb);
|
||||
struct http_data *http_data = data;
|
||||
|
||||
http_data->bytes += realsize;
|
||||
|
||||
if (http_data->bytes > http_data->max_bytes) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) http_data->bytes);
|
||||
http_data->err = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_data->stream.write_function(&http_data->stream, "%.*s", realsize, ptr);
|
||||
return realsize;
|
||||
}
|
||||
|
||||
char *gcs_auth_request(char *content, char *url) {
|
||||
switch_CURL *curl_handle = NULL;
|
||||
long httpRes = 0;
|
||||
char *response = NULL;
|
||||
switch_curl_slist_t *headers = NULL;
|
||||
char *ct = "Content-Type: application/x-www-form-urlencoded";
|
||||
struct http_data http_data;
|
||||
CURLcode res;
|
||||
|
||||
memset(&http_data, 0, sizeof(http_data));
|
||||
http_data.max_bytes = 10240;
|
||||
SWITCH_STANDARD_STREAM(http_data.stream);
|
||||
|
||||
curl_handle = switch_curl_easy_init();
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, content);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
headers = switch_curl_slist_append(headers, ct);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0");
|
||||
switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, gcs_auth_callback);
|
||||
switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&http_data);
|
||||
|
||||
res = switch_curl_easy_perform(curl_handle);
|
||||
switch_curl_easy_cleanup(curl_handle);
|
||||
|
||||
if(res != CURLE_OK)
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n",
|
||||
switch_curl_easy_strerror(res));
|
||||
|
||||
if (http_data.stream.data && !zstr((char *) http_data.stream.data) && strcmp(" ", http_data.stream.data)) {
|
||||
cJSON *json = {0};
|
||||
json = cJSON_Parse(http_data.stream.data);
|
||||
|
||||
if (cJSON_GetObjectItem(json,"access_token") != NULL) {
|
||||
char *jsonstr;
|
||||
jsonstr = cJSON_GetObjectItem(json,"access_token")->valuestring;
|
||||
response = malloc(sizeof(char) * (1+strlen(jsonstr)));
|
||||
strcpy(response, jsonstr);
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
|
||||
*/
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* gcs.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2013-2014, Grasshopper
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is gcs.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is Baroukh Ovadia
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Baroukh Ovadia <ovadbar@gmail.com>
|
||||
*
|
||||
* gcs.h - Some Google Cloud Storage helper functions
|
||||
*
|
||||
*/
|
||||
#ifndef GCS_H
|
||||
#define GCS_H
|
||||
|
||||
#include <switch.h>
|
||||
#include <switch_curl.h>
|
||||
#include <common.h>
|
||||
|
||||
/* (SHA1_LENGTH * 1.37 base64 bytes per byte * 3 url-encoded bytes per byte) */
|
||||
#define GCS_SIGNATURE_LENGTH_MAX 1024
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_curl_slist_t*) gcs_append_headers(http_profile_t *profile, switch_curl_slist_t *headers,
|
||||
const char *verb, unsigned int content_length, const char *content_type, const char *url, const unsigned int block_num, char **query_string);
|
||||
SWITCH_MOD_DECLARE(switch_status_t) gcs_config_profile(switch_xml_t xml, http_profile_t *profile,switch_memory_pool_t *pool);
|
||||
|
||||
#endif
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
|
||||
*/
|
|
@ -36,6 +36,7 @@
|
|||
#include <switch_curl.h>
|
||||
#include "aws.h"
|
||||
#include "azure.h"
|
||||
#include "gcs.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -1698,14 +1699,21 @@ static switch_status_t do_config(url_cache_t *cache)
|
|||
continue;
|
||||
}
|
||||
} else {
|
||||
profile_xml = switch_xml_child(profile, "default");
|
||||
profile_xml = switch_xml_child(profile, "gcs");
|
||||
if (profile_xml) {
|
||||
if (default_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) {
|
||||
if (gcs_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
profile_xml = switch_xml_child(profile, "default");
|
||||
if (profile_xml) {
|
||||
if (default_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding profile \"%s\" to cache\n", name);
|
||||
switch_core_hash_insert(cache->profiles, profile_obj->name, profile_obj);
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* aws.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2013-2014, Grasshopper
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is aws.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is Grasshopper
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Rienzo <chris.rienzo@grasshopper.com>
|
||||
* Quoc-Bao Nguyen <baonq5@vng.com.vn>
|
||||
*
|
||||
* test_aws.c - Unit tests for functions in aws.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <test/switch_test.h>
|
||||
#include "../gcs.c"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Run test
|
||||
// make && libtool --mode=execute valgrind --leak-check=full --log-file=vg.log ./test/test_aws && cat vg.log
|
||||
|
||||
FST_BEGIN()
|
||||
{
|
||||
FST_SUITE_BEGIN()
|
||||
{
|
||||
|
||||
FST_SETUP_BEGIN()
|
||||
{
|
||||
}
|
||||
FST_SETUP_END()
|
||||
|
||||
FST_TEARDOWN_BEGIN()
|
||||
{
|
||||
}
|
||||
FST_TEARDOWN_END()
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
FST_TEST_BEGIN(encoded_token)
|
||||
{
|
||||
//char *encoded_token(const char *token_uri, const char *client_email, const char *private_key_id, int *token_length, time_t now) {
|
||||
time_t now = 1615402513;
|
||||
char *token = NULL;
|
||||
int token_length;
|
||||
|
||||
/*
|
||||
char *tkn = "TESTER";
|
||||
int encoded_tlength = 8;
|
||||
unsigned char *tknb64 = malloc(encoded_tlength * sizeof(char));
|
||||
switch_b64_encode((unsigned char *) tkn,sizeof(tkn), tknb64, encoded_tlength);
|
||||
*/
|
||||
|
||||
token = encoded_token("https://accounts.google.com/o/oauth2/token", "gcs@freeswitch.com", "667265657377697463682D676373", &token_length, now);
|
||||
fst_check_string_equals("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2NzI2NTY1NzM3NzY5NzQ2MzY4MkQ2NzYzNzMifQ.eyJpYXQiOiIxNjE1NDAyNTEzIiwiZXhwIjoiMTYxNTQwNjExMyIsImlzcyI6Imdjc0BmcmVlc3dpdGNoLmNvbSIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kZXZzdG9yYWdlLmZ1bGxfY29udHJvbCBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2RldnN0b3JhZ2UucmVhZF9vbmx5IGh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIn0", token);
|
||||
free(token);
|
||||
}
|
||||
FST_TEST_END()
|
||||
|
||||
FST_TEST_BEGIN(signtoken)
|
||||
{
|
||||
//void signtoken(char *token, int tokenlen,char *pkey, char *out) {
|
||||
char *encoded = NULL;
|
||||
char *pkey = "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXiVOV3h61llym\nnpHamUHsuVjrdDiEQnNX1KA4k/kcfP+gqRjL9m3YAxXfUA9GFCIC2OYvdciW3Ggm\nt4CSYmSltljKjbN2JHs7iNQw4CcFfiAXhxL0TYNhNE9wBDOZRsC1Uusv38RPwqwd\n922pAGzF+PqNE2j+zSxlNOnlJfyJDMKrqCGV8CclS+j/u41MaT6cpOlHP6KgaS01\nJJVyaqLpnMLWAx9/5G6Y/YWah+obEyn7yoDva/1Yhlnq20CMHlh3ifDfYYrS9rtp\nhdutz4fESKFPKymlG9aGfFuCME3GP8Rn8ZdPbsAWZ2cf388CLjlxEid1EU8klr2X\n+G1+3di/AgMBAAECggEAHL6UZ9+/5IMWpRZ8JUKgAjbwWo1rsQ7n0TfIgqLzBIfj\nd4bL6NigYnLHYdpOY2UrRG3/T+5gM9mwOfPiBCJ85AAwXI+/hIAMDjF4yqKiVETl\n8oCRRF01uCkTjnSFkyQcJukJKsYf918+hdqq5v1pJK6DXGJbrsWdj78XRPvNKPPD\naEEvSNwdet12Jz9wkmj2g7zg8KMX18v3IUyf7HKjb/cjomB0WuIDfJchDRYlxrfJ\n4DShcIfMi+04C/FFN+vbP77tXQM7O3Z81uqDQAO3k3NoXTTNBZGLP+SOyDUSRZQS\nCNb3J6cwTC737e0M6K+zVP1f03ynuU2u+dHiVVMTtQKBgQDNw/19HSxoJ/5nGHbD\nLW1g33Jm4Nr05lYnetEPS0wkruzEbTy3B6prl34KUslreuUTAuhtFxCLsEgT/sMO\nrxX/OM7RaNUILrpLrzen/eVNeiquM1wLEI52VNkRU5GlTZEJohGE+a3YP+kQTLe5\n8xmzKfJUllyfpXGxDNkryjaeSwKBgQC8iBqDJs6h9tkdxC/XC8+qSJ+WBk5tr3PM\nyx/x1NGKO5TpgdhRr98GYYUhoph+TIp0/8/+d2lVDzO4SAOzms3xnANPEzJcLi7c\nCa8ECOW3S4HhWE61QsbVY5xA83hGAO2WQN22vu9KwhyFU145aSQH0tJrQoevkdBl\ndpqtP15W3QKBgQCsL8gePJ1+g4k2SJiJd6hCGnoncR6JNX7/Bp2PiNktEVx8e1UF\nbNrFsj388Y4v7OVo5VQOhfCIlHmckeI0lXt42dboEivC7ydiUjvmzmZmUUcKA1yQ\nvcgZaaNEBoSoqaInR4IVnsJFZiXoR+qvJqlo7j8lXbYgulfLaw8Iv+y4xQKBgGK6\no6eq2urWajy8UJE9DjMOdQQLqWanSu0kMkZiPJk3OnROGwosH48n4qAKlfEOBDPh\nAvsvbWmt3FfU3ptfphmwqcrvMqAzTzbLm2txfVrPn+RyakViAt4cm+cnmQSP19un\nfHQG6SktHeJ0FhPai5PNQ4QIAyZeJdP8mGPBm5XBAoGBAJWnXiapFMcHi3DgsVb4\n+ni8Qvs293OvsdjHlo2eent/Kwbrdytw/V8uhq9awJb1npdgVd54RrbZ+Jq4x19K\nt06Jz9/EAoLLfL+tqzpEiKvSLjdKpedPm1Cgfj0KTM+MqoSU0bv4gMssnM428luJ\nv5ptWjeaHoYpJzvGfGBouVCI\n-----END PRIVATE KEY-----\n";
|
||||
int tlength = snprintf(NULL, 0, "%s", pkey);
|
||||
encoded = malloc(sizeof(char) * 343);
|
||||
signtoken("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2NzI2NTY1NzM3NzY5NzQ2MzY4MkQ2NzYzNzMifQ.eyJpYXQiOiIxNjE1NDAyNTEzIiwiZXhwIjoiMTYxNTQwNjExMyIsImlzcyI6Imdjc0BmcmVlc3dpdGNoLmNvbSIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kZXZzdG9yYWdlLmZ1bGxfY29udHJvbCBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2RldnN0b3JhZ2UucmVhZF9vbmx5IGh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIn0", tlength, pkey, encoded);
|
||||
fst_check_string_equals("iSNfnz8gn1q8cYUrs7+m3hhXRalFRemt9SXvlnrzr1tyYx2ztL0m/882jghpsHMDVcti59avBjamcbrwMRDah7KXomu2cuCEwuYaGo6C7KTe9WZf+J0ep2shOAJJwnDKvUfuyVT21EEf/rWXfy8lblsq/YK0D7982FsXUgPDFU5NzPiw2fWk6YEPWbET3Yy+gJiTqYj5P0Fo1s66LNrLIX1RbH6/GW7d+lDl2RNMCZxGTtEygLdAXj+l0OPQG4Qvfzeymi9zWEq6j0rAChT0OppjZKqWf9IjMRbPUbuwIpNmgntfU7OcYtTFeiLy+W9fFutXEbSrq72MxMOrDEWWhQ", encoded);
|
||||
free(encoded);
|
||||
}
|
||||
FST_TEST_END()
|
||||
|
||||
FST_TEST_BEGIN(parse_xml_config_with_gcs)
|
||||
{
|
||||
switch_xml_t cfg, profiles, profile, gcs_profile;
|
||||
http_profile_t http_profile;
|
||||
int fd;
|
||||
int i = 0;
|
||||
|
||||
|
||||
fd = open("test_gcs_http_cache.conf.xml", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
//printf("Open in test dir\n");
|
||||
fd = open("test/parse_xml_config_with_gcs", O_RDONLY);
|
||||
}
|
||||
fst_check(fd > 0);
|
||||
|
||||
cfg = switch_xml_parse_fd(fd);
|
||||
fst_check(cfg != NULL);
|
||||
|
||||
profiles = switch_xml_child(cfg, "profiles");
|
||||
fst_check(profiles);
|
||||
|
||||
for (profile = switch_xml_child(profiles, "profile"); profile; profile = profile->next) {
|
||||
const char *name = NULL;
|
||||
switch_memory_pool_t *pool;
|
||||
switch_core_new_memory_pool(&pool);
|
||||
i++;
|
||||
|
||||
fst_check(profile);
|
||||
|
||||
name = switch_xml_attr_soft(profile, "name");
|
||||
fst_check(name);
|
||||
|
||||
http_profile.name = name;
|
||||
http_profile.aws_s3_access_key_id = NULL;
|
||||
http_profile.secret_access_key = NULL;
|
||||
http_profile.base_domain = NULL;
|
||||
http_profile.region = NULL;
|
||||
|
||||
gcs_profile = switch_xml_child(profile, "gcs");
|
||||
gcs_config_profile(gcs_profile, &http_profile, pool);
|
||||
//aws_s3_access_key_id, secret_access_key, gcs_email, region
|
||||
fst_check(!zstr(http_profile.region));
|
||||
fst_check(!zstr(http_profile.aws_s3_access_key_id));
|
||||
fst_check(!zstr(http_profile.secret_access_key));
|
||||
switch_safe_free(http_profile.region);
|
||||
switch_safe_free(http_profile.aws_s3_access_key_id);
|
||||
switch_safe_free(http_profile.secret_access_key);
|
||||
switch_safe_free(http_profile.base_domain);
|
||||
switch_core_destroy_memory_pool(&pool);
|
||||
}
|
||||
|
||||
fst_check(i == 1); // test data contain two config
|
||||
|
||||
switch_xml_free(cfg);
|
||||
}
|
||||
FST_TEST_END()
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
FST_SUITE_END()
|
||||
|
||||
}
|
||||
FST_END()
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "service_account",
|
||||
"project_id": "freeswitch-gcs",
|
||||
"private_key_id": "667265657377697463682D676373",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXiVOV3h61llym\nnpHamUHsuVjrdDiEQnNX1KA4k/kcfP+gqRjL9m3YAxXfUA9GFCIC2OYvdciW3Ggm\nt4CSYmSltljKjbN2JHs7iNQw4CcFfiAXhxL0TYNhNE9wBDOZRsC1Uusv38RPwqwd\n922pAGzF+PqNE2j+zSxlNOnlJfyJDMKrqCGV8CclS+j/u41MaT6cpOlHP6KgaS01\nJJVyaqLpnMLWAx9/5G6Y/YWah+obEyn7yoDva/1Yhlnq20CMHlh3ifDfYYrS9rtp\nhdutz4fESKFPKymlG9aGfFuCME3GP8Rn8ZdPbsAWZ2cf388CLjlxEid1EU8klr2X\n+G1+3di/AgMBAAECggEAHL6UZ9+/5IMWpRZ8JUKgAjbwWo1rsQ7n0TfIgqLzBIfj\nd4bL6NigYnLHYdpOY2UrRG3/T+5gM9mwOfPiBCJ85AAwXI+/hIAMDjF4yqKiVETl\n8oCRRF01uCkTjnSFkyQcJukJKsYf918+hdqq5v1pJK6DXGJbrsWdj78XRPvNKPPD\naEEvSNwdet12Jz9wkmj2g7zg8KMX18v3IUyf7HKjb/cjomB0WuIDfJchDRYlxrfJ\n4DShcIfMi+04C/FFN+vbP77tXQM7O3Z81uqDQAO3k3NoXTTNBZGLP+SOyDUSRZQS\nCNb3J6cwTC737e0M6K+zVP1f03ynuU2u+dHiVVMTtQKBgQDNw/19HSxoJ/5nGHbD\nLW1g33Jm4Nr05lYnetEPS0wkruzEbTy3B6prl34KUslreuUTAuhtFxCLsEgT/sMO\nrxX/OM7RaNUILrpLrzen/eVNeiquM1wLEI52VNkRU5GlTZEJohGE+a3YP+kQTLe5\n8xmzKfJUllyfpXGxDNkryjaeSwKBgQC8iBqDJs6h9tkdxC/XC8+qSJ+WBk5tr3PM\nyx/x1NGKO5TpgdhRr98GYYUhoph+TIp0/8/+d2lVDzO4SAOzms3xnANPEzJcLi7c\nCa8ECOW3S4HhWE61QsbVY5xA83hGAO2WQN22vu9KwhyFU145aSQH0tJrQoevkdBl\ndpqtP15W3QKBgQCsL8gePJ1+g4k2SJiJd6hCGnoncR6JNX7/Bp2PiNktEVx8e1UF\nbNrFsj388Y4v7OVo5VQOhfCIlHmckeI0lXt42dboEivC7ydiUjvmzmZmUUcKA1yQ\nvcgZaaNEBoSoqaInR4IVnsJFZiXoR+qvJqlo7j8lXbYgulfLaw8Iv+y4xQKBgGK6\no6eq2urWajy8UJE9DjMOdQQLqWanSu0kMkZiPJk3OnROGwosH48n4qAKlfEOBDPh\nAvsvbWmt3FfU3ptfphmwqcrvMqAzTzbLm2txfVrPn+RyakViAt4cm+cnmQSP19un\nfHQG6SktHeJ0FhPai5PNQ4QIAyZeJdP8mGPBm5XBAoGBAJWnXiapFMcHi3DgsVb4\n+ni8Qvs293OvsdjHlo2eent/Kwbrdytw/V8uhq9awJb1npdgVd54RrbZ+Jq4x19K\nt06Jz9/EAoLLfL+tqzpEiKvSLjdKpedPm1Cgfj0KTM+MqoSU0bv4gMssnM428luJ\nv5ptWjeaHoYpJzvGfGBouVCI\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "gcs@freeswitch.com",
|
||||
"client_id": "105862293685176461395",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gcs%40freeswitch.com"
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<configuration name="http_cache.conf" description="HTTP GET cache">
|
||||
<settings>
|
||||
<!-- set to true if you want to enable http:// and https:// formats. Do not use if mod_httapi is also loaded -->
|
||||
<param name="enable-file-formats" value="true"/>
|
||||
<param name="max-urls" value="10000"/>
|
||||
<param name="location" value="$${cache_dir}"/>
|
||||
<param name="default-max-age" value="86400"/>
|
||||
<param name="prefetch-thread-count" value="8"/>
|
||||
<param name="prefetch-queue-size" value="100"/>
|
||||
<!-- absolute path to CA bundle file -->
|
||||
<param name="ssl-cacert" value="$${certs_dir}/cacert.pem"/>
|
||||
<!-- verify certificates -->
|
||||
<param name="ssl-verifypeer" value="true"/>
|
||||
<!-- verify host name matches certificate -->
|
||||
<param name="ssl-verifyhost" value="true"/>
|
||||
<!-- default is 300 seconds, override here -->
|
||||
<!--param name="connect-timeout" value="300"/-->
|
||||
<!-- default is 300 seconds, override here -->
|
||||
<!--param name="download-timeout" value="300"/-->
|
||||
</settings>
|
||||
<profiles>
|
||||
<!-- amazon s3 security credentials -->
|
||||
<profile name="gcs">
|
||||
<!-- optional list of domains that this profile will automatically be applied to -->
|
||||
<!-- if you wish to apply the s3 credentials to a domain not listed here, then use
|
||||
{profile=s3}http://foo.s3... -->
|
||||
<domains>
|
||||
<!-- FORMAT https://<bucket>.storage.googleapis.com -->
|
||||
<domain name="dyl1.storage.googleapis.com"/>
|
||||
</domains>
|
||||
<credential_file>test_gcs.json</credential_file>
|
||||
</profile>
|
||||
</profiles>
|
||||
</configuration>
|
|
@ -106,6 +106,22 @@ char * pgsql_handle_get_error(switch_pgsql_handle_t *handle)
|
|||
return err_str;
|
||||
}
|
||||
|
||||
void pgsql_handle_set_error_if_not_set(switch_pgsql_handle_t *handle, char **err)
|
||||
{
|
||||
char *err_str;
|
||||
|
||||
if (err && !(*err)) {
|
||||
err_str = pgsql_handle_get_error(handle);
|
||||
|
||||
if (zstr(err_str)) {
|
||||
switch_safe_free(err_str);
|
||||
err_str = strdup((char *)"SQL ERROR!");
|
||||
}
|
||||
|
||||
*err = err_str;
|
||||
}
|
||||
}
|
||||
|
||||
static int db_is_up(switch_pgsql_handle_t *handle)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -553,8 +569,15 @@ switch_status_t pgsql_handle_exec_detailed(const char *file, const char *func, i
|
|||
goto error;
|
||||
}
|
||||
|
||||
return pgsql_finish_results(handle);
|
||||
if (pgsql_finish_results(handle) != SWITCH_STATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
error:
|
||||
pgsql_handle_set_error_if_not_set(handle, err);
|
||||
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -630,6 +653,7 @@ done:
|
|||
|
||||
pgsql_free_result(&result);
|
||||
if (pgsql_finish_results(handle) != SWITCH_STATUS_SUCCESS) {
|
||||
pgsql_handle_set_error_if_not_set(handle, err);
|
||||
sstatus = SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -638,6 +662,7 @@ done:
|
|||
error:
|
||||
|
||||
pgsql_free_result(&result);
|
||||
pgsql_handle_set_error_if_not_set(handle, err);
|
||||
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
@ -1050,6 +1075,8 @@ switch_status_t pgsql_handle_callback_exec_detailed(const char *file, const char
|
|||
return SWITCH_STATUS_SUCCESS;
|
||||
error:
|
||||
|
||||
pgsql_handle_set_error_if_not_set(handle, err);
|
||||
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue