forked from Telodendria/Telodendria
Convert all code to new Stream API.
Also made a number of improvmements to tt, making it compatible with more shells.
This commit is contained in:
parent
a97a593f21
commit
5289c16e2b
35 changed files with 714 additions and 492 deletions
|
@ -37,7 +37,7 @@ CanonicalJsonKeyCompare(void *k1, void *k2)
|
|||
}
|
||||
|
||||
static void
|
||||
CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
|
||||
CanonicalJsonEncodeValue(JsonValue * value, Stream * out)
|
||||
{
|
||||
Array *arr;
|
||||
size_t i, len;
|
||||
|
@ -52,7 +52,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
|
|||
arr = JsonValueAsArray(value);
|
||||
len = ArraySize(arr);
|
||||
|
||||
fputc('[', out);
|
||||
StreamPutc(out, '[');
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
|
@ -67,11 +67,11 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
|
|||
CanonicalJsonEncodeValue(aVal, out);
|
||||
if (i < len - 1)
|
||||
{
|
||||
fputc(',', out);
|
||||
StreamPutc(out, ',');
|
||||
}
|
||||
}
|
||||
|
||||
fputc(']', out);
|
||||
StreamPutc(out, ']');
|
||||
break;
|
||||
default:
|
||||
JsonEncodeValue(value, out, JSON_DEFAULT);
|
||||
|
@ -80,7 +80,7 @@ CanonicalJsonEncodeValue(JsonValue * value, FILE * out)
|
|||
}
|
||||
|
||||
int
|
||||
CanonicalJsonEncode(HashMap * object, FILE * out)
|
||||
CanonicalJsonEncode(HashMap * object, Stream * out)
|
||||
{
|
||||
char *key;
|
||||
JsonValue *value;
|
||||
|
@ -106,7 +106,7 @@ CanonicalJsonEncode(HashMap * object, FILE * out)
|
|||
|
||||
ArraySort(keys, CanonicalJsonKeyCompare);
|
||||
|
||||
fputc('{', out);
|
||||
StreamPutc(out, '{');
|
||||
|
||||
keyCount = ArraySize(keys);
|
||||
for (i = 0; i < keyCount; i++)
|
||||
|
@ -129,16 +129,16 @@ CanonicalJsonEncode(HashMap * object, FILE * out)
|
|||
}
|
||||
|
||||
JsonEncodeString(key, out);
|
||||
fputc(':', out);
|
||||
StreamPutc(out, ':');
|
||||
CanonicalJsonEncodeValue(value, out);
|
||||
|
||||
if (i < keyCount - 1)
|
||||
{
|
||||
fputc(',', out);
|
||||
StreamPutc(out, ',');
|
||||
}
|
||||
}
|
||||
|
||||
fputc('}', out);
|
||||
StreamPutc(out, '}');
|
||||
|
||||
ArrayFree(keys);
|
||||
return 1;
|
||||
|
|
54
src/Db.c
54
src/Db.c
|
@ -27,6 +27,7 @@
|
|||
#include <Json.h>
|
||||
#include <Util.h>
|
||||
#include <Str.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
@ -83,7 +84,8 @@ struct DbRef
|
|||
DbRef *prev;
|
||||
DbRef *next;
|
||||
|
||||
FILE *fp;
|
||||
int fd;
|
||||
Stream *stream;
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -394,9 +396,11 @@ DbLockFromArr(Db * db, Array * args)
|
|||
char *file;
|
||||
char *hash;
|
||||
DbRef *ref;
|
||||
FILE *fp;
|
||||
struct flock lock;
|
||||
|
||||
int fd;
|
||||
Stream *stream;
|
||||
|
||||
if (!db || !args)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -412,9 +416,8 @@ DbLockFromArr(Db * db, Array * args)
|
|||
ref = HashMapGet(db->cache, hash);
|
||||
file = DbFileName(db, args);
|
||||
|
||||
/* Open the file for reading and writing so we can lock it */
|
||||
fp = fopen(file, "r+");
|
||||
if (!fp)
|
||||
fd = open(file, O_RDWR);
|
||||
if (fd == -1)
|
||||
{
|
||||
if (ref)
|
||||
{
|
||||
|
@ -452,15 +455,17 @@ DbLockFromArr(Db * db, Array * args)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
stream = StreamFd(fd);
|
||||
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0;
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
|
||||
/* Lock the file on the disk */
|
||||
if (fcntl(fileno(fp), F_SETLK, &lock) < 0)
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
{
|
||||
fclose(fp);
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
@ -470,17 +475,16 @@ DbLockFromArr(Db * db, Array * args)
|
|||
unsigned long diskTs = UtilLastModified(file);
|
||||
|
||||
pthread_mutex_lock(&ref->lock);
|
||||
ref->fp = fp;
|
||||
|
||||
if (diskTs > ref->ts)
|
||||
{
|
||||
/* File was modified on disk since it was cached */
|
||||
HashMap *json = JsonDecode(fp);
|
||||
HashMap *json = JsonDecode(ref->stream);
|
||||
|
||||
if (!json)
|
||||
{
|
||||
pthread_mutex_unlock(&ref->lock);
|
||||
fclose(fp);
|
||||
StreamClose(ref->stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
@ -489,7 +493,6 @@ DbLockFromArr(Db * db, Array * args)
|
|||
ref->json = json;
|
||||
ref->ts = diskTs;
|
||||
ref->size = DbComputeSize(ref->json);
|
||||
|
||||
}
|
||||
|
||||
/* Float this ref to mostRecent */
|
||||
|
@ -525,25 +528,26 @@ DbLockFromArr(Db * db, Array * args)
|
|||
ref = Malloc(sizeof(DbRef));
|
||||
if (!ref)
|
||||
{
|
||||
fclose(fp);
|
||||
StreamClose(stream);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ref->json = JsonDecode(fp);
|
||||
ref->fp = fp;
|
||||
ref->json = JsonDecode(stream);
|
||||
|
||||
if (!ref->json)
|
||||
{
|
||||
Free(ref);
|
||||
fclose(fp);
|
||||
StreamClose(stream);
|
||||
ref = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ref->fd = fd;
|
||||
ref->stream = stream;
|
||||
|
||||
pthread_mutex_init(&ref->lock, NULL);
|
||||
pthread_mutex_lock(&ref->lock);
|
||||
|
||||
|
||||
for (i = 0; i < ArraySize(args); i++)
|
||||
{
|
||||
ArrayAdd(name, StrDuplicate(ArrayGet(args, i)));
|
||||
|
@ -579,7 +583,7 @@ finish:
|
|||
DbRef *
|
||||
DbCreate(Db * db, size_t nArgs,...)
|
||||
{
|
||||
FILE *fp;
|
||||
Stream *fp;
|
||||
char *file;
|
||||
char *dir;
|
||||
va_list ap;
|
||||
|
@ -620,7 +624,7 @@ DbCreate(Db * db, size_t nArgs,...)
|
|||
|
||||
Free(dir);
|
||||
|
||||
fp = fopen(file, "w");
|
||||
fp = StreamOpen(file, "w");
|
||||
Free(file);
|
||||
if (!fp)
|
||||
{
|
||||
|
@ -628,9 +632,8 @@ DbCreate(Db * db, size_t nArgs,...)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(fp, "{}");
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
StreamPuts(fp, "{}");
|
||||
StreamClose(fp);
|
||||
|
||||
ret = DbLockFromArr(db, args);
|
||||
|
||||
|
@ -744,17 +747,16 @@ DbUnlock(Db * db, DbRef * ref)
|
|||
|
||||
pthread_mutex_lock(&db->lock);
|
||||
|
||||
rewind(ref->fp);
|
||||
if (ftruncate(fileno(ref->fp), 0) < 0)
|
||||
lseek(ref->fd, 0L, SEEK_SET);
|
||||
if (ftruncate(ref->fd, 0) < 0)
|
||||
{
|
||||
pthread_mutex_unlock(&db->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
JsonEncode(ref->json, ref->fp, JSON_DEFAULT);
|
||||
JsonEncode(ref->json, ref->stream, JSON_DEFAULT);
|
||||
|
||||
fflush(ref->fp);
|
||||
fclose(ref->fp);
|
||||
StreamClose(ref->stream);
|
||||
|
||||
if (db->cache)
|
||||
{
|
||||
|
|
134
src/Html.c
134
src/Html.c
|
@ -28,7 +28,7 @@
|
|||
#include <Telodendria.h>
|
||||
|
||||
void
|
||||
HtmlBegin(FILE * stream, char *title)
|
||||
HtmlBegin(Stream * stream, char *title)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
|
@ -37,87 +37,87 @@ HtmlBegin(FILE * stream, char *title)
|
|||
return;
|
||||
}
|
||||
|
||||
fprintf(stream,
|
||||
"<!DOCTYPE html>"
|
||||
"<html>"
|
||||
"<head>"
|
||||
"<meta charset=\"utf-8\">"
|
||||
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
|
||||
"<title>%s | Telodendria</title>"
|
||||
,title
|
||||
StreamPrintf(stream,
|
||||
"<!DOCTYPE html>"
|
||||
"<html>"
|
||||
"<head>"
|
||||
"<meta charset=\"utf-8\">"
|
||||
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
|
||||
"<title>%s | Telodendria</title>"
|
||||
,title
|
||||
);
|
||||
HtmlBeginStyle(stream);
|
||||
fprintf(stream,
|
||||
":root {"
|
||||
" color-scheme: dark;"
|
||||
" --accent: #7b8333;"
|
||||
"}"
|
||||
"body {"
|
||||
" margin: auto;"
|
||||
" width: 100%%;"
|
||||
" max-width: 8.5in;"
|
||||
" padding: 0.25in;"
|
||||
" background-color: #0d1117;"
|
||||
" color: white;"
|
||||
"}"
|
||||
"a {"
|
||||
" color: var(--accent);"
|
||||
" text-decoration: none;"
|
||||
"}"
|
||||
"h1 {"
|
||||
" text-align: center;"
|
||||
"}"
|
||||
".logo {"
|
||||
" color: var(--accent);"
|
||||
" text-align: center;"
|
||||
" font-weight: bold;"
|
||||
"}"
|
||||
StreamPuts(stream,
|
||||
":root {"
|
||||
" color-scheme: dark;"
|
||||
" --accent: #7b8333;"
|
||||
"}"
|
||||
"body {"
|
||||
" margin: auto;"
|
||||
" width: 100%;"
|
||||
" max-width: 8.5in;"
|
||||
" padding: 0.25in;"
|
||||
" background-color: #0d1117;"
|
||||
" color: white;"
|
||||
"}"
|
||||
"a {"
|
||||
" color: var(--accent);"
|
||||
" text-decoration: none;"
|
||||
"}"
|
||||
"h1 {"
|
||||
" text-align: center;"
|
||||
"}"
|
||||
".logo {"
|
||||
" color: var(--accent);"
|
||||
" text-align: center;"
|
||||
" font-weight: bold;"
|
||||
"}"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
".form {"
|
||||
" margin: auto;"
|
||||
" width: 100%%;"
|
||||
" max-width: 400px;"
|
||||
" border-radius: 10px;"
|
||||
" border: 1px var(--accent) solid;"
|
||||
" padding: 10px;"
|
||||
"}"
|
||||
"form {"
|
||||
" display: block;"
|
||||
"}"
|
||||
"form > input, label {"
|
||||
" width: 95%%;"
|
||||
" height: 25px;"
|
||||
" display: block;"
|
||||
" margin-bottom: 5px;"
|
||||
" margin-left: auto;"
|
||||
" margin-right: auto;"
|
||||
"}"
|
||||
StreamPuts(stream,
|
||||
".form {"
|
||||
" margin: auto;"
|
||||
" width: 100%;"
|
||||
" max-width: 400px;"
|
||||
" border-radius: 10px;"
|
||||
" border: 1px var(--accent) solid;"
|
||||
" padding: 10px;"
|
||||
"}"
|
||||
"form {"
|
||||
" display: block;"
|
||||
"}"
|
||||
"form > input, label {"
|
||||
" width: 95%;"
|
||||
" height: 25px;"
|
||||
" display: block;"
|
||||
" margin-bottom: 5px;"
|
||||
" margin-left: auto;"
|
||||
" margin-right: auto;"
|
||||
"}"
|
||||
);
|
||||
HtmlEndStyle(stream);
|
||||
|
||||
fprintf(stream,
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<pre class=\"logo\">"
|
||||
StreamPuts(stream,
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<pre class=\"logo\">"
|
||||
);
|
||||
|
||||
for (i = 0; i < TELODENDRIA_LOGO_HEIGHT; i++)
|
||||
{
|
||||
fprintf(stream, "%s\n", TelodendriaLogo[i]);
|
||||
StreamPrintf(stream, "%s\n", TelodendriaLogo[i]);
|
||||
}
|
||||
|
||||
fprintf(stream,
|
||||
"</pre>"
|
||||
"<h1>%s</h1>"
|
||||
,title);
|
||||
StreamPrintf(stream,
|
||||
"</pre>"
|
||||
"<h1>%s</h1>"
|
||||
,title);
|
||||
}
|
||||
|
||||
void
|
||||
HtmlEnd(FILE * stream)
|
||||
HtmlEnd(Stream * stream)
|
||||
{
|
||||
fprintf(stream,
|
||||
"</body>"
|
||||
"</html>");
|
||||
StreamPuts(stream,
|
||||
"</body>"
|
||||
"</html>");
|
||||
}
|
||||
|
|
|
@ -536,7 +536,7 @@ HttpParamEncode(HashMap * params)
|
|||
}
|
||||
|
||||
HashMap *
|
||||
HttpParseHeaders(FILE * fp)
|
||||
HttpParseHeaders(Stream * fp)
|
||||
{
|
||||
HashMap *headers;
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
struct HttpClientContext
|
||||
{
|
||||
HashMap *responseHeaders;
|
||||
FILE *stream;
|
||||
Stream *stream;
|
||||
int sd;
|
||||
};
|
||||
|
||||
HttpClientContext *
|
||||
|
@ -126,7 +127,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
|
|||
|
||||
freeaddrinfo(res0);
|
||||
|
||||
context->stream = fdopen(sd, "r+");
|
||||
context->sd = sd;
|
||||
context->stream = StreamFd(sd);
|
||||
if (!context->stream)
|
||||
{
|
||||
Free(context);
|
||||
|
@ -134,8 +136,8 @@ HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host
|
|||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(context->stream, "%s %s HTTP/1.1\r\n",
|
||||
HttpRequestMethodToString(method), path);
|
||||
StreamPrintf(context->stream, "%s %s HTTP/1.1\r\n",
|
||||
HttpRequestMethodToString(method), path);
|
||||
|
||||
HttpRequestHeader(context, "Connection", "close");
|
||||
HttpRequestHeader(context, "User-Agent", "Telodendria/" TELODENDRIA_VERSION);
|
||||
|
@ -152,7 +154,7 @@ HttpRequestHeader(HttpClientContext * context, char *key, char *val)
|
|||
return;
|
||||
}
|
||||
|
||||
fprintf(context->stream, "%s: %s\r\n", key, val);
|
||||
StreamPrintf(context->stream, "%s: %s\r\n", key, val);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -163,8 +165,8 @@ HttpRequestSendHeaders(HttpClientContext * context)
|
|||
return;
|
||||
}
|
||||
|
||||
fprintf(context->stream, "\r\n");
|
||||
fflush(context->stream);
|
||||
StreamPuts(context->stream, "\r\n");
|
||||
StreamFlush(context->stream);
|
||||
}
|
||||
|
||||
HttpStatus
|
||||
|
@ -182,8 +184,8 @@ HttpRequestSend(HttpClientContext * context)
|
|||
return 0;
|
||||
}
|
||||
|
||||
fflush(context->stream);
|
||||
shutdown(fileno(context->stream), SHUT_WR);
|
||||
StreamFlush(context->stream);
|
||||
shutdown(context->sd, SHUT_WR);
|
||||
|
||||
lineLen = UtilGetLine(&line, &lineSize, context->stream);
|
||||
|
||||
|
@ -238,7 +240,7 @@ HttpResponseHeaders(HttpClientContext * context)
|
|||
return context->responseHeaders;
|
||||
}
|
||||
|
||||
FILE *
|
||||
Stream *
|
||||
HttpClientStream(HttpClientContext * context)
|
||||
{
|
||||
if (!context)
|
||||
|
@ -267,6 +269,6 @@ HttpClientContextFree(HttpClientContext * context)
|
|||
|
||||
HashMapFree(context->responseHeaders);
|
||||
|
||||
fclose(context->stream);
|
||||
StreamClose(context->stream);
|
||||
Free(context);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ struct HttpServerContext
|
|||
HashMap *responseHeaders;
|
||||
HttpStatus responseStatus;
|
||||
|
||||
FILE *stream;
|
||||
Stream *stream;
|
||||
};
|
||||
|
||||
typedef struct HttpServerWorkerThreadArgs
|
||||
|
@ -86,7 +86,7 @@ typedef struct HttpServerWorkerThreadArgs
|
|||
|
||||
static HttpServerContext *
|
||||
HttpServerContextCreate(HttpRequestMethod requestMethod,
|
||||
char *requestPath, HashMap * requestParams, FILE * stream)
|
||||
char *requestPath, HashMap * requestParams, Stream * stream)
|
||||
{
|
||||
HttpServerContext *c;
|
||||
|
||||
|
@ -158,7 +158,7 @@ HttpServerContextFree(HttpServerContext * c)
|
|||
HashMapFree(c->requestParams);
|
||||
|
||||
Free(c->requestPath);
|
||||
fclose(c->stream);
|
||||
StreamClose(c->stream);
|
||||
|
||||
Free(c);
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ HttpResponseStatus(HttpServerContext * c, HttpStatus status)
|
|||
c->responseStatus = status;
|
||||
}
|
||||
|
||||
FILE *
|
||||
Stream *
|
||||
HttpServerStream(HttpServerContext * c)
|
||||
{
|
||||
if (!c)
|
||||
|
@ -243,25 +243,25 @@ HttpServerStream(HttpServerContext * c)
|
|||
void
|
||||
HttpSendHeaders(HttpServerContext * c)
|
||||
{
|
||||
FILE *fp = c->stream;
|
||||
Stream *fp = c->stream;
|
||||
|
||||
char *key;
|
||||
char *val;
|
||||
|
||||
fprintf(fp, "HTTP/1.0 %d %s\n", c->responseStatus, HttpStatusToString(c->responseStatus));
|
||||
StreamPrintf(fp, "HTTP/1.0 %d %s\n", c->responseStatus, HttpStatusToString(c->responseStatus));
|
||||
|
||||
while (HashMapIterate(c->responseHeaders, &key, (void **) &val))
|
||||
{
|
||||
fprintf(fp, "%s: %s\n", key, val);
|
||||
StreamPrintf(fp, "%s: %s\n", key, val);
|
||||
}
|
||||
|
||||
fprintf(fp, "\n");
|
||||
StreamPuts(fp, "\n");
|
||||
}
|
||||
|
||||
static FILE *
|
||||
static Stream *
|
||||
DequeueConnection(HttpServer * server)
|
||||
{
|
||||
FILE *fp;
|
||||
Stream *fp;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
|
@ -409,7 +409,7 @@ HttpServerWorkerThread(void *args)
|
|||
|
||||
while (!server->stop)
|
||||
{
|
||||
FILE *fp;
|
||||
Stream *fp;
|
||||
HttpServerContext *context;
|
||||
|
||||
char *line = NULL;
|
||||
|
@ -450,7 +450,7 @@ HttpServerWorkerThread(void *args)
|
|||
while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1
|
||||
&& errno == EAGAIN)
|
||||
{
|
||||
clearerr(fp);
|
||||
StreamClearError(fp);
|
||||
|
||||
/* If the server is stopped, or it's been a while, just
|
||||
* give up so we aren't wasting a thread on this client. */
|
||||
|
@ -541,24 +541,24 @@ HttpServerWorkerThread(void *args)
|
|||
|
||||
HttpServerContextFree(context);
|
||||
fp = NULL; /* The above call will close this
|
||||
* FILE */
|
||||
* Stream */
|
||||
goto finish;
|
||||
|
||||
internal_error:
|
||||
fprintf(fp, "HTTP/1.0 500 Internal Server Error\n");
|
||||
fprintf(fp, "Connection: close\n");
|
||||
StreamPuts(fp, "HTTP/1.0 500 Internal Server Error\n");
|
||||
StreamPuts(fp, "Connection: close\n");
|
||||
goto finish;
|
||||
|
||||
bad_request:
|
||||
fprintf(fp, "HTTP/1.0 400 Bad Request\n");
|
||||
fprintf(fp, "Connection: close\n");
|
||||
StreamPuts(fp, "HTTP/1.0 400 Bad Request\n");
|
||||
StreamPuts(fp, "Connection: close\n");
|
||||
goto finish;
|
||||
|
||||
finish:
|
||||
Free(line);
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
StreamClose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,7 +570,7 @@ HttpServerEventThread(void *args)
|
|||
{
|
||||
HttpServer *server = (HttpServer *) args;
|
||||
struct pollfd pollFds[1];
|
||||
FILE *fp;
|
||||
Stream *fp;
|
||||
size_t i;
|
||||
|
||||
server->isRunning = 1;
|
||||
|
@ -632,7 +632,7 @@ HttpServerEventThread(void *args)
|
|||
continue;
|
||||
}
|
||||
|
||||
fp = fdopen(connFd, "r+");
|
||||
fp = StreamFd(connFd);
|
||||
if (!fp)
|
||||
{
|
||||
pthread_mutex_unlock(&server->connQueueMutex);
|
||||
|
@ -655,7 +655,7 @@ HttpServerEventThread(void *args)
|
|||
|
||||
while ((fp = DequeueConnection(server)))
|
||||
{
|
||||
fclose(fp);
|
||||
StreamClose(fp);
|
||||
}
|
||||
|
||||
server->isRunning = 0;
|
||||
|
|
2
src/Io.c
2
src/Io.c
|
@ -134,7 +134,7 @@ int
|
|||
IoVprintf(Io * io, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf;
|
||||
size_t write;
|
||||
int write;
|
||||
|
||||
int ret;
|
||||
|
||||
|
|
106
src/Json.c
106
src/Json.c
|
@ -67,7 +67,7 @@ typedef enum JsonToken
|
|||
|
||||
typedef struct JsonParserState
|
||||
{
|
||||
FILE *stream;
|
||||
Stream *stream;
|
||||
|
||||
JsonToken tokenType;
|
||||
char *token;
|
||||
|
@ -334,12 +334,12 @@ JsonValueFree(JsonValue * value)
|
|||
}
|
||||
|
||||
void
|
||||
JsonEncodeString(const char *str, FILE * out)
|
||||
JsonEncodeString(const char *str, Stream * out)
|
||||
{
|
||||
size_t i;
|
||||
char c;
|
||||
|
||||
fputc('"', out);
|
||||
StreamPutc(out, '"');
|
||||
|
||||
i = 0;
|
||||
while ((c = str[i]) != '\0')
|
||||
|
@ -349,23 +349,23 @@ JsonEncodeString(const char *str, FILE * out)
|
|||
case '\\':
|
||||
case '"':
|
||||
case '/':
|
||||
fputc('\\', out);
|
||||
fputc(c, out);
|
||||
StreamPutc(out, '\\');
|
||||
StreamPutc(out, c);
|
||||
break;
|
||||
case '\b':
|
||||
fputs("\\b", out);
|
||||
StreamPuts(out, "\\b");
|
||||
break;
|
||||
case '\t':
|
||||
fputs("\\t", out);
|
||||
StreamPuts(out, "\\t");
|
||||
break;
|
||||
case '\n':
|
||||
fputs("\\n", out);
|
||||
StreamPuts(out, "\\n");
|
||||
break;
|
||||
case '\f':
|
||||
fputs("\\f", out);
|
||||
StreamPuts(out, "\\f");
|
||||
break;
|
||||
case '\r':
|
||||
fputs("\\r", out);
|
||||
StreamPuts(out, "\\r");
|
||||
break;
|
||||
default: /* Assume UTF-8 input */
|
||||
/*
|
||||
|
@ -381,22 +381,22 @@ JsonEncodeString(const char *str, FILE * out)
|
|||
*/
|
||||
if (c <= 0x001F)
|
||||
{
|
||||
fprintf(out, "\\u%04x", c);
|
||||
StreamPrintf(out, "\\u%04x", c);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputc(c, out);
|
||||
StreamPutc(out, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
fputc('"', out);
|
||||
StreamPutc(out, '"');
|
||||
}
|
||||
|
||||
static char *
|
||||
JsonDecodeString(FILE * in)
|
||||
JsonDecodeString(Stream * in)
|
||||
{
|
||||
const size_t strBlockSize = 16;
|
||||
|
||||
|
@ -419,7 +419,7 @@ JsonDecodeString(FILE * in)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
while ((c = fgetc(in)) != EOF)
|
||||
while ((c = StreamGetc(in)) != EOF)
|
||||
{
|
||||
if (c <= 0x001F)
|
||||
{
|
||||
|
@ -449,7 +449,7 @@ JsonDecodeString(FILE * in)
|
|||
return str;
|
||||
break;
|
||||
case '\\':
|
||||
c = fgetc(in);
|
||||
c = StreamGetc(in);
|
||||
switch (c)
|
||||
{
|
||||
case '\\':
|
||||
|
@ -479,8 +479,14 @@ JsonDecodeString(FILE * in)
|
|||
a[1] = '\0';
|
||||
break;
|
||||
case 'u':
|
||||
/* Read \uXXXX point into a 4-byte buffer */
|
||||
if (fscanf(in, "%04lx", &utf8) != 1)
|
||||
/* Read 4 characters into a */
|
||||
if (!StreamGets(in, a, sizeof(a)))
|
||||
{
|
||||
Free(str);
|
||||
return NULL;
|
||||
}
|
||||
/* Interpret characters as a hex number */
|
||||
if (sscanf(a, "%04lx", &utf8) != 1)
|
||||
{
|
||||
/* Bad hex value */
|
||||
Free(str);
|
||||
|
@ -562,7 +568,7 @@ JsonDecodeString(FILE * in)
|
|||
}
|
||||
|
||||
void
|
||||
JsonEncodeValue(JsonValue * value, FILE * out, int level)
|
||||
JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
||||
{
|
||||
size_t i;
|
||||
size_t len;
|
||||
|
@ -577,47 +583,47 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level)
|
|||
arr = value->as.array;
|
||||
len = ArraySize(arr);
|
||||
|
||||
fputc('[', out);
|
||||
StreamPutc(out, '[');
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (level >= 0)
|
||||
{
|
||||
fprintf(out, "\n%*s", level + 2, "");
|
||||
StreamPrintf(out, "\n%*s", level + 2, "");
|
||||
}
|
||||
JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level);
|
||||
if (i < len - 1)
|
||||
{
|
||||
fputc(',', out);
|
||||
StreamPutc(out, ',');
|
||||
}
|
||||
}
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
fprintf(out, "\n%*s", level, "");
|
||||
StreamPrintf(out, "\n%*s", level, "");
|
||||
}
|
||||
fputc(']', out);
|
||||
StreamPutc(out, ']');
|
||||
break;
|
||||
case JSON_STRING:
|
||||
JsonEncodeString(value->as.string, out);
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
fprintf(out, "%ld", value->as.integer);
|
||||
StreamPrintf(out, "%ld", value->as.integer);
|
||||
break;
|
||||
case JSON_FLOAT:
|
||||
fprintf(out, "%f", value->as.floating);
|
||||
StreamPrintf(out, "%f", value->as.floating);
|
||||
break;
|
||||
case JSON_BOOLEAN:
|
||||
if (value->as.boolean)
|
||||
{
|
||||
fputs("true", out);
|
||||
StreamPuts(out, "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("false", out);
|
||||
StreamPuts(out, "false");
|
||||
}
|
||||
break;
|
||||
case JSON_NULL:
|
||||
fputs("null", out);
|
||||
StreamPuts(out, "null");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -625,7 +631,7 @@ JsonEncodeValue(JsonValue * value, FILE * out, int level)
|
|||
}
|
||||
|
||||
int
|
||||
JsonEncode(HashMap * object, FILE * out, int level)
|
||||
JsonEncode(HashMap * object, Stream * out, int level)
|
||||
{
|
||||
size_t index;
|
||||
size_t count;
|
||||
|
@ -643,10 +649,10 @@ JsonEncode(HashMap * object, FILE * out, int level)
|
|||
count++;
|
||||
}
|
||||
|
||||
fputc('{', out);
|
||||
StreamPutc(out, '{');
|
||||
if (level >= 0)
|
||||
{
|
||||
fputc('\n', out);
|
||||
StreamPutc(out, '\n');
|
||||
}
|
||||
|
||||
index = 0;
|
||||
|
@ -654,27 +660,27 @@ JsonEncode(HashMap * object, FILE * out, int level)
|
|||
{
|
||||
if (level >= 0)
|
||||
{
|
||||
fprintf(out, "%*s", level + 2, "");
|
||||
StreamPrintf(out, "%*s", level + 2, "");
|
||||
}
|
||||
|
||||
JsonEncodeString(key, out);
|
||||
|
||||
fputc(':', out);
|
||||
StreamPutc(out, ':');
|
||||
if (level >= 0)
|
||||
{
|
||||
fputc(' ', out);
|
||||
StreamPutc(out, ' ');
|
||||
}
|
||||
|
||||
JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
|
||||
|
||||
if (index < count - 1)
|
||||
{
|
||||
fputc(',', out);
|
||||
StreamPutc(out, ',');
|
||||
}
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
fputc('\n', out);
|
||||
StreamPutc(out, '\n');
|
||||
}
|
||||
|
||||
index++;
|
||||
|
@ -682,9 +688,9 @@ JsonEncode(HashMap * object, FILE * out, int level)
|
|||
|
||||
if (level >= 0)
|
||||
{
|
||||
fprintf(out, "%*s", level, "");
|
||||
StreamPrintf(out, "%*s", level, "");
|
||||
}
|
||||
fputc('}', out);
|
||||
StreamPutc(out, '}');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -715,18 +721,18 @@ JsonConsumeWhitespace(JsonParserState * state)
|
|||
|
||||
while (1)
|
||||
{
|
||||
c = fgetc(state->stream);
|
||||
c = StreamGetc(state->stream);
|
||||
|
||||
if (feof(state->stream))
|
||||
if (StreamEof(state->stream))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ferror(state->stream))
|
||||
if (StreamError(state->stream))
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
{
|
||||
clearerr(state->stream);
|
||||
StreamClearError(state->stream);
|
||||
tries++;
|
||||
|
||||
if (tries >= nRetries || readFlg)
|
||||
|
@ -765,7 +771,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
{
|
||||
int c = JsonConsumeWhitespace(state);
|
||||
|
||||
if (feof(state->stream))
|
||||
if (StreamEof(state->stream))
|
||||
{
|
||||
state->tokenType = TOKEN_EOF;
|
||||
return;
|
||||
|
@ -822,7 +828,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
}
|
||||
state->token[0] = c;
|
||||
|
||||
while ((c = fgetc(state->stream)) != EOF)
|
||||
while ((c = StreamGetc(state->stream)) != EOF)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
|
@ -838,7 +844,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
}
|
||||
else if (!isdigit(c))
|
||||
{
|
||||
ungetc(c, state->stream);
|
||||
StreamUngetc(state->stream, c);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -893,7 +899,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
switch (c)
|
||||
{
|
||||
case 't':
|
||||
if (!fgets(state->token + 1, 4, state->stream))
|
||||
if (!StreamGets(state->stream, state->token + 1, 4))
|
||||
{
|
||||
state->tokenType = TOKEN_EOF;
|
||||
Free(state->token);
|
||||
|
@ -914,7 +920,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (!fgets(state->token + 1, 5, state->stream))
|
||||
if (!StreamGets(state->stream, state->token + 1, 5))
|
||||
{
|
||||
state->tokenType = TOKEN_EOF;
|
||||
Free(state->token);
|
||||
|
@ -935,7 +941,7 @@ JsonTokenSeek(JsonParserState * state)
|
|||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (!fgets(state->token + 1, 4, state->stream))
|
||||
if (!StreamGets(state->stream, state->token + 1, 4))
|
||||
{
|
||||
state->tokenType = TOKEN_EOF;
|
||||
Free(state->token);
|
||||
|
@ -1155,7 +1161,7 @@ error:
|
|||
}
|
||||
|
||||
HashMap *
|
||||
JsonDecode(FILE * stream)
|
||||
JsonDecode(Stream * stream)
|
||||
{
|
||||
HashMap *result;
|
||||
|
||||
|
|
38
src/Log.c
38
src/Log.c
|
@ -38,7 +38,7 @@ struct LogConfig
|
|||
{
|
||||
int level;
|
||||
size_t indent;
|
||||
FILE *out;
|
||||
Stream *out;
|
||||
int flags;
|
||||
char *tsFmt;
|
||||
|
||||
|
@ -109,7 +109,7 @@ LogConfigFree(LogConfig * config)
|
|||
return;
|
||||
}
|
||||
|
||||
fclose(config->out);
|
||||
StreamClose(config->out);
|
||||
Free(config);
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ LogConfigLevelSet(LogConfig * config, int level)
|
|||
}
|
||||
|
||||
void
|
||||
LogConfigOutputSet(LogConfig * config, FILE * out)
|
||||
LogConfigOutputSet(LogConfig * config, Stream * out)
|
||||
{
|
||||
if (!config)
|
||||
{
|
||||
|
@ -178,7 +178,7 @@ LogConfigOutputSet(LogConfig * config, FILE * out)
|
|||
}
|
||||
else
|
||||
{
|
||||
config->out = stdout;
|
||||
config->out = StreamStdout();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ Log(LogConfig * config, int level, const char *msg,...)
|
|||
}
|
||||
|
||||
doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR)
|
||||
&& isatty(fileno(config->out));
|
||||
&& isatty(StreamFileno(config->out));
|
||||
|
||||
if (doColor)
|
||||
{
|
||||
|
@ -276,10 +276,10 @@ Log(LogConfig * config, int level, const char *msg,...)
|
|||
break;
|
||||
}
|
||||
|
||||
fputs(ansi, config->out);
|
||||
StreamPuts(config->out, ansi);
|
||||
}
|
||||
|
||||
fputc('[', config->out);
|
||||
StreamPutc(config->out, '[');
|
||||
|
||||
if (config->tsFmt)
|
||||
{
|
||||
|
@ -292,10 +292,10 @@ Log(LogConfig * config, int level, const char *msg,...)
|
|||
|
||||
if (tsLength)
|
||||
{
|
||||
fputs(tsBuffer, config->out);
|
||||
StreamPuts(config->out, tsBuffer);
|
||||
if (!isspace((unsigned char) tsBuffer[tsLength - 1]))
|
||||
{
|
||||
fputc(' ', config->out);
|
||||
StreamPutc(config->out, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,32 +331,26 @@ Log(LogConfig * config, int level, const char *msg,...)
|
|||
break;
|
||||
}
|
||||
|
||||
fprintf(config->out, "%c]", indicator);
|
||||
StreamPrintf(config->out, "%c]", indicator);
|
||||
|
||||
if (doColor)
|
||||
{
|
||||
/* ANSI Reset */
|
||||
fputs("\033[0m", config->out);
|
||||
StreamPuts(config->out, "\033[0m");
|
||||
}
|
||||
|
||||
fputc(' ', config->out);
|
||||
StreamPutc(config->out, ' ');
|
||||
for (i = 0; i < config->indent; i++)
|
||||
{
|
||||
fputc(' ', config->out);
|
||||
StreamPutc(config->out, ' ');
|
||||
}
|
||||
|
||||
va_start(argp, msg);
|
||||
vfprintf(config->out, msg, argp);
|
||||
fputc('\n', config->out);
|
||||
StreamVprintf(config->out, msg, argp);
|
||||
StreamPutc(config->out, '\n');
|
||||
va_end(argp);
|
||||
|
||||
/* If we are debugging, there might be something that's going to
|
||||
* segfault the program coming up, so flush the output stream
|
||||
* immediately. */
|
||||
if (config->level == LOG_DEBUG)
|
||||
{
|
||||
fflush(config->out);
|
||||
}
|
||||
StreamFlush(config->out);
|
||||
|
||||
pthread_mutex_unlock(&config->lock);
|
||||
}
|
||||
|
|
37
src/Main.c
37
src/Main.c
|
@ -72,7 +72,7 @@ main(int argc, char **argv)
|
|||
char *configArg = NULL;
|
||||
|
||||
/* Config file */
|
||||
FILE *configFile = NULL;
|
||||
Stream *configFile = NULL;
|
||||
HashMap *config = NULL;
|
||||
|
||||
/* Program configuration */
|
||||
|
@ -155,11 +155,11 @@ main(int argc, char **argv)
|
|||
}
|
||||
else if (strcmp(configArg, "-") == 0)
|
||||
{
|
||||
configFile = stdin;
|
||||
configFile = StreamStdin();
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(stdin);
|
||||
StreamClose(StreamStdin());
|
||||
#ifdef __OpenBSD__
|
||||
if (unveil(configArg, "r") != 0)
|
||||
{
|
||||
|
@ -168,7 +168,7 @@ main(int argc, char **argv)
|
|||
goto finish;
|
||||
}
|
||||
#endif
|
||||
configFile = fopen(configArg, "r");
|
||||
configFile = StreamOpen(configArg, "r");
|
||||
if (!configFile)
|
||||
{
|
||||
Log(lc, LOG_ERR, "Unable to open configuration file '%s' for reading.", configArg);
|
||||
|
@ -180,7 +180,7 @@ main(int argc, char **argv)
|
|||
Log(lc, LOG_NOTICE, "Processing configuration file '%s'.", configArg);
|
||||
|
||||
config = JsonDecode(configFile);
|
||||
fclose(configFile);
|
||||
StreamClose(configFile);
|
||||
|
||||
if (!config)
|
||||
{
|
||||
|
@ -250,7 +250,7 @@ main(int argc, char **argv)
|
|||
|
||||
if (tConfig->flags & TELODENDRIA_LOG_FILE)
|
||||
{
|
||||
FILE *logFile = fopen("telodendria.log", "a");
|
||||
Stream *logFile = StreamOpen("telodendria.log", "a");
|
||||
|
||||
if (!logFile)
|
||||
{
|
||||
|
@ -476,9 +476,9 @@ finish:
|
|||
* If we're not logging to standard output, then we can close it. Otherwise,
|
||||
* if we are logging to stdout, LogConfigFree() will close it for us.
|
||||
*/
|
||||
if (!tConfig || !(tConfig->flags & TELODENDRIA_LOG_STDOUT))
|
||||
if (tConfig && !(tConfig->flags & TELODENDRIA_LOG_STDOUT))
|
||||
{
|
||||
fclose(stdout);
|
||||
StreamClose(StreamStdout());
|
||||
}
|
||||
|
||||
DbClose(matrixArgs.db);
|
||||
|
@ -486,15 +486,26 @@ finish:
|
|||
LogConfigTimeStampFormatSet(lc, NULL);
|
||||
TelodendriaConfigFree(tConfig);
|
||||
|
||||
Log(lc, LOG_DEBUG, "");
|
||||
MemoryIterate(TelodendriaMemoryIterator, lc);
|
||||
Log(lc, LOG_DEBUG, "");
|
||||
|
||||
Log(lc, LOG_DEBUG, "Exiting with code '%d'.", exit);
|
||||
|
||||
/*
|
||||
* Uninstall the memory hook because it uses the Log
|
||||
* API, whose configuration is being freed now, so it
|
||||
* won't work anymore.
|
||||
*/
|
||||
MemoryHook(NULL, NULL);
|
||||
|
||||
LogConfigFree(lc);
|
||||
|
||||
MemoryFreeAll();
|
||||
/* Standard error should never have been opened, but just in case
|
||||
* it was, this doesn't hurt anything. */
|
||||
StreamClose(StreamStderr());
|
||||
|
||||
fclose(stderr);
|
||||
/* Generate a memory report if any leaks occurred. At this point no
|
||||
* memory should be allocated. */
|
||||
TelodendriaGenerateMemReport();
|
||||
|
||||
MemoryFreeAll();
|
||||
return exit;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
|
|||
{
|
||||
MatrixHttpHandlerArgs *args = (MatrixHttpHandlerArgs *) argp;
|
||||
LogConfig *lc = args->lc;
|
||||
FILE *stream;
|
||||
Stream *stream;
|
||||
HashMap *response = NULL;
|
||||
|
||||
char *key;
|
||||
|
@ -133,7 +133,7 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
|
|||
JsonEncode(response, stream, JSON_DEFAULT);
|
||||
JsonFree(response);
|
||||
|
||||
fprintf(stream, "\n");
|
||||
StreamPrintf(stream, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
ROUTE_IMPL(RouteStatic, args)
|
||||
{
|
||||
FILE *stream = HttpServerStream(args->context);
|
||||
Stream *stream = HttpServerStream(args->context);
|
||||
char *pathPart = MATRIX_PATH_POP(args->path);
|
||||
|
||||
HttpResponseHeader(args->context, "Content-Type", "text/html");
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <Http.h>
|
||||
|
||||
void
|
||||
StaticError(FILE * stream, HttpStatus error)
|
||||
StaticError(Stream * stream, HttpStatus error)
|
||||
{
|
||||
char title[10];
|
||||
|
||||
|
@ -34,8 +34,8 @@ StaticError(FILE * stream, HttpStatus error)
|
|||
|
||||
HtmlBegin(stream, title);
|
||||
|
||||
fprintf(stream, "<h2 style=\"text-align: center\">%s</h2>",
|
||||
HttpStatusToString(error));
|
||||
StreamPrintf(stream, "<h2 style=\"text-align: center\">%s</h2>",
|
||||
HttpStatusToString(error));
|
||||
|
||||
HtmlEnd(stream);
|
||||
}
|
||||
|
|
|
@ -25,30 +25,30 @@
|
|||
#include <Html.h>
|
||||
|
||||
void
|
||||
StaticItWorks(FILE * stream)
|
||||
StaticItWorks(Stream * stream)
|
||||
{
|
||||
HtmlBegin(stream, "It works! Telodendria is running.");
|
||||
|
||||
fprintf(stream,
|
||||
"<style>"
|
||||
"p {"
|
||||
" text-align: center;"
|
||||
"}"
|
||||
"</style>"
|
||||
StreamPuts(stream,
|
||||
"<style>"
|
||||
"p {"
|
||||
" text-align: center;"
|
||||
"}"
|
||||
"</style>"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
"<p>"
|
||||
StreamPuts(stream,
|
||||
"<p>"
|
||||
"Your Telodendria server is listening on this port and is ready "
|
||||
"for messages."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"To use this server, you'll need <a href=\"https://matrix.org/clients\">"
|
||||
"a Matrix client</a>."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"Welcome to the Matrix universe :)"
|
||||
"</p>"
|
||||
"for messages."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"To use this server, you'll need <a href=\"https://matrix.org/clients\">"
|
||||
"a Matrix client</a>."
|
||||
"</p>"
|
||||
"<p>"
|
||||
"Welcome to the Matrix universe :)"
|
||||
"</p>"
|
||||
);
|
||||
|
||||
HtmlEnd(stream);
|
||||
|
|
|
@ -25,121 +25,121 @@
|
|||
#include <Html.h>
|
||||
|
||||
void
|
||||
StaticLogin(FILE * stream)
|
||||
StaticLogin(Stream * stream)
|
||||
{
|
||||
HtmlBegin(stream, "Log In");
|
||||
|
||||
fprintf(stream,
|
||||
"<div class=\"form\">"
|
||||
"<form id=\"login-form\">"
|
||||
"<label for=\"user\">Username:</label>"
|
||||
"<input type=\"text\" id=\"user\">"
|
||||
"<label for=\"password\">Password:</label>"
|
||||
"<input type=\"password\" id=\"password\">"
|
||||
"<br>"
|
||||
"<input type=\"submit\" value=\"Log In\">"
|
||||
"</form>");
|
||||
StreamPuts(stream,
|
||||
"<div class=\"form\">"
|
||||
"<form id=\"login-form\">"
|
||||
"<label for=\"user\">Username:</label>"
|
||||
"<input type=\"text\" id=\"user\">"
|
||||
"<label for=\"password\">Password:</label>"
|
||||
"<input type=\"password\" id=\"password\">"
|
||||
"<br>"
|
||||
"<input type=\"submit\" value=\"Log In\">"
|
||||
"</form>");
|
||||
|
||||
HtmlBeginStyle(stream);
|
||||
fprintf(stream,
|
||||
"#error-msg {"
|
||||
" display: none;"
|
||||
" color: red;"
|
||||
" text-align: center;"
|
||||
" font-weight: bold;"
|
||||
" font-size: larger;"
|
||||
"}");
|
||||
StreamPuts(stream,
|
||||
"#error-msg {"
|
||||
" display: none;"
|
||||
" color: red;"
|
||||
" text-align: center;"
|
||||
" font-weight: bold;"
|
||||
" font-size: larger;"
|
||||
"}");
|
||||
HtmlEndStyle(stream);
|
||||
|
||||
fprintf(stream,
|
||||
"<p id=\"error-msg\"></p>"
|
||||
"</div>"
|
||||
StreamPuts(stream,
|
||||
"<p id=\"error-msg\"></p>"
|
||||
"</div>"
|
||||
);
|
||||
|
||||
HtmlBeginJs(stream);
|
||||
fprintf(stream,
|
||||
"function findGetParameter(parameterName) {"
|
||||
" var result = null;"
|
||||
" var tmp = [];"
|
||||
" var items = location.search.substr(1).split(\"&\");"
|
||||
" for (var index = 0; index < items.length; index++) {"
|
||||
" tmp = items[index].split(\"=\");"
|
||||
" if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);"
|
||||
" }"
|
||||
" return result;"
|
||||
"}"
|
||||
"function setError(msg) {"
|
||||
" var err = document.getElementById('error-msg');"
|
||||
" if (msg) {"
|
||||
" err.style.display = 'block';"
|
||||
" err.innerHTML = msg;"
|
||||
" } else {"
|
||||
" err.style.display = 'none';"
|
||||
" }"
|
||||
"}"
|
||||
StreamPuts(stream,
|
||||
"function findGetParameter(parameterName) {"
|
||||
" var result = null;"
|
||||
" var tmp = [];"
|
||||
" var items = location.search.substr(1).split(\"&\");"
|
||||
" for (var index = 0; index < items.length; index++) {"
|
||||
" tmp = items[index].split(\"=\");"
|
||||
" if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);"
|
||||
" }"
|
||||
" return result;"
|
||||
"}"
|
||||
"function setError(msg) {"
|
||||
" var err = document.getElementById('error-msg');"
|
||||
" if (msg) {"
|
||||
" err.style.display = 'block';"
|
||||
" err.innerHTML = msg;"
|
||||
" } else {"
|
||||
" err.style.display = 'none';"
|
||||
" }"
|
||||
"}"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
"function buildRequest(user, pass) {"
|
||||
" var d = findGetParameter('device_id');"
|
||||
StreamPuts(stream,
|
||||
"function buildRequest(user, pass) {"
|
||||
" var d = findGetParameter('device_id');"
|
||||
" var i = findGetParameter('initial_device_display_name');"
|
||||
" var r = findGetParameter('refresh_token') === 'true';"
|
||||
" var request = {};"
|
||||
" request['type'] = 'm.login.password';"
|
||||
" request['identifier'] = {"
|
||||
" type: 'm.id.user',"
|
||||
" user: user"
|
||||
" };"
|
||||
" request['password'] = pass;"
|
||||
" if (d) request['device_id'] = d;"
|
||||
" if (i) request['initial_device_display_name'] = i;"
|
||||
" if (r) request['refresh_token'] = r;"
|
||||
" return request;"
|
||||
"}"
|
||||
" var r = findGetParameter('refresh_token') === 'true';"
|
||||
" var request = {};"
|
||||
" request['type'] = 'm.login.password';"
|
||||
" request['identifier'] = {"
|
||||
" type: 'm.id.user',"
|
||||
" user: user"
|
||||
" };"
|
||||
" request['password'] = pass;"
|
||||
" if (d) request['device_id'] = d;"
|
||||
" if (i) request['initial_device_display_name'] = i;"
|
||||
" if (r) request['refresh_token'] = r;"
|
||||
" return request;"
|
||||
"}"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
"function processResponse(xhr) {"
|
||||
" if (xhr.readyState == 4) {"
|
||||
" var r = JSON.parse(xhr.responseText);"
|
||||
" if (!r.error) {"
|
||||
" if (window.onLogin) {"
|
||||
" window.onLogin(r);"
|
||||
" } else {"
|
||||
" setError('Client malfunction. Your client should have defined window.onLogin()');"
|
||||
" }"
|
||||
" } else {"
|
||||
" setError(r.errcode + ': ' + r.error);"
|
||||
" }"
|
||||
" }"
|
||||
"}"
|
||||
StreamPuts(stream,
|
||||
"function processResponse(xhr) {"
|
||||
" if (xhr.readyState == 4) {"
|
||||
" var r = JSON.parse(xhr.responseText);"
|
||||
" if (!r.error) {"
|
||||
" if (window.onLogin) {"
|
||||
" window.onLogin(r);"
|
||||
" } else {"
|
||||
" setError('Client malfunction. Your client should have defined window.onLogin()');"
|
||||
" }"
|
||||
" } else {"
|
||||
" setError(r.errcode + ': ' + r.error);"
|
||||
" }"
|
||||
" }"
|
||||
"}"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
"function sendRequest(request) {"
|
||||
" var xhr = new XMLHttpRequest();"
|
||||
" xhr.open('POST', '/_matrix/client/v3/login');"
|
||||
StreamPuts(stream,
|
||||
"function sendRequest(request) {"
|
||||
" var xhr = new XMLHttpRequest();"
|
||||
" xhr.open('POST', '/_matrix/client/v3/login');"
|
||||
" xhr.setRequestHeader('Content-Type', 'application/json');"
|
||||
" xhr.onreadystatechange = () => processResponse(xhr);"
|
||||
" xhr.send(JSON.stringify(request));"
|
||||
"}"
|
||||
" xhr.onreadystatechange = () => processResponse(xhr);"
|
||||
" xhr.send(JSON.stringify(request));"
|
||||
"}"
|
||||
);
|
||||
|
||||
fprintf(stream,
|
||||
"window.addEventListener('load', () => {"
|
||||
" document.getElementById('login-form').addEventListener('submit', (e) => {"
|
||||
" e.preventDefault();"
|
||||
" var user = document.getElementById('user').value;"
|
||||
" var pass = document.getElementById('password').value;"
|
||||
" if (!user || !pass) {"
|
||||
StreamPuts(stream,
|
||||
"window.addEventListener('load', () => {"
|
||||
" document.getElementById('login-form').addEventListener('submit', (e) => {"
|
||||
" e.preventDefault();"
|
||||
" var user = document.getElementById('user').value;"
|
||||
" var pass = document.getElementById('password').value;"
|
||||
" if (!user || !pass) {"
|
||||
" setError('Please provide a username and password.');"
|
||||
" return;"
|
||||
" }"
|
||||
" setError(null);"
|
||||
" var request = buildRequest(user, pass);"
|
||||
" sendRequest(request);"
|
||||
" });"
|
||||
"});"
|
||||
" return;"
|
||||
" }"
|
||||
" setError(null);"
|
||||
" var request = buildRequest(user, pass);"
|
||||
" sendRequest(request);"
|
||||
" });"
|
||||
"});"
|
||||
);
|
||||
HtmlEndJs(stream);
|
||||
|
||||
|
|
194
src/Stream.c
194
src/Stream.c
|
@ -28,6 +28,7 @@
|
|||
#include <Util.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -42,23 +43,26 @@
|
|||
|
||||
#define STREAM_EOF (1 << 0)
|
||||
#define STREAM_ERR (1 << 1)
|
||||
#define STREAM_TTY (1 << 2)
|
||||
|
||||
struct Stream
|
||||
{
|
||||
Io *io;
|
||||
|
||||
int *rBuf;
|
||||
char *rBuf;
|
||||
size_t rLen;
|
||||
size_t rOff;
|
||||
|
||||
int *wBuf;
|
||||
char *wBuf;
|
||||
size_t wLen;
|
||||
|
||||
int *ugBuf;
|
||||
char *ugBuf;
|
||||
size_t ugSize;
|
||||
size_t ugLen;
|
||||
|
||||
int flags:2;
|
||||
int flags;
|
||||
|
||||
int fd;
|
||||
};
|
||||
|
||||
Stream *
|
||||
|
@ -79,6 +83,7 @@ StreamIo(Io * io)
|
|||
|
||||
memset(stream, 0, sizeof(Stream));
|
||||
stream->io = io;
|
||||
stream->fd = -1;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
@ -87,26 +92,54 @@ Stream *
|
|||
StreamFd(int fd)
|
||||
{
|
||||
Io *io = IoFd(fd);
|
||||
Stream *stream;
|
||||
|
||||
if (!io)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return StreamIo(io);
|
||||
stream = StreamIo(io);
|
||||
if (!stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->fd = fd;
|
||||
|
||||
if (isatty(stream->fd))
|
||||
{
|
||||
stream->flags |= STREAM_TTY;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Stream *
|
||||
StreamFile(FILE * fp)
|
||||
{
|
||||
Io *io = IoFile(fp);
|
||||
Stream *stream;
|
||||
|
||||
if (!io)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return StreamIo(io);
|
||||
stream = StreamIo(io);
|
||||
if (!stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->fd = fileno(fp);
|
||||
|
||||
if (isatty(stream->fd))
|
||||
{
|
||||
stream->flags |= STREAM_TTY;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Stream *
|
||||
|
@ -122,6 +155,45 @@ StreamOpen(const char *path, const char *mode)
|
|||
return StreamFile(fp);
|
||||
}
|
||||
|
||||
Stream *
|
||||
StreamStdout(void)
|
||||
{
|
||||
static Stream *stdOut = NULL;
|
||||
|
||||
if (!stdOut)
|
||||
{
|
||||
stdOut = StreamFd(STDOUT_FILENO);
|
||||
}
|
||||
|
||||
return stdOut;
|
||||
}
|
||||
|
||||
Stream *
|
||||
StreamStderr(void)
|
||||
{
|
||||
static Stream *stdErr = NULL;
|
||||
|
||||
if (!stdErr)
|
||||
{
|
||||
stdErr = StreamFd(STDERR_FILENO);
|
||||
}
|
||||
|
||||
return stdErr;
|
||||
}
|
||||
|
||||
Stream *
|
||||
StreamStdin(void)
|
||||
{
|
||||
static Stream *stdIn = NULL;
|
||||
|
||||
if (!stdIn)
|
||||
{
|
||||
stdIn = StreamFd(STDIN_FILENO);
|
||||
}
|
||||
|
||||
return stdIn;
|
||||
}
|
||||
|
||||
int
|
||||
StreamClose(Stream * stream)
|
||||
{
|
||||
|
@ -171,8 +243,10 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
|||
* buffered. It therefore allows us to finish filling the buffer
|
||||
* and then only flush it when necessary, preventing superfluous
|
||||
* writes. */
|
||||
char *buf;
|
||||
ssize_t len;
|
||||
|
||||
char *buf = NULL;
|
||||
size_t bufSize = 0;
|
||||
FILE *fp;
|
||||
|
||||
int ret;
|
||||
|
||||
|
@ -181,38 +255,22 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
|||
return -1;
|
||||
}
|
||||
|
||||
buf = Malloc(IO_BUFFER);
|
||||
if (!buf)
|
||||
fp = open_memstream(&buf, &bufSize);
|
||||
if (!fp)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = vsnprintf(buf, IO_BUFFER, fmt, ap);
|
||||
ret = vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
|
||||
if (len < 0)
|
||||
if (ret >= 0)
|
||||
{
|
||||
Free(buf);
|
||||
return len;
|
||||
ret = StreamPuts(stream, buf);
|
||||
}
|
||||
|
||||
if (len >= IO_BUFFER)
|
||||
{
|
||||
char *new = Realloc(buf, len + 1);
|
||||
|
||||
if (!new)
|
||||
{
|
||||
Free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = new;
|
||||
|
||||
vsnprintf(buf, len, fmt, ap);
|
||||
}
|
||||
|
||||
ret = StreamPuts(stream, buf);
|
||||
|
||||
Free(buf);
|
||||
free(buf); /* Allocated by stdlib, not Memory
|
||||
* API */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -257,7 +315,7 @@ StreamGetc(Stream * stream)
|
|||
if (!stream->rBuf)
|
||||
{
|
||||
/* No buffer allocated yet */
|
||||
stream->rBuf = Malloc(IO_BUFFER * sizeof(int));
|
||||
stream->rBuf = Malloc(IO_BUFFER);
|
||||
if (!stream->rBuf)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
|
@ -319,7 +377,7 @@ StreamUngetc(Stream * stream, int c)
|
|||
|
||||
if (stream->ugLen >= stream->ugSize)
|
||||
{
|
||||
int *new;
|
||||
char *new;
|
||||
|
||||
stream->ugSize += IO_BUFFER;
|
||||
new = Realloc(stream->ugBuf, stream->ugSize);
|
||||
|
@ -335,7 +393,7 @@ StreamUngetc(Stream * stream, int c)
|
|||
stream->ugBuf = new;
|
||||
}
|
||||
|
||||
stream->ugBuf[stream->ugLen - 1] = c;
|
||||
stream->ugBuf[stream->ugLen] = c;
|
||||
stream->ugLen++;
|
||||
|
||||
return c;
|
||||
|
@ -352,7 +410,7 @@ StreamPutc(Stream * stream, int c)
|
|||
|
||||
if (!stream->wBuf)
|
||||
{
|
||||
stream->wBuf = Malloc(IO_BUFFER * sizeof(int));
|
||||
stream->wBuf = Malloc(IO_BUFFER);
|
||||
if (!stream->wBuf)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
|
@ -377,6 +435,24 @@ StreamPutc(Stream * stream, int c)
|
|||
stream->wBuf[stream->wLen] = c;
|
||||
stream->wLen++;
|
||||
|
||||
if (stream->flags & STREAM_TTY && c == '\n')
|
||||
{
|
||||
/* Newline encountered on a TTY; write now. This fixes some
|
||||
* strange behavior on certain TTYs where a newline is written
|
||||
* to the screen upon flush even when no newline exists in the
|
||||
* stream. We just flush on newlines, but only if we're
|
||||
* directly writing to a TTY. */
|
||||
ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen);
|
||||
|
||||
if (writeRes == -1)
|
||||
{
|
||||
stream->flags |= STREAM_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
stream->wLen = 0;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -405,6 +481,46 @@ StreamPuts(Stream * stream, char *str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
StreamGets(Stream * stream, char *str, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
errno = EBADF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size <= 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < size - 1; i++)
|
||||
{
|
||||
int c = StreamGetc(stream);
|
||||
|
||||
if (StreamEof(stream) || StreamError(stream))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
str[i] = c;
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int
|
||||
StreamEof(Stream * stream)
|
||||
{
|
||||
|
@ -505,3 +621,9 @@ StreamCopy(Stream * in, Stream * out)
|
|||
StreamFlush(out);
|
||||
return nBytes;
|
||||
}
|
||||
|
||||
int
|
||||
StreamFileno(Stream * stream)
|
||||
{
|
||||
return stream ? stream->fd : -1;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <Memory.h>
|
||||
#include <Log.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
const char
|
||||
TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH] = {
|
||||
" .= -=- ",
|
||||
|
@ -62,21 +64,6 @@ const char
|
|||
"======================================================="
|
||||
};
|
||||
|
||||
void
|
||||
TelodendriaHexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
|
||||
{
|
||||
LogConfig *lc = args;
|
||||
|
||||
if (hexBuf && asciiBuf)
|
||||
{
|
||||
Log(lc, LOG_DEBUG, "%04x: %s | %s |", off, hexBuf, asciiBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(lc, LOG_DEBUG, "%04x", off);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
|
||||
{
|
||||
|
@ -109,20 +96,78 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
|
|||
MemoryInfoGetPointer(i));
|
||||
}
|
||||
|
||||
void
|
||||
TelodendriaMemoryIterator(MemoryInfo * i, void *args)
|
||||
static void
|
||||
HexDump(size_t off, char *hexBuf, char *asciiBuf, void *args)
|
||||
{
|
||||
LogConfig *lc = (LogConfig *) args;
|
||||
FILE *report = args;
|
||||
|
||||
/* We haven't freed the logger memory yet */
|
||||
if (MemoryInfoGetPointer(i) != lc)
|
||||
if (hexBuf && asciiBuf)
|
||||
{
|
||||
Log(lc, LOG_WARNING, "%s:%d: %lu bytes of memory at %p leaked.",
|
||||
fprintf(report, "%04lx: %s | %s |\n", off, hexBuf, asciiBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(report, "%04lx\n", off);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
MemoryIterator(MemoryInfo * i, void *args)
|
||||
{
|
||||
FILE *report = args;
|
||||
|
||||
fprintf(report, "%s:%d: %lu bytes at %p\n",
|
||||
MemoryInfoGetFile(i), MemoryInfoGetLine(i),
|
||||
MemoryInfoGetSize(i), MemoryInfoGetPointer(i));
|
||||
|
||||
MemoryHexDump(i, TelodendriaHexDump, lc);
|
||||
MemoryHexDump(i, HexDump, report);
|
||||
|
||||
fprintf(report, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
TelodendriaGenerateMemReport(void)
|
||||
{
|
||||
static const char *reportName = "Memory.txt";
|
||||
|
||||
/*
|
||||
* Use C standard IO instead of the Stream or Io APIs, because
|
||||
* those use the Memory API, and that's exactly what we're trying
|
||||
* to assess, so using it would generate false positives. None of
|
||||
* this code should leak memory.
|
||||
*/
|
||||
FILE *report;
|
||||
time_t currentTime;
|
||||
struct tm *timeInfo;
|
||||
char tsBuffer[1024];
|
||||
|
||||
if (!MemoryAllocated())
|
||||
{
|
||||
/* No memory leaked, no need to write the report. This is the
|
||||
* ideal situation; we only want the report to show up if leaks
|
||||
* occurred. */
|
||||
return;
|
||||
}
|
||||
|
||||
report = fopen(reportName, "a");
|
||||
if (!report)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentTime = time(NULL);
|
||||
timeInfo = localtime(¤tTime);
|
||||
strftime(tsBuffer, sizeof(tsBuffer), "%c", timeInfo);
|
||||
|
||||
fprintf(report, "---------- Telodendria Memory Report ----------\n");
|
||||
fprintf(report, "Date: %s\n", tsBuffer);
|
||||
fprintf(report, "Total Bytes: %lu\n", MemoryAllocated());
|
||||
fprintf(report, "\n");
|
||||
|
||||
MemoryIterate(MemoryIterator, report);
|
||||
|
||||
fclose(report);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
20
src/Util.c
20
src/Util.c
|
@ -219,7 +219,7 @@ UtilParseBytes(char *str)
|
|||
}
|
||||
|
||||
ssize_t
|
||||
UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
|
||||
UtilGetDelim(char **linePtr, size_t * n, int delim, Stream * stream)
|
||||
{
|
||||
char *curPos, *newLinePtr;
|
||||
size_t newLinePtrLen;
|
||||
|
@ -246,9 +246,9 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
|
|||
|
||||
while (1)
|
||||
{
|
||||
c = fgetc(stream);
|
||||
c = StreamGetc(stream);
|
||||
|
||||
if (ferror(stream) || (c == EOF && curPos == *linePtr))
|
||||
if (StreamError(stream) || (c == EOF && curPos == *linePtr))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -296,31 +296,31 @@ UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
|
|||
}
|
||||
|
||||
ssize_t
|
||||
UtilGetLine(char **linePtr, size_t * n, FILE * stream)
|
||||
UtilGetLine(char **linePtr, size_t * n, Stream * stream)
|
||||
{
|
||||
return UtilGetDelim(linePtr, n, '\n', stream);
|
||||
}
|
||||
|
||||
size_t
|
||||
UtilStreamCopy(FILE * in, FILE * out)
|
||||
UtilStreamCopy(Stream * in, Stream * out)
|
||||
{
|
||||
size_t bytes = 0;
|
||||
int c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
c = fgetc(in);
|
||||
c = StreamGetc(in);
|
||||
|
||||
if (feof(in))
|
||||
if (StreamEof(in))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ferror(in))
|
||||
if (StreamError(in))
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
{
|
||||
clearerr(in);
|
||||
StreamClearError(in);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
@ -329,7 +329,7 @@ UtilStreamCopy(FILE * in, FILE * out)
|
|||
}
|
||||
}
|
||||
|
||||
fputc(c, out);
|
||||
StreamPutc(out, c);
|
||||
bytes++;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,12 @@
|
|||
#define TELODENDRIA_CANONICALJSON_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Stream.h>
|
||||
|
||||
extern int
|
||||
CanonicalJsonEncode(HashMap *, FILE *);
|
||||
CanonicalJsonEncode(HashMap *, Stream *);
|
||||
|
||||
extern char *
|
||||
CanonicalJsonEncodeToString(HashMap *);
|
||||
|
|
|
@ -24,23 +24,23 @@
|
|||
#ifndef TELODENDRIA_HTML_H
|
||||
#define TELODENDRIA_HTML_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#define HtmlBeginJs(stream) fprintf(stream, \
|
||||
#define HtmlBeginJs(stream) StreamPuts(stream, \
|
||||
"<script>" \
|
||||
"\n// @license magnet:?xt=" \
|
||||
"urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt " \
|
||||
"Expat\n")
|
||||
|
||||
#define HtmlEndJs(stream) fprintf(stream, "\n// @license-end\n</script>")
|
||||
#define HtmlEndJs(stream) StreamPuts(stream, "\n// @license-end\n</script>")
|
||||
|
||||
#define HtmlBeginStyle(stream) fprintf(stream, "<style>")
|
||||
#define HtmlEndStyle(stream) fprintf(stream, "</style>")
|
||||
#define HtmlBeginStyle(stream) StreamPuts(stream, "<style>")
|
||||
#define HtmlEndStyle(stream) StreamPuts(stream, "</style>")
|
||||
|
||||
extern void
|
||||
HtmlBegin(FILE *, char *);
|
||||
HtmlBegin(Stream *, char *);
|
||||
|
||||
extern void
|
||||
HtmlEnd(FILE *);
|
||||
HtmlEnd(Stream *);
|
||||
|
||||
#endif /* TELODENDRIA_HTML_H */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Stream.h>
|
||||
|
||||
typedef enum HttpRequestMethod
|
||||
{
|
||||
|
@ -126,6 +127,6 @@ extern char *
|
|||
HttpParamEncode(HashMap *);
|
||||
|
||||
extern HashMap *
|
||||
HttpParseHeaders(FILE *);
|
||||
HttpParseHeaders(Stream *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -49,7 +49,7 @@ extern HttpStatus
|
|||
extern HashMap *
|
||||
HttpResponseHeaders(HttpClientContext *);
|
||||
|
||||
extern FILE *
|
||||
extern Stream *
|
||||
HttpClientStream(HttpClientContext *);
|
||||
|
||||
extern void
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Stream.h>
|
||||
|
||||
typedef struct HttpServer HttpServer;
|
||||
|
||||
|
@ -68,7 +69,7 @@ extern char *
|
|||
extern void
|
||||
HttpResponseStatus(HttpServerContext *, HttpStatus);
|
||||
|
||||
extern FILE *
|
||||
extern Stream *
|
||||
HttpServerStream(HttpServerContext *);
|
||||
|
||||
extern void
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef IO_BUFFER
|
||||
#define IO_BUFFER 4096
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <HashMap.h>
|
||||
#include <Array.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
@ -96,16 +97,16 @@ extern void
|
|||
JsonFree(HashMap *);
|
||||
|
||||
extern void
|
||||
JsonEncodeString(const char *, FILE *);
|
||||
JsonEncodeString(const char *, Stream *);
|
||||
|
||||
extern void
|
||||
JsonEncodeValue(JsonValue * value, FILE * out, int);
|
||||
JsonEncodeValue(JsonValue * value, Stream * out, int);
|
||||
|
||||
extern int
|
||||
JsonEncode(HashMap *, FILE *, int);
|
||||
JsonEncode(HashMap *, Stream *, int);
|
||||
|
||||
extern HashMap *
|
||||
JsonDecode(FILE *);
|
||||
JsonDecode(Stream *);
|
||||
|
||||
extern JsonValue *
|
||||
JsonGet(HashMap *, size_t,...);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <stddef.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <Stream.h>
|
||||
|
||||
#define LOG_FLAG_COLOR (1 << 0)
|
||||
#define LOG_FLAG_SYSLOG (1 << 1)
|
||||
|
||||
|
@ -53,7 +55,7 @@ extern void
|
|||
LogConfigIndentSet(LogConfig *, size_t);
|
||||
|
||||
extern void
|
||||
LogConfigOutputSet(LogConfig *, FILE *);
|
||||
LogConfigOutputSet(LogConfig *, Stream *);
|
||||
|
||||
extern void
|
||||
LogConfigFlagSet(LogConfig *, int);
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
#include <Http.h>
|
||||
|
||||
extern void
|
||||
StaticItWorks(FILE *);
|
||||
StaticItWorks(Stream *);
|
||||
|
||||
extern void
|
||||
StaticLogin(FILE *);
|
||||
StaticLogin(Stream *);
|
||||
|
||||
extern void
|
||||
StaticError(FILE *, HttpStatus);
|
||||
StaticError(Stream *, HttpStatus);
|
||||
|
||||
#endif /* TELODENDRIA_STATIC_H */
|
||||
|
|
|
@ -42,6 +42,15 @@ extern Stream *
|
|||
extern Stream *
|
||||
StreamOpen(const char *, const char *);
|
||||
|
||||
extern Stream *
|
||||
StreamStdout(void);
|
||||
|
||||
extern Stream *
|
||||
StreamStderr(void);
|
||||
|
||||
extern Stream *
|
||||
StreamStdin(void);
|
||||
|
||||
extern int
|
||||
StreamClose(Stream *);
|
||||
|
||||
|
@ -63,6 +72,9 @@ extern int
|
|||
extern int
|
||||
StreamPuts(Stream *, char *);
|
||||
|
||||
extern char *
|
||||
StreamGets(Stream *, char *, int);
|
||||
|
||||
extern int
|
||||
StreamEof(Stream *);
|
||||
|
||||
|
@ -78,4 +90,7 @@ extern int
|
|||
extern ssize_t
|
||||
StreamCopy(Stream *, Stream *);
|
||||
|
||||
extern int
|
||||
StreamFileno(Stream *);
|
||||
|
||||
#endif /* TELODENDRIA_STREAM_H */
|
||||
|
|
|
@ -39,14 +39,11 @@ extern const char
|
|||
extern const char
|
||||
TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH];
|
||||
|
||||
extern void
|
||||
TelodendriaHexDump(size_t, char *, char *, void *);
|
||||
|
||||
extern void
|
||||
TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *);
|
||||
|
||||
extern void
|
||||
TelodendriaMemoryIterator(MemoryInfo *, void *);
|
||||
TelodendriaGenerateMemReport(void);
|
||||
|
||||
extern void
|
||||
TelodendriaPrintHeader(LogConfig *);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <Stream.h>
|
||||
|
||||
extern unsigned long
|
||||
UtilServerTs(void);
|
||||
|
||||
|
@ -45,12 +47,12 @@ extern size_t
|
|||
UtilParseBytes(char *);
|
||||
|
||||
extern ssize_t
|
||||
UtilGetDelim(char **, size_t *, int, FILE *);
|
||||
UtilGetDelim(char **, size_t *, int, Stream *);
|
||||
|
||||
extern ssize_t
|
||||
UtilGetLine(char **, size_t *, FILE *);
|
||||
UtilGetLine(char **, size_t *, Stream *);
|
||||
|
||||
extern size_t
|
||||
UtilStreamCopy(FILE *, FILE *);
|
||||
UtilStreamCopy(Stream *, Stream *);
|
||||
|
||||
#endif /* TELODENDRIA_UTIL_H */
|
||||
|
|
|
@ -39,7 +39,7 @@ MAIN="Main"
|
|||
|
||||
if [ "$DEBUG" = "1" ]; then
|
||||
CFLAGS="$CFLAGS -O0 -g -pg"
|
||||
LDFLAGS="-lm -pthread -v"
|
||||
LDFLAGS="-lm -pthread"
|
||||
PROG="$PROG-debug"
|
||||
fi
|
||||
|
||||
|
|
44
tools/bin/tt
44
tools/bin/tt
|
@ -8,25 +8,19 @@ ENDPOINT="$1"
|
|||
|
||||
: "${METH:=GET}"
|
||||
|
||||
# Check if user is available. If it is, register it.
|
||||
user_available=$(http "$BASE/_matrix/client/v3/register/available?username=$USERNAME" | json -s "available")
|
||||
if [ "$user_available" = "true" ]; then
|
||||
session=$(echo '{}' | http -X POST "$BASE/_matrix/client/v3/register" | json -s "session->@decode")
|
||||
(
|
||||
printf '{'
|
||||
printf ' "auth": {'
|
||||
printf ' "type": "m.login.dummy",'
|
||||
printf ' "session": %s' "$(json -e "$session")"
|
||||
printf ' },'
|
||||
printf ' "username": %s,' "$(json -e "$USERNAME")"
|
||||
printf ' "password": %s,' "$PASSWORD"
|
||||
printf ' "inhibit_login": true '
|
||||
printf '}'
|
||||
) | http -X POST -d @- "$BASE/_matrix/client/v3/register" > /dev/null
|
||||
fi
|
||||
register_payload() {
|
||||
printf '{'
|
||||
printf ' "auth": {'
|
||||
printf ' "type": "m.login.dummy",'
|
||||
printf ' "session": %s' "$(json -e "$session")"
|
||||
printf ' },'
|
||||
printf ' "username": %s,' "$(json -e "$USERNAME")"
|
||||
printf ' "password": %s,' "$PASSWORD"
|
||||
printf ' "inhibit_login": true '
|
||||
printf '}'
|
||||
}
|
||||
|
||||
# Log in
|
||||
RESPONSE=$((
|
||||
login_payload() {
|
||||
printf '{'
|
||||
printf ' "identifier": {'
|
||||
printf ' "type": "m.id.user",'
|
||||
|
@ -35,7 +29,17 @@ RESPONSE=$((
|
|||
printf ' "type": "m.login.password",'
|
||||
printf ' "password": %s' "$PASSWORD"
|
||||
printf '}'
|
||||
) | http -X POST -d @- "$BASE/_matrix/client/v3/login")
|
||||
}
|
||||
|
||||
# Check if user is available. If it is, register it.
|
||||
user_available=$(http "$BASE/_matrix/client/v3/register/available?username=$USERNAME" | json -s "available")
|
||||
if [ "$user_available" = "true" ]; then
|
||||
session=$(echo '{}' | http -X POST "$BASE/_matrix/client/v3/register" | json -s "session->@decode")
|
||||
register_payload | http -X POST -d @- "$BASE/_matrix/client/v3/register" > /dev/null
|
||||
fi
|
||||
|
||||
# Log in
|
||||
RESPONSE=$(login_payload | http -X POST -d @- "$BASE/_matrix/client/v3/login")
|
||||
|
||||
ACCESS_TOKEN=$(echo "$RESPONSE" | json -s "access_token->@decode")
|
||||
|
||||
|
@ -53,4 +57,4 @@ fi
|
|||
|
||||
# Log out
|
||||
http -X POST -H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
"$BASE/_matrix/client/v3/logout" > /dev/null
|
||||
"$BASE/_matrix/client/v3/logout/all" > /dev/null
|
||||
|
|
|
@ -49,24 +49,24 @@ HttpHandle(HttpServerContext * cx, void *args)
|
|||
|
||||
(void) args;
|
||||
|
||||
printf("%s %s\n", HttpRequestMethodToString(method),
|
||||
HttpRequestPath(cx));
|
||||
StreamPrintf(StreamStdout(), "%s %s\n", HttpRequestMethodToString(method),
|
||||
HttpRequestPath(cx));
|
||||
|
||||
while (HashMapIterate(headers, &key, (void **) &val))
|
||||
{
|
||||
printf("%s: %s\n", key, val);
|
||||
StreamPrintf(StreamStdout(), "%s: %s\n", key, val);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
|
||||
bytes = UtilStreamCopy(HttpServerStream(cx), stdout);
|
||||
bytes = UtilStreamCopy(HttpServerStream(cx), StreamStdout());
|
||||
|
||||
printf("\n");
|
||||
printf("(%lu bytes)\n", bytes);
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
StreamPrintf(StreamStdout(), "(%lu bytes)\n", bytes);
|
||||
|
||||
HttpSendHeaders(cx);
|
||||
|
||||
fprintf(HttpServerStream(cx), "{}\n");
|
||||
StreamPuts(HttpServerStream(cx), "{}\n");
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -77,12 +77,12 @@ main(void)
|
|||
server = HttpServerCreate(8008, 1, 1, HttpHandle, NULL);
|
||||
if (!HttpServerStart(server))
|
||||
{
|
||||
fprintf(stderr, "Unable to start HTTP server.\n");
|
||||
StreamPuts(StreamStderr(), "Unable to start HTTP server.\n");
|
||||
HttpServerFree(server);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Listening on port 8008.\n");
|
||||
StreamPuts(StreamStdout(), "Listening on port 8008.\n");
|
||||
|
||||
sa.sa_handler = SignalHandle;
|
||||
sigfillset(&sa.sa_mask);
|
||||
|
@ -90,7 +90,7 @@ main(void)
|
|||
|
||||
if (sigaction(SIGINT, &sa, NULL) < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to install signal handler.\n");
|
||||
StreamPuts(StreamStderr(), "Unable to install signal handler.\n");
|
||||
HttpServerStop(server);
|
||||
HttpServerJoin(server);
|
||||
HttpServerFree(server);
|
||||
|
@ -99,7 +99,12 @@ main(void)
|
|||
|
||||
HttpServerJoin(server);
|
||||
|
||||
printf("Shutting down.\n");
|
||||
StreamPuts(StreamStdout(), "Shutting down.\n");
|
||||
HttpServerStop(server);
|
||||
|
||||
StreamClose(StreamStdout());
|
||||
StreamClose(StreamStderr());
|
||||
StreamClose(StreamStdin());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
static void
|
||||
usage(char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-i -X method -H header -d data] url\n", prog);
|
||||
StreamPrintf(StreamStderr(), "Usage: %s [-i -X method -H header -d data] url\n", prog);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -72,7 +72,7 @@ main(int argc, char **argv)
|
|||
method = HttpRequestMethodFromString(optarg);
|
||||
if (!method)
|
||||
{
|
||||
fprintf(stderr, "Unknown request method: %s\n", optarg);
|
||||
StreamPrintf(StreamStderr(), "Unknown request method: %s\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
@ -113,7 +113,7 @@ main(int argc, char **argv)
|
|||
uri = UriParse(argv[optind]);
|
||||
if (!uri)
|
||||
{
|
||||
fprintf(stderr, "Failed to parse URI: %s\n", argv[optind]);
|
||||
StreamPrintf(StreamStderr(), "Failed to parse URI: %s\n", argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ main(int argc, char **argv)
|
|||
|
||||
if (!uri->port)
|
||||
{
|
||||
fprintf(stderr, "Unknown protocol: %s\n", uri->proto);
|
||||
StreamPrintf(StreamStderr(), "Unknown protocol: %s\n", uri->proto);
|
||||
UriFree(uri);
|
||||
return 1;
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ main(int argc, char **argv)
|
|||
|
||||
if (!cx)
|
||||
{
|
||||
fprintf(stderr, "Failed to connect.\n");
|
||||
StreamPuts(StreamStderr(), "Failed to connect.\n");
|
||||
UriFree(uri);
|
||||
return 1;
|
||||
}
|
||||
|
@ -163,32 +163,32 @@ main(int argc, char **argv)
|
|||
{
|
||||
if (*data == '@')
|
||||
{
|
||||
FILE *in;
|
||||
Stream *in;
|
||||
|
||||
data++;
|
||||
|
||||
if (strcmp(data, "-") == 0)
|
||||
{
|
||||
in = stdin;
|
||||
in = StreamStdin();
|
||||
}
|
||||
else
|
||||
{
|
||||
in = fopen(data, "r");
|
||||
in = StreamOpen(data, "r");
|
||||
}
|
||||
|
||||
if (!in)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", data, strerror(errno));
|
||||
StreamPrintf(StreamStderr(), "%s: %s\n", data, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
UtilStreamCopy(in, HttpClientStream(cx));
|
||||
|
||||
fclose(in);
|
||||
StreamClose(in);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(HttpClientStream(cx), "%s", data);
|
||||
StreamPuts(HttpClientStream(cx), data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ main(int argc, char **argv)
|
|||
|
||||
if (!res)
|
||||
{
|
||||
fprintf(stderr, "Failed to send request.\n");
|
||||
StreamPuts(StreamStderr(), "Failed to send request.\n");
|
||||
HttpClientContextFree(cx);
|
||||
UriFree(uri);
|
||||
return 1;
|
||||
|
@ -206,20 +206,25 @@ main(int argc, char **argv)
|
|||
{
|
||||
HashMap *responseHeaders = HttpResponseHeaders(cx);
|
||||
|
||||
printf("HTTP/1.0 %d %s\n", res, HttpStatusToString(res));
|
||||
StreamPrintf(StreamStdout(), "HTTP/1.0 %d %s\n", res, HttpStatusToString(res));
|
||||
|
||||
while (HashMapIterate(responseHeaders, &key, (void **) &val))
|
||||
{
|
||||
printf("%s: %s\n", key, val);
|
||||
StreamPrintf(StreamStdout(), "%s: %s\n", key, val);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
}
|
||||
|
||||
UtilStreamCopy(HttpClientStream(cx), stdout);
|
||||
UtilStreamCopy(HttpClientStream(cx), StreamStdout());
|
||||
StreamClose(StreamStdout());
|
||||
|
||||
HttpClientContextFree(cx);
|
||||
UriFree(uri);
|
||||
|
||||
StreamClose(StreamStdout());
|
||||
StreamClose(StreamStderr());
|
||||
StreamClose(StreamStdin());
|
||||
|
||||
return !(res == HTTP_OK);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
static void
|
||||
usage(char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-s query|-e str]\n", prog);
|
||||
StreamPrintf(StreamStderr(), "Usage: %s [-s query|-e str]\n", prog);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -89,7 +89,7 @@ query(char *select, HashMap * json)
|
|||
}
|
||||
else if (JsonValueType(val) == JSON_STRING && strcmp(keyName + 1, "decode") == 0)
|
||||
{
|
||||
printf("%s\n", JsonValueAsString(val));
|
||||
StreamPrintf(StreamStdout(), "%s\n", JsonValueAsString(val));
|
||||
val = NULL;
|
||||
break;
|
||||
}
|
||||
|
@ -160,8 +160,8 @@ query(char *select, HashMap * json)
|
|||
|
||||
if (val)
|
||||
{
|
||||
JsonEncodeValue(val, stdout, JSON_PRETTY);
|
||||
printf("\n");
|
||||
JsonEncodeValue(val, StreamStdout(), JSON_PRETTY);
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,9 +170,9 @@ encode(char *str)
|
|||
{
|
||||
JsonValue *val = JsonValueString(str);
|
||||
|
||||
JsonEncodeValue(val, stdout, JSON_DEFAULT);
|
||||
JsonEncodeValue(val, StreamStdout(), JSON_DEFAULT);
|
||||
JsonValueFree(val);
|
||||
printf("\n");
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -203,11 +203,11 @@ main(int argc, char **argv)
|
|||
|
||||
if (flag != FLAG_ENCODE)
|
||||
{
|
||||
json = JsonDecode(stdin);
|
||||
json = JsonDecode(StreamStdin());
|
||||
|
||||
if (!json)
|
||||
{
|
||||
fprintf(stderr, "Malformed JSON.\n");
|
||||
StreamPuts(StreamStderr(), "Malformed JSON.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -221,11 +221,15 @@ main(int argc, char **argv)
|
|||
encode(input);
|
||||
break;
|
||||
default:
|
||||
JsonEncode(json, stdout, JSON_PRETTY);
|
||||
printf("\n");
|
||||
JsonEncode(json, StreamStdout(), JSON_PRETTY);
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
StreamClose(StreamStdout());
|
||||
StreamClose(StreamStderr());
|
||||
StreamClose(StreamStdin());
|
||||
|
||||
MemoryFreeAll();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue