diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index ea19d53..d41f2d3 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -13,4 +13,5 @@ add_subdirectory(ModBusCRC16) add_subdirectory(LedControl) add_subdirectory(KeyControl) add_subdirectory(MediaAdapter) -add_subdirectory(FxHttpServer) \ No newline at end of file +add_subdirectory(FxHttpServer) +add_subdirectory(Servers) \ No newline at end of file diff --git a/utils/Servers/CMakeLists.txt b/utils/Servers/CMakeLists.txt new file mode 100755 index 0000000..da1a1a2 --- /dev/null +++ b/utils/Servers/CMakeLists.txt @@ -0,0 +1,136 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME Servers) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} Log) +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a) +else() + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.so) +endif() + +# ------------------ openssl ------------------ start +execute_process(COMMAND sh build_openssl.sh WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/openssl/) + +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + # set(OPENSSL_TAILOR no-asm no-async no-md2 no-mdc2 no-poly1305 no-blake2 + # no-siphash no-sm3 no-rc2 no-rc4 no-rc5 no-idea no-aria no-bf no-cast + # no-camellia no-chacha no-ec no-sm2 no-dso + # no-err no-comp no-cms no-ts no-cmac no-ct + # no-hw-padlock no-nextprotoneg no-psk no-rfc3779 no-srtp + # no-dgram no-dynamic-engine no-ec2m no-filenames no-gost + # no-afalgeng no-async no-autoalginit no-autoerrinit no-capieng + # no-tests + # no-ssl-trace no-static-engine no-stdio no-threads no-deprecated no-makedepend + # no-multiblock) +else() + # set(OPENSSL_TAILOR no-asm no-tests) +endif() +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + set(OPENSSL_OS_PLATFORM "linux-x86_64") +else() + set(OPENSSL_OS_PLATFORM "linux-armv4") + # set(LINK_FLAGS "${LINK_FLAGS} -Wl,-rpath,${CURL_SHARED_LIBS_PATH}") + # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS}") + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINK_FLAGS}") +endif() +add_custom_target( + openssl + COMMAND echo "Build openssl." + COMMAND rm -rf ${EXTERNAL_SOURCE_PATH}/openssl/build + COMMAND ./Configure ${OPENSSL_TAILOR} ${OPENSSL_OS_PLATFORM} --prefix=${EXTERNAL_SOURCE_PATH}/openssl/build --openssldir=${EXTERNAL_SOURCE_PATH}/openssl/build --cross-compile-prefix=${CROSS_COMPILE_PREFIX} CC=gcc AR=ar + COMMAND make + COMMAND make install + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/openssl/openssl-1.1.1s/ +) +# ------------------ openssl ------------------ end + +# ------------------ curl ------------------ start +execute_process(COMMAND sh build_curl.sh WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/) +set(CURL_TAILOR --disable-dict --disable-file --disable-gopher + --disable-imap --disable-ldap --disable-pop3 --disable-rtmp + --disable-rtsp --disable-scp --disable-sftp --disable-smb + --disable-telnet --disable-tftp --disable-ipv6) +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + set(CURL_HOST "") +else() + set(CURL_HOST "--host=arm-linux") +endif() +add_custom_target( + curl + DEPENDS openssl + COMMAND echo "Build curl. openssl path = ${EXTERNAL_SOURCE_PATH}" + COMMAND ./configure --without-zlib --prefix=${EXTERNAL_SOURCE_PATH}/curl --with-ssl=${EXTERNAL_SOURCE_PATH}/openssl/build ${CURL_HOST} CC=${CMAKE_C_COMPILER} + COMMAND make + COMMAND cp ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/lib*.a ${LIBS_OUTPUT_PATH} + COMMAND cp ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/lib*.a ${LIBS_OUTPUT_PATH} + # COMMAND cp ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/lib*.so* ${LIBS_OUTPUT_PATH} + # COMMAND cp ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/lib*.so* ${LIBS_OUTPUT_PATH} + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/ +) +# ------------------ curl ------------------ end + +add_custom_command( + OUTPUT ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a + COMMAND make curl + WORKING_DIRECTORY ${PROJECT_ROOT_PATH}/cmake-shell/ +) +add_custom_target( + compile_curl + DEPENDS ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/ +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make compile_curl + WORKING_DIRECTORY ${PROJECT_ROOT_PATH}/cmake-shell/ +) + +if ("${CLANG_TIDY_SUPPORT}" MATCHES "true") +add_custom_target( + Servers_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Servers +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + Servers_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Servers +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make Servers_code_check + COMMAND make Servers_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) \ No newline at end of file diff --git a/utils/Servers/README.md b/utils/Servers/README.md new file mode 100644 index 0000000..522a1c5 --- /dev/null +++ b/utils/Servers/README.md @@ -0,0 +1,109 @@ +# 1. 服务器协议接口 + +## 1.1. 概述 +   提供HTTP/FTP/SMTP协议的接口数据,使用**curl+openssl开源库**二次封装。 + +## 1.2. 开发过程记录 +1. 先编译openssl,再编译curl,curl的构建配置需要指定openssl的输出文件目录,否则curl将会构建失败; +2. http协议,指定url,指定post数据,curl即可按照POST发送请求; +3. ftp/ftps协议对应不同的服务器IP,必须一一对应; +4. curl配置了忽略证书校验,未知有什么实际影响; +5. 分别对openssl和curl进行了裁剪; + +## 1.3. 关于MQTT +   curl源码包里并未提供MQTT相关的example,网上搜索编码示例未找到。 + +## 1.4. Servers协议栈模块构建 +   使用SifarSDK的构建体系进行编译,编译curl时自动关联openssl进行编译。 +* 配置文件(此处以/build/cmake/toolchain/linux.toolchain.cmake为示例): +``` +# ------------ build curl + openssl ------------ start +# 工具链前缀 +set(CROSS_COMPILE_PREFIX "") +# 是否启动动态库(预留,暂未支持) +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +# ------------ build curl + openssl ------------ end +``` +* 项目根目录编译 +``` +make cmake +cd cmake-shell-linux/ +make curl // 由于curl属于三方构建的开源库,首次需要先编译curl + openssl +make Servers +``` + +## 1.5. Example +头文件:servser.h +链接库:libServers.a,libLog.a +三方依赖库(依次按序): libcurl.a,libssl.a,libcrypto.a + +下述以FTP为例子: +``` +TEST(ServersTest, FtpsUpload) + { + const char *url = "ftp://150.109.112.64/ServersTest";test/bin/ServersTest"; + const char *uploadFile = "./ServersTest"; + const char *user_password = "ftp_user:Sifar%123456"; + SERVERS_INIT init = { + .logFlag = LOG_FLAG_ENABLE, // 开启curl日志 + .sslVerifyFlag = SSL_VERIFY_DISABLE, //关闭ssl的证书校验功能 + }; + InitLog(LOG_EASYLOGGING, nullptr); // 初始化自研log库 + servers_init(init); // 初始化Servers模块 + LogInfo("servers test start.\n"); + SERVERS_FTP *ftp = new_servers_ftp(url, FTPS_FLAG_ENABLE); // 创建ftp的参数“句柄” + if (ftp) + { + ftp->user_password = (char *)user_password; + ftp->filePath = (char *)uploadFile; + ftp_upload(ftp); // 使用FTP上传文件 + if (SERVERS_CODE_OK == ftp->code) + { + LogInfo("ftp succeed.\n"); + } + else + { + LogError("ftp failed, code = %d.\n", static_cast(ftp->code)); + } + delete_servers_ftp(ftp); // 释放ftp“句柄” + } + UnInitLog(); + } +``` +**注意:** +* 必须调用servers_init()函数初始化Servers协议栈库; +* 必须使用模块接口创建对应的协议参数“句柄”,示例为new_servers_ftp(); +* 必须释放协议参数“句柄”; + +更多示例详见:/test/component/Servers/src/ServersTest.cpp + +## 1.6. 测试 +   libServers.a自研代码通过asan测试,未存在内存安全相关漏洞。 +测试用例详见:/test/component/Servers/src/ServersTest.cpp + +* 测试用例编译 +``` +make cmake +cd cmake-shell-linux/ +make ServersTest +``` + +## 1.7. 总结 +* 实现http/https/ftp/ftps/smtp/smtps接口; +* 交叉编译测试ftp/ftps上传下载速率大概为1M/s(333DE芯片平台); +* 裁剪openssl和curl后编译的demo大小 < 1.2M; + +## 1.8. S530项目配置 +* 工具链 +``` +set(CMAKE_C_COMPILER /opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-gcc) +set(CMAKE_CXX_COMPILER /opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-g++) +``` +* curl + openssl +``` +# ------------ build curl + openssl ------------ start +set(CROSS_COMPILE_PREFIX "/opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-") +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +set(CURL_SHARED_LIBS_PATH "/mnt/mmc") +# ------------ build curl + openssl ------------ end +``` \ No newline at end of file diff --git a/utils/Servers/include/servers.h b/utils/Servers/include/servers.h new file mode 100644 index 0000000..a124a3a --- /dev/null +++ b/utils/Servers/include/servers.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SERVERS_H +#define SERVERS_H +#ifdef __cplusplus +extern "C" { +#endif +#define SERVERS_PARAM_NULL_MEANS_END NULL +#define SERVERS_NEVER_TIMEOUT 0 +typedef enum +{ + SERVERS_CODE_DEFAULT = -1, + SERVERS_CODE_OK = 0, + SERVERS_CODE_END +} ServersCode; +typedef enum +{ + FTPS_FLAG_ENABLE = 0, + FTPS_FLAG_DISABLE, + FTPS_FLAG_END +} FtpsFlag; +typedef enum +{ + LOG_FLAG_ENABLE = 0, + LOG_FLAG_DISABLE, + LOG_FLAG_END +} LogFlag; +typedef enum +{ + SSL_VERIFY_ENABLE = 0, + SSL_VERIFY_DISABLE, + SSL_VERIFY_END +} SslFlag; +typedef struct servers_init +{ + LogFlag logFlag; + SslFlag sslVerifyFlag; +} SERVERS_INIT; +typedef struct servers_http +{ + const char *url; + char *postData; + char *filePath; + char **header; + char *reply; + unsigned int replyLength; + int code; +} SERVERS_HTTP; +typedef struct servers_ftp +{ + const char *url; + const FtpsFlag ftpsFlag; + char *user_password; + char *filePath; + unsigned int timeOutMs; + int code; +} SERVERS_FTP; +typedef struct servers_smtp +{ + const char *url; + const char *subject; + const char *from; + const char *to; + const char *userName; + const char *password; + const char *date; + char *inlineText; + char **toList; + unsigned int toListLength; + // char *text; + // unsigned int textLength; + char **attachment; + int code; +} SERVERS_SMTP; +void servers_init(SERVERS_INIT init); +void servers_unit(void); +// HTTP API +SERVERS_HTTP *new_servers_http(const char *url); +void delete_servers_http(SERVERS_HTTP *ptr); +void http_get(SERVERS_HTTP *param); +void http_post(SERVERS_HTTP *param); +void http_put(SERVERS_HTTP *param); +// FTP API +SERVERS_FTP *new_servers_ftp(const char *url, const FtpsFlag ftpsFlag); +void delete_servers_ftp(SERVERS_FTP *ptr); +void ftp_servers_check(SERVERS_FTP *param); +void ftp_download(SERVERS_FTP *param); +void ftp_upload(SERVERS_FTP *param); +// SMTP API +SERVERS_SMTP *new_servers_smtp(const char *url, const char *subject, const char *from, const char *to, + const char *userName, const char *password, const char *date); +void delete_servers_smtp(SERVERS_SMTP *ptr); +void smtp_send_email(SERVERS_SMTP *param); +#ifdef __cplusplus +} +#endif +#endif // !SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/curl_serve.c b/utils/Servers/src/curl_serve.c new file mode 100644 index 0000000..55b4d8f --- /dev/null +++ b/utils/Servers/src/curl_serve.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "curl_serve.h" +static SERVERS_INIT gCurlServe; +void set_verbose_log(LogFlag flag) { gCurlServe.logFlag = flag; } +void set_ssl_verify(SslFlag flag) { gCurlServe.sslVerifyFlag = flag; } +CURL *curl_easy_make(void) +{ + CURL *curl; + curl = curl_easy_init(); + if (!curl) { + return curl; + } + if (SSL_VERIFY_DISABLE == gCurlServe.sslVerifyFlag) { + + /* + * If you want to connect to a site who is not 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(curl, CURLOPT_SSL_VERIFYPEER, 0L); + /* + * If the site you are 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(curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + if (LOG_FLAG_ENABLE == gCurlServe.logFlag) { + /* Switch on full protocol/debug output */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + } + return curl; +} \ No newline at end of file diff --git a/utils/Servers/src/curl_serve.h b/utils/Servers/src/curl_serve.h new file mode 100644 index 0000000..999c58b --- /dev/null +++ b/utils/Servers/src/curl_serve.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CURL_SERVE_H +#define CURL_SERVE_H +#include "servers.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +void set_verbose_log(LogFlag flag); +void set_ssl_verify(SslFlag flag); +CURL *curl_easy_make(void); +#ifdef __cplusplus +} +#endif +#endif // !CURL_SERVE_H \ No newline at end of file diff --git a/utils/Servers/src/ftp_servers.c b/utils/Servers/src/ftp_servers.c new file mode 100644 index 0000000..0e675fb --- /dev/null +++ b/utils/Servers/src/ftp_servers.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ftp_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +#include +struct FtpFile +{ + const char *filename; + FILE *stream; +}; +static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) +{ + struct FtpFile *out = (struct FtpFile *)stream; + if (!out->stream) { + /* open file for writing */ + out->stream = fopen(out->filename, "wb"); + if (!out->stream) + return -1; /* failure, cannot open file to write */ + } + return fwrite(buffer, size, nmemb, out->stream); +} +static CURL *ftp_curl_easy_make(SERVERS_FTP *param) +{ + CURL *curl = NULL; + curl = curl_easy_make(); + if (curl) { + /* + * You better replace the URL with one that works! + */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* User and password for the FTP login */ + if (param->user_password) { + curl_easy_setopt(curl, CURLOPT_USERPWD, param->user_password); + } + /* We activate SSL and we require it for both control and data */ + if (FTPS_FLAG_ENABLE == param->ftpsFlag) { + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); + } + if (SERVERS_NEVER_TIMEOUT < param->timeOutMs) { + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, param->timeOutMs); + } + } + return curl; +} +void ftp_servers_check_connect(SERVERS_FTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = ftp_curl_easy_make(param); + if (curl) { + curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); + res = curl_easy_perform(curl); + param->code = res; + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (CURLE_OK != res) { + /* we failed */ + fprintf(stderr, "curl told us %d\n", res); + } + } + + curl_global_cleanup(); +} +void ftp_servers_download(SERVERS_FTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + struct FtpFile ftpfile = {param->filePath, /* name to store the file as if successful */ + NULL}; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = ftp_curl_easy_make(param); + if (curl) { + /* Define our callback to get called when there's data to be written */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + /* Set a pointer to our struct to pass to the callback */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); + + res = curl_easy_perform(curl); + param->code = res; + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (CURLE_OK != res) { + /* we failed */ + fprintf(stderr, "curl told us %d\n", res); + } + } + + if (ftpfile.stream) + fclose(ftpfile.stream); /* close the local file */ + + curl_global_cleanup(); +} +/* NOTE: if you want this example to work on Windows with libcurl as a + DLL, you MUST also provide a read callback with CURLOPT_READFUNCTION. + Failing to do so will give you a crash since a DLL may not use the + variable's memory when passed in to it from an app like this. */ +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +{ + unsigned long nread; + /* in real-world cases, this would probably get this data differently + as this fread() stuff is exactly what the library already would do + by default internally */ + size_t retcode = fread(ptr, size, nmemb, stream); + + if (retcode > 0) { + nread = (unsigned long)retcode; + fprintf(stderr, "*** We read %lu bytes from file\n", nread); + } + + return retcode; +} +void ftp_servers_upload(SERVERS_FTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + FILE *hd_src; + struct stat file_info; + unsigned long fsize; + + struct curl_slist *headerlist = NULL; + // static const char buf_1[] = "RNFR " UPLOAD_FILE_AS; // TODO: + // static const char buf_2[] = "RNTO " RENAME_FILE_TO; + + /* get the file size of the local file */ + if (stat(param->filePath, &file_info)) { + LogInfo("Couldn't open '%s'\n", param->filePath); + return; + } + fsize = (unsigned long)file_info.st_size; + + LogInfo("Local file size: %lu bytes.\n", fsize); + + /* get a FILE * of the same file */ + hd_src = fopen(param->filePath, "rb"); + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = ftp_curl_easy_make(param); + if (curl) { + /* build a list of commands to pass to libcurl */ + // headerlist = curl_slist_append(headerlist, buf_1); // TODO: + // headerlist = curl_slist_append(headerlist, buf_2); + + /* we want to use our own read function */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + /* pass in that last of FTP commands to run after the transfer */ + curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist); + + /* now specify which file to upload */ + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + + /* Set the size of the file to upload (optional). If you give a *_LARGE + option you MUST make sure that the type of the passed-in argument is a + curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must + make sure that to pass in a type 'long' argument. */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize); + + /* Now run off and do what you have been told! */ + res = curl_easy_perform(curl); + param->code = res; + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* clean up the FTP commands list */ + curl_slist_free_all(headerlist); + + /* always cleanup */ + curl_easy_cleanup(curl); + } + fclose(hd_src); /* close the local file */ + + curl_global_cleanup(); +} \ No newline at end of file diff --git a/utils/Servers/src/ftp_servers.h b/utils/Servers/src/ftp_servers.h new file mode 100644 index 0000000..7374b95 --- /dev/null +++ b/utils/Servers/src/ftp_servers.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FTP_SERVERS_H +#define FTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void ftp_servers_check_connect(SERVERS_FTP *param); +void ftp_servers_download(SERVERS_FTP *param); +void ftp_servers_upload(SERVERS_FTP *param); +#ifdef __cplusplus +} +#endif +#endif // !FTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/http_servers.c b/utils/Servers/src/http_servers.c new file mode 100644 index 0000000..44a44f6 --- /dev/null +++ b/utils/Servers/src/http_servers.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "http_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +#include +#include + +static size_t write_cb(char *data, size_t n, size_t l, void *userp) +{ + /* take care of the data here, ignored in this example */ + (void)data; + (void)userp; + SERVERS_HTTP *p = (SERVERS_HTTP *)userp; + const int LENGTH = n * l; + // p->reply = (char *)realloc(p->reply, LENGTH); + char *newData = (char *)malloc(p->replyLength + LENGTH); + if (newData) { + if (p->reply) { + memcpy(newData, p->reply, p->replyLength); + free(p->reply); + p->reply = NULL; + } + memcpy(newData + p->replyLength, data, LENGTH); + p->replyLength += LENGTH; + p->reply = newData; + } + return n * l; +} +void http_servers_get(SERVERS_HTTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + curl = curl_easy_make(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + // + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + param->code = res; + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } +} +void http_servers_post(SERVERS_HTTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = curl_easy_make(); + if (curl) { + /* First set the URL that is about to receive our POST. This URL can + just as well be an https:// URL if that is what should receive the + data. */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + struct curl_slist *headers = NULL; + + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want */ + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + /* Now specify the POST data */ + if (param->postData) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, param->postData); + } + else { + const char *NULL_POST_DATA = "{}"; + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL_POST_DATA); + } + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); + // curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + /* free headers */ + curl_slist_free_all(headers); + } + curl_global_cleanup(); +} +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t retcode; + unsigned long nread; + + /* in real-world cases, this would probably get this data differently + as this fread() stuff is exactly what the library already would do + by default internally */ + retcode = fread(ptr, size, nmemb, stream); + + if (retcode > 0) { + nread = (unsigned long)retcode; + fprintf(stderr, "*** We read %lu bytes from file\n", nread); + } + + return retcode; +} +void http_servers_put(SERVERS_HTTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + FILE *hd_src; + struct stat file_info; + struct curl_slist *headers = NULL; + + char *file; + if (!param->filePath) { + LogError("file path is needed.\n"); + return; + } + + file = param->filePath; + + /* get the file size of the local file */ + stat(file, &file_info); + + /* get a FILE * of the same file, could also be made with + fdopen() from the previous descriptor, but hey this is just + an example! */ + hd_src = fopen(file, "rb"); + if (!hd_src) { + LogError("fopen files failed.\n"); + return; + } + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = curl_easy_make(); + if (curl) { + /* we want to use our own read function */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + + /* enable uploading (implies PUT over HTTP) */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + /* specify target URL, and note that this URL should include a file + name, not only a directory */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + + /* now specify which file to upload */ + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + + /* provide the size of the upload, we typecast the value to curl_off_t + since we must be sure to use the correct data size */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + // Add header + char **cpp = NULL; + for (cpp = param->header; *cpp; cpp++) { + headers = curl_slist_append(headers, *cpp); + } + if (headers) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + } + + /* Now run off and do what you have been told! */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + /* free headers */ + if (headers) { + curl_slist_free_all(headers); + } + } + fclose(hd_src); /* close the local file */ + + curl_global_cleanup(); +} \ No newline at end of file diff --git a/utils/Servers/src/http_servers.h b/utils/Servers/src/http_servers.h new file mode 100644 index 0000000..0cb3c55 --- /dev/null +++ b/utils/Servers/src/http_servers.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HTTP_SERVERS_H +#define HTTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void http_servers_get(SERVERS_HTTP *param); +void http_servers_post(SERVERS_HTTP *param); +void http_servers_put(SERVERS_HTTP *param); +#ifdef __cplusplus +} +#endif +#endif // !HTTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/servers.c b/utils/Servers/src/servers.c new file mode 100644 index 0000000..da15fe3 --- /dev/null +++ b/utils/Servers/src/servers.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include "ftp_servers.h" +#include "http_servers.h" +#include "smtp_servers.h" +#include +#include +#include +void servers_init(SERVERS_INIT init) +{ + set_verbose_log(init.logFlag); + set_ssl_verify(init.sslVerifyFlag); +} +void servers_unit(void) {} +void http_get(SERVERS_HTTP *param) +{ + LogInfo("http_get\n"); + http_servers_get(param); +} +void http_post(SERVERS_HTTP *param) +{ + LogInfo("http_post\n"); + http_servers_post(param); +} +void http_put(SERVERS_HTTP *param) +{ + LogInfo("http_put\n"); + http_servers_put(param); +} +void smtp_send_email(SERVERS_SMTP *param) +{ + LogInfo("smtp_send_email\n"); + smtp_servers_send_email(param); + // smtp_servers_send_email_only_text(param); +} +SERVERS_HTTP *new_servers_http(const char *url) +{ + if (!url) { + LogError("new_servers_http failed.\n"); + return NULL; + } + SERVERS_HTTP result = { + .url = url, .postData = NULL, .filePath = NULL, .header = NULL, .reply = NULL, .replyLength = 0, .code = 0}; + const int LENGTH = sizeof(SERVERS_HTTP); + SERVERS_HTTP *p = (SERVERS_HTTP *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void delete_servers_http(SERVERS_HTTP *ptr) +{ + if (ptr) { + if (ptr->reply) { + free(ptr->reply); + ptr->reply = NULL; + } + free(ptr); + } +} +SERVERS_FTP *new_servers_ftp(const char *url, const FtpsFlag ftpsFlag) +{ + if (!url) { + LogError("new_servers_ftp failed.\n"); + return NULL; + } + SERVERS_FTP result = {.url = url, + .ftpsFlag = ftpsFlag, + .user_password = NULL, + .filePath = NULL, + .timeOutMs = SERVERS_NEVER_TIMEOUT, + .code = -1}; + const int LENGTH = sizeof(SERVERS_FTP); + SERVERS_FTP *p = (SERVERS_FTP *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void delete_servers_ftp(SERVERS_FTP *ptr) +{ + if (ptr) { + free(ptr); + } +} +void ftp_servers_check(SERVERS_FTP *param) { ftp_servers_check_connect(param); } +void ftp_download(SERVERS_FTP *param) +{ + LogInfo("ftp_download\n"); + ftp_servers_download(param); +} +void ftp_upload(SERVERS_FTP *param) +{ + LogInfo("ftp_upload\n"); + ftp_servers_upload(param); +} +SERVERS_SMTP *new_servers_smtp(const char *url, const char *subject, const char *from, const char *to, + const char *userName, const char *password, const char *date) +{ + if (!url || !from || !to || !userName || !password || !subject || !date) { + LogError("new_servers_smtp failed.\n"); + return NULL; + } + SERVERS_SMTP result = {.url = url, + .subject = subject, + .from = from, + .to = to, + .userName = userName, + .password = password, + .date = date, + .inlineText = NULL, + // .text = NULL, + // .textLength = 0, + .attachment = NULL, + .code = -1}; + const int LENGTH = sizeof(SERVERS_SMTP); + SERVERS_SMTP *p = (SERVERS_SMTP *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void delete_servers_smtp(SERVERS_SMTP *ptr) +{ + if (ptr) { + free(ptr); + } +} \ No newline at end of file diff --git a/utils/Servers/src/smtp_servers.c b/utils/Servers/src/smtp_servers.c new file mode 100644 index 0000000..a66fdc7 --- /dev/null +++ b/utils/Servers/src/smtp_servers.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "smtp_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +struct upload_status +{ + char *payload_text; + size_t bytes_read; +}; +// static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +// { +// struct upload_status *upload_ctx = (struct upload_status *)userp; +// const char *data; +// size_t room = size * nmemb; + +// if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) +// { +// return 0; +// } + +// data = &upload_ctx->payload_text[upload_ctx->bytes_read]; + +// if (data) +// { +// size_t len = strlen(data); +// if (room < len) +// len = room; +// memcpy(ptr, data, len); +// upload_ctx->bytes_read += len; + +// return len; +// } + +// return 0; +// } +// static const char inline_html[] = +// "\r\n" +// "

This is the inline HTML message of the email.

" +// "
\r\n" +// "

It could be a lot of HTML data that would be displayed by " +// "email viewers able to handle HTML.

" +// "\r\n"; +#define SEND_EMAIL_HEADERS_ARRAY_LENGTH 5 +#define SEND_EMAIL_HEADERS_TEXT_LENGTH 1024 +#define DATE "Date: " +#define TO "To: " +#define FROM "From: " +#define SUBJECT "Subject: " +#define HEADER_END "\r\n" +enum HEADERS_NAME +{ + HEADERS_NAME_DATE = 0, + HEADERS_NAME_TO, + HEADERS_NAME_FROM, + HEADERS_NAME_SUBJECT, + HEADERS_NAME_END +}; +static void headers_make(char **headers, SERVERS_SMTP *param) +{ + int textLength = strlen(DATE) + strlen(param->date) + 1; + char *text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", DATE, param->date); + headers[HEADERS_NAME_DATE] = text; + LogInfo("HEADERS_NAME_DATE:%s\n", text); + } + textLength = strlen(TO) + strlen(param->to) + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", TO, param->to); + headers[HEADERS_NAME_TO] = text; + LogInfo("HEADERS_NAME_TO:%s\n", text); + } + textLength = strlen(FROM) + strlen(param->from) + strlen(param->userName) + strlen("()") + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s(%s)", FROM, param->from, param->userName); + headers[HEADERS_NAME_FROM] = text; + LogInfo("HEADERS_NAME_FROM:%s\n", text); + } + textLength = strlen(SUBJECT) + strlen(param->subject) + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", SUBJECT, param->subject); + headers[HEADERS_NAME_SUBJECT] = text; + LogInfo("HEADERS_NAME_SUBJECT:%s\n", text); + } +} +static void headers_free(char **headers) +{ + char **cpp = headers; + for (int i = 0; i < SEND_EMAIL_HEADERS_ARRAY_LENGTH; i++) { + if (*cpp) { + free(*cpp); + headers[i] = NULL; + } + cpp++; + } +} +void smtp_servers_send_email(SERVERS_SMTP *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + char *headers_text[SEND_EMAIL_HEADERS_ARRAY_LENGTH] = {NULL}; + headers_make(headers_text, param); + CURL *curl; + CURLcode res = CURLE_OK; + + curl = curl_easy_make(); + if (curl) { + struct curl_slist *headers = NULL; + struct curl_slist *recipients = NULL; + struct curl_slist *slist = NULL; + curl_mime *mime; + curl_mime *alt; + curl_mimepart *part; + const char **cpp; + const char **attachment; + + /* This is the URL for your mailserver */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* Set the username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); + curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); + + /* Note that this option is not strictly required, omitting it will result + * in libcurl sending the MAIL FROM command with empty sender data. All + * autoresponses should have an empty reverse-path, and should be directed + * to the address in the reverse-path which triggered them. Otherwise, + * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more + * details. + */ + curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); + + /* Add two recipients, in this particular case they correspond to the + * To: and Cc: addressees in the header, but they could be any kind of + * recipient. */ + recipients = curl_slist_append(recipients, param->to); + // recipients = curl_slist_append(recipients, CC); + curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + + /* Build and set the message header list. */ + for (cpp = (const char **)headers_text; *cpp; cpp++) { + headers = curl_slist_append(headers, *cpp); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* Build the mime message. */ + mime = curl_mime_init(curl); + + /* The inline part is an alternative proposing the html and the text + versions of the email. */ + alt = curl_mime_init(curl); + + /* HTML message. */ + // part = curl_mime_addpart(alt); + // curl_mime_data(part, inline_html, CURL_ZERO_TERMINATED); + // curl_mime_type(part, "text/html"); + + /* Text message. */ + if (param->inlineText) { + part = curl_mime_addpart(alt); + curl_mime_data(part, param->inlineText, CURL_ZERO_TERMINATED); + curl_mime_type(part, "text/plain"); + } + + /* Create the inline part. */ + part = curl_mime_addpart(mime); + curl_mime_subparts(part, alt); + curl_mime_type(part, "multipart/alternative"); + slist = curl_slist_append(NULL, "Content-Disposition: inline"); + curl_mime_headers(part, slist, 1); + + /* Add the attachment. */ + if (param->attachment) { + for (attachment = (const char **)param->attachment; *attachment; attachment++) { + part = curl_mime_addpart(mime); + curl_mime_filedata(part, *attachment); + } + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + } + + /* Send the message */ + res = curl_easy_perform(curl); + param->code = res; + + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* Free lists. */ + curl_slist_free_all(recipients); + if (headers) + curl_slist_free_all(headers); + + /* curl will not send the QUIT command until you call cleanup, so you + * should be able to re-use this connection for additional messages + * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and + * calling curl_easy_perform() again. It may not be a good idea to keep + * the connection open for a very long time though (more than a few + * minutes may result in the server timing out the connection), and you do + * want to clean up in the end. + */ + curl_easy_cleanup(curl); + + /* Free multipart message. */ + curl_mime_free(mime); + headers_free(headers_text); + } +} +void smtp_servers_send_email_only_text(SERVERS_SMTP *param) +{ +#if 0 // only for test. + if (!param) + { + LogError("null pointer.\n"); + return; + } + int payloadTextLength = + strlen(DATE HEADER_END) + strlen(TO HEADER_END) + strlen(FROM HEADER_END) + strlen(SUBJECT HEADER_END) + + strlen(param->date) + strlen(param->to) + strlen(param->from) + strlen(param->subject); + if (param->inlineText) + { + payloadTextLength += strlen(param->inlineText); + } + char *payload_text = (char *)malloc(payloadTextLength); + if (!payload_text) + { + LogError("null pointer.\n"); + return; + } + snprintf(payload_text, payloadTextLength, "%s%s%s" + "%s%s%s" + "%s%s%s" + "%s%s%s" + "%s", + DATE, param->date, HEADER_END, + TO, param->to, HEADER_END, + FROM, param->from, HEADER_END, + SUBJECT, param->subject, HEADER_END HEADER_END, + param->inlineText); + // LogInfo("payload_text =\n%s\n", payload_text); + CURL *curl; + CURLcode res = CURLE_OK; + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = {0}; + upload_ctx.payload_text = payload_text; + + curl = curl_easy_make(); + if (curl) + { + curl_mime *mime; + curl_mime *alt; + curl_mimepart *part; + struct curl_slist *slist = NULL; + /* This is the URL for your mailserver */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* Set the username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); + curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); + + /* Note that this option is not strictly required, omitting it will result + * in libcurl sending the MAIL FROM command with empty sender data. All + * autoresponses should have an empty reverse-path, and should be directed + * to the address in the reverse-path which triggered them. Otherwise, + * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more + * details. + */ + curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); + + /* Add two recipients, in this particular case they correspond to the + * To: and Cc: addressees in the header, but they could be any kind of + * recipient. */ + recipients = curl_slist_append(recipients, param->to); + // recipients = curl_slist_append(recipients, CC_ADDR); + curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + + /* We are using a callback function to specify the payload (the headers and + * body of the message). You could just use the CURLOPT_READDATA option to + * specify a FILE pointer to read from. */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + // /* Build the mime message. */ + // mime = curl_mime_init(curl); + // /* Create the inline part. */ + // part = curl_mime_addpart(mime); + // curl_mime_subparts(part, alt); + // curl_mime_type(part, "multipart/alternative"); + // slist = curl_slist_append(NULL, "Content-Disposition: inline"); + // curl_mime_headers(part, slist, 1); + // curl_mime_filedata(part, "./Makefile"); + // curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + + /* Send the message */ + res = curl_easy_perform(curl); + param->code = res; + + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* Free the list of recipients */ + curl_slist_free_all(recipients); + + /* curl will not send the QUIT command until you call cleanup, so you + * should be able to re-use this connection for additional messages + * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and + * calling curl_easy_perform() again. It may not be a good idea to keep + * the connection open for a very long time though (more than a few + * minutes may result in the server timing out the connection), and you do + * want to clean up in the end. + */ + curl_easy_cleanup(curl); + /* Free multipart message. */ + curl_mime_free(mime); + free(payload_text); + } +#endif +} \ No newline at end of file diff --git a/utils/Servers/src/smtp_servers.h b/utils/Servers/src/smtp_servers.h new file mode 100644 index 0000000..1122e96 --- /dev/null +++ b/utils/Servers/src/smtp_servers.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SMTP_SERVERS_H +#define SMTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void smtp_servers_send_email(SERVERS_SMTP *param); +void smtp_servers_send_email_only_text(SERVERS_SMTP *param); +#ifdef __cplusplus +} +#endif +#endif // !SMTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/test/CMakeLists.txt b/utils/Servers/test/CMakeLists.txt new file mode 100644 index 0000000..85d2bd4 --- /dev/null +++ b/utils/Servers/test/CMakeLists.txt @@ -0,0 +1,35 @@ + +include(${CMAKE_SOURCE_DIR_SIFARSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ${COMPONENT_SOURCE_PATH}/Log/include + ${COMPONENT_SOURCE_PATH}/Servers/include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +aux_source_directory(./ SRC_FILES1) + +set(TARGET_NAME ServersMainTest) +add_executable(${TARGET_NAME} ${SRC_FILES1}) +target_link_libraries(${TARGET_NAME} Servers) +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libssl.a) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libcrypto.a) +else() + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.so) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libssl.so) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libcrypto.so) +endif() +target_link_libraries(${TARGET_NAME} pthread dl) +if(${COVERAGE_ON} MATCHES "true") + target_link_libraries(${TARGET_NAME} gcov) +endif() \ No newline at end of file diff --git a/utils/Servers/test/test_main.c b/utils/Servers/test/test_main.c new file mode 100644 index 0000000..4bfbf38 --- /dev/null +++ b/utils/Servers/test/test_main.c @@ -0,0 +1,38 @@ +#include +#include "servers.h" +void test0() +{ + // InitLog(LOG_EASYLOGGING, NULL); + // LogInfo("servers test start.\n"); + SERVERS_HTTP *http = new_servers_http("http://example.com"); + if (http) + { + http_get(http); + if (http->reply) + { + // LogInfo("HttpGet ========\n %s\n", http->reply); + } + delete_servers_http(http); + } + // UnInitLog(); +} +void test1() +{ + // InitLog(LOG_EASYLOGGING, NULL); + // LogInfo("servers test start.\n"); + // SERVERS_HTTP *http = new_servers_http("https://example.com"); + // if (http) + // { + // http_get(http); + // if (http->reply) + // { + // LogInfo("HttpGet ========\n %s\n", http->reply); + // } + // delete_servers_http(http); + // } + // UnInitLog(); +} +int main(int argc, char *argv[]) +{ + return 0; +} \ No newline at end of file