mirror of
https://github.com/m5stack/M5Stack_MicroPython.git
synced 2026-05-20 10:14:44 -07:00
1448 lines
43 KiB
C
1448 lines
43 KiB
C
/*
|
|
* This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018 LoBo (https://github.com/loboris)
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#if defined(CONFIG_MICROPY_USE_CURL) || defined(CONFIG_MICROPY_USE_SSH)
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "libs/espcurl.h"
|
|
#include "libs/libGSM.h"
|
|
|
|
#include "lwip/err.h"
|
|
#include "lwip/sockets.h"
|
|
#include "lwip/sys.h"
|
|
#include "lwip/netdb.h"
|
|
#include "lwip/dns.h"
|
|
#include "esp_log.h"
|
|
#include "esp_task_wdt.h"
|
|
#include "mphalport.h"
|
|
|
|
#include "esp_wifi_types.h"
|
|
#include "tcpip_adapter.h"
|
|
|
|
#include "modnetwork.h"
|
|
#include "modmachine.h"
|
|
#include "py/mpthread.h"
|
|
#include "py/nlr.h"
|
|
|
|
#ifdef CONFIG_MICROPY_USE_CURL
|
|
|
|
const char *CURL_TAG = "[Curl]";
|
|
|
|
// Set default values for configuration variables
|
|
uint8_t curl_verbose = 0; // show detailed info of what curl functions are doing
|
|
uint8_t curl_progress = 0; // show progress during curl transfers
|
|
uint16_t curl_timeout = 60; // curl operations timeout in seconds
|
|
uint32_t curl_maxbytes = 300000; // limit download length
|
|
uint8_t curl_initialized = 0;
|
|
uint8_t curl_nodecode = 0; // if set to 1, do not use compression in http transfers
|
|
|
|
static uint8_t curl_sim_fs = 0;
|
|
|
|
struct curl_Transfer {
|
|
char *ptr;
|
|
uint32_t len;
|
|
uint32_t size;
|
|
int status;
|
|
uint8_t tofile;
|
|
uint32_t maxlen;
|
|
double lastruntime;
|
|
FILE *file;
|
|
CURL *curl;
|
|
};
|
|
|
|
struct curl_httppost *formpost = NULL;
|
|
struct curl_httppost *lastptr = NULL;
|
|
|
|
|
|
// Initialize the structure used in curlWrite callback
|
|
//-----------------------------------------------------------------------------------------------------------
|
|
static void init_curl_Transfer(CURL *curl, struct curl_Transfer *s, char *buf, uint16_t maxlen, FILE* file) {
|
|
s->len = 0;
|
|
s->size = 0;
|
|
s->status = 0;
|
|
s->maxlen = maxlen;
|
|
s->lastruntime = 0;
|
|
s->tofile = 0;
|
|
s->file = file;
|
|
s->ptr = buf;
|
|
s->curl = curl;
|
|
if (s->ptr) s->ptr[0] = '\0';
|
|
}
|
|
|
|
// Callback: Get response header or body to buffer or file
|
|
//--------------------------------------------------------------------------------
|
|
static size_t curlWrite(void *buffer, size_t size, size_t nmemb, void *userdata) {
|
|
struct curl_Transfer *s = (struct curl_Transfer *)userdata;
|
|
CURL *curl = s->curl;
|
|
double curtime = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
|
|
|
|
mp_hal_reset_wdt();
|
|
|
|
if (!s->tofile) {
|
|
// === Downloading to buffer ===
|
|
char *buf = (char *)buffer;
|
|
if (s->ptr) {
|
|
if ((s->status == 0) && ((size*nmemb) > 0)) {
|
|
for (int i=0; i<(size*nmemb); i++) {
|
|
if (s->len < (s->maxlen-2)) {
|
|
if ((buf[i] == 0x0a) || (buf[i] == 0x0d) || (buf[i] == 0x09) || (buf[i] >= 0x20)) s->ptr[s->len] = buf[i];
|
|
else s->ptr[s->len] = '.';
|
|
s->len++;
|
|
s->ptr[s->len] = '\0';
|
|
}
|
|
else {
|
|
s->status = 1;
|
|
break;
|
|
}
|
|
}
|
|
if ((curl_progress) && ((curtime - s->lastruntime) > curl_progress)) {
|
|
s->lastruntime = curtime;
|
|
ESP_LOGI(CURL_TAG, "* Download: received %d", s->len);
|
|
}
|
|
}
|
|
}
|
|
return size*nmemb;
|
|
}
|
|
else {
|
|
// === Downloading to file ===
|
|
size_t nwrite;
|
|
|
|
if (curl_sim_fs) nwrite = size*nmemb;
|
|
else {
|
|
nwrite = fwrite(buffer, 1, size*nmemb, s->file);
|
|
if (nwrite <= 0) {
|
|
ESP_LOGE(CURL_TAG, "* Download: Error writing to file %d", nwrite);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
s->len += nwrite;
|
|
if ((curl_progress) && ((curtime - s->lastruntime) > curl_progress)) {
|
|
s->lastruntime = curtime;
|
|
ESP_LOGI(CURL_TAG, "* Download: received %d", s->len);
|
|
}
|
|
|
|
return nwrite;
|
|
}
|
|
}
|
|
|
|
// Callback: ftp PUT file
|
|
//--------------------------------------------------------------------------
|
|
static size_t curlRead(void *ptr, size_t size, size_t nmemb, void *userdata)
|
|
{
|
|
struct curl_Transfer *s = (struct curl_Transfer *)userdata;
|
|
CURL *curl = s->curl;
|
|
double curtime = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
|
|
size_t nread;
|
|
|
|
mp_hal_reset_wdt();
|
|
|
|
if (curl_sim_fs) {
|
|
if (s->len < 1024) {
|
|
size_t reqsize = size*nmemb;
|
|
nread = 0;
|
|
while ((nread+23) < reqsize) {
|
|
sprintf((char *)(ptr+nread), "%s", "Simulate upload data\r\n");
|
|
nread += 22;
|
|
}
|
|
}
|
|
else nread = 0;
|
|
}
|
|
else {
|
|
nread = fread(ptr, 1, size*nmemb, s->file);
|
|
}
|
|
|
|
s->len += nread;
|
|
if ((curl_progress) && ((curtime - s->lastruntime) > curl_progress)) {
|
|
s->lastruntime = curtime;
|
|
ESP_LOGI(CURL_TAG, "* Upload: sent %d", s->len);
|
|
}
|
|
|
|
//ESP_LOGI(CURL_TAG, "**Upload, read %d (%d,%d)", nread,size,nmemb);
|
|
if (nread <= 0) return 0;
|
|
|
|
return nread;
|
|
}
|
|
|
|
/*
|
|
//------------------------------------------------------------------
|
|
static int closesocket_callback(void *clientp, curl_socket_t item) {
|
|
int ret = lwip_close(item);
|
|
return ret;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
static curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) {
|
|
int s = lwip_socket(address->family, address->socktype, address->protocol);
|
|
return s;
|
|
}
|
|
*/
|
|
|
|
// Set some common curl options
|
|
//----------------------------------------------
|
|
static void _set_default_options(CURL *handle) {
|
|
curl_easy_setopt(handle, CURLOPT_VERBOSE, curl_verbose);
|
|
|
|
// ** Set SSL Options
|
|
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
|
|
curl_easy_setopt(handle, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
|
|
curl_easy_setopt(handle, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
|
|
//curl_easy_setopt(handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
|
|
|
// ==== Provide CA Certs from different file than default ====
|
|
//curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt");
|
|
// ===========================================================
|
|
|
|
/*
|
|
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER , 1L);
|
|
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST , 1L);
|
|
*/
|
|
|
|
/* If the server is redirected, we tell libcurl if we want to follow redirection
|
|
* There are some problems when redirecting ssl requests, so for now we disable redirection
|
|
*/
|
|
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 0L); // set to 1 to enable redirection
|
|
curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 3L);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_TIMEOUT, (long)curl_timeout);
|
|
|
|
curl_easy_setopt(handle, CURLOPT_MAXFILESIZE, (long)curl_maxbytes);
|
|
curl_easy_setopt(handle, CURLOPT_FORBID_REUSE, 1L);
|
|
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L);
|
|
|
|
if (curl_nodecode) {
|
|
// Do not decode/decompress received content
|
|
curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "identity");
|
|
curl_easy_setopt(handle, CURLOPT_HTTP_CONTENT_DECODING, 0L);
|
|
}
|
|
else {
|
|
curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "");
|
|
curl_easy_setopt(handle, CURLOPT_HTTP_CONTENT_DECODING, 1L);
|
|
}
|
|
|
|
//curl_easy_setopt(handle, CURLOPT_LOW_SPEED_LIMIT, 1024L); //bytes/sec
|
|
//curl_easy_setopt(handle, CURLOPT_LOW_SPEED_TIME, 4); //seconds
|
|
|
|
// Open&Close socket callbacks can be set if special handling is needed
|
|
/*
|
|
curl_easy_setopt(handle, CURLOPT_CLOSESOCKETFUNCTION, closesocket_callback);
|
|
curl_easy_setopt(handle, CURLOPT_CLOSESOCKETDATA, NULL);
|
|
curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback);
|
|
curl_easy_setopt(handle, CURLOPT_OPENSOCKETDATA, NULL);
|
|
*/
|
|
}
|
|
|
|
//==================================================================================
|
|
int Curl_GET(char *url, char *fname, char *hdr, char *body, int hdrlen, int bodylen)
|
|
{
|
|
CURL *curl = NULL;
|
|
CURLcode res = 0;
|
|
FILE* file = NULL;
|
|
int err = 0;
|
|
|
|
if ((hdr) && (hdrlen < MIN_HDR_BUF_LEN)) {
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
if ((body) && (bodylen < MIN_BODY_BUF_LEN)) {
|
|
err = -2;
|
|
goto exit;
|
|
}
|
|
|
|
struct curl_Transfer get_data;
|
|
struct curl_Transfer get_header;
|
|
|
|
if (!url) {
|
|
err = -3;
|
|
goto exit;
|
|
}
|
|
|
|
if (!curl_initialized) {
|
|
res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
if (res) {
|
|
err = -4;
|
|
goto exit;
|
|
}
|
|
curl_initialized = 1;
|
|
}
|
|
|
|
// Create a curl curl
|
|
curl = curl_easy_init();
|
|
if (curl == NULL) {
|
|
err = -5;
|
|
goto exit;
|
|
}
|
|
|
|
init_curl_Transfer(curl, &get_data, body, bodylen, NULL);
|
|
init_curl_Transfer(curl, &get_header, hdr, hdrlen, NULL);
|
|
|
|
if (fname) {
|
|
if (strcmp(fname, "simulate") == 0) {
|
|
get_data.tofile = 1;
|
|
curl_sim_fs = 1;
|
|
}
|
|
else {
|
|
file = fopen(fname, "wb");
|
|
if (file == NULL) {
|
|
err = -6;
|
|
goto exit;
|
|
}
|
|
get_data.file = file;
|
|
get_data.tofile = 1;
|
|
curl_sim_fs = 0;
|
|
}
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
_set_default_options(curl);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &get_header);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &get_data);
|
|
|
|
// Perform the request, res will get the return code
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK) {
|
|
if (curl_verbose) {
|
|
ESP_LOGE(CURL_TAG, "curl_easy_perform failed: %s", curl_easy_strerror(res));
|
|
}
|
|
if (body) snprintf(body, bodylen, "%s", curl_easy_strerror(res));
|
|
err = -7;
|
|
goto exit;
|
|
}
|
|
|
|
if (get_data.tofile) {
|
|
if (curl_progress) {
|
|
double curtime = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
|
|
ESP_LOGI(CURL_TAG, "* Download: received %d B; time=%0.1f s; speed=%0.1f KB/sec", get_data.len, curtime, (float)(((get_data.len*10)/curtime) / 10240.0));
|
|
}
|
|
if (body) {
|
|
if (strcmp(fname, "simulate") == 0) snprintf(body, bodylen, "SIMULATED save to file; size=%d", get_data.len);
|
|
else snprintf(body, bodylen, "Saved to file %s, size=%d", fname, get_data.len);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (file) fclose(file);
|
|
if (curl) curl_easy_cleanup(curl);
|
|
|
|
return err;
|
|
}
|
|
|
|
// Converts an integer value to its hex character
|
|
//-----------------------------
|
|
static char to_hex(char code) {
|
|
static char hex[] = "0123456789abcdef";
|
|
return hex[code & 15];
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
/* Returns a url encoded version of str
|
|
* IMPORTANT: be sure to free() the returned string after use
|
|
*/
|
|
//=================================
|
|
char *url_encode(const char *str) {
|
|
const char *pstr = str;
|
|
char *buf = malloc(strlen(str) * 3 + 1);
|
|
char *pbuf = buf;
|
|
while (*pstr) {
|
|
if (*pstr == ' ') {
|
|
*pbuf++ = '%';
|
|
*pbuf++ = to_hex(*pstr >> 4);
|
|
*pbuf++ = to_hex(*pstr & 15);
|
|
}
|
|
else {
|
|
*pbuf++ = *pstr;
|
|
}
|
|
pstr++;
|
|
}
|
|
*pbuf = '\0';
|
|
return buf;
|
|
}
|
|
|
|
//=========================================================================================================================================================================================================
|
|
int Curl_IMAP_GET(const char *opts, char *fname, char *hdr, char *body, int hdrlen, int bodylen, const char* imapserver, unsigned int imapport, const char* username, const char* password, char *cust_req)
|
|
{
|
|
CURL *curl = NULL;
|
|
CURLcode res = 0;
|
|
FILE* file = NULL;
|
|
int err = 0;
|
|
|
|
if ((hdr) && (hdrlen < MIN_HDR_BUF_LEN)) {
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
if ((body) && (bodylen < MIN_BODY_BUF_LEN)) {
|
|
err = -2;
|
|
goto exit;
|
|
}
|
|
|
|
struct curl_Transfer get_data;
|
|
struct curl_Transfer get_header;
|
|
|
|
if (!curl_initialized) {
|
|
res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
if (res) {
|
|
err = -3;
|
|
goto exit;
|
|
}
|
|
curl_initialized = 1;
|
|
}
|
|
|
|
// Create a curl curl
|
|
curl = curl_easy_init();
|
|
if (curl == NULL) {
|
|
err = -4;
|
|
goto exit;
|
|
}
|
|
|
|
init_curl_Transfer(curl, &get_data, body, bodylen, NULL);
|
|
init_curl_Transfer(curl, &get_header, hdr, hdrlen, NULL);
|
|
|
|
if (fname) {
|
|
if (strcmp(fname, "simulate") == 0) {
|
|
get_data.tofile = 1;
|
|
curl_sim_fs = 1;
|
|
}
|
|
else {
|
|
file = fopen(fname, "wb");
|
|
if (file == NULL) {
|
|
err = -5;
|
|
goto exit;
|
|
}
|
|
get_data.file = file;
|
|
get_data.tofile = 1;
|
|
curl_sim_fs = 0;
|
|
}
|
|
}
|
|
|
|
//set destination URL
|
|
char* url;
|
|
char *opts_out = url_encode(opts);
|
|
int opts_len = strlen(opts_out);
|
|
size_t len = strlen(imapserver) + opts_len + 16;
|
|
if ((url = (char*)malloc(len)) == NULL) {
|
|
free(opts_out);
|
|
ESP_LOGE(CURL_TAG, "Error allocating memory");
|
|
err = -6;
|
|
goto exit;
|
|
}
|
|
|
|
sprintf(url, "imaps://%s:%u/%s", imapserver, imapport, opts_out);
|
|
free(opts_out);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
printf("=== URL=[%s]\n", url);
|
|
free(url);
|
|
|
|
_set_default_options(curl);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &get_header);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &get_data);
|
|
|
|
// Try using Transport Layer Security (TLS), but continue anyway if it fails
|
|
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
|
|
|
|
//set authentication credentials if provided
|
|
if (username && *username) curl_easy_setopt(curl, CURLOPT_USERNAME, username);
|
|
if (password) curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
|
|
|
|
if (cust_req) {
|
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, cust_req);
|
|
}
|
|
// Only allow IMAPS, we do not want this to work when unencrypted.
|
|
curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_IMAPS);
|
|
|
|
// Perform the request, res will get the return code
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK) {
|
|
if (curl_verbose) {
|
|
ESP_LOGE(CURL_TAG, "curl_easy_perform failed: %s", curl_easy_strerror(res));
|
|
}
|
|
if (body) snprintf(body, bodylen, "%s", curl_easy_strerror(res));
|
|
err = -7;
|
|
goto exit;
|
|
}
|
|
|
|
if (get_data.tofile) {
|
|
if (curl_progress) {
|
|
double curtime = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
|
|
ESP_LOGI(CURL_TAG, "* Download: received %d B; time=%0.1f s; speed=%0.1f KB/sec", get_data.len, curtime, (float)(((get_data.len*10)/curtime) / 10240.0));
|
|
}
|
|
if (body) {
|
|
if (strcmp(fname, "simulate") == 0) snprintf(body, bodylen, "SIMULATED save to file; size=%d", get_data.len);
|
|
else snprintf(body, bodylen, "Saved to file %s, size=%d", fname, get_data.len);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (file) fclose(file);
|
|
if (curl) curl_easy_cleanup(curl);
|
|
|
|
return err;
|
|
}
|
|
|
|
//=======================================================================
|
|
int Curl_POST(char *url , char *hdr, char *body, int hdrlen, int bodylen)
|
|
{
|
|
CURL *curl = NULL;
|
|
CURLcode res = 0;
|
|
int err = 0;
|
|
struct curl_slist *headerlist=NULL;
|
|
const char hl_buf[] = "Expect:";
|
|
|
|
if ((hdr) && (hdrlen < MIN_HDR_BUF_LEN)) {
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
if ((body) && (bodylen < MIN_BODY_BUF_LEN)) {
|
|
err = -2;
|
|
goto exit;
|
|
}
|
|
|
|
struct curl_Transfer get_data;
|
|
struct curl_Transfer get_header;
|
|
|
|
if (!url) {
|
|
err = -3;
|
|
goto exit;
|
|
}
|
|
|
|
if (!curl_initialized) {
|
|
res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
if (res) {
|
|
err = -4;
|
|
goto exit;
|
|
}
|
|
curl_initialized = 1;
|
|
}
|
|
|
|
// Create a curl curl
|
|
curl = curl_easy_init();
|
|
if (curl == NULL) {
|
|
err = -5;
|
|
goto exit;
|
|
}
|
|
|
|
init_curl_Transfer(curl, &get_data, body, bodylen, NULL);
|
|
init_curl_Transfer(curl, &get_header, hdr, hdrlen, NULL);
|
|
|
|
// initialize custom header list (stating that Expect: 100-continue is not wanted
|
|
headerlist = curl_slist_append(headerlist, hl_buf);
|
|
|
|
// set URL that receives this POST
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
_set_default_options(curl);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &get_header);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &get_data);
|
|
|
|
if (formpost) curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
|
|
|
|
// Perform the request, res will get the return code
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK) {
|
|
if (curl_verbose) {
|
|
ESP_LOGE(CURL_TAG, "curl_easy_perform failed: %s", curl_easy_strerror(res));
|
|
}
|
|
if (body) snprintf(body, bodylen, "%s", curl_easy_strerror(res));
|
|
err = -7;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (curl) curl_easy_cleanup(curl);
|
|
if (formpost) {
|
|
curl_formfree(formpost);
|
|
formpost = NULL;
|
|
}
|
|
curl_slist_free_all(headerlist);
|
|
|
|
return err;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
#ifdef CONFIG_MICROPY_USE_CURLFTP
|
|
|
|
//===================================================================================================================
|
|
int Curl_FTP(uint8_t upload, char *url, char *user_pass, char *fname, char *hdr, char *body, int hdrlen, int bodylen)
|
|
{
|
|
#undef DISABLE_SSH_AGENT
|
|
|
|
CURL *curl = NULL;
|
|
CURLcode res = 0;
|
|
int err = 0;
|
|
FILE* file = NULL;
|
|
int fsize = 0;
|
|
|
|
if ((hdr) && (hdrlen < MIN_HDR_BUF_LEN)) {
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
if ((body) && (bodylen < MIN_BODY_BUF_LEN)) {
|
|
err = -2;
|
|
goto exit;
|
|
}
|
|
|
|
struct curl_Transfer get_data;
|
|
struct curl_Transfer get_header;
|
|
|
|
if ((!url) || (!user_pass)) {
|
|
err = -3;
|
|
goto exit;
|
|
}
|
|
|
|
if (!curl_initialized) {
|
|
res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
if (res) {
|
|
err = -4;
|
|
goto exit;
|
|
}
|
|
curl_initialized = 1;
|
|
}
|
|
|
|
// Create a curl curl
|
|
curl = curl_easy_init();
|
|
if (curl == NULL) {
|
|
err = -5;
|
|
goto exit;
|
|
}
|
|
|
|
init_curl_Transfer(curl, &get_data, body, bodylen, NULL);
|
|
init_curl_Transfer(curl, &get_header, hdr, hdrlen, NULL);
|
|
|
|
if (fname) {
|
|
if (strcmp(fname, "simulate") == 0) {
|
|
get_data.tofile = 1;
|
|
curl_sim_fs = 1;
|
|
}
|
|
else {
|
|
if (upload) {
|
|
// Uploading the file
|
|
struct stat sb;
|
|
if ((stat(fname, &sb) == 0) && (sb.st_size > 0)) {
|
|
fsize = sb.st_size;
|
|
file = fopen(fname, "rb");
|
|
}
|
|
}
|
|
else {
|
|
// Downloading to file (LIST or Get file)
|
|
file = fopen(fname, "wb");
|
|
}
|
|
if (file == NULL) {
|
|
err = -6;
|
|
goto exit;
|
|
}
|
|
get_data.file = file;
|
|
get_data.tofile = 1;
|
|
get_data.size = fsize;
|
|
curl_sim_fs = 0;
|
|
}
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
_set_default_options(curl);
|
|
|
|
/// build a list of commands to pass to libcurl
|
|
//headerlist = curl_slist_append(headerlist, "QUIT");
|
|
curl_easy_setopt(curl, CURLOPT_USERPWD, user_pass);
|
|
|
|
//curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist);
|
|
curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L);
|
|
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &get_header);
|
|
if (upload) {
|
|
curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR_RETRY);
|
|
// we want to use our own read function
|
|
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curlRead);
|
|
// specify which file to upload
|
|
curl_easy_setopt(curl, CURLOPT_READDATA, &get_data);
|
|
// enable uploading
|
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
|
|
|
if (fsize > 0) curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)fsize);
|
|
}
|
|
else {
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &get_data);
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)curl_timeout);
|
|
|
|
// Perform the request, res will get the return code
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK) {
|
|
if (curl_verbose) {
|
|
ESP_LOGE(CURL_TAG, "curl_easy_perform failed: %s", curl_easy_strerror(res));
|
|
}
|
|
if (body) snprintf(body, bodylen, "%s", curl_easy_strerror(res));
|
|
err = -7;
|
|
goto exit;
|
|
}
|
|
|
|
if (get_data.tofile) {
|
|
if (curl_progress) {
|
|
double curtime = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);
|
|
if (upload) {
|
|
ESP_LOGI(CURL_TAG, "* Upload: sent %d B; time=%0.1f s; speed=%0.1f KB/sec", get_data.len, curtime, (float)(((get_data.len*10)/curtime) / 10240.0));
|
|
}
|
|
else {
|
|
ESP_LOGI(CURL_TAG, "* Download: received %d B; time=%0.1f s; speed=%0.1f KB/sec", get_data.len, curtime, (float)(((get_data.len*10)/curtime) / 10240.0));
|
|
}
|
|
}
|
|
|
|
if (body) {
|
|
if (upload) {
|
|
if (strcmp(fname, "simulate") == 0) snprintf(body, bodylen, "SIMULATED upload from file; size=%d", get_data.len);
|
|
else snprintf(body, bodylen, "Uploaded file %s, size=%d", fname, fsize);
|
|
}
|
|
else {
|
|
if (strcmp(fname, "simulate") == 0) snprintf(body, bodylen, "SIMULATED download to file; size=%d", get_data.len);
|
|
else snprintf(body, bodylen, "Downloaded to file %s, size=%d", fname, get_data.len);
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (file) fclose(file);
|
|
if (curl) curl_easy_cleanup(curl);
|
|
|
|
return err;
|
|
}
|
|
|
|
#endif
|
|
|
|
//-------------------
|
|
void Curl_cleanup() {
|
|
if (curl_initialized) {
|
|
curl_global_cleanup();
|
|
curl_initialized = 0;
|
|
}
|
|
}
|
|
|
|
#endif // CONFIG_MICROPY_USE_CURL
|
|
|
|
#ifdef CONFIG_MICROPY_USE_SSH
|
|
|
|
#include "libssh2.h"
|
|
#include "libssh2_sftp.h"
|
|
|
|
const char *SSH_TAG = "[SSH]";
|
|
|
|
uint8_t ssh2_verbose = 0; // show detailed info of what ssh2 functions are doing
|
|
uint8_t ssh2_progress = 0; // show progress during ssh2 transfers
|
|
uint16_t ssh2_session_trace = 0;
|
|
uint8_t ssh2_session_timeout = 8;
|
|
uint32_t ssh2_maxbytes = 300000; // limit download length
|
|
|
|
|
|
// ==== LIBSSH2 functions ====
|
|
|
|
//------------------------------------------------------------
|
|
static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
|
|
{
|
|
struct timeval timeout;
|
|
int rc;
|
|
fd_set fd;
|
|
fd_set *writefd = NULL;
|
|
fd_set *readfd = NULL;
|
|
int dir;
|
|
|
|
timeout.tv_sec = 5;
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&fd);
|
|
|
|
FD_SET(socket_fd, &fd);
|
|
|
|
// now make sure we wait in the correct direction
|
|
dir = libssh2_session_block_directions(session);
|
|
|
|
if (dir & LIBSSH2_SESSION_BLOCK_INBOUND) readfd = &fd;
|
|
|
|
if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) writefd = &fd;
|
|
|
|
rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void _append_msg(char *messages, char *msg, int msglen, uint8_t type)
|
|
{
|
|
if ((messages) && ((strlen(messages) + strlen(msg)+1 < msglen))) {
|
|
strcat(messages, msg);
|
|
strcat(messages, "\n");
|
|
}
|
|
if (ssh2_verbose) {
|
|
if (type == ESP_LOG_ERROR) {
|
|
ESP_LOGE(SSH_TAG, "%s", msg);
|
|
}
|
|
else if (type == ESP_LOG_WARN) {
|
|
ESP_LOGW(SSH_TAG, "%s", msg);
|
|
}
|
|
else if (type == ESP_LOG_INFO) {
|
|
ESP_LOGI(SSH_TAG, "%s", msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static int sock_connect(char *server, char *port, char *messages, int msglen)
|
|
{
|
|
int sock=-1;
|
|
int rc;
|
|
char msg[80];
|
|
struct addrinfo *res;
|
|
struct in_addr *addr;
|
|
const struct addrinfo hints = {
|
|
.ai_family = AF_INET,
|
|
.ai_socktype = SOCK_STREAM,
|
|
};
|
|
|
|
// Resolve IP address
|
|
rc = getaddrinfo(server, port, &hints, &res);
|
|
if (rc != 0 || res == NULL) {
|
|
sprintf(msg, "* DNS lookup failed err=%d res=%p", rc, res);
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
return -1;
|
|
}
|
|
// Print the resolved IP. Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code
|
|
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
|
sprintf(msg, "* DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
|
|
// Create the socket
|
|
sock = socket(res->ai_family, res->ai_socktype, 0);
|
|
if (sock < 0) {
|
|
sprintf(msg, "* Failed to allocate socket.");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
freeaddrinfo(res);
|
|
return sock;
|
|
}
|
|
|
|
// Connect
|
|
if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) {
|
|
sprintf(msg, "* Failed to connect!");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
freeaddrinfo(res);
|
|
close(sock);
|
|
return -1;
|
|
}
|
|
sprintf(msg, "* Connected");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
freeaddrinfo(res);
|
|
|
|
return sock;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------------------------------------------------------------
|
|
static LIBSSH2_SESSION *getSSHSession(int sock, char *username, char *password, char* privkey, char *pubkey, int auth_pw, char *messages, int msglen)
|
|
{
|
|
int rc;
|
|
const char *fingerprint;
|
|
LIBSSH2_SESSION *session;
|
|
char msg[96];
|
|
|
|
// Create a session instance
|
|
session = libssh2_session_init();
|
|
if(!session) {
|
|
sprintf(msg, "* Failed to create session!");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
return NULL;
|
|
}
|
|
sprintf(msg, "* SSH session created");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
|
|
// Set trace level
|
|
libssh2_trace(session, ssh2_session_trace);
|
|
|
|
// Set timeout, reset wdt first
|
|
mp_hal_set_wdt_tmo();
|
|
#ifdef CONFIG_MICROPY_USE_TASK_WDT
|
|
esp_task_wdt_reset();
|
|
#endif
|
|
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 || CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
|
vTaskDelay(1); // allow other core idle task to reset the watchdog
|
|
#endif
|
|
libssh2_session_set_timeout(session, ssh2_session_timeout*1000);
|
|
|
|
// Set preferred key exchange methods
|
|
//libssh2_session_method_pref(session, LIBSSH2_METHOD_KEX, "diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1");
|
|
|
|
// ... start it up. This will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
|
|
rc = libssh2_session_handshake(session, sock);
|
|
if (rc) {
|
|
libssh2_session_disconnect(session, "Error Shutdown.");
|
|
libssh2_session_free(session);
|
|
sprintf(msg, "* Failure establishing SSH session: %d", rc);
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
return NULL;
|
|
}
|
|
sprintf(msg, "* SSH session established");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
|
|
/* At this point we havn't yet authenticated. The first thing to do
|
|
* is check the hostkey's fingerprint against our known hosts. Your app
|
|
* may have it hard coded, may go to a file, may present it to the
|
|
* user, that's your call
|
|
*/
|
|
int fplen = 0, fpofst = 0;
|
|
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
|
|
if (fingerprint) {
|
|
fplen = 32;
|
|
fpofst = 25;
|
|
sprintf(msg, "* Fingerprint (SHA256): [");
|
|
}
|
|
else {
|
|
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
|
if (fingerprint) {
|
|
fplen = 20;
|
|
fpofst = 23;
|
|
sprintf(msg, "* Fingerprint (SHA1): [");
|
|
}
|
|
else {
|
|
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
|
|
if (fingerprint) {
|
|
fplen = 16;
|
|
fpofst = 22;
|
|
sprintf(msg, "* Fingerprint (MD5): [");
|
|
}
|
|
}
|
|
}
|
|
if (fplen > 0) {
|
|
for(int i = 0; i < fplen; i++) {
|
|
sprintf(msg+fpofst+(i*2), "%02X", (unsigned char)fingerprint[i]);
|
|
}
|
|
strcat(msg, "]");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
}
|
|
|
|
if (auth_pw) {
|
|
// We could authenticate via password
|
|
if (libssh2_userauth_password(session, username, password)) {
|
|
libssh2_session_disconnect(session, "Error Shutdown.");
|
|
libssh2_session_free(session);
|
|
sprintf(msg, "* Authentication by password failed.");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
return NULL;
|
|
}
|
|
sprintf(msg,"* Authentication by password succeed.");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
}
|
|
else {
|
|
// Or by public key
|
|
if (libssh2_userauth_publickey_fromfile(session, username, pubkey, privkey, password)) {
|
|
libssh2_session_disconnect(session, "Error Shutdown.");
|
|
libssh2_session_free(session);
|
|
sprintf(msg, "* Authentication by public key failed");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_ERROR);
|
|
return NULL;
|
|
}
|
|
sprintf(msg, "* Authentication by public key succeed.");
|
|
_append_msg(messages, msg, msglen, ESP_LOG_INFO);
|
|
}
|
|
|
|
return session;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------------------
|
|
static int sshDownload(LIBSSH2_CHANNEL *channel, libssh2_struct_stat *fileinfo, FILE *fdd, char *hdr, char *body, int hdrlen, int bodylen) {
|
|
libssh2_struct_stat_size got = 0;
|
|
uint64_t tstart = mp_hal_ticks_ms(), tlatest=0;
|
|
int rc, err = 0;
|
|
uint32_t bodypos = 0;
|
|
char msg[80];
|
|
char mem[1024];
|
|
|
|
while(got < fileinfo->st_size) {
|
|
int amount = sizeof(mem);
|
|
|
|
if ((fileinfo->st_size -got) < amount) {
|
|
amount = (int)(fileinfo->st_size -got);
|
|
}
|
|
|
|
rc = libssh2_channel_read(channel, mem, amount);
|
|
if (rc > 0) {
|
|
if (fdd != NULL) {
|
|
// === Downloading to file ===
|
|
if (err == 0) {
|
|
if ((got + amount) < ssh2_maxbytes) {
|
|
size_t nwrite = fwrite(mem, 1, amount, fdd);
|
|
if (nwrite <= 0) {
|
|
sprintf(msg, "* Download: Error writing to file at %u", (uint32_t)got);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
err = -10;
|
|
}
|
|
}
|
|
else {
|
|
sprintf(msg, "* Max file size exceeded at %u", (uint32_t)got);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
err = -11;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// === Download to buffer ===
|
|
if (err == 0) {
|
|
if ((bodypos + amount) < bodylen) {
|
|
memcpy(body+bodypos, mem, amount);
|
|
bodypos += amount;
|
|
body[bodypos] = 0;
|
|
}
|
|
else {
|
|
sprintf(msg, "* Buffer full at %u", (uint32_t)got);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
err = -12;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( rc < 0) {
|
|
sprintf(msg, "\n* libssh2_channel_read() failed: %d", rc);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
err = -13;
|
|
break;
|
|
}
|
|
got += rc;
|
|
if (ssh2_progress) {
|
|
if (tlatest == 0) {
|
|
printf("\n");
|
|
tlatest = mp_hal_ticks_ms();
|
|
}
|
|
if ((tlatest + (ssh2_progress*1000)) >= mp_hal_ticks_ms()) {
|
|
tlatest = mp_hal_ticks_ms();
|
|
printf("Download: %u\r", (uint32_t)got);
|
|
}
|
|
}
|
|
}
|
|
tstart = mp_hal_ticks_ms() - tstart;
|
|
if (ssh2_progress) {
|
|
printf(" \r");
|
|
}
|
|
sprintf(msg, "* Received: %u bytes in %0.1f sec (%0.3f KB/s)", (uint32_t)got, (float)(tstart / 1000.0), (float)((float)(got)/1024.0/((float)(tstart) / 1000.0)));
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_INFO);
|
|
|
|
return err;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
static int sshUpload(LIBSSH2_CHANNEL *channel, FILE *fdd, char *hdr, int hdrlen) {
|
|
uint64_t tstart = mp_hal_ticks_ms(), tlatest=0;
|
|
int rc, err = 0;
|
|
char msg[80];
|
|
char mem[1024];
|
|
size_t nread;
|
|
char *ptr;
|
|
uint32_t sent = 0;
|
|
|
|
do {
|
|
nread = fread(mem, 1, sizeof(mem), fdd);
|
|
if (nread <= 0) {
|
|
// end of file
|
|
break;
|
|
}
|
|
ptr = mem;
|
|
|
|
do {
|
|
// write the same data over and over, until error or completion
|
|
rc = libssh2_channel_write(channel, ptr, nread);
|
|
if (rc < 0) {
|
|
sprintf(msg, "* Upload: Error sending: %d at %u", rc, sent);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
err = -10;
|
|
break;
|
|
}
|
|
else {
|
|
// rc indicates how many bytes were written this time
|
|
ptr += rc;
|
|
nread -= rc;
|
|
sent += rc;
|
|
}
|
|
if (ssh2_progress) {
|
|
if (tlatest == 0) {
|
|
printf("\n");
|
|
tlatest = mp_hal_ticks_ms();
|
|
}
|
|
if ((tlatest + (ssh2_progress*1000)) >= mp_hal_ticks_ms()) {
|
|
tlatest = mp_hal_ticks_ms();
|
|
printf("Upload: %u\r", sent);
|
|
}
|
|
}
|
|
} while (nread);
|
|
|
|
} while (1);
|
|
tstart = mp_hal_ticks_ms() - tstart;
|
|
if (ssh2_progress) {
|
|
printf(" \r");
|
|
}
|
|
sprintf(msg, "* Sent: %u bytes in %0.1f sec (%0.3f KB/s)", sent, (float)(tstart / 1000.0), (float)((float)(sent)/1024.0/((float)(tstart) / 1000.0)));
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_INFO);
|
|
|
|
libssh2_channel_send_eof(channel);
|
|
libssh2_channel_wait_eof(channel);
|
|
libssh2_channel_wait_closed(channel);
|
|
|
|
return err;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
int ssh_SCP(uint8_t type, char *server, char *port, char * scppath, char *user, char *pass, char *key, char *fname, char *hdr, char *body, int hdrlen, int bodylen)
|
|
{
|
|
|
|
char msg[80];
|
|
LIBSSH2_SESSION *session;
|
|
libssh2_struct_stat fileinfo;
|
|
FILE *fdd = NULL;
|
|
int fsize = 0;
|
|
hdrlen -=1;
|
|
bodylen -=1;
|
|
int auth = 1;
|
|
char pub_key[128];
|
|
char *privkey = NULL;
|
|
char *pubkey = NULL;
|
|
if (key) {
|
|
// Check the authentication keys
|
|
struct stat sb_key;
|
|
if ((stat(key, &sb_key) != 0) || (sb_key.st_size = 0)) {
|
|
sprintf(msg, "* Error opening private key file");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
return -5;
|
|
}
|
|
sprintf(pub_key, key);
|
|
strcat(pub_key, ".pub");
|
|
if ((stat(pub_key, &sb_key) != 0) || (sb_key.st_size = 0)) {
|
|
sprintf(msg, "* Error opening public key file");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
return -5;
|
|
}
|
|
privkey = key;
|
|
pubkey = pub_key;
|
|
auth = 0;
|
|
}
|
|
|
|
if ((fname) && ((type == 0) || (type == 1))){
|
|
if (strcmp(fname, "simulate") != 0) {
|
|
if (type == 1) {
|
|
// Uploading the file
|
|
struct stat sb_local;
|
|
if ((stat(fname, &sb_local) == 0) && (sb_local.st_size > 0)) {
|
|
fsize = sb_local.st_size;
|
|
fdd = fopen(fname, "rb");
|
|
}
|
|
}
|
|
else {
|
|
// Downloading to file (LIST or Get file)
|
|
fdd = fopen(fname, "wb");
|
|
}
|
|
if (fdd == NULL) {
|
|
sprintf(msg, "* Error opening file");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
return -5;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ** Initialize libssh2
|
|
int rc = libssh2_init (0);
|
|
if (rc != 0) {
|
|
sprintf(msg, "* libssh2 initialization failed (%d)", rc);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
if (fdd) fclose(fdd);
|
|
return -1;
|
|
}
|
|
|
|
int sock = sock_connect(server, port, hdr, hdrlen);
|
|
if (sock < 0) {
|
|
if (fdd) fclose(fdd);
|
|
libssh2_exit();
|
|
return -2;
|
|
}
|
|
vTaskDelay(50 / portTICK_RATE_MS);
|
|
|
|
// ** Create session
|
|
session = getSSHSession(sock, user, pass, privkey, pubkey, auth, hdr, hdrlen);
|
|
if (session == NULL) {
|
|
if (fdd) fclose(fdd);
|
|
close(sock);
|
|
libssh2_exit();
|
|
return -3;
|
|
}
|
|
vTaskDelay(100 / portTICK_RATE_MS);
|
|
|
|
LIBSSH2_CHANNEL *channel = NULL;
|
|
// ** Open session
|
|
if (type == 1) {
|
|
// === SCP File upload ===
|
|
channel = libssh2_scp_send(session, scppath, 0555, (unsigned long)fsize);
|
|
if (!channel) {
|
|
char *errmsg;
|
|
int errlen;
|
|
int err = libssh2_session_last_error(session, &errmsg, &errlen, 0);
|
|
sprintf(msg, "* Unable to open a session: (%d) %s", err, errmsg);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
rc = -4;
|
|
goto shutdown;
|
|
}
|
|
// ** Upload a file via SCP
|
|
rc = sshUpload(channel, fdd, hdr, hdrlen);
|
|
if (fdd != NULL) {
|
|
if (rc == 0) sprintf(body, "Uploaded file %s", fname);
|
|
else sprintf(body, "Error uploading file %s", fname);
|
|
}
|
|
}
|
|
else if (type == 0) {
|
|
// === SCP File download ===
|
|
channel = libssh2_scp_recv2(session, scppath, &fileinfo);
|
|
if (!channel) {
|
|
sprintf(msg, "* Unable to open a session: %d", libssh2_session_last_errno(session));
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
rc = -4;
|
|
goto shutdown;
|
|
}
|
|
|
|
if (fileinfo.st_size > ssh2_maxbytes) {
|
|
sprintf(msg, "* Warning: file size (%u) > max allowed (%u) !", (uint32_t)fileinfo.st_size, ssh2_maxbytes);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
}
|
|
// ** Download a file via SCP
|
|
rc = sshDownload(channel, &fileinfo, fdd, hdr, body, hdrlen, bodylen);
|
|
if (fdd != NULL) {
|
|
if (rc == 0) sprintf(body, "Downloaded file %s", fname);
|
|
else sprintf(body, "Error downloading file %s", fname);
|
|
}
|
|
}
|
|
else if (type == 5) {
|
|
// === SSH exec, Execute non-blocking on the remove host ===
|
|
int bytecount = 0;
|
|
char *exitsignal=(char *)"none";
|
|
|
|
while (((channel = libssh2_channel_open_session(session)) == NULL) && (libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN)) {
|
|
waitsocket(sock, session);
|
|
}
|
|
if (channel == NULL) {
|
|
sprintf(msg, "* Channel Error");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
rc = -1;
|
|
goto endchannel;
|
|
}
|
|
sprintf(msg, "* Exec: '%s'", scppath);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_INFO);
|
|
|
|
while( (rc = libssh2_channel_exec(channel, scppath)) == LIBSSH2_ERROR_EAGAIN ) {
|
|
vTaskDelay(2 / portTICK_RATE_MS);
|
|
waitsocket(sock, session);
|
|
}
|
|
if ( rc != 0 ) {
|
|
sprintf(msg, "* Channel Exec Error %d", rc);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
goto endchannel;
|
|
}
|
|
char buffer[1024];
|
|
int body_pos = 0;
|
|
for( ;; ) {
|
|
// loop until we block
|
|
int rc;
|
|
do {
|
|
rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
|
|
if (rc > 0) {
|
|
if ((body_pos+rc) < bodylen) {
|
|
memcpy(body+body_pos, buffer, rc);
|
|
body_pos += rc;
|
|
}
|
|
bytecount += rc;
|
|
}
|
|
}
|
|
while( rc > 0 );
|
|
|
|
// this is due to blocking that would occur otherwise so we loop on this condition
|
|
if ( rc == LIBSSH2_ERROR_EAGAIN ) waitsocket(sock, session);
|
|
else break;
|
|
}
|
|
int exitcode = 127;
|
|
while ( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN ) waitsocket(sock, session);
|
|
|
|
if ( rc == 0 ) {
|
|
exitcode = libssh2_channel_get_exit_status( channel );
|
|
libssh2_channel_get_exit_signal(channel, &exitsignal, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
if (exitsignal) {
|
|
sprintf(msg, "* Got signal '%s'", exitsignal);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_INFO);
|
|
rc = -1;
|
|
}
|
|
else {
|
|
sprintf(msg, "* Exit: %d bytecount: %d", exitcode, bytecount);
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_INFO);
|
|
rc = exitcode;
|
|
}
|
|
}
|
|
else if (type == 4) {
|
|
// === SFTP mkdir ===
|
|
LIBSSH2_SFTP *sftp_session;
|
|
|
|
sftp_session = libssh2_sftp_init(session);
|
|
if (!sftp_session) {
|
|
sprintf(msg, "Unable to init SFTP session");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
goto shutdown;
|
|
}
|
|
vTaskDelay(20 / portTICK_RATE_MS);
|
|
// Since we have not set non-blocking, tell libssh2 we are blocking
|
|
libssh2_session_set_blocking(session, 1);
|
|
// Make a directory via SFTP
|
|
rc = libssh2_sftp_mkdir(sftp_session, scppath,
|
|
LIBSSH2_SFTP_S_IRWXU|
|
|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IXGRP|
|
|
LIBSSH2_SFTP_S_IROTH|LIBSSH2_SFTP_S_IXOTH);
|
|
|
|
if (rc) {
|
|
sprintf(msg, "SFTP mkdir failed\n");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
}
|
|
libssh2_sftp_shutdown(sftp_session);
|
|
}
|
|
else if ((type == 2) || (type == 3)) {
|
|
// === SFTP List ===
|
|
LIBSSH2_SFTP *sftp_session;
|
|
LIBSSH2_SFTP_HANDLE *sftp_handle;
|
|
|
|
sftp_session = libssh2_sftp_init(session);
|
|
vTaskDelay(20 / portTICK_RATE_MS);
|
|
if (!sftp_session) {
|
|
sprintf(msg, "Unable to init SFTP session\n");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
goto shutdown;
|
|
}
|
|
// Since we have not set non-blocking, tell libssh2 we are blocking
|
|
libssh2_session_set_blocking(session, 1);
|
|
// Request a dir listing via SFTP
|
|
sftp_handle = libssh2_sftp_opendir(sftp_session, scppath);
|
|
if (!sftp_handle) {
|
|
sprintf(msg, "Unable to open dir with SFTP\n");
|
|
_append_msg(hdr, msg, hdrlen, ESP_LOG_ERROR);
|
|
goto shutdown;
|
|
}
|
|
int body_pos = 0;
|
|
do {
|
|
char mem[128];
|
|
char longentry[256];
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
attrs.flags = LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_ACMODTIME;
|
|
|
|
/* loop until we fail */
|
|
rc = libssh2_sftp_readdir_ex(sftp_handle, mem, sizeof(mem), longentry, sizeof(longentry), &attrs);
|
|
if (rc > 0) {
|
|
// rc is the length of the file name in the mem buffer
|
|
|
|
if ((longentry[0] != '\0') && (type == 3)) {
|
|
if ((body_pos+strlen(longentry)) < bodylen) {
|
|
sprintf(body+body_pos, "%s\n", longentry);
|
|
body_pos += strlen(longentry) + 1;
|
|
}
|
|
} else {
|
|
if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
|
|
uint32_t prm = attrs.permissions >> 12;
|
|
if (prm == 4) sprintf(msg, "D\t");
|
|
else if (prm == 8) sprintf(msg, "F\t");
|
|
else if (prm == 10) sprintf(msg, "L\t");
|
|
else sprintf(msg, "?\t");
|
|
}
|
|
else sprintf(msg, "?\t");
|
|
|
|
if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) sprintf(msg+strlen(msg), "%llu\t" , (uint64_t)attrs.filesize);
|
|
else (sprintf(msg, "0\t"));
|
|
|
|
if ((body_pos+strlen(msg)+strlen(mem)+1) < bodylen) {
|
|
sprintf(body+body_pos, "%s%s\n", msg, mem);
|
|
body_pos += strlen(msg) + strlen(mem) + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
libssh2_sftp_closedir(sftp_handle);
|
|
libssh2_sftp_shutdown(sftp_session);
|
|
}
|
|
|
|
endchannel:
|
|
if (channel) {
|
|
libssh2_channel_free(channel);
|
|
channel = NULL;
|
|
}
|
|
|
|
shutdown:
|
|
if (fdd) fclose(fdd);
|
|
libssh2_session_disconnect(session, "Normal Shutdown.");
|
|
libssh2_session_free(session);
|
|
close(sock);
|
|
libssh2_exit();
|
|
if (ssh2_verbose) {
|
|
ESP_LOGI(SSH_TAG, "All done");
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif // CONFIG_MICROPY_USE_SSH
|
|
|
|
#endif // defined(CONFIG_MICROPY_USE_CURL) || defined(CONFIG_MICROPY_USE_SSH)
|