aboutsummaryrefslogtreecommitdiff
path: root/software/michi_funcs/SimplePgSQL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'software/michi_funcs/SimplePgSQL.cpp')
-rw-r--r--software/michi_funcs/SimplePgSQL.cpp1278
1 files changed, 1278 insertions, 0 deletions
diff --git a/software/michi_funcs/SimplePgSQL.cpp b/software/michi_funcs/SimplePgSQL.cpp
new file mode 100644
index 0000000..0d1612a
--- /dev/null
+++ b/software/michi_funcs/SimplePgSQL.cpp
@@ -0,0 +1,1278 @@
+/*
+ * SimplePgSQL.c - Lightweight PostgreSQL connector for Arduino
+ * Copyright (C) Bohdan R. Rau 2016 <ethanak@polip.com>
+ *
+ * SimplePgSQL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SimplePgSQL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SimplePgSQL. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+ // 238 469 / 34004
+
+ //version 2022-02-17
+
+#include "SimplePgSQL.h"
+
+
+
+#ifdef ESP32
+#define strchr_P strchr
+#endif
+
+#ifdef PG_USE_MD5
+static void
+bytesToHex(const uint8_t b[16], char *s)
+{
+ int q, w;
+#ifndef ESP32
+ static PROGMEM const char hex[] = "0123456789abcdef";
+ for (q = 0, w = 0; q < 16; q++)
+ {
+ s[w++] = pgm_read_byte(&hex[(b[q] >> 4) & 0x0F]);
+ s[w++] = pgm_read_byte(&hex[b[q] & 0x0F]);
+ }
+#else
+ static const char hex[] = "0123456789abcdef";
+ for (q = 0, w = 0; q < 16; q++)
+ {
+ s[w++] = hex[(b[q] >> 4) & 0x0F];
+ s[w++] = hex[b[q] & 0x0F];
+ }
+#endif
+ s[w] = '\0';
+}
+
+#ifdef ESP32_IDF
+#include "esp_log.h"
+#endif
+
+#ifdef ESP32
+#include <mbedtls/md5.h>
+
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ md5_context_t context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+ mbedtls_md5_init(&context);
+ mbedtls_md5_update_ret(&context, (uint8_t *)password, strlen(password));
+ mbedtls_md5_update_ret(&context, (uint8_t *)salt, salt_len);
+ mbedtls_md5_finish_ret( &context, sum);
+ bytesToHex(sum, outbuf);
+}
+#elif defined(ESP8266)
+#include <md5.h>
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ md5_context_t context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+ MD5Init(&context);
+ MD5Update(&context, (uint8_t *)password, strlen(password));
+ MD5Update(&context, (uint8_t *)salt, salt_len);
+ MD5Final(sum, &context);
+ bytesToHex(sum, outbuf);
+}
+#else
+//#include <MD5.h>
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ MD5_CTX context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+
+ MD5::MD5Init(&context);
+ MD5::MD5Update(&context, (uint8_t *)password, strlen(password));
+ MD5::MD5Update(&context, (uint8_t *)salt, salt_len);
+ MD5::MD5Final(sum, &context);
+ bytesToHex(sum, outbuf);
+}
+#endif
+#endif
+
+#define MD5_PASSWD_LEN 35
+#define AUTH_REQ_OK 0 /* User is authenticated */
+#define AUTH_REQ_PASSWORD 3 /* Password */
+#define AUTH_REQ_MD5 5 /* md5 password */
+
+#ifndef ESP32_IDF
+static PROGMEM const char EM_OOM [] = "Out of memory";
+static PROGMEM const char EM_READ [] = "Backend read error";
+static PROGMEM const char EM_WRITE [] = "Backend write error";
+static PROGMEM const char EM_CONN [] = "Cannot connect to server";
+static PROGMEM const char EM_SYNC [] = "Backend out of sync";
+static PROGMEM const char EM_INTR [] = "Internal error";
+static PROGMEM const char EM_UAUTH [] = "Unsupported auth method";
+static PROGMEM const char EM_BIN [] = "Binary format not supported";
+static PROGMEM const char EM_EXEC [] = "Previous execution not finished";
+static PROGMEM const char EM_PASSWD [] = "Password required";
+static PROGMEM const char EM_EMPTY [] = "Query is empty";
+static PROGMEM const char EM_FORMAT [] = "Illegal formatting character";
+#else
+static const char EM_OOM [] = "Out of memory";
+static const char EM_READ [] = "Backend read error";
+static const char EM_WRITE [] = "Backend write error";
+static const char EM_CONN [] = "Cannot connect to server";
+static const char EM_SYNC [] = "Backend out of sync";
+static const char EM_INTR [] = "Internal error";
+static const char EM_UAUTH [] = "Unsupported auth method";
+static const char EM_BIN [] = "Binary format not supported";
+static const char EM_EXEC [] = "Previous execution not finished";
+static const char EM_PASSWD [] = "Password required";
+static const char EM_EMPTY [] = "Query is empty";
+static const char EM_FORMAT [] = "Illegal formatting character";
+
+#define PGTAG "PGSQL"
+#endif
+
+#ifndef ESP32_IDF
+PGconnection::PGconnection(Client *c,
+ int flags,
+ int memory,
+ char *foreignBuffer)
+{
+ conn_status = CONNECTION_NEEDED;
+ client = c;
+ Buffer = foreignBuffer;
+ _passwd = NULL;
+ _flags = flags & ~PG_FLAG_STATIC_BUFFER;
+
+ if (memory <= 0) bufSize = PG_BUFFER_SIZE;
+ else bufSize = memory;
+ if (foreignBuffer) {
+ _flags |= PG_FLAG_STATIC_BUFFER;
+ }
+}
+#else
+PGconnection::PGconnection(const int flags, const unsigned char *_Buffer, const int _bufSize) {
+ conn_status = CONNECTION_NEEDED;
+ _passwd = NULL;
+ _user = NULL;
+ Buffer = (char *) _Buffer;
+ bufSize = _bufSize;
+ bufPos = 0;
+ result_status=0;
+ _available=0;
+ _nfields=0;
+ _ntuples=0;
+ _flags=0;
+
+ conn_status = CONNECTION_NEEDED;
+ _flags = flags & ~PG_FLAG_STATIC_BUFFER;
+ bufSize = PG_BUFFER_SIZE;
+}
+#endif
+
+#ifndef ESP32_IDF
+int PGconnection::setDbLogin(IPAddress server,
+ const char *user,
+ const char *passwd,
+ const char *db,
+ const char *charset,
+ int port)
+{
+
+ char *startpacket;
+ int packetlen;
+ int len;
+
+ close();
+ if (!db) db = user;
+ len = strlen(user) + 1;
+ if (passwd) {
+ len += strlen(passwd) + 1;
+ }
+ _user = (char *)malloc(len);
+ strcpy(_user, user);
+ if (passwd) {
+ _passwd = _user + strlen(user) + 1;
+ strcpy(_passwd, passwd);
+ }
+ else {
+ _passwd = NULL;
+ }
+ if (!Buffer) Buffer = (char *) malloc(bufSize);
+ byte connected = client -> connect(server, port);
+ if (!connected) {
+ setMsg_P(EM_CONN, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ packetlen = build_startup_packet(NULL, db, charset);
+ if (packetlen > bufSize - 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return conn_status;
+ }
+ startpacket=Buffer + (bufSize - (packetlen + 1));
+ build_startup_packet(startpacket, db, charset);
+ if (pqPacketSend(0, startpacket, packetlen) < 0) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ attempts = 0;
+ return conn_status = CONNECTION_AWAITING_RESPONSE;
+}
+#else
+int PGconnection::setDbLogin(const char *ServerIP, int ServerPort, const char *dbName, const char *dbUser, const char *dbPasswd, const char *dbCharset) {
+
+ char *startpacket;
+ int packetlen;
+ int len;
+
+// close();
+ memset(&DestAddr, 0, sizeof(DestAddr));
+ AddrFamily = AF_INET;
+ ipProtocol = IPPROTO_IP;
+ DestAddr.sin_addr.s_addr = inet_addr(ServerIP);
+ DestAddr.sin_family = AF_INET;
+ DestAddr.sin_port = htons(ServerPort);
+
+ if (!dbName)
+ dbName = dbUser;
+ len = strlen(dbUser) + 1;
+ if (dbPasswd) {
+ len += strlen(dbPasswd) + 1;
+ }
+ _user = (char *) malloc(len);
+ strcpy(_user, dbUser);
+ if (dbPasswd) {
+ _passwd = _user + strlen(dbUser) + 1;
+ strcpy(_passwd, dbPasswd);
+ } else {
+ _passwd = NULL;
+ }
+
+ //int8_t connected = connect(client->connect(server, port);
+ SockH = socket(AddrFamily, SOCK_STREAM, ipProtocol);
+ if (SockH < 0) {
+ ESP_LOGE(PGTAG, "Unable to create socket: errno %d", errno);
+ setMsg_P(EM_CONN, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ } else {
+ int err = connect(SockH, (struct sockaddr *) &DestAddr, sizeof(DestAddr));
+ if (err != 0) {
+ ESP_LOGE(PGTAG, "Socket unable to connect: errno %d", errno);
+ return conn_status = CONNECTION_BAD;
+ }
+ NetConnected = 1;
+ ESP_LOGI(PGTAG, "Successfully connected");
+ }
+
+ packetlen = build_startup_packet(NULL, dbName, dbCharset);
+ if (packetlen > bufSize - 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return conn_status;
+ }
+
+ startpacket = Buffer + (bufSize - (packetlen + 1));
+ build_startup_packet(startpacket, dbName, dbCharset);
+ if (pqPacketSend(0, startpacket, packetlen) < 0) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ attempts = 0;
+ return conn_status = CONNECTION_AWAITING_RESPONSE;
+}
+
+void close_socket(int _SockH)
+{
+ close(_SockH);
+}
+#endif
+
+void PGconnection::close(void)
+{
+#ifndef ESP32_IDF
+ if (client->connected()) {
+#else
+ if (NetConnected) {
+#endif
+ pqPacketSend('X', NULL, 0);
+#ifndef ESP32_IDF
+ client->stop();
+#else
+ shutdown(SockH, 0);
+ close_socket(SockH);
+#endif
+ }
+ if (Buffer && !(_flags & PG_FLAG_STATIC_BUFFER)) {
+ free(Buffer);
+ Buffer = NULL;
+ }
+ if (_user) {
+ free(_user);
+ _user = _passwd = NULL;
+ }
+ conn_status = CONNECTION_NEEDED;
+
+#ifndef ESP32_IDF
+ NetConnected = 0;
+#endif
+}
+
+#ifdef ESP32_IDF
+int PGconnection::dataAvailable() {
+ int res=0;
+// if (_available) return _available;
+ ioctl(SockH,FIONREAD,&res);
+ return res;
+}
+#endif
+
+int PGconnection::status(void)
+{
+ char bereq;
+ char rc;
+ int32_t msgLen;
+ int32_t areq;
+ char * pwd = _passwd;
+#ifdef PG_USE_MD5
+ char salt[4];
+#endif
+
+ switch(conn_status) {
+ case CONNECTION_NEEDED:
+ case CONNECTION_OK:
+ case CONNECTION_BAD:
+
+ return conn_status;
+
+ case CONNECTION_AWAITING_RESPONSE:
+#ifndef ESP32_IDF
+ if (!client->available()) return conn_status;
+#else
+ if (dataAvailable() == 0) return conn_status;
+#endif
+ if (attempts++ >= 2) {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetc(&bereq)) {
+ goto read_error;
+ }
+ if (bereq == 'E') {
+ pqGetInt4(&msgLen);
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (bereq != 'R') {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetInt4(&msgLen)) {
+ goto read_error;
+ }
+ if (pqGetInt4(&areq)) {
+ goto read_error;
+ }
+ if (areq == AUTH_REQ_OK) {
+ if (_user) {
+ free(_user);
+ _user = _passwd=NULL;
+ }
+ result_status = PG_RSTAT_READY;
+ return conn_status = CONNECTION_AUTH_OK;
+ }
+ if (
+#ifdef PG_USE_MD5
+ areq != AUTH_REQ_MD5 &&
+#endif
+ areq != AUTH_REQ_PASSWORD) {
+ setMsg_P(EM_UAUTH, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (!_passwd || !*_passwd) {
+ setMsg_P(EM_PASSWD, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ pwd = _passwd;
+#ifdef PG_USE_MD5
+ if (areq == AUTH_REQ_MD5) {
+ if (pqGetnchar(salt, 4)) goto read_error;
+ if (bufSize < 3 * MD5_PASSWD_LEN + 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ char *crypt_pwd = Buffer + (bufSize - (2 * (MD5_PASSWD_LEN + 1)));
+ char *crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
+ pg_md5_encrypt(pwd, _user, strlen(_user), crypt_pwd2);
+ pg_md5_encrypt(crypt_pwd2 + 3, salt,4, crypt_pwd);
+ pwd = crypt_pwd;
+ }
+#endif
+ rc=pqPacketSend('p', pwd, strlen(pwd) + 1);
+ if (rc) {
+ goto write_error;
+ }
+ return conn_status;
+
+ case CONNECTION_AUTH_OK:
+ for (;;) {
+#ifndef ESP32_IDF
+ if (!client -> available()) return conn_status;
+#else
+ if (dataAvailable() == 0) return conn_status;
+#endif
+ if (pqGetc(&bereq)) goto read_error;
+ if (pqGetInt4(&msgLen)) goto read_error;
+ msgLen -= 4;
+ if (bereq == 'A' || bereq == 'N' || bereq == 'S' || bereq == 'K') {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ continue;
+ }
+ if (bereq == 'E') {
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+
+/* if (bereq == 'K') {
+ if (pqGetInt4(&be_pid)) goto read_error;
+ if (pqGetInt4(&be_key)) goto read_error;
+ continue;
+ }
+*/
+ if (bereq == 'Z') {
+ pqSkipnchar(msgLen);
+ return conn_status = CONNECTION_OK;
+ }
+ return conn_status = CONNECTION_BAD;
+ }
+
+ default:
+ setMsg_P(EM_INTR, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+read_error:
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+write_error:
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+}
+
+int PGconnection::execute(const char *query, int progmem)
+{
+#ifndef ESP32
+// for unknown reason, this status check get wrong on ESP32-IDF.
+// uncommenting solve the problem for now, but need invesitgation
+ if (!(result_status & PG_RSTAT_READY)) {
+ setMsg_P(EM_EXEC, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+#endif
+ int len =
+#ifndef ESP32
+ progmem ? strlen_P(query) :
+#endif
+ strlen(query);
+
+#ifndef ESP32_IDF
+ if (pqPacketSend('Q', query, len+1, progmem)) {
+#else
+ if (pqPacketSend('Q', query, len + 1)) {
+#endif
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+
+}
+/*
+int PGconnection::PGexecute(const char *query) {
+ int len = strlen(query);
+ if (pqPacketSend('Q', query, len + 1)) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+*/
+
+
+int PGconnection::escapeName(const char *inbuf, char *outbuf)
+{
+ const char *c;
+ int l = 2;
+ for (c=inbuf; *c; c++) {
+ l++;
+ if (*c == '\\' || *c == '"') l++;
+ }
+ if (outbuf) {
+ *outbuf++='"';
+ for (c=inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '"') *outbuf++ = *c;
+ }
+ *outbuf++='"';
+ }
+ return l;
+}
+
+int PGconnection::escapeString(const char *inbuf, char *outbuf)
+{
+ const char *c;
+ int e = 0, l;
+ for (c=inbuf; *c; c++) {
+ if (*c == '\\' || *c == '\'') e++;
+ }
+ l = e + (c - inbuf) + (e ? 4 : 2);
+ if (outbuf) {
+ if (e) {
+ *outbuf++=' ';
+ *outbuf++='E';
+ }
+ *outbuf++='\'';
+ for (c=inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '\'') *outbuf++ = *c;
+ }
+ *outbuf++='\'';
+ }
+ return l;
+}
+
+char * PGconnection::getValue(int nr)
+{
+ int i;
+ if (_null & (1<<nr)) return NULL;
+ char *c=Buffer;
+ if (nr < 0 || nr >= _nfields) return NULL;
+ for (i=0; i < nr; i++) {
+ if (_null & (1 <<i)) continue;
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::getColumn(int n)
+{
+ char *c;int i;
+ if (!(result_status & PG_RSTAT_HAVE_COLUMNS)) return NULL;
+ if (n < 0 || n >= _nfields) return NULL;
+ for (c = Buffer, i = 0; i<n; i++) {
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::getMessage(void)
+{
+ if (!(result_status & PG_RSTAT_HAVE_MESSAGE)) return NULL;
+ return Buffer;
+}
+
+int PGconnection::getData(void)
+{
+ char id;
+ int32_t msgLen;
+ int rc;
+ char *c;
+#ifndef ESP32_IDF
+ if (!client -> available()) return 0;
+#else
+ if (dataAvailable() == 0) return 0;
+#endif
+
+
+ if (pqGetc(&id)) goto read_error;
+ if (pqGetInt4(&msgLen)) goto read_error;
+ //Serial.printf("ID=%c\n", id);
+ msgLen -= 4;
+ switch(id) {
+ case 'T':
+ if ((rc=pqGetRowDescriptions())) {
+ if (rc == -2) setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3) setMsg_P(EM_BIN, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_COLUMNS;
+
+ case 'E':
+ if (pqGetNotice(PG_RSTAT_HAVE_ERROR)) goto read_error;
+ return result_status;
+
+ case 'N':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+ }
+ if(pqGetNotice(PG_RSTAT_HAVE_NOTICE)) goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'A':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+ }
+ if (pqGetNotify(msgLen)) goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'Z':
+ if (pqSkipnchar(msgLen)) goto read_error;
+ result_status = (result_status & PG_RSTAT_HAVE_SUMMARY) | PG_RSTAT_READY;
+ return PG_RSTAT_READY;
+
+ case 'S': // parameters setting ignored
+ case 'K': // should not be here?
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+
+ case 'C': // summary
+ if (msgLen > bufSize - 1) goto oom;
+ if (pqGetnchar(Buffer, msgLen)) goto read_error;
+ Buffer[msgLen] = 0;
+ _ntuples = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_SUMMARY;
+ for (c = Buffer; *c && !isdigit(*c); c++);
+ if (!*c) return result_status;
+ if (strncmp(Buffer,"SELECT ",7)) {
+ for (; *c && isdigit(*c); c++);
+ for (; *c && !isdigit(*c); c++);
+ }
+ if (*c) _ntuples = strtol(c, NULL, 10);
+ return result_status;
+
+ case 'D':
+ if ((rc=pqGetRow())) {
+ if (rc == -2) setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3) setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_ROW;
+
+ case 'I':
+ if (pqSkipnchar(msgLen)) goto read_error;
+ setMsg_P(EM_EMPTY, PG_RSTAT_HAVE_ERROR);
+ return result_status;
+
+ default:
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+
+oom:
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+
+read_error:
+ if (!(result_status & PG_RSTAT_HAVE_ERROR)) {
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ }
+ conn_status = CONNECTION_BAD;
+ return -1;
+}
+
+int PGconnection::executeFormat(int progmem, const char *format, ...)
+{
+ int32_t msgLen;
+ va_list va;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(0, progmem, format, va);
+ va_end(va);
+ if (msgLen < 0) return -1;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(msgLen, progmem, format, va);
+ va_end(va);
+ if (msgLen) {
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+
+#ifdef ESP8266
+
+// there is no strchr_P in ESP8266 ROM :(
+
+static const char *strchr_P(const char *str, char c)
+{
+ char z;
+ for (;;) {
+ z = pgm_read_byte(str);
+ if (!z) return NULL;
+ if (z == c) return str;
+ str++;
+ }
+}
+
+#endif
+
+#ifdef ESP32_IDF
+//emulate Arduino PSTR()
+char *PSTR(char *str)
+{
+ return str;
+}
+const char *PSTR(const char *str)
+{
+ return str;
+}
+
+//emulate strcpy_P()
+char *strcpy_P(char * destination, const char * source )
+{
+ return strcpy(destination, source);
+}
+
+int strlen_P(char *str)
+{
+ return strlen(str);
+}
+int strlen_P(const char *str)
+{
+ return strlen(str);
+}
+#endif
+
+int PGconnection::build_startup_packet(
+ char *packet,
+ const char *db,
+ const char *charset)
+{
+ int packet_len = 4;
+ if (packet) {
+ memcpy(packet,"\0\003\0\0", 4);
+ }
+#define ADD_STARTUP_OPTION(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optname); \
+ packet_len += strlen_P((char *)optname) + 1; \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optval); \
+ packet_len += strlen((char *)optval) + 1; \
+ } while(0)
+
+#define ADD_STARTUP_OPTION_P(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optname); \
+ packet_len += strlen_P((char *)optname) + 1; \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optval); \
+ packet_len += strlen_P((char *)optval) + 1; \
+ } while(0)
+
+ if (_user && _user[0])
+ ADD_STARTUP_OPTION(PSTR("user"), _user);
+ if (db && db[0])
+ ADD_STARTUP_OPTION(PSTR("database"), db);
+ if (charset && charset[0])
+ ADD_STARTUP_OPTION(PSTR("client_encoding"), charset);
+ ADD_STARTUP_OPTION_P(PSTR("application_name"), PSTR("arduino"));
+#undef ADD_STARTUP_OPTION
+ if (packet)
+ packet[packet_len] = '\0';
+ packet_len++;
+
+ return packet_len;
+}
+
+int PGconnection::pqPacketSend(char pack_type, const char *buf, int buf_len, int progmem)
+{
+ char *start = Buffer;
+ int l = bufSize - 4;
+#ifndef ESP32
+ int n;
+#endif
+ if (pack_type) {
+ *start++ = pack_type;
+ l--;
+ }
+#ifdef __AVR__
+ *start++=0;
+ *start++=0;
+#else
+ *start++ = ((buf_len + 4) >> 24) & 0xff;
+ *start++ = ((buf_len + 4) >> 16) & 0xff;
+#endif
+ *start++ = ((buf_len + 4) >> 8) & 0xff;
+ *start++ = (buf_len + 4) & 0xff;
+#ifndef ESP32
+ if (progmem) {
+ while (buf_len > 0) {
+ while (buf_len > 0 && l > 0) {
+ *start++ = pgm_read_byte(buf++);
+ buf_len--;
+ }
+ n = client->write((const uint8_t *)Buffer, start - Buffer);
+ if (n != start - Buffer) return -1;
+ start = Buffer;
+ l = bufSize;
+ }
+ }
+ else {
+#endif
+ if (buf) {
+ if (buf_len <= l) {
+ memcpy(start, buf, buf_len);
+ start += buf_len;
+ buf_len = 0;
+ }
+ else {
+ memcpy(start, buf, l);
+ start += l;
+ buf_len -= l;
+ buf += l;
+ }
+ }
+#ifndef ESP32_IDF
+ n = client->write((const uint8_t *)Buffer, start - Buffer);
+ if (n != start - Buffer) return -1;
+ if (buf && buf_len) {
+ n = client->write((const uint8_t *)buf, buf_len);
+ if (n != buf_len) return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, start - /*_buffer*/Buffer, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "pqPacketSend: Send Error occurred during sending: errno %d", errno);
+ return err;
+ }
+ if (buf && buf_len) {
+ err = send(SockH, (const char *) buf, (size_t) buf_len, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "Send2 Error occurred during sending: errno %d", errno);
+ return err;
+
+ }
+ }
+#endif
+
+#ifndef ESP32
+ }
+#endif
+ return 0;
+}
+
+int PGconnection::pqGetc(char *buf)
+{
+ int i;
+#ifndef ESP32_IDF
+ for (i=0; !client->available() && i < 10; i++) {
+ delay (i * 10 + 10);
+ }
+ if (!client->available()) {
+ return -1;
+ }
+ *buf = client->read();
+ return 0;
+#else
+ i = 0;
+ while (i<10) {
+ if (dataAvailable()>0) break;
+ else {
+ vTaskDelay(i++);
+ }
+ }
+ if (i==10) return -1;
+
+ int len = read(SockH, (void *) buf, 1);
+ _available-=len;
+ return -1+len;
+#endif
+}
+
+int PGconnection::pqGetInt4(int32_t *result)
+{
+ uint32_t tmp4 = 0;
+ byte tmp,i;
+ for (i = 0; i < 4; i++) {
+ if (pqGetc((char *)&tmp)) return -1;
+ tmp4 = (tmp4 << 8) | tmp;
+ }
+ *result = tmp4;
+ return 0;
+}
+
+int PGconnection::pqGetInt2(int16_t *result)
+{
+ uint16_t tmp2 = 0;
+ byte tmp,i;
+ for (i = 0; i < 2; i++) {
+ if (pqGetc((char *)&tmp)) return -1;
+ tmp2 = (tmp2 << 8) | tmp;
+ }
+ *result = tmp2;
+ return 0;
+}
+
+int PGconnection::pqGetnchar(char *s, int len)
+{
+ while (len-- > 0) {
+ if (pqGetc(s++)) return -1;
+ }
+ return 0;
+}
+
+int PGconnection::pqGets(char *s, int maxlen)
+{
+ int len;
+ char z;
+ for (len = 0;len < maxlen; len++) {
+ if (pqGetc(&z)) return -1;
+ if (s) *s++ = z;
+ if (!z) return len+1;
+ }
+ return - (len + 1);
+}
+
+int PGconnection::pqSkipnchar(int len)
+{
+ char dummy;
+ while (len-- > 0) {
+ if (pqGetc(&dummy)) return -1;
+ }
+ return 0;
+}
+
+int PGconnection::pqGetRow(void)
+{
+ int i;
+ int bufpos = 0;
+ int32_t len;
+ int16_t cols;
+
+ _null = 0;
+ if (pqGetInt2(&cols)) return -1;
+ if (cols != _nfields) {
+ return -3;
+ }
+ for (i=0; i < _nfields; i++) {
+ if (pqGetInt4(&len)) return -1;
+ if (len < 0) {
+ _null |= 1<<i;
+ continue;
+ }
+ if (bufpos + len + 1 > bufSize) {
+ return -2;
+ }
+ if (pqGetnchar(Buffer + bufpos, len)) return -1;
+ bufpos += len;
+ Buffer[bufpos++]=0;
+ }
+ return 0;
+}
+
+
+int PGconnection::pqGetRowDescriptions(void)
+{
+ int i;
+ int16_t format;
+ int rc;
+ int bufpos;
+ if (pqGetInt2(&_nfields)) return -1;
+ if (_nfields > PG_MAX_FIELDS) return -2; // implementation limit
+ _formats = 0;
+ bufpos = 0;
+ for (i = 0;i < _nfields; i++) {
+ if (!(_flags & PG_FLAG_IGNORE_COLUMNS)) {
+ if (bufpos >= bufSize - 1) return -2;
+ rc = pqGets(Buffer + bufpos, bufSize - bufpos);
+ if (rc < 0) return -1;
+ bufpos += rc;
+ }
+ else {
+ if (pqGets(NULL, 8192) < 0) {
+ return -1;
+ }
+ }
+ if (pqSkipnchar(16)) return -1;
+ if (pqGetInt2(&format)) return -1;
+ format = format ? 1 : 0;
+ _formats |= format << i;
+ }
+ if (_formats) return -3;
+ return 0;
+}
+
+void PGconnection::setMsg(const char *s, int type)
+{
+ strcpy(Buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+void PGconnection::setMsg_P(const char *s, int type)
+{
+ strcpy_P(Buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+int PGconnection::pqGetNotice(int type)
+{
+ int bufpos = 0;
+ char id;
+ int rc;
+ for (;;) {
+ if (pqGetc(&id)) goto read_error;
+ if (!id) break;
+ if (id == 'S' || id == 'M') {
+ if (bufpos && bufpos < bufSize - 1) Buffer[bufpos++]=':';
+ rc = pqGets(Buffer + bufpos, bufSize - bufpos);
+ if (rc < 0) goto read_error;
+ bufpos += rc -1;
+ }
+ else {
+ rc = pqGets(NULL, 8192);
+ if (rc < 0) goto read_error;
+ }
+ }
+ Buffer[bufpos] = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+ return 0;
+
+read_error:
+ if (!bufpos) setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return -1;
+}
+
+int PGconnection::pqGetNotify(int32_t msgLen)
+{
+ int32_t pid;
+ int bufpos, i;
+ if (pqGetInt4(&pid)) return -1;
+ msgLen -= 4;
+ bufpos = sprintf(Buffer,"%d:",pid);
+ if (msgLen > bufSize - (bufpos + 1)) {
+ if (pqGetnchar(Buffer+bufpos, bufSize - (bufpos + 1)))
+ return -1;
+ msgLen -= bufSize - (bufpos + 1);
+ if (pqSkipnchar(msgLen)) return -1;
+ Buffer[msgLen = bufSize - 1] = 0;
+
+ }
+ else {
+ if (pqGetnchar(Buffer+ bufpos, msgLen)) return -1;
+ Buffer[bufpos + msgLen] = 0;
+ msgLen += bufpos;
+ }
+ for (i=0; i<msgLen; i++) if (!Buffer[i]) Buffer[i] = ':';
+ return 0;
+}
+
+#ifndef ESP32
+int PGconnection::writeMsgPart_P(const char *s, int len, int fine)
+{
+ while (len > 0) {
+ if (bufPos >= bufSize) {
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+ bufPos = 0;
+ }
+ Buffer[bufPos++] = pgm_read_byte(s++);
+ len--;
+ }
+ if (bufPos && fine) {
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+ bufPos = 0;
+ }
+ return 0;
+}
+#endif
+
+int PGconnection::writeMsgPart(const char *s, int len, int fine)
+{
+ while (len > 0) {
+ int n = len;
+ if (n > bufSize - bufPos) n = bufSize - bufPos;
+ memcpy(Buffer + bufPos, s, n);
+ bufPos += n;
+ s += n;
+ len -= n;
+ if (bufPos >= bufSize) {
+#ifndef ESP32_IDF
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+#endif
+
+ bufPos = 0;
+ }
+ }
+ if (bufPos && fine) {
+#ifndef ESP32_IDF
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+#endif
+ bufPos = 0;
+ }
+
+ return 0;
+}
+
+int32_t PGconnection::writeFormattedQuery(int32_t length, int progmem, const char *format, va_list va)
+{
+ int32_t msgLen = 0;
+ const char *percent;
+ int blen, rc;
+ char buf[32], znak;
+#ifdef ESP32
+ (void) progmem;
+#endif
+ if (length) {
+ length += 4;
+ bufPos = 0;
+ Buffer[bufPos++] = 'Q';
+ Buffer[bufPos++] = (length >> 24) & 0xff;
+ Buffer[bufPos++] = (length >> 16) & 0xff;
+ Buffer[bufPos++] = (length >> 8) & 0xff;
+ Buffer[bufPos++] = (length) & 0xff;
+ }
+ for (;;) {
+#ifndef ESP32
+ if (progmem) {
+ percent = strchr_P(format, '%');
+ }
+ else {
+#endif
+ percent = strchr(format, '%');
+#ifndef ESP32
+ }
+#endif
+ if (!percent) break;
+#ifndef ESP32
+ if (progmem) {
+ znak = pgm_read_byte(percent+1);
+ }
+ else {
+#endif
+ znak = percent[1];
+#ifndef ESP32
+ }
+#endif
+ if (!length) {
+ msgLen += (percent - format);
+ }
+ else {
+#ifndef ESP32
+ if (progmem) {
+ rc = writeMsgPart_P(format, percent - format, false);
+ }
+ else {
+#endif
+ rc = writeMsgPart(format, percent - format, false);
+#ifndef ESP32
+ }
+#endif
+ if (rc) goto write_error;
+ }
+ format = percent + 2;
+ if (znak == 's' || znak == 'n') {
+ char *str = va_arg(va, char *);
+ blen = (znak == 's') ? escapeString(str, NULL) : escapeName(str, NULL);
+ if (!length) {
+ msgLen += blen;
+ }
+ else {
+ if (bufPos + blen > bufSize) {
+ rc = writeMsgPart(NULL, 0, true);
+ if (rc) goto write_error;
+ }
+ }
+ if (znak == 's') {
+ escapeString(str, Buffer + bufPos);
+ }
+ else {
+ escapeName(str, Buffer + bufPos);
+ }
+ bufPos += blen;
+ continue;
+ }
+ if (znak == 'l' || znak == 'd') {
+ if (znak == 'l') {
+ long n = va_arg(va, long);
+ blen = snprintf(buf, 32, "'%ld'", n);
+ }
+ else {
+ int n = va_arg(va, int);
+ blen = snprintf(buf, 32, "'%d'", n);
+ }
+ if (length) {
+ rc = writeMsgPart(buf, blen, false);
+ if (rc) goto write_error;
+ }
+ else {
+ msgLen += blen;
+ }
+ }
+ setMsg_P(EM_FORMAT, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+#ifndef ESP32
+ if (progmem) {
+ blen = strlen_P(format);
+ }
+ else {
+#endif
+ blen = strlen(format);
+#ifndef ESP32
+ }
+#endif
+ if (length) {
+#ifndef ESP32
+ if (progmem) {
+ rc = writeMsgPart_P(format, blen, false);
+ }
+ else {
+#endif
+ rc = writeMsgPart(format, blen, false);
+#ifndef ESP32
+ }
+#endif
+ if (!rc) {
+ rc = writeMsgPart("\0",1,true);
+ }
+ if (rc) goto write_error;
+ }
+ else {
+ msgLen += blen + 1;
+ }
+ return msgLen;
+write_error:
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+}