diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0187b3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +*~ diff --git a/0BUILD b/0BUILD new file mode 100644 index 0000000..17a8e9e --- /dev/null +++ b/0BUILD @@ -0,0 +1,260 @@ + + + + + + + + + $(project_name) + + + + $(project_vmajor).$(project_vminor).$(project_vpatchlevel) + + $(project_vmajor).$(project_vminor).$(project_vpatchlevel).$(project_vbuild)$(project_vtag) + + + + + + + + + + + + + + $(option_prefix) + $(option_prefix)/etc + $(option_prefix)/bin + $(option_prefix)/lib/cgi-bin + $(option_prefix)/lib + $(option_prefix)/include + $(option_prefix)/share + $(option_prefix)/share/locale + + $(libdir)/$(package) + $(includedir)/$(package) + $(datadir)/$(package) + + + + + + -ggdb -Wall -O0 + -ggdb -Wall -O0 + + + + + + + + + + + + + + + + + + + + + + + + $(pkglibdir)/plugins/$(project_so_effective) + + + + 1 + etc + share/locale + share/aqfinance" + + + + 0 + + + etc + share/locale + share/aqfinance + + + $(sysconfdir) + $(datadir)/locale + $(datadir)/aqfinance + + + + + + + + + + + + + + + + + + + + -fvisibility=hidden + + + + -fvisibility=hidden + + + + + + locale.h libintl.h iconv.h + fcntl.h stdlib.h string.h unistd.h + assert.h ctype.h errno.h fcntl.h stdio.h stdlib.h string.h strings.h locale.h + netinet/in.h signal.h + + + + + setlocale + memmove + memset + strcasecmp + strdup + strerror + snprintf + + + + + + + + + + + + + + + + + + + + + + + + + aqfinance.pot + + + + -C -c -ki18n -ktr2i18n -kI18N -kI18S -kI18N_NOOP -ktranslate -kaliasLocale -ktr -ktrUtf8 + --msgid-bugs-address=aqbanking-user@lists.aqbanking.de + -o $(OUTPUT[0]) $(INPUT[]) + + + + Extracting I18N strings into $(OUTPUT[0]) + + + + + + + + + + + + + + + + + + + --style=stroustrup + -s2 + --min-conditional-indent=0 + --indent-labels + --max-continuation-indent=80 + --pad-comma + --pad-header + --unpad-paren + --align-pointer=name + --break-closing-braces + --break-one-line-headers + --attach-return-type + --convert-tabs + --max-code-length=120 + --break-after-logical + --preserve-date + --suffix=none + $(INPUT[]) + + + + Formatting source files in-place. + + + + + + + AUTHORS + COPYING + README + + + + + src + + + + + + + diff --git a/src/0BUILD b/src/0BUILD new file mode 100644 index 0000000..1fd25c9 --- /dev/null +++ b/src/0BUILD @@ -0,0 +1,11 @@ + + + + + + + aqcgi + + + + diff --git a/src/aqcgi/0BUILD b/src/aqcgi/0BUILD new file mode 100644 index 0000000..8b78e27 --- /dev/null +++ b/src/aqcgi/0BUILD @@ -0,0 +1,103 @@ + + + + + + + + + + $(gmp_cflags) + $(gwenhywfar_cflags) + -I$(topsrcdir)/src + -I$(topbuilddir)/src + -I$(topbuilddir) + -I$(topsrcdir) + -I$(srcdir) + + + + --include=$(builddir) + --include=$(srcdir) + --include=$(builddir)/../types + --include=$(topsrcdir)/src/lib/typemaker2/c + --include=$(topbuilddir)/src/lib/typemaker2/c + + + + + $(visibility_cflags) + + + + --api=AQCGI_API + + + + + + + + + + + + + + + + + + + $(local/built_headers_pub) + $(local/built_headers_priv) + + + + + request_p.h + + + + + api.h + cgi.h + request.h + + + + + $(local/typefiles) + cgi.c + request.c + + + + + + + + + + + + + + + + $(gwenhywfar_libs) + + + + + + + + + + + diff --git a/src/aqcgi/api.h b/src/aqcgi/api.h new file mode 100644 index 0000000..5792725 --- /dev/null +++ b/src/aqcgi/api.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQCGI_API_H +#define AQCGI_API_H + + +#ifdef BUILDING_AQCGI +# /* building AqGmControl */ +# if AQCGI_SYS_IS_WINDOWS +# /* for windows */ +# ifdef __declspec +# define AQCGI_API __declspec (dllexport) +# else /* if __declspec */ +# define AQCGI_API +# endif /* if NOT __declspec */ +# else +# /* for non-win32 */ +# ifdef GCC_WITH_VISIBILITY_ATTRIBUTE +# define AQCGI_API __attribute__((visibility("default"))) +# else +# define AQCGI_API +# endif +# endif +#else +# /* not building AqFinance */ +# if AQCGI_SYS_IS_WINDOWS +# /* for windows */ +# ifdef __declspec +# define AQCGI_API __declspec (dllimport) +# else /* if __declspec */ +# define AQCGI_API +# endif /* if NOT __declspec */ +# else +# /* for non-win32 */ +# define AQCGI_API +# endif +#endif + +#ifdef GCC_WITH_VISIBILITY_ATTRIBUTE +# define AQCGI_EXPORT __attribute__((visibility("default"))) +# define AQCGI_NOEXPORT __attribute__((visibility("hidden"))) +#else +# define AQCGI_EXPORT +# define AQCGI_NOEXPORT +#endif + + +#define AQCGI_LOGDOMAIN "aqcgi" + +#endif + diff --git a/src/aqcgi/cgi.c b/src/aqcgi/cgi.c new file mode 100644 index 0000000..c105825 --- /dev/null +++ b/src/aqcgi/cgi.c @@ -0,0 +1,622 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "./cgi.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define AQCGI_VARNAME_TEXT "text" + + + +/* ------------------------------------------------------------------------------------------------ + * external declarations + * ------------------------------------------------------------------------------------------------ + */ + + +extern char **environ; + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _handleHeaders(AQCGI_REQUEST *rq); +static int _handleBody(AQCGI_REQUEST *rq); +static int _finishAndSendHeader(AQCGI_REQUEST *rq, GWEN_SYNCIO *sio); +static int _sendBody(AQCGI_REQUEST *rq, GWEN_SYNCIO *sio); +static int _unescapeUrlEncoded(const char *src, unsigned int srclen, char *buffer, unsigned int maxsize); +static GWEN_DB_NODE *_parseEnv(void); +static int _parseEnvVarIntoDb(const char *var, GWEN_DB_NODE *db); +static void _parseCookieHeaderIntoDb(const char *s, GWEN_DB_NODE *db); + + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AQCGI_ReadForcedStdInToBuffer(int len, GWEN_BUFFER *buf) +{ + GWEN_SYNCIO *sio; + int rv; + + sio=GWEN_SyncIo_File_fromStdin(); + GWEN_Buffer_AllocRoom(buf, len); + rv=GWEN_SyncIo_ReadForced(sio, (uint8_t *) GWEN_Buffer_GetPosPointer(buf), len); + if (rv<0) { + DBG_INFO(AQCGI_LOGDOMAIN, "here (%d)", rv); + GWEN_SyncIo_free(sio); + return rv; + } + GWEN_Buffer_IncrementPos(buf, rv); + GWEN_Buffer_AdjustUsedBytes(buf); + GWEN_SyncIo_free(sio); + return 0; +} + + + +int AQCGI_GenerateSessionId(GWEN_BUFFER *buf) +{ + GWEN_CRYPT_KEY *sk; + uint8_t *ptr; + uint32_t len; + int rv; + + sk=GWEN_Crypt_KeyDes3K_Generate(GWEN_Crypt_CryptMode_Cbc, 24, 2); + if (sk==NULL) { + DBG_INFO(AQCGI_LOGDOMAIN, "Could not generate DES key"); + return GWEN_ERROR_IO; + } + ptr=GWEN_Crypt_KeyDes3K_GetKeyDataPtr(sk); + len=GWEN_Crypt_KeyDes3K_GetKeyDataLen(sk); + + rv=GWEN_Text_ToHexBuffer((const char*) ptr, len, buf,0, 0, 0); + if (rv<0) { + DBG_INFO(AQCGI_LOGDOMAIN, "here (%d)", rv); + GWEN_Crypt_Key_free(sk); + return rv; + } + GWEN_Crypt_Key_free(sk); + return 0; +} + + + +int AQCGI_HashMd256ToBuffer(const char *input, GWEN_BUFFER *buf) +{ + GWEN_MDIGEST *md; + int rv; + int digestSize; + uint8_t tmpBuf[32]; + + md=GWEN_MDigest_Sha256_new(); + digestSize=GWEN_MDigest_GetDigestSize(md); + assert(digestSize<=sizeof(tmpBuf)); + rv=GWEN_MDigest_Digest(md, (const uint8_t*) input, strlen(input), tmpBuf, sizeof(tmpBuf)); + GWEN_MDigest_free(md); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error hashing input (%d)", rv); + return rv; + } + rv=GWEN_Text_ToHexBuffer((const char*) tmpBuf, digestSize, buf, 0, 0, 0); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error converting hash to hex (%d)", rv); + return rv; + } + + return 0; +} + + + +int AQCGI_ParseUrlEncoded(const char *s, int contentLength, GWEN_DB_NODE *dbDecoded) +{ + while(contentLength>0) { + const char *sNameStart; + int nameLen; + + while((contentLength>0) && (*s<33)) { + contentLength--; + s++; + } + sNameStart=s; + while((contentLength>0) && (*s!='=') && (*s!='&')) { + contentLength--; + s++; + } + nameLen=s-sNameStart; + + if ((contentLength>0) && (*s=='=')) { + const char *sValueStart; + int valueLen; + + s++; + contentLength--; + while((contentLength>0) && (*s<33)) { + s++; + contentLength--; + } + sValueStart=s; + while((contentLength>0) && (*s!='&')) { + contentLength--; + s++; + } + valueLen=s-sValueStart; + + if (nameLen && valueLen) { + char sNameBuf[32]; + char sValueBuf[64]; + + if (_unescapeUrlEncoded(sNameStart, nameLen, sNameBuf, sizeof(sNameBuf))>=0 && + _unescapeUrlEncoded(sValueStart, valueLen, sValueBuf, sizeof(sValueBuf))>=0) + GWEN_DB_SetCharValue(dbDecoded, 0, sNameBuf, sValueBuf); + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Either name or value invalid in body, aborting"); + return GWEN_ERROR_BAD_DATA; + } + } + if ((contentLength>0) && (*s=='&')) { + contentLength--; + s++; + } + } + } /* while */ + return 0; +} + + + +AQCGI_REQUEST *AQCGI_ReadRequest(void) +{ + AQCGI_REQUEST *rq; + int rv; + + rq=AQCGI_Request_new(); + + rv=_handleHeaders(rq); + if (rv<0) { + DBG_INFO(AQCGI_LOGDOMAIN, "here (%d)", rv); + AQCGI_SendResponseWithStatus(rq, 500, "Internal error"); + AQCGI_Request_free(rq); + return NULL; + } + + rv=_handleBody(rq); + if (rv<0) { + DBG_INFO(AQCGI_LOGDOMAIN, "here (%d)", rv); + AQCGI_SendResponseWithStatus(rq, 500, "Internal error"); + AQCGI_Request_free(rq); + return NULL; + } + return rq; +} + + + +int AQCGI_SendResponseWithStatus(AQCGI_REQUEST *rq, int code, const char *text) +{ + AQCGI_Request_SetResponseCode(rq, code); + AQCGI_Request_SetResponseText(rq, text); + return AQCGI_SendResponse(rq); +} + + + +int AQCGI_SendResponse(AQCGI_REQUEST *rq) +{ + GWEN_SYNCIO *sio; + int rv; + + sio=GWEN_SyncIo_File_fromStdout(); + assert(sio); + + rv=_finishAndSendHeader(rq, sio); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + GWEN_SyncIo_free(sio); + return rv; + } + + rv=_sendBody(rq, sio); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + GWEN_SyncIo_free(sio); + return rv; + } + + rv=GWEN_SyncIo_Flush(sio); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + GWEN_SyncIo_free(sio); + return rv; + } + GWEN_SyncIo_free(sio); + return 0; +} + + + +int _finishAndSendHeader(AQCGI_REQUEST *rq, GWEN_SYNCIO *sio) +{ + int rv; + GWEN_BUFFER *buf; + int responseBodyLength=0; + + buf=AQCGI_Request_GetBufferResponseBody(rq); + if (buf) + responseBodyLength=GWEN_Buffer_GetUsedBytes(buf); + + AQCGI_Request_AddResponseStatusHeader(rq); + buf=AQCGI_Request_GetBufferResponseHeader(rq); + GWEN_Buffer_AppendArgs(buf, "Content-length: %d\n", responseBodyLength); + if (buf && GWEN_Buffer_GetUsedBytes(buf)) { + rv=GWEN_SyncIo_WriteForced(sio, (const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf)); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + + rv=GWEN_SyncIo_WriteForced(sio, (const uint8_t*)"\n", 1); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + return rv; + } + + return 0; +} + + + +int _sendBody(AQCGI_REQUEST *rq, GWEN_SYNCIO *sio) +{ + int rv; + GWEN_BUFFER *buf; + + buf=AQCGI_Request_GetBufferResponseBody(rq); + if (buf && GWEN_Buffer_GetUsedBytes(buf)) { + rv=GWEN_SyncIo_WriteForced(sio, (const uint8_t*)GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf)); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "here (%d)", rv); + return rv; + } + } + + return 0; +} + + + + + + + +int _unescapeUrlEncoded(const char *src, unsigned int srclen, char *buffer, unsigned int maxsize) +{ + unsigned int size; + + size=0; + + while (srclen>0 && *src) { + unsigned char x; + + x=(unsigned char)*src; + if ( + (x>='A' && x<='Z') || + (x>='a' && x<='z') || + (x>='0' && x<='9') || + x==' ' || + x=='.' || + x==',' || + x=='.' || + x=='*' || + x=='?' || + x=='+' || + x=='-' || + x=='_' + ) { + if (size<(maxsize-1)) { + buffer[size++]=(x=='+')?' ':x; + } + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Buffer too small"); + return GWEN_ERROR_BUFFER_OVERFLOW; + } + } + else { + if (*src=='%') { + unsigned char d1, d2; + unsigned char c; + + if (srclen<3) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Incomplete escape sequence (EOLN met)"); + return GWEN_ERROR_BAD_DATA; + } + /* skip '%' */ + src++; + if (!(*src) || !isxdigit((int)*src)) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Incomplete escape sequence (no digits)"); + return GWEN_ERROR_BAD_DATA; + } + /* read first digit */ + d1=(unsigned char)(toupper(*src)); + + /* get second digit */ + src++; + if (!(*src) || !isxdigit((int)*src)) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Incomplete escape sequence (only 1 digit)"); + return GWEN_ERROR_BAD_DATA; + } + d2=(unsigned char)(toupper(*src)); + /* compute character */ + d1-='0'; + if (d1>9) + d1-=7; + c=(d1<<4)&0xf0; + d2-='0'; + if (d2>9) + d2-=7; + c+=(d2&0xf); + /* store character */ + if (size<(maxsize-1)) + buffer[size++]=(char)c; + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Buffer too small"); + return GWEN_ERROR_BUFFER_OVERFLOW; + } + srclen-=2; + } + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Found non-alphanum characters in escaped string (\"%s\")", src); + return GWEN_ERROR_BAD_DATA; + } + } + srclen--; + src++; + } /* while */ + + buffer[size]=0; + return 0; +} + + + +GWEN_DB_NODE *_parseEnv(void) +{ + GWEN_DB_NODE *db; + GWEN_DB_NODE *dbCookies; + int i=0; + const char *s; + + db=GWEN_DB_Group_new("env"); + while( (s=environ[i++]) ) + _parseEnvVarIntoDb(s, db); + + dbCookies=GWEN_DB_GetGroup(db, 0, "cookies"); + i=0; + while( (s=GWEN_DB_GetCharValue(db, "HTTP_COOKIE", i++, NULL)) ) + _parseCookieHeaderIntoDb(s, dbCookies); + + return db; +} + + + +int _parseEnvVarIntoDb(const char *var, GWEN_DB_NODE *db) +{ + const char *s; + + while(isblank(*var)) + var++; + s=strchr(var, '='); + if (s) { + int nameLen; + + nameLen=s-var; + if (nameLen) { + char *name; + + name=GWEN_Text_strndup(var, nameLen); + s++; + if (*s) { + GWEN_DB_SetCharValue(db, 0, name, s); + free(name); + return 0; + } + free(name); + } + } + return GWEN_ERROR_BAD_DATA; +} + + + +void _parseCookieHeaderIntoDb(const char *s, GWEN_DB_NODE *db) +{ + while(*s) { + const char *nameStart; + char *t; + + while(isblank(*s)) + s++; + if (!*s) + break; + nameStart=s; + t=strchr(s, '='); + if (t) { + int nameLen; + + nameLen=t-nameStart; + s=t+1; + if (nameLen) { + char *name; + + name=GWEN_Text_strndup(nameStart, nameLen); + if (*s) { + const char *dataBegin; + int dataLen; + + while(isblank(*s)) + s++; + dataBegin=s; + while(*s && *s!=';') + s++; + dataLen=s-dataBegin; + if (dataLen) { + char *data; + + data=GWEN_Text_strndup(dataBegin, dataLen); + GWEN_DB_SetCharValue(db, 0, name, data); + free(data); + } + } + free(name); + } + } + s=strchr(s, ';'); + if (s==NULL) + break; + s++; + } +} + + + +int _handleHeaders(AQCGI_REQUEST *rq) +{ + GWEN_DB_NODE *db; + const char *s; + + db=_parseEnv(); + if (db==NULL) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error parsing environment variables"); + return GWEN_ERROR_GENERIC; + } + AQCGI_Request_SetDbRequestHeader(rq, db); + + s=GWEN_DB_GetCharValue(db, "REQUEST_METHOD", 0, NULL); + if (s && *s) { + if (strcasecmp(s, "GET")==0) + AQCGI_Request_SetRequestMethod(rq, AQCGI_REQUEST_METHOD_GET); + else if (strcasecmp(s, "POST")==0) + AQCGI_Request_SetRequestMethod(rq, AQCGI_REQUEST_METHOD_POST); + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Unhandled request method \"%s\"", s); + return GWEN_ERROR_GENERIC; + } + } + else { + DBG_ERROR(AQCGI_LOGDOMAIN, "Missing request method"); + return GWEN_ERROR_GENERIC; + } + + s=GWEN_DB_GetCharValue(db, "PATH_INFO", 0, NULL); + if (s && *s) { + GWEN_STRINGLIST *sl; + + if (*s=='/') + s++; + sl=GWEN_StringList_fromString2(s, "/", 0, + GWEN_TEXT_FLAGS_DEL_QUOTES | + GWEN_TEXT_FLAGS_DEL_LEADING_BLANKS | + GWEN_TEXT_FLAGS_DEL_TRAILING_BLANKS); + AQCGI_Request_SetStringlistPath(rq, sl); + } + + s=GWEN_DB_GetCharValue(db, "QUERY_STRING", 0, NULL); + if (s && *s) { + GWEN_DB_NODE *dbQuery; + int rv; + + dbQuery=GWEN_DB_Group_new("query"); + rv=AQCGI_ParseUrlEncoded(s, strlen(s), dbQuery); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error parsing query string \"%s\" (%d)", s, rv); + GWEN_DB_Group_free(dbQuery); + } + else + AQCGI_Request_SetDbQuery(rq, dbQuery); + } + + return 0; +} + + + +int _handleBody(AQCGI_REQUEST *rq) +{ + GWEN_DB_NODE *db; + const char *s; + int bodyLength=0; + + db=AQCGI_Request_GetDbRequestHeader(rq); + + s=GWEN_DB_GetCharValue(db, "CONTENT_LENGTH", 0, NULL); + if (s && *s) { + int i; + + if (1==sscanf(s, "%d", &i)) { + bodyLength=i; + AQCGI_Request_SetRequestBodyLength(rq, i); + } + } + + s=GWEN_DB_GetCharValue(db, "CONTENT_TYPE", 0, NULL); + if (bodyLength>0 && s && *s && strcasecmp(s, "application/x-www-form-urlencoded")==0) { + GWEN_BUFFER *buf; + GWEN_DB_NODE *dbDecoded; + int rv; + + buf=GWEN_Buffer_new(0, bodyLength, 0, 1); + rv=AQCGI_ReadForcedStdInToBuffer(bodyLength, buf); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error reading body from stdin (%d)", rv); + GWEN_Buffer_free(buf); + return rv; + } + + dbDecoded=GWEN_DB_Group_new("body"); + rv=AQCGI_ParseUrlEncoded(GWEN_Buffer_GetStart(buf), GWEN_Buffer_GetUsedBytes(buf), dbDecoded); + if (rv<0) { + DBG_ERROR(AQCGI_LOGDOMAIN, "Error reading body from stdin (%d)", rv); + GWEN_DB_Group_free(dbDecoded); + GWEN_Buffer_free(buf); + return rv; + } + AQCGI_Request_SetDbPostBody(rq, dbDecoded); + GWEN_Buffer_free(buf); + } + + return 0; +} + + + diff --git a/src/aqcgi/cgi.h b/src/aqcgi/cgi.h new file mode 100644 index 0000000..8e889ae --- /dev/null +++ b/src/aqcgi/cgi.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQCGI_CGI_H +#define AQCGI_CGI_H + +#include +#include + +#include +#include + + + +AQCGI_API AQCGI_REQUEST *AQCGI_ReadRequest(void); +AQCGI_API int AQCGI_SendResponseWithStatus(AQCGI_REQUEST *rq, int code, const char *text); +AQCGI_API int AQCGI_SendResponse(AQCGI_REQUEST *rq); + +AQCGI_API int AQCGI_ReadForcedStdInToBuffer(int len, GWEN_BUFFER *buf); +AQCGI_API int AQCGI_GenerateSessionId(GWEN_BUFFER *buf); +AQCGI_API int AQCGI_HashMd256ToBuffer(const char *input, GWEN_BUFFER *buf); +AQCGI_API int AQCGI_ParseUrlEncoded(const char *s, int contentLength, GWEN_DB_NODE *dbDecoded); + + + + +#endif + diff --git a/src/aqcgi/request.c b/src/aqcgi/request.c new file mode 100644 index 0000000..77af71f --- /dev/null +++ b/src/aqcgi/request.c @@ -0,0 +1,265 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + +#include "./request_p.h" + +#include +#include + + + +GWEN_INHERIT_FUNCTIONS(AQCGI_REQUEST); + + + + +AQCGI_REQUEST *AQCGI_Request_new(void) +{ + AQCGI_REQUEST *rq; + + GWEN_NEW_OBJECT(AQCGI_REQUEST, rq); + GWEN_INHERIT_INIT(AQCGI_REQUEST, rq); + rq->bufferResponseHeader=GWEN_Buffer_new(0, 256, 0, 1); + rq->bufferResponseBody=GWEN_Buffer_new(0, 256, 0, 1); + + return rq; +} + + + +void AQCGI_Request_free(AQCGI_REQUEST *rq) +{ + if (rq) { + GWEN_INHERIT_FINI(AQCGI_REQUEST, rq); + GWEN_Buffer_free(rq->bufferResponseHeader); + GWEN_Buffer_free(rq->bufferResponseBody); + GWEN_StringList_free(rq->stringlistPath); + GWEN_DB_Group_free(rq->dbRequestHeader); + GWEN_DB_Group_free(rq->dbQuery); + GWEN_DB_Group_free(rq->dbPostBody); + free(rq->responseText); + + GWEN_FREE_OBJECT(rq); + } +} + + + +GWEN_BUFFER *AQCGI_Request_GetBufferResponseHeader(const AQCGI_REQUEST *rq) +{ + return rq?rq->bufferResponseHeader:NULL; +} + + + +void AQCGI_Request_AddResponseHeaderData(AQCGI_REQUEST *rq, const char *s) +{ + if (rq && s && *s) { + GWEN_Buffer_AppendString(rq->bufferResponseHeader, s); + } +} + + + +void AQCGI_Request_AddResponseStatusHeader(AQCGI_REQUEST *rq) +{ + if (rq) { + GWEN_Buffer_AppendArgs(rq->bufferResponseHeader, "Status: %d %s\n", rq->responseCode, rq->responseText?rq->responseText:""); + if (rq->responseRedirect) + GWEN_Buffer_AppendArgs(rq->bufferResponseHeader, "Location: %s\n", rq->responseRedirect); + } +} + + + +GWEN_BUFFER *AQCGI_Request_GetBufferResponseBody(const AQCGI_REQUEST *rq) +{ + return rq?rq->bufferResponseBody:NULL; +} + + + +void AQCGI_Request_SetBufferResponseBody(AQCGI_REQUEST *rq, GWEN_BUFFER *buf) +{ + if (rq) { + GWEN_Buffer_free(rq->bufferResponseBody); + rq->bufferResponseBody=buf; + } +} + + + +void AQCGI_Request_AddResponseBodyData(AQCGI_REQUEST *rq, const char *s) +{ + if (rq && s && *s) { + GWEN_Buffer_AppendString(rq->bufferResponseBody, s); + } +} + + + +int AQCGI_Request_GetRequestMethod(const AQCGI_REQUEST *rq) +{ + return rq?rq->requestMethod:AQCGI_REQUEST_METHOD_GET; +} + + + +void AQCGI_Request_SetRequestMethod(AQCGI_REQUEST *rq, int m) +{ + if (rq) + rq->requestMethod=m; +} + + + +GWEN_STRINGLIST *AQCGI_Request_GetStringlistPath(const AQCGI_REQUEST *rq) +{ + return rq?rq->stringlistPath:NULL; +} + + + +GWEN_STRINGLISTENTRY *AQCGI_Request_GetFirstPathEntry(const AQCGI_REQUEST *rq) +{ + return (rq && rq->stringlistPath)?GWEN_StringList_FirstEntry(rq->stringlistPath):NULL; +} + + + +void AQCGI_Request_SetStringlistPath(AQCGI_REQUEST *rq, GWEN_STRINGLIST *sl) +{ + if (rq) { + GWEN_StringList_free(rq->stringlistPath); + rq->stringlistPath=sl; + } +} + + + +int AQCGI_Request_GetRequestBodyLength(const AQCGI_REQUEST *rq) +{ + return rq?rq->requestBodyLength:0; +} + + + +void AQCGI_Request_SetRequestBodyLength(AQCGI_REQUEST *rq, int i) +{ + if (rq) + rq->requestBodyLength=i; +} + + + +GWEN_DB_NODE *AQCGI_Request_GetDbRequestHeader(const AQCGI_REQUEST *rq) +{ + return rq?rq->dbRequestHeader:NULL; +} + + + +void AQCGI_Request_SetDbRequestHeader(AQCGI_REQUEST *rq, GWEN_DB_NODE *db) +{ + if (rq) { + GWEN_DB_Group_free(rq->dbRequestHeader); + rq->dbRequestHeader=db; + } +} + + + +GWEN_DB_NODE *AQCGI_Request_GetDbQuery(const AQCGI_REQUEST *rq) +{ + return rq?rq->dbQuery:NULL; +} + + + +void AQCGI_Request_SetDbQuery(AQCGI_REQUEST *rq, GWEN_DB_NODE *db) +{ + if (rq) { + GWEN_DB_Group_free(rq->dbQuery); + rq->dbQuery=db; + } +} + + + +GWEN_DB_NODE *AQCGI_Request_GetDbPostBody(const AQCGI_REQUEST *rq) +{ + return rq?rq->dbPostBody:NULL; +} + + + +void AQCGI_Request_SetDbPostBody(AQCGI_REQUEST *rq, GWEN_DB_NODE *db) +{ + if (rq) { + GWEN_DB_Group_free(rq->dbPostBody); + rq->dbPostBody=db; + } +} + + + +int AQCGI_Request_GetResponseCode(const AQCGI_REQUEST *rq) +{ + return rq?rq->responseCode:200; +} + + + +void AQCGI_Request_SetResponseCode(AQCGI_REQUEST *rq, int i) +{ + if (rq) + rq->responseCode=i; +} + + +const char *AQCGI_Request_GetResponseText(const AQCGI_REQUEST *rq) +{ + return rq?rq->responseText:"Ok."; +} + + + +void AQCGI_Request_SetResponseText(AQCGI_REQUEST *rq, const char *s) +{ + if (rq) { + free(rq->responseText); + rq->responseText=s?strdup(s):NULL; + } +} + + + +const char *AQCGI_Request_GetResponseRedirect(const AQCGI_REQUEST *rq) +{ + return rq?rq->responseRedirect:NULL; +} + + + +void AQCGI_Request_SetResponseRedirect(AQCGI_REQUEST *rq, const char *s) +{ + if (rq) { + free(rq->responseRedirect); + rq->responseRedirect=s?strdup(s):NULL; + } +} + + + + + diff --git a/src/aqcgi/request.h b/src/aqcgi/request.h new file mode 100644 index 0000000..dd49da5 --- /dev/null +++ b/src/aqcgi/request.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQCGI_REQUEST_H +#define AQCGI_REQUEST_H + + +#include + +#include +#include +#include + + +enum { + AQCGI_REQUEST_METHOD_GET=0, + AQCGI_REQUEST_METHOD_POST +}; + + + +typedef struct AQCGI_REQUEST AQCGI_REQUEST; +GWEN_INHERIT_FUNCTION_LIB_DEFS(AQCGI_REQUEST, AQCGI_API); + + + +AQCGI_API AQCGI_REQUEST *AQCGI_Request_new(void); +AQCGI_API void AQCGI_Request_free(AQCGI_REQUEST *rq); + +AQCGI_API GWEN_BUFFER *AQCGI_Request_GetBufferResponseHeader(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_AddResponseHeaderData(AQCGI_REQUEST *rq, const char *s); +AQCGI_API void AQCGI_Request_AddResponseStatusHeader(AQCGI_REQUEST *rq); + +AQCGI_API GWEN_BUFFER *AQCGI_Request_GetBufferResponseBody(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetBufferResponseBody(AQCGI_REQUEST *rq, GWEN_BUFFER *buf); +AQCGI_API void AQCGI_Request_AddResponseBodyData(AQCGI_REQUEST *rq, const char *s); + +AQCGI_API int AQCGI_Request_GetRequestMethod(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetRequestMethod(AQCGI_REQUEST *rq, int m); + +AQCGI_API GWEN_STRINGLIST *AQCGI_Request_GetStringlistPath(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetStringlistPath(AQCGI_REQUEST *rq, GWEN_STRINGLIST *sl); +AQCGI_API GWEN_STRINGLISTENTRY *AQCGI_Request_GetFirstPathEntry(const AQCGI_REQUEST *rq); + +AQCGI_API int AQCGI_Request_GetRequestBodyLength(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetRequestBodyLength(AQCGI_REQUEST *rq, int i); + +AQCGI_API GWEN_DB_NODE *AQCGI_Request_GetDbRequestHeader(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetDbRequestHeader(AQCGI_REQUEST *rq, GWEN_DB_NODE *db); + +AQCGI_API GWEN_DB_NODE *AQCGI_Request_GetDbQuery(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetDbQuery(AQCGI_REQUEST *rq, GWEN_DB_NODE *db); + +AQCGI_API GWEN_DB_NODE *AQCGI_Request_GetDbPostBody(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetDbPostBody(AQCGI_REQUEST *rq, GWEN_DB_NODE *db); + +AQCGI_API int AQCGI_Request_GetResponseCode(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetResponseCode(AQCGI_REQUEST *rq, int i); + +AQCGI_API const char *AQCGI_Request_GetResponseText(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetResponseText(AQCGI_REQUEST *rq, const char *s); + +AQCGI_API const char *AQCGI_Request_GetResponseRedirect(const AQCGI_REQUEST *rq); +AQCGI_API void AQCGI_Request_SetResponseRedirect(AQCGI_REQUEST *rq, const char *s); + + +#endif diff --git a/src/aqcgi/request_p.h b/src/aqcgi/request_p.h new file mode 100644 index 0000000..63441a2 --- /dev/null +++ b/src/aqcgi/request_p.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * This file is part of the project AqCGI. + * AqCGI (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQCGI_REQUEST_P_H +#define AQCGI_REQUEST_P_H + +#include "./request.h" + + + +struct AQCGI_REQUEST { + GWEN_INHERIT_ELEMENT(AQCGI_REQUEST); + GWEN_BUFFER *bufferResponseHeader; + GWEN_BUFFER *bufferResponseBody; + + int requestMethod; + GWEN_STRINGLIST *stringlistPath; + int requestBodyLength; + + GWEN_DB_NODE *dbRequestHeader; + GWEN_DB_NODE *dbQuery; + GWEN_DB_NODE *dbPostBody; + + int responseCode; + char *responseText; + + char *responseRedirect; +}; + + + +#endif