mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 21:25:11 +00:00
355 lines
10 KiB
C
355 lines
10 KiB
C
#include <Cytoplasm/HttpServer.h>
|
|
#include <Cytoplasm/Cytoplasm.h>
|
|
#include <Cytoplasm/Platform.h>
|
|
#include <Cytoplasm/Memory.h>
|
|
#include <Cytoplasm/Util.h>
|
|
#include <Cytoplasm/Cron.h>
|
|
#include <Cytoplasm/Args.h>
|
|
#include <Cytoplasm/Log.h>
|
|
#include <Cytoplasm/Str.h>
|
|
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <StanzaBuilder.h>
|
|
#include <Parsee.h>
|
|
#include <XMPP.h>
|
|
#include <AS.h>
|
|
|
|
static HttpServer *server = NULL;
|
|
static pthread_t xmpp_thr;
|
|
static XMPPComponent *jabber = NULL;
|
|
|
|
static volatile uint64_t start;
|
|
uint64_t
|
|
ParseeUptime(void)
|
|
{
|
|
return UtilTsMillis() - start;
|
|
}
|
|
|
|
static const Argument arguments[] =
|
|
{
|
|
#define Arg(c, req, vdesc, desc) \
|
|
{ \
|
|
.end = false, \
|
|
.argument = c, .value_req = req, \
|
|
.value_descr = vdesc, \
|
|
.description = desc \
|
|
},
|
|
#define EndOfArgs { .end = true }
|
|
|
|
Arg('H', true, "number(=8)", "Sets the number of HTTP threads")
|
|
Arg('J', true, "number(=8)", "Sets the number of XMPP threads")
|
|
Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use")
|
|
|
|
Arg('g', false, NULL,
|
|
"Generates a parsee.yaml AS file before exiting")
|
|
Arg('v', false, NULL,
|
|
"Forces Parsee to print in a more verbose fashion "
|
|
"(-vvv prints stanzas to stderr)")
|
|
Arg('h', false, NULL,
|
|
"Generates an help screen(this one!)")
|
|
|
|
EndOfArgs
|
|
|
|
#undef EndOfArgs
|
|
#undef Argument
|
|
};
|
|
|
|
void
|
|
ParseeCheckMatrix(void *datp)
|
|
{
|
|
static volatile uint64_t streak = 0;
|
|
ParseeData *data = datp;
|
|
if (data->config->accept_pings && !ASPing(data->config))
|
|
{
|
|
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
|
if (++streak == 10)
|
|
{
|
|
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
|
HashMap *json = DbJson(ref);
|
|
HashMap *mucs = GrabObject(json, 1, "mucs");
|
|
char *muc;
|
|
void *ignored;
|
|
|
|
|
|
/* Notify any potential MUCs about this */
|
|
while (HashMapIterate(mucs, &muc, &ignored))
|
|
{
|
|
char *id = StrRandom(32);
|
|
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
|
|
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
|
|
SetStanzaType(b, "groupchat");
|
|
SetStanzaBody(b,
|
|
"This bridge hasn't been able to reach the Matrix host, and "
|
|
"as such, some messages may not have been sent over."
|
|
);
|
|
|
|
WriteoutStanza(b, data->jabber, 0);
|
|
DestroyStanzaBuilder(b);
|
|
|
|
Free(sender);
|
|
Free(id);
|
|
}
|
|
(void) ignored;
|
|
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
return;
|
|
}
|
|
streak = 0;
|
|
}
|
|
|
|
int
|
|
Main(Array *args, HashMap *env)
|
|
{
|
|
HttpServerConfig conf;
|
|
const ParseeConfig *parsee_conf;
|
|
Stream *yaml;
|
|
Cron *cron = NULL;
|
|
|
|
char *configuration = "parsee.json";
|
|
int xmpp = 8;
|
|
int http = 8;
|
|
int verbose = 0;
|
|
|
|
start = UtilTsMillis();
|
|
|
|
memset(&conf, 0, sizeof(conf));
|
|
Log(LOG_INFO,
|
|
"%s - v%s[%s] (Cytoplasm %s)",
|
|
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
|
);
|
|
ParseePrintASCII();
|
|
Log(LOG_INFO, "=======================");
|
|
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
|
|
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
|
|
|
#ifdef PLATFORM_IPHONE
|
|
Log(LOG_WARNING, "Wait. Are you running this on an iPhone?");
|
|
Log(LOG_WARNING, "You *ought* to have spoofed this, haven't you?");
|
|
Log(LOG_WARNING, "Simply jealous of you for doing this.");
|
|
#endif
|
|
|
|
LogConfigIndent(LogConfigGlobal());
|
|
|
|
{
|
|
ArgParseState state;
|
|
char *opts = ParseeGenerateGetopt(arguments);
|
|
int flag;
|
|
ArgParseStateInit(&state);
|
|
while ((flag = ArgParse(&state, args, opts)) != -1)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case 'h':
|
|
ParseeGenerateHelp(arguments);
|
|
Free(opts);
|
|
goto end;
|
|
case 'H':
|
|
http = strtol(state.optArg, NULL, 10);
|
|
break;
|
|
case 'J':
|
|
xmpp = strtol(state.optArg, NULL, 10);
|
|
break;
|
|
case 'g':
|
|
/* Write out the config file to a YAML document */
|
|
Log(LOG_INFO, "Generating YAML...");
|
|
yaml = StreamOpen("parsee.yaml", "w");
|
|
ParseeConfigLoad(configuration);
|
|
ParseeConfigInit();
|
|
ParseeExportConfigYAML(yaml);
|
|
StreamClose(yaml);
|
|
Free(opts);
|
|
goto end;
|
|
case 'v':
|
|
switch (++verbose)
|
|
{
|
|
case PARSEE_VERBOSE_LOG:
|
|
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
|
|
break;
|
|
case PARSEE_VERBOSE_TIMINGS:
|
|
Log(LOG_DEBUG, "Logging bench information.");
|
|
break;
|
|
case PARSEE_VERBOSE_STANZA:
|
|
Log(LOG_DEBUG, "Enabling stanza printing.");
|
|
break;
|
|
case PARSEE_VERBOSE_COMICAL:
|
|
Log(LOG_DEBUG, "What?");
|
|
Log(LOG_DEBUG, "No, but like, what do you except?");
|
|
Log(LOG_DEBUG, "Like do you want to log _every_ instruction?");
|
|
Log(LOG_DEBUG, "Like just every single thing %s does?", NAME);
|
|
Log(LOG_DEBUG, " ( why??? )");
|
|
Log(LOG_DEBUG, ".....................................");
|
|
Log(LOG_DEBUG, "Argh.");
|
|
Log(LOG_DEBUG, "Alright. I'll do my best.");
|
|
Log(LOG_DEBUG, "Get what you paid for.");
|
|
break;
|
|
}
|
|
break;
|
|
case 'C':
|
|
if (!UtilLastModified(state.optArg))
|
|
{
|
|
Log(LOG_ERR, "Invalid config: %s", state.optArg);
|
|
Log(LOG_ERR, "Ignoring.");
|
|
break;
|
|
}
|
|
configuration = state.optArg;
|
|
break;
|
|
case '?':
|
|
Log(LOG_ERR, "INVALID ARGUMENT GIVEN");
|
|
Free(opts);
|
|
goto end;
|
|
}
|
|
}
|
|
Free(opts);
|
|
}
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Loading configuration...");
|
|
}
|
|
ParseeConfigLoad(configuration);
|
|
ParseeConfigInit();
|
|
parsee_conf = ParseeConfigGet();
|
|
if (!parsee_conf)
|
|
{
|
|
goto end;
|
|
}
|
|
ParseeSetThreads(xmpp, http);
|
|
|
|
|
|
Log(LOG_NOTICE, "Connecting to XMPP...");
|
|
jabber = XMPPInitialiseCompStream(
|
|
parsee_conf->component_addr,
|
|
parsee_conf->component_host,
|
|
parsee_conf->component_port
|
|
);
|
|
Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber);
|
|
if (!XMPPAuthenticateCompStream(
|
|
jabber,
|
|
parsee_conf->shared_comp_secret
|
|
))
|
|
{
|
|
Log(LOG_ERR, "Could not connect to XMPP...");
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Destroying component...");
|
|
}
|
|
XMPPEndCompStream(jabber);
|
|
goto end;
|
|
}
|
|
|
|
Log(LOG_NOTICE, "Creating volatile tables...");
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Initialising JID table");
|
|
}
|
|
ParseeInitialiseJIDTable();
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Initialising OID table");
|
|
}
|
|
ParseeInitialiseOIDTable();
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Initialising head table");
|
|
}
|
|
ParseeInitialiseHeadTable();
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Initialising nick table");
|
|
}
|
|
ParseeInitialiseNickTable();
|
|
|
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
{
|
|
Log(LOG_DEBUG, "Initialising affiliation table");
|
|
}
|
|
ParseeInitialiseAffiliationTable();
|
|
|
|
conf.port = parsee_conf->port;
|
|
conf.threads = parsee_conf->http_threads;
|
|
conf.maxConnections = conf.threads << 2;
|
|
conf.handlerArgs = ParseeInitData(jabber);
|
|
conf.handler = ParseeRequest;
|
|
if (!conf.handlerArgs)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
Log(LOG_DEBUG, "Verbosity level: %d", verbose);
|
|
((ParseeData *) conf.handlerArgs)->verbosity = verbose;
|
|
|
|
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
|
if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart))
|
|
{
|
|
char *parsee = ParseeMXID(conf.handlerArgs);
|
|
|
|
ASSetAvatar(parsee_conf,
|
|
parsee,
|
|
"mxc://tedomum.net/"
|
|
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
|
);
|
|
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
|
|
|
Free(parsee);
|
|
}
|
|
|
|
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
|
cron = CronCreate(10 SECONDS);
|
|
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
|
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
|
|
ParseeCleanup(conf.handlerArgs);
|
|
|
|
CronStart(cron);
|
|
|
|
Log(LOG_NOTICE, "Creating XMPP listener thread...");
|
|
if (pthread_create(&xmpp_thr, NULL, ParseeXMPPThread, conf.handlerArgs))
|
|
{
|
|
Log(LOG_ERR, "Couldn't start XMPP listener thread.");
|
|
goto end;
|
|
}
|
|
|
|
server = HttpServerCreate(&conf);
|
|
((ParseeData *) conf.handlerArgs)->server = server;
|
|
|
|
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
HttpServerStart(server);
|
|
|
|
Log(LOG_NOTICE, "Listening to MUCs...");
|
|
LogConfigIndent(LogConfigGlobal());
|
|
ParseeSendPresence(conf.handlerArgs);
|
|
LogConfigUnindent(LogConfigGlobal());
|
|
|
|
|
|
LogConfigUnindent(LogConfigGlobal());
|
|
Log(LOG_INFO, "=======================");
|
|
HttpServerJoin(server);
|
|
|
|
end:
|
|
Log(LOG_INFO, "Exiting...");
|
|
HttpServerFree(server);
|
|
ParseeConfigFree();
|
|
CronStop(cron);
|
|
CronFree(cron);
|
|
ParseeFreeData(conf.handlerArgs);
|
|
ParseeDestroyAffiliationTable();
|
|
ParseeDestroyNickTable();
|
|
ParseeDestroyOIDTable();
|
|
ParseeDestroyHeadTable();
|
|
ParseeDestroyJIDTable();
|
|
|
|
(void) env;
|
|
return 0;
|
|
}
|