PV1_Comm/Framework/Network/inet_api.c

1397 lines
34 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libuv_dbus.h>
#include <curl/curl.h>
#include <uthash/uthash.h>
#include <uthash/utstring.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <quickmail.h>
#include <uthash/utlist.h>
#include "log.h"
#include "inet_api.h"
#define MAX_TIMEOUT_VALUE (10)
#define SSL_CA_FILE ("/etc/ssl/certs/ca-certificates.crt")
typedef enum
{
INET_HTTP_DOWNLOAD_FILE = 0,
INET_HTTP_WEBSERVICE_POST,
} INET_ACCESS_TYPE;
typedef struct
{
uv_poll_t uvPool;
curl_socket_t sock;
} CURL_CONTEXT_DATA, *PCURL_CONTEXT_DATA;
typedef struct
{
char* pReqUrl;
char sPath[MAX_PATH];
char sDlPath[MAX_PATH];
char* pTaskUuid;
INET_ACCESS_TYPE type;
unsigned int dlSize;
unsigned int lastTm;
unsigned int createTm;
uv_fs_t uvFsOpen;
uv_fs_t uvFsWrite;
uv_fs_t uvFsDataSync;
uv_fs_t uvFsClose;
uv_buf_t uvFsBuf;
OnProgressNotify onPrgCb;
OnHttpResponse onRspCb;
int isCancel;
CURL* pCurl;
void* pData;
int errCode;
} HTTP_REQ_PARAMS, *PHTTP_REQ_PARAMS;
typedef struct
{
char *pTaskUuid;
unsigned int uRetryTimes;
PHTTP_REQ_PARAMS pCurlItem;
UT_hash_handle hh; ///< UT Hash handle
} CURL_HANDLE_TBL, *PCURL_HANDLE_TBL;
static uv_timer_t g_uvCurlTm;
static uv_timer_t g_uvDlTm;
static CURLM* g_pCurl = NULL;
static uv_loop_t* g_pMainLoop = NULL;
static PCURL_HANDLE_TBL g_ReqHandleTbl = NULL;
static uv_rwlock_t g_uvHashRwLock;
static unsigned g_TotalDownloads = 0;
static void __addReqIdToTable(const char* pTaskUuid, PHTTP_REQ_PARAMS pParams)
{
PCURL_HANDLE_TBL pItem = NULL;
HASH_FIND_STR(g_ReqHandleTbl, pTaskUuid, pItem);
if(pItem == NULL)
{
pItem = (PCURL_HANDLE_TBL)malloc(sizeof(CURL_HANDLE_TBL));
memset(pItem, 0, sizeof(CURL_HANDLE_TBL));
pItem->pTaskUuid = (char*)pTaskUuid;
uv_rwlock_wrlock(&g_uvHashRwLock);
HASH_ADD_STR(g_ReqHandleTbl, pTaskUuid, pItem);
uv_rwlock_wrunlock(&g_uvHashRwLock);
}
pItem->pCurlItem = pParams;
pItem->uRetryTimes++;
}
static void __removeReqIdFromTable(const char* pTaskUuid)
{
PCURL_HANDLE_TBL pItem = NULL;
uv_rwlock_wrlock(&g_uvHashRwLock);
HASH_FIND_STR(g_ReqHandleTbl, pTaskUuid, pItem);
if(pItem != NULL)
{
HASH_DEL(g_ReqHandleTbl, pItem);
if(pItem->pTaskUuid)
{
free(pItem->pTaskUuid);
}
free(pItem);
}
uv_rwlock_wrunlock(&g_uvHashRwLock);
}
static void __cancelDownloadTask(PHTTP_REQ_PARAMS pItem)
{
if(pItem)
{
pItem->isCancel = TRUE;
}
}
static void __uvFsCloseCb(uv_fs_t *puvFs)
{
PHTTP_REQ_PARAMS pParams = (PHTTP_REQ_PARAMS)puvFs->data;
if(puvFs->result < 0)
{
LOG_EX(LOG_Error, "[%s] Error: %d\n", __FUNCTION__, puvFs->result);
}
uv_fs_req_cleanup(puvFs);
if(pParams->type == INET_HTTP_DOWNLOAD_FILE)
{
if(strcmp(pParams->sDlPath, pParams->sPath) != 0)
{
CopyFile(pParams->sDlPath, pParams->sPath);
unlink(pParams->sDlPath);
}
if(pParams->errCode == CURLE_ABORTED_BY_CALLBACK)
{
pParams->errCode = CURLE_OPERATION_TIMEDOUT;
}
if(pParams->onRspCb && pParams->isCancel == FALSE)
{
pParams->onRspCb(NULL, pParams->dlSize, pParams->pReqUrl, pParams->sPath,
pParams->pTaskUuid, -pParams->errCode, pParams->pData);
}
}
__removeReqIdFromTable(pParams->pTaskUuid);
if(pParams->pReqUrl)
{
free(pParams->pReqUrl);
pParams->pReqUrl = NULL;
}
free(pParams);
pParams = NULL;
}
static void __uvFsDataSyncCb(uv_fs_t *puvFs)
{
PHTTP_REQ_PARAMS pParams = (PHTTP_REQ_PARAMS)puvFs->data;
if(puvFs->result < 0)
{
LOG_EX(LOG_Error, "[%s] Error: %d\n", __FUNCTION__, puvFs->result);
}
uv_fs_req_cleanup(puvFs);
uv_fs_close(g_pMainLoop, &pParams->uvFsClose, pParams->uvFsOpen.result, __uvFsCloseCb);
}
static PCURL_CONTEXT_DATA __createCurlContext(curl_socket_t sock)
{
PCURL_CONTEXT_DATA pContext = (PCURL_CONTEXT_DATA)malloc(sizeof(CURL_CONTEXT_DATA));
pContext->sock = sock;
if(uv_poll_init_socket(g_pMainLoop, &pContext->uvPool, sock) != 0)
{
LOG_EX(LOG_Error, "uv_poll_init_socket Error\n");
}
pContext->uvPool.data = pContext;
return (pContext);
}
static void __uvCloseCb(uv_handle_t *puvPoll)
{
PCURL_CONTEXT_DATA pContext = (PCURL_CONTEXT_DATA)puvPoll->data;
free(pContext);
}
static void __destoryCurlContext(PCURL_CONTEXT_DATA pContext)
{
uv_close((uv_handle_t *)&pContext->uvPool, __uvCloseCb);
}
static void __checkMultiInfoTimeout(void)
{
PHTTP_REQ_PARAMS pReq;
CURLMsg *pMsg = NULL;
int iPending;
while((pMsg = curl_multi_info_read(g_pCurl, &iPending)))
{
switch(pMsg->msg)
{
case CURLMSG_DONE:
curl_easy_getinfo(pMsg->easy_handle, CURLINFO_PRIVATE, (void*)&pReq);
LOG_EX(LOG_Debug, "Cleanup CURL: %p\n", pMsg->easy_handle);
curl_multi_remove_handle(g_pCurl, pMsg->easy_handle);
curl_easy_cleanup(pMsg->easy_handle);
if(pReq)
{
if(pReq->type == INET_HTTP_DOWNLOAD_FILE)
{
uv_fs_close(g_pMainLoop, &pReq->uvFsDataSync, pReq->uvFsOpen.result, NULL);
}
if(pReq->onRspCb && pReq->isCancel == FALSE)
{
pReq->onRspCb(NULL, 0, pReq->pReqUrl, pReq->sPath, pReq->pTaskUuid,
-CURLE_OPERATION_TIMEDOUT, pReq->pData);
}
if(pReq->pReqUrl)
{
free(pReq->pReqUrl);
pReq->pReqUrl = NULL;
}
__removeReqIdFromTable(pReq->pTaskUuid);
free(pReq);
pReq = NULL;
}
break;
default:
LOG_EX(LOG_Error, "pMsg->msg(%d) != CURLMSG_DONE\n", pMsg->msg);
return;
}
}
}
static void __checkMultiInfo(void)
{
PHTTP_REQ_PARAMS pReq;
CURLMsg *pMsg = NULL;
int iPending;
while((pMsg = curl_multi_info_read(g_pCurl, &iPending)))
{
switch(pMsg->msg)
{
case CURLMSG_DONE:
curl_easy_getinfo(pMsg->easy_handle, CURLINFO_PRIVATE, (void*)&pReq);
curl_multi_remove_handle(g_pCurl, pMsg->easy_handle);
LOG_EX(LOG_Debug, "Cleanup CURL: %p\n", pMsg->easy_handle);
curl_easy_cleanup(pMsg->easy_handle);
if(pReq)
{
if(pReq->type == INET_HTTP_DOWNLOAD_FILE)
{
if(pMsg->data.result != CURLE_OK)
{
pReq->errCode = pMsg->data.result;
}
else
{
pReq->errCode = 0;
}
uv_fs_fdatasync(g_pMainLoop, &pReq->uvFsDataSync, pReq->uvFsOpen.result, __uvFsDataSyncCb);
}
else if(pReq->type == INET_HTTP_WEBSERVICE_POST)
{
if(pMsg->data.result != CURLE_OK)
{
if(pReq->onRspCb && pReq->isCancel == FALSE)
{
pReq->onRspCb(pReq->uvFsBuf.base, pReq->dlSize, pReq->pReqUrl, pReq->sPath,
pReq->pTaskUuid, -pMsg->data.result, pReq->pData);
}
}
else
{
if(pReq->onRspCb && pReq->isCancel == FALSE)
{
pReq->onRspCb(pReq->uvFsBuf.base, pReq->dlSize, pReq->pReqUrl, pReq->sPath,
pReq->pTaskUuid, 0, pReq->pData);
}
}
if(pReq->uvFsBuf.base)
{
free(pReq->uvFsBuf.base);
}
if(pReq->pReqUrl)
{
free(pReq->pReqUrl);
pReq->pReqUrl = NULL;
}
__removeReqIdFromTable(pReq->pTaskUuid);
free(pReq);
pReq = NULL;
}
else
{
if(pMsg->data.result != CURLE_OK)
{
if(pReq->onRspCb && pReq->isCancel == FALSE){
pReq->onRspCb(NULL, 0, pReq->pReqUrl, pReq->sPath, pReq->pTaskUuid, -pMsg->data.result, pReq->pData);
}
}
else
{
if(pReq->onRspCb && pReq->isCancel == FALSE)
{
pReq->onRspCb(NULL, 0, pReq->pReqUrl, pReq->sPath, pReq->pTaskUuid, 0, pReq->pData);
}
}
if(pReq->pReqUrl)
{
free(pReq->pReqUrl);
pReq->pReqUrl = NULL;
}
__removeReqIdFromTable(pReq->pTaskUuid);
free(pReq);
pReq = NULL;
}
}
break;
default:
LOG_EX(LOG_Error, "pMsg->msg(%d) != CURLMSG_DONE\n", pMsg->msg);
return;
}
}
}
static void __onDlTmoutCb(uv_timer_t *pufTimer)
{
PCURL_HANDLE_TBL pItem = NULL, pTemp = NULL;
unsigned int curTm = (unsigned int)LIBUV_CURRENT_TIME_S();
HASH_ITER(hh, g_ReqHandleTbl, pItem, pTemp)
{
int dlTime;
if(pItem->pCurlItem->isCancel)
{
continue;
}
dlTime = curTm - pItem->pCurlItem->createTm;
// 下载时间大于10s且平均下载速度小于10K/s超时
if((dlTime * 10000 > pItem->pCurlItem->dlSize) && dlTime > 10)
{
LOG_EX(LOG_Error, "Download Speed less than 10k/s: %s (%uK/%ds)\n",
pItem->pTaskUuid, pItem->pCurlItem->dlSize / 1000, dlTime);
__cancelDownloadTask(pItem->pCurlItem);
if(pItem->pCurlItem->onRspCb)
{
pItem->pCurlItem->onRspCb(NULL,
pItem->pCurlItem->dlSize,
pItem->pCurlItem->pReqUrl,
pItem->pCurlItem->sPath,
pItem->pCurlItem->pTaskUuid,
-CURLE_OPERATION_TIMEDOUT,
pItem->pCurlItem->pData);
}
break;
}
// 10秒内没有下载任何数据超时
if(pItem->pCurlItem->lastTm > 0)
{
if(curTm > pItem->pCurlItem->lastTm + MAX_TIMEOUT_VALUE)
{
LOG_EX(LOG_Error, "Download Timeout: %s\n", pItem->pTaskUuid);
__cancelDownloadTask(pItem->pCurlItem);
if(pItem->pCurlItem->onRspCb)
{
pItem->pCurlItem->onRspCb(NULL,
pItem->pCurlItem->dlSize,
pItem->pCurlItem->pReqUrl,
pItem->pCurlItem->sPath,
pItem->pCurlItem->pTaskUuid,
-CURLE_OPERATION_TIMEDOUT,
pItem->pCurlItem->pData);
}
break;
}
}
// 下载最长时间设置为1800秒(30分钟)
if(dlTime > 1800)
{
LOG_EX(LOG_Error, "Download More than 1800 seconds: %s (%uK/%ds)\n",
pItem->pTaskUuid, pItem->pCurlItem->dlSize/1000, dlTime);
__cancelDownloadTask(pItem->pCurlItem);
if(pItem->pCurlItem->onRspCb)
{
pItem->pCurlItem->onRspCb(NULL,
pItem->pCurlItem->dlSize,
pItem->pCurlItem->pReqUrl,
pItem->pCurlItem->sPath,
pItem->pCurlItem->pTaskUuid,
-CURLE_OPERATION_TIMEDOUT,
pItem->pCurlItem->pData);
}
break;
}
}
}
static void __onTimeoutCb(uv_timer_t *pufTimer)
{
int iRun;
curl_multi_socket_action(g_pCurl, CURL_SOCKET_TIMEOUT, 0, &iRun);
__checkMultiInfoTimeout();
}
static int __curlTimerCb(CURLM *multi, /* multi handle */
long timeout_ms, /* see above */
void *userp) /* private callback pointer */
{
if(timeout_ms <= 0)
{
timeout_ms = 1;
}
uv_timer_start(&g_uvCurlTm, __onTimeoutCb, timeout_ms, 0);
return 0;
}
static void __curlPollCb(uv_poll_t *pPoll, int status, int events)
{
int iRun;
int flags;
PCURL_CONTEXT_DATA pContext = NULL;
uv_timer_stop(&g_uvCurlTm);
if(events & UV_READABLE)
{
flags = CURL_CSELECT_IN;
}
else if(events & UV_WRITABLE)
{
flags = CURL_CSELECT_OUT;
}
pContext = (PCURL_CONTEXT_DATA)pPoll;
curl_multi_socket_action(g_pCurl, pContext->sock, flags, &iRun);
__checkMultiInfo();
}
static int __curlSockCb(CURL *easy, /* easy handle */
curl_socket_t s, /* socket */
int what, /* describes the socket */
void *userp, /* private callback pointer */
void *socketp) /* private socket pointer */
{
PCURL_CONTEXT_DATA pContext = NULL;
if(what == CURL_POLL_IN || what == CURL_POLL_OUT)
{
if(socketp)
{
pContext = (PCURL_CONTEXT_DATA)socketp;
}
else
{
pContext = __createCurlContext(s);
}
curl_multi_assign(g_pCurl, s, (void *)pContext);
}
switch(what)
{
case CURL_POLL_IN:
uv_poll_start(&pContext->uvPool, UV_READABLE, __curlPollCb);
break;
case CURL_POLL_OUT:
uv_poll_start(&pContext->uvPool, UV_WRITABLE, __curlPollCb);
break;
case CURL_POLL_REMOVE:
if(socketp)
{
uv_poll_stop(&((PCURL_CONTEXT_DATA)socketp)->uvPool);
__destoryCurlContext((PCURL_CONTEXT_DATA)socketp);
curl_multi_assign(g_pCurl, s, NULL);
}
break;
default:
return (0);
}
return (0);
}
static size_t __writeDataCb(void *pData, size_t size, size_t nmemb, void *pParams)
{
PHTTP_REQ_PARAMS pReq = (PHTTP_REQ_PARAMS)pParams;
int iMemSize = size * nmemb;
//print_hex_dump_bytes("OTA", DUMP_PREFIX_ADDRESS, pData, size * nmemb);
if(pReq->isCancel)
{
return 0;
}
pReq->lastTm = LIBUV_CURRENT_TIME_S();
if(pReq->type == INET_HTTP_DOWNLOAD_FILE)
{
int wr = 0;
pReq->uvFsBuf = uv_buf_init(pData, iMemSize);
wr = uv_fs_write(g_pMainLoop, &pReq->uvFsWrite, pReq->uvFsOpen.result, &pReq->uvFsBuf, 1, -1, NULL);
if(wr > 0)
{
pReq->dlSize += wr;
}
}
else if(pReq->type == INET_HTTP_WEBSERVICE_POST)
{
int newSize = 0;
if(pReq->uvFsBuf.base == NULL && pReq->uvFsBuf.len == 0)
{
newSize = iMemSize + 1;
//fprintf(stdout, "size = %d, newsize = %d, dlsize = %d\n", iMemSize, newSize, pReq->dlSize);
pReq->uvFsBuf.base = malloc(newSize);
memcpy(pReq->uvFsBuf.base, pData, iMemSize);
}
else
{
newSize = pReq->dlSize + iMemSize + 1;
//fprintf(stdout, "::size = %d, newsize = %d, dlsize = %d\n", iMemSize, newSize, pReq->dlSize);
pReq->uvFsBuf.base = realloc(pReq->uvFsBuf.base, newSize);
memcpy(pReq->uvFsBuf.base + pReq->dlSize, pData, iMemSize);
}
pReq->uvFsBuf.base[pReq->dlSize] = 0;
pReq->dlSize += iMemSize;
}
return (size * nmemb);
}
static int __progressCb(void* pData,
double total,
double now,
double ultotal,
double ulnow)
{
PHTTP_REQ_PARAMS pParams = (PHTTP_REQ_PARAMS)pData;
if(pParams->onPrgCb)
{
if(pParams->type == INET_HTTP_DOWNLOAD_FILE)
{
pParams->onPrgCb(pParams->pReqUrl, pParams->pTaskUuid, (unsigned char)(now * 100.0 / total), pParams->pData);
}
}
if(pParams->isCancel)
{
LOG_EX(LOG_Debug, "Cancel Download: %s\n", pParams->pTaskUuid);
return (-CURLE_OPERATION_TIMEDOUT);
}
return (0);
}
static size_t __getRemoteSizeCb(void *pData, size_t size, size_t nmemb, void *pParams)
{
return (size * nmemb);
}
static int __iNetGetRemoteSize(const char* pURL, unsigned int reqId)
{
double size = 0.0;
CURL *pCurl = curl_easy_init();
CURLcode res;
curl_easy_setopt(pCurl, CURLOPT_URL, pURL);
curl_easy_setopt(pCurl, CURLOPT_NOBODY, 1L);
curl_easy_setopt(pCurl, CURLOPT_HEADERFUNCTION, __getRemoteSizeCb);
curl_easy_setopt(pCurl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_perform(pCurl);
res = curl_easy_getinfo(pCurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);
if(res != CURLE_OK)
{
return (-1);
}
curl_easy_cleanup(pCurl);
return (int)(size);
}
#if 0
static const char* __restartDlFileAsync(PHTTP_REQ_PARAMS pParams)
{
CURL *pCurl = curl_easy_init();
pParams->type = INET_HTTP_DOWNLOAD_FILE;
pParams->dlSize = 0;
pParams->pCurl = pCurl;
pParams->lastTm = 0;
uv_fs_open(g_pMainLoop,
&pParams->uvFsOpen,
pParams->sDlPath,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR,
NULL);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, __writeDataCb);
curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, pParams);
curl_easy_setopt(pCurl, CURLOPT_PRIVATE, pParams);
curl_easy_setopt(pCurl, CURLOPT_URL, pParams->pReqUrl);
curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(pCurl, CURLOPT_PROGRESSFUNCTION, __progressCb);
curl_easy_setopt(pCurl, CURLOPT_PROGRESSDATA, pParams);
curl_easy_setopt(pCurl, CURLOPT_LOW_SPEED_LIMIT, 10000L); // 10K bytes
curl_easy_setopt(pCurl, CURLOPT_LOW_SPEED_TIME, 10L); // 30 seconds
curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10L);
#ifdef SKIP_PEER_VERIFICATION
/*
* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
#else
curl_easy_setopt(pCurl, CURLOPT_CAINFO, SSL_CA_FILE);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
//LOG_EX(LOG_Debug, "Total Size = %d\n", __iNetGetRemoteSize(pURL, 0));
curl_multi_add_handle(g_pCurl, pCurl);
__addReqIdToTable(pParams->pTaskUuid, pCurl);
return (pParams->pTaskUuid);
}
#endif
const char* InetHttpDlFileAsync(const char *pURL,
const char *pPath,
OnHttpResponse onRespCb,
OnProgressNotify onProgressCb,
void* pData)
{
CURLMcode ret;
uuid_t msgId;
char strMsgId[64];
PHTTP_REQ_PARAMS pParams = NULL;
CURL *pCurl = NULL;
unsigned long long uMemFreeSize = GetPartitionFreeSize("/tmp/");
if(pURL == NULL || strlen(pURL) == 0 || onRespCb == NULL)
{
free(pParams);
return (NULL);
}
LOG_EX(LOG_Debug, "Begin Download: %s --> %s\n", pURL, pPath);
pParams = (PHTTP_REQ_PARAMS)malloc(sizeof(HTTP_REQ_PARAMS));
memset(pParams, 0, sizeof(HTTP_REQ_PARAMS));
pCurl = curl_easy_init();
pParams->onRspCb = onRespCb;
pParams->pReqUrl = (char *)malloc(strlen(pURL) + 1);
pParams->type = INET_HTTP_DOWNLOAD_FILE;
pParams->dlSize = 0;
pParams->onPrgCb = onProgressCb;
pParams->pData = pData;
pParams->pCurl = pCurl;
pParams->lastTm = 0;
pParams->isCancel = FALSE;
pParams->createTm = (unsigned int)LIBUV_CURRENT_TIME_S();
memset(pParams->pReqUrl, 0, strlen(pURL) + 1);
strcpy(pParams->pReqUrl, pURL);
uuid_generate_random(msgId);
memset(strMsgId, 0, 64);
uuid_unparse_lower(msgId, strMsgId);
pParams->pTaskUuid = strdup(strMsgId);
if(pPath == NULL)
{
sprintf(pParams->sPath, "./%s", basename_v2(pURL));
}
else
{
strcpy(pParams->sPath, pPath);
}
// Memory Free More Than 100M, Download Temp File To Memory
if(uMemFreeSize >= 100 * 1024 * 1024 &&
strncmp(pParams->sPath, "/tmp/", 5) != 0)
{
int ret = system("mkdir /tmp/dl -p");
sprintf(pParams->sDlPath, "/tmp/dl/%s_%s.dl", basename_v2(pParams->sPath), pParams->pTaskUuid);
}
else
{
strcpy(pParams->sDlPath, pParams->sPath);
}
pParams->uvFsDataSync.data = pParams;
pParams->uvFsClose.data = pParams;
uv_fs_open(g_pMainLoop,
&pParams->uvFsOpen,
pParams->sDlPath,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR,
NULL);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, __writeDataCb);
curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, pParams);
curl_easy_setopt(pCurl, CURLOPT_PRIVATE, pParams);
curl_easy_setopt(pCurl, CURLOPT_URL, pURL);
curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(pCurl, CURLOPT_PROGRESSFUNCTION, __progressCb);
curl_easy_setopt(pCurl, CURLOPT_PROGRESSDATA, pParams);
//curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 1800L); // Max download times (30 minutes)1800s
curl_easy_setopt(pCurl, CURLOPT_LOW_SPEED_LIMIT, 10000L); // 10K bytes
curl_easy_setopt(pCurl, CURLOPT_LOW_SPEED_TIME, 10L); // 30 seconds
curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10L);
//curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT_MS, 10L);
//curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1L);
#ifdef SKIP_PEER_VERIFICATION
/*
* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
#else
curl_easy_setopt(pCurl, CURLOPT_CAINFO, SSL_CA_FILE);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
LOG_EX(LOG_Debug, "Download(%u): %s --> %p\n", g_TotalDownloads++, pParams->pTaskUuid, pCurl);
ret = curl_multi_add_handle(g_pCurl, pCurl);
if(ret == CURLE_OK)
{
__addReqIdToTable(pParams->pTaskUuid, pParams);
return (pParams->pTaskUuid);
}
else
{
free(pParams->pTaskUuid);
LOG_EX(LOG_Error, "Add Handle Error: %d\n", ret);
return NULL;
}
}
int InetCancelDownload(const char *pTaskUuid)
{
if(pTaskUuid && strlen(pTaskUuid) > 0)
{
PCURL_HANDLE_TBL pItem = NULL;
HASH_FIND_STR(g_ReqHandleTbl, pTaskUuid, pItem);
if(pItem != NULL && pItem->pCurlItem->isCancel != TRUE)
{
__cancelDownloadTask(pItem->pCurlItem);
if(pItem->pCurlItem->onRspCb)
{
pItem->pCurlItem->onRspCb(NULL,
pItem->pCurlItem->dlSize,
pItem->pCurlItem->pReqUrl,
pItem->pCurlItem->sPath,
pItem->pCurlItem->pTaskUuid,
-CURLE_OPERATION_TIMEDOUT,
pItem->pCurlItem->pData);
}
}
}
return (0);
}
static size_t __uploadCb(char *d, size_t n, size_t l, void *p)
{
return n*l;
}
#ifdef LIBCURL_DEBUG
struct data {
char trace_ascii; /* 1 or 0 */
};
static
void dump(const char *text,
FILE *stream, unsigned char *ptr, size_t size,
char nohex)
{
size_t i;
size_t c;
unsigned int width = 0x10;
if(nohex)
/* without the hex output, we can fit more on screen */
width = 0x40;
fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n",
text, (long)size, (long)size);
for(i = 0; i<size; i += width) {
fprintf(stream, "%4.4lx: ", (long)i);
if(!nohex) {
/* hex not disabled, show it */
for(c = 0; c < width; c++)
if(i + c < size)
fprintf(stream, "%02x ", ptr[i + c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i + c < size); c++) {
/* check for 0D0A; if found, skip past and start a new line of output */
if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&
ptr[i + c + 1] == 0x0A) {
i += (c + 2 - width);
break;
}
fprintf(stream, "%c",
(ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.');
/* check again for 0D0A, to avoid an extra \n if it's at width */
if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
ptr[i + c + 2] == 0x0A) {
i += (c + 3 - width);
break;
}
}
fputc('\n', stream); /* newline */
}
fflush(stream);
}
static
int my_trace(CURL *handle, curl_infotype type,
char *data, size_t size,
void *userp)
{
struct data *config = (struct data *)userp;
const char *text;
(void)handle; /* prevent compiler warning */
switch(type) {
case CURLINFO_TEXT:
fprintf(stderr, "== Info: %s", data);
/* FALLTHROUGH */
default: /* in case a new one is introduced to shock us */
return 0;
case CURLINFO_HEADER_OUT:
text = "=> Send header";
break;
case CURLINFO_DATA_OUT:
text = "=> Send data";
break;
case CURLINFO_SSL_DATA_OUT:
text = "=> Send SSL data";
break;
case CURLINFO_HEADER_IN:
text = "<= Recv header";
break;
case CURLINFO_DATA_IN:
text = "<= Recv data";
break;
case CURLINFO_SSL_DATA_IN:
text = "<= Recv SSL data";
break;
}
dump(text, stderr, (unsigned char *)data, size, config->trace_ascii);
return 0;
}
#endif
int InetHttpUploadFileSync(const char *pURL, const char* pPath, void* pAttachInfo)
{
CURL *pCurl = NULL;
int rc = 0;
CURLcode ret;
struct curl_httppost *pPost = NULL, *pLastPtr = NULL;
#ifdef LIBCURL_DEBUG
struct data config;
config.trace_ascii = 1; /* enable ascii tracing */
#endif
if(pURL == NULL || strlen(pURL) == 0)
{
LOG_EX(LOG_Error, "Url: %s(%p)\n", SAFE_STRING_VALUE(pURL), pURL);
return -ERR_INPUT_PARAMS;
}
if(pPath == NULL || strlen(pPath) == 0)
{
LOG_EX(LOG_Error, "Url: %s(%p)\n", SAFE_STRING_VALUE(pPath), pPath);
return -ERR_INPUT_PARAMS;
}
curl_formadd(&pPost, &pLastPtr,
CURLFORM_COPYNAME, "file",
CURLFORM_FILE, pPath,
CURLFORM_END);
if(pAttachInfo)
{
PHTTP_POST_ATTACH pDevInfoArray = (PHTTP_POST_ATTACH)pAttachInfo;
PHTTP_POST_ATTACH pItem = NULL, pTmp = NULL;
LL_FOREACH_SAFE(pDevInfoArray, pItem, pTmp)
{
curl_formadd(&pPost, &pLastPtr,
CURLFORM_COPYNAME, pItem->keyName,
CURLFORM_COPYCONTENTS, pItem->keyValue,
CURLFORM_END);
}
}
pCurl = curl_easy_init();
if(pCurl == NULL)
{
LOG_EX(LOG_Error, "curl_easy_init() Error\n");
return -ERR_MALLOC_MEMORY;
}
curl_easy_setopt(pCurl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
curl_easy_setopt(pCurl, CURLOPT_POST, 1L);
curl_easy_setopt(pCurl, CURLOPT_URL, pURL);
curl_easy_setopt(pCurl, CURLOPT_HTTPPOST, pPost);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, __uploadCb);
curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10L);
#ifdef LIBCURL_DEBUG
curl_easy_setopt(pCurl, CURLOPT_DEBUGFUNCTION, my_trace);
curl_easy_setopt(pCurl, CURLOPT_DEBUGDATA, &config);
#endif
#ifdef SKIP_PEER_VERIFICATION
/*
* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
#else
curl_easy_setopt(pCurl, CURLOPT_CAINFO, SSL_CA_FILE);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
#ifdef LIBCURL_DEBUG
curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 1L);
#else
curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 0L);
#endif
ret = curl_easy_perform(pCurl);
if(ret != CURLE_OK)
{
LOG_EX(LOG_Error, "Upload %s File %s Error %s(%d)\n", pURL, pPath, curl_easy_strerror(ret), ret);
rc = -ERR_NETWORK_SEND;
}
curl_easy_cleanup(pCurl);
curl_formfree(pPost);
return rc;
}
const char* InetHttpWebServicePostAsync(const char *pURL, const char* pPost, OnHttpResponse onRespCb, void* pData)
{
uuid_t msgId;
char strMsgId[64];
PHTTP_REQ_PARAMS pParams = (PHTTP_REQ_PARAMS)malloc(sizeof(HTTP_REQ_PARAMS));
CURL *pCurl = NULL;
if(pURL == NULL || strlen(pURL) == 0 || onRespCb == NULL)
{
free(pParams);
return (NULL);
}
memset(pParams, 0, sizeof(HTTP_REQ_PARAMS));
pCurl = curl_easy_init();
pParams->onRspCb = onRespCb;
pParams->pReqUrl = (char *)malloc(strlen(pURL) + 1);
pParams->type = INET_HTTP_WEBSERVICE_POST;
pParams->dlSize = 0;
pParams->pData = pData;
pParams->pCurl = pCurl;
pParams->lastTm = 0;
pParams->isCancel = FALSE;
memset(pParams->pReqUrl, 0, strlen(pURL) + 1);
strcpy(pParams->pReqUrl, pURL);
uuid_generate_random(msgId);
memset(strMsgId, 0, 64);
uuid_unparse_lower(msgId, strMsgId);
pParams->pTaskUuid = strdup(strMsgId);
curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, __writeDataCb);
curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, pParams);
curl_easy_setopt(pCurl, CURLOPT_PRIVATE, pParams);
curl_easy_setopt(pCurl, CURLOPT_URL, pURL);
curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(pCurl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if(pPost != NULL && strlen(pPost) > 0)
{
curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, pPost);
curl_easy_setopt(pCurl, CURLOPT_POSTFIELDSIZE, (long)strlen(pPost));
}
#ifdef SKIP_PEER_VERIFICATION
/*
* If you want to connect to a site who isn't using a certificate that is
* signed by one of the certs in the CA bundle you have, you can skip the
* verification of the server's certificate. This makes the connection
* A LOT LESS SECURE.
*
* If you have a CA cert for the server stored someplace else than in the
* default bundle, then the CURLOPT_CAPATH option might come handy for
* you.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
#else
curl_easy_setopt(pCurl, CURLOPT_CAINFO, SSL_CA_FILE);
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* If the site you're connecting to uses a different host name that what
* they have mentioned in their server certificate's commonName (or
* subjectAltName) fields, libcurl will refuse to connect. You can skip
* this check, but this will make the connection less secure.
*/
curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
curl_multi_add_handle(g_pCurl, pCurl);
__addReqIdToTable(pParams->pTaskUuid, pParams);
return (pParams->pTaskUuid);
}
#if 0
static void __curlTaskRuntimeCb(void *pParams)
{
PCURL_HANDLE_TBL pItem = NULL, pTmpItem = NULL;
while(TRUE)
{
uv_rwlock_rdlock(&g_uvHashRwLock);
HASH_ITER(hh, g_ReqHandleTbl, pItem, pTmpItem)
{
if(pItem->pCurlItem->type == INET_HTTP_DOWNLOAD_FILE
&& pItem->pCurlItem->lastTm > 0)
{
//unsigned int tmNow = LIBUV_CURRENT_TIME_S();
if(pItem->pCurlItem->lastTm > 0)
{
//curl_multi_cleanup(pItem->pCurlItem->pCurl);
curl_multi_remove_handle(g_pCurl, pItem->pCurlItem->pCurl);
if(pItem->uRetryTimes >= 3)
{
if(pItem->pCurlItem->onRspCb)
{
if(strcmp(pItem->pCurlItem->sPath, pItem->pCurlItem->sDlPath) != 0)
{
unlink(pItem->pCurlItem->sDlPath);
}
pItem->pCurlItem->onRspCb(NULL,
pItem->pCurlItem->dlSize,
pItem->pCurlItem->pReqUrl,
pItem->pCurlItem->sPath,
pItem->pCurlItem->pTaskUuid,
TRUE,
pItem->pCurlItem->pData);
}
if(pItem->pCurlItem->pReqUrl)
{
free(pItem->pCurlItem->pReqUrl);
}
}
else
{
__restartDlFileAsync(pItem->pCurlItem);
}
}
}
}
uv_rwlock_rdunlock(&g_uvHashRwLock);
usleep(100000);
}
pthread_detach(pthread_self());
}
#endif
static int __getUsernameFromMail(const char *pMailAddr, char **pUsername)
{
char *pTail;
if(pMailAddr == NULL || pUsername == NULL || strlen(pMailAddr) == 0)
{
LOG_EX(LOG_Error, "Input Params Error: pMailAddr = [%s], pUsername = %p\n",
pMailAddr ? pMailAddr : "NULL", pUsername);
return (-ERR_INPUT_PARAMS);
}
*pUsername = (char *)malloc(strlen(pMailAddr) + 1);
if(*pUsername == NULL)
{
LOG_EX(LOG_Error, "Error Malloc Memory\n");
*pUsername = "";
return (-ERR_MALLOC_MEMORY);
}
memset(*pUsername, 0, strlen(pMailAddr) + 1);
pTail = strchr(pMailAddr, '@');
if(pTail == NULL)
{
strcpy(*pUsername, pMailAddr);
}
else
{
memcpy(*pUsername, pMailAddr, pTail - pMailAddr);
}
return (0);
}
int InetSmtpSendEmail(const char* pFrom,
const char* pTo[],
const char* pCc[],
const char* pTitle,
const char* pMessage,
const char* pAttach[],
PSMTP_MAIL_CONFIG pConfig)
{
const char *pErrMsg = NULL;
quickmail pMail;
if(pConfig == NULL)
{
LOG_EX(LOG_Error, "Input Param pConfig = NULL\n");
return (-ERR_INPUT_PARAMS);
}
if(pConfig->pPassword == NULL || strlen(pConfig->pPassword) == 0)
{
LOG_EX(LOG_Error, "Input Param Error: pConfig->pPassword = [%s]\n", pConfig->pPassword ? pConfig->pPassword : "NULL");
return (-ERR_INPUT_PARAMS);
}
if(pConfig->pUserName == NULL || strlen(pConfig->pUserName) == 0)
{
LOG_EX(LOG_Error, "Input Param Error: pConfig->pUserName = [%s]\n", pConfig->pUserName ? pConfig->pUserName : "NULL");
return (-ERR_INPUT_PARAMS);
}
if(pConfig->pSmtpServer == NULL || strlen(pConfig->pSmtpServer) == 0)
{
LOG_EX(LOG_Error, "Input Param Error: pConfig->pUserName = [%s]\n", pConfig->pSmtpServer ? pConfig->pSmtpServer : "NULL");
return (-ERR_INPUT_PARAMS);
}
if(pFrom == NULL)
{
LOG_EX(LOG_Error, "Input Param pFrom = NULL\n");
return (-ERR_INPUT_PARAMS);
}
if(pTo == NULL && pCc == NULL)
{
LOG_EX(LOG_Error, "Input Param pTo = %p, pCc = %p\n", pTo, pCc);
return (-ERR_INPUT_PARAMS);
}
if(pTitle == NULL)
{
pTitle = "";
}
if(pMessage == NULL)
{
pMessage = "";
}
quickmail_initialize();
pMail = quickmail_create(pFrom, pTitle);
if(pMail == NULL)
{
LOG_EX(LOG_Error, "Create Quickmail Object Error\n");
return (-ERR_MALLOC_MEMORY);
}
for(const char **pValue = pTo; pTo && *pValue; pValue++)
{
quickmail_add_to(pMail, *pValue);
}
for(const char **pValue = pCc; pCc && *pValue; pValue++)
{
quickmail_add_cc(pMail, *pValue);
}
quickmail_add_header(pMail, "Importance: Low");
quickmail_add_header(pMail, "X-Priority: 5");
quickmail_add_header(pMail, "X-MSMail-Priority: Low");
quickmail_add_body_memory(pMail, "text/html", (char*)pMessage, strlen(pMessage), 0);
for(const char **pValue = pAttach; pAttach && *pValue; pValue++)
{
quickmail_add_attachment_file(pMail, *pValue, NULL);
}
//quickmail_set_debug_log(pMail, stderr);
pErrMsg = quickmail_send(pMail, pConfig->pSmtpServer, pConfig->smtpPort, pConfig->pUserName, pConfig->pPassword);
if(pErrMsg != NULL)
{
LOG_EX(LOG_Error, "Send Mail Error: %s\n", pErrMsg);
return (-ERR_SEND_MAIL);
}
return (0);
}
int InetInit(void)
{
int ret = 0;
ret = curl_global_init(CURL_GLOBAL_ALL);
if(ret != 0)
{
LOG_EX(LOG_Error, "curl init error: %d\n", ret);
return ret;
}
g_pMainLoop = DBusLibuvGetRuntime()->pLoop;
uv_timer_init(g_pMainLoop, &g_uvCurlTm);
uv_timer_init(g_pMainLoop, &g_uvDlTm);
g_pCurl = curl_multi_init();
curl_multi_setopt(g_pCurl, CURLMOPT_SOCKETFUNCTION, __curlSockCb);
curl_multi_setopt(g_pCurl, CURLMOPT_TIMERFUNCTION, __curlTimerCb);
uv_rwlock_init(&g_uvHashRwLock);
uv_timer_start(&g_uvDlTm, __onDlTmoutCb, 1000, 1000);
return (0);
}
void InetUnInit(void)
{
curl_multi_cleanup(g_pCurl);
curl_global_cleanup();
}