#include #include #include #include #include #include #include #include #include #include #include #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= 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(); }