slon/Slon/Api/V2/Search.HC

158 lines
5.5 KiB
HolyC

JsonObject* @slon_api_v2_search_remote_account_from_webfinger(SlonHttpSession* session, U8* user, U8* domain)
{
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
no_warn request_json;
SLON_AUTH_ACCOUNT_ID
I64 i;
Bool tld_is_valid = FALSE;
for (i = 0; i < SLON_TLDS->length; i++) {
if (String.EndsWith(tld_array[i], domain)) {
tld_is_valid = TRUE;
break;
}
}
if (!tld_is_valid) {
@slon_log(LOG_HTTPD, "Could not query webfinger, tld is not valid for %s", domain);
return NULL;
}
// WebFinger
StrPrint(scratch_buffer, "https://%s/.well-known/webfinger?resource=acct:%s@%s", domain, user, domain);
HttpUrl* url = @http_parse_url(scratch_buffer);
if (!url) {
@slon_log(LOG_HTTPD, "Could not query webfinger, malformed url or unspecified error");
return NULL;
}
U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, slon_mem_task);
JsonObject* http_headers = Json.CreateObject();
http_headers->set("accept", "application/json", JSON_STRING);
@http_response* resp = Http.Get(url, fetch_buffer, NULL, http_headers);
if (!resp) {
@slon_log(LOG_HTTPD, "Could not query webfinger, invalid response from remote server");
Free(fetch_buffer);
return NULL;
}
while (resp->state != HTTP_STATE_DONE) {
Sleep(1);
}
if (!resp->body.length) {
@slon_log(LOG_HTTPD, "Could not query webfinger, empty response from remote server");
Free(fetch_buffer);
return NULL;
}
Free(fetch_buffer);
JsonObject* webfinger_object = Json.Parse(resp->body.data);
if (!webfinger_object) {
@slon_log(LOG_HTTPD, "Error querying webfinger, object not present in response from remote server");
return NULL;
}
if (!webfinger_object->@("links")) {
@slon_log(LOG_HTTPD, "Error querying webfinger, links not present in object in response from remote server");
return NULL;
}
U8* remote_actor = NULL;
JsonArray* links = webfinger_object->a("links");
JsonObject* link_object = NULL;
for (i = 0; i < links->length; i++) {
link_object = links->@(i);
if (link_object->@("rel") && !StrICmp("self", link_object->@("rel"))) {
remote_actor = link_object->@("href");
break;
}
}
if (!remote_actor) {
@slon_log(LOG_HTTPD, "Error querying webfinger, actor not present in links in object in response from remote server");
return NULL;
}
// We have the remote actor uri, let's fetch and create a local account
fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, slon_mem_task);
U8* signatory = db->o("actors")->o(@slon_api_account_by_id(account_id)->@("username"))->@("id");
resp = @slon_activitypub_signed_request(remote_actor, fetch_buffer, NULL, SLON_HTTP_VERB_GET, signatory);
if (!resp) {
@slon_log(LOG_HTTPD, "Could not fetch actor, invalid response from remote server");
Free(fetch_buffer);
return NULL;
}
if (!resp->body.length) {
@slon_log(LOG_HTTPD, "Could not fetch actor, empty response from remote server");
Free(fetch_buffer);
return NULL;
}
JsonObject* actor_object = Json.Parse(resp->body.data);
JsonObject* account = @slon_accounts_create_local_for_remote_actor(session, actor_object, remote_actor, url);
Free(fetch_buffer);
return account;
}
U0 @slon_api_v2_search_get(SlonHttpSession* session)
{
SLON_SCRATCH_BUFFER_AND_REQUEST_JSON
if (@slon_api_authorized(session)) {
// SLON_AUTH_ACCOUNT_ID
JsonObject* results = Json.CreateObject();
results->set("accounts", Json.CreateArray(), JSON_ARRAY);
results->set("statuses", Json.CreateArray(), JSON_ARRAY);
results->set("hashtags", Json.CreateArray(), JSON_ARRAY);
U8* q = request_json->@("q");
if (!q) {
goto slon_api_v2_search_get_return;
}
// FIXME: if "type" is specified, value must be "accounts" for now
if (request_json->@("type") && StrICmp("accounts", request_json->@("type"))) {
goto slon_api_v2_search_get_return;
}
if (q[0] == '@' || StrFind("@", request_json->@("q")) > 0) {
I64 at_fragment_count = 0;
U8* q_copy = @slon_strnew(session, q);
U8** at_fragments = String.Split(q_copy, '@', &at_fragment_count);
switch (at_fragment_count) {
case 2:
case 3:
// Remote user
StrPrint(scratch_buffer, "%s@%s", at_fragments[0], at_fragments[1]);
JsonObject* remote_account = @slon_api_account_by_acct(scratch_buffer);
if (!remote_account && request_json->@("resolve") && !StrICmp("true", request_json->@("resolve"))) {
// if "resolve" is TRUE, do WebFinger lookup if the remote account doesn't exist on this server
remote_account = @slon_api_v2_search_remote_account_from_webfinger(session, at_fragments[0], at_fragments[1]);
}
if (remote_account) {
results->a("accounts")->append(Json.CreateItem(remote_account, JSON_OBJECT));
}
break;
default:
// Unsupported
break;
}
@slon_free(session, q_copy);
}
slon_api_v2_search_get_return:
session->send(results);
Json.Delete(results);
} else {
session->status(401);
}
}