matrix-3ds-sdk/source/matrixclient.cpp

371 lines
10 KiB
C++
Raw Normal View History

2019-10-16 18:39:57 +02:00
#include "../include/matrixclient.h"
#include <inttypes.h>
#include <stdio.h>
#include <3ds.h>
#include <jansson.h>
2019-10-16 21:26:06 +02:00
#include <malloc.h>
#include <curl/curl.h>
#include <string.h>
2019-10-17 10:45:55 +02:00
#include "util.h"
#include "memorystore.h"
2019-10-16 22:57:55 +02:00
2019-10-16 21:26:06 +02:00
#include <sys/socket.h>
#define SOC_ALIGN 0x1000
#define SOC_BUFFERSIZE 0x100000
2019-10-17 10:45:55 +02:00
#define SYNC_TIMEOUT 10000
2019-10-17 20:31:00 +02:00
#define DEBUG 1
#if DEBUG
#define D
#else
#define D for(;0;)
#endif
2019-10-17 10:45:55 +02:00
namespace Matrix {
2019-10-16 21:26:06 +02:00
static u32 *SOC_buffer = NULL;
2019-10-16 18:39:57 +02:00
2019-10-17 10:45:55 +02:00
Client::Client(std::string homeserverUrl, std::string matrixToken, Store* clientStore) {
2019-10-16 18:39:57 +02:00
hsUrl = homeserverUrl;
token = matrixToken;
2019-10-17 10:45:55 +02:00
if (!clientStore) {
clientStore = new MemoryStore();
}
store = clientStore;
2019-10-16 22:57:55 +02:00
}
2019-10-17 14:00:02 +02:00
std::string Client::getToken() {
return token;
}
bool Client::login(std::string username, std::string password) {
json_t* request = json_object();
json_object_set_new(request, "type", json_string("m.login.password"));
json_t* identifier = json_object();
json_object_set_new(identifier, "type", json_string("m.id.user"));
json_object_set_new(identifier, "user", json_string(username.c_str()));
json_object_set_new(request, "identifier", identifier);
json_object_set_new(request, "password", json_string(password.c_str()));
json_object_set_new(request, "initial_device_display_name", json_string("Nintendo 3DS"));
json_t* ret = doRequest("POST", "/_matrix/client/r0/login", request);
json_decref(request);
if (!ret) {
return false;
}
json_t* accessToken = json_object_get(ret, "access_token");
if (!accessToken) {
json_decref(ret);
return false;
}
token = json_string_value(accessToken);
json_decref(ret);
return true;
}
2019-10-17 13:22:39 +02:00
std::string Client::getUserId() {
2019-10-17 12:54:49 +02:00
if (userIdCache != "") {
return userIdCache;
}
2019-10-17 10:45:55 +02:00
json_t* ret = doRequest("GET", "/_matrix/client/r0/account/whoami");
if (!ret) {
return "";
}
json_t* userId = json_object_get(ret, "user_id");
if (!userId) {
json_decref(ret);
return "";
}
const char* userIdStr = json_string_value(userId);
json_decref(ret);
2019-10-17 12:54:49 +02:00
userIdCache = std::string(userIdStr);
return userIdCache;
2019-10-17 10:45:55 +02:00
}
2019-10-17 13:22:39 +02:00
std::string Client::resolveRoom(std::string alias) {
if (alias[0] == '!') {
return alias; // this is already a room ID, nothing to do
}
json_t* ret = doRequest("GET", "/_matrix/client/r0/directory/room/" + urlencode(alias));
if (!ret) {
return "";
}
json_t* roomId = json_object_get(ret, "room_id");
if (!roomId) {
json_decref(ret);
return "";
}
const char* roomIdStr = json_string_value(roomId);
json_decref(ret);
return roomIdStr;
}
std::string Client::sendEmote(std::string roomId, std::string text) {
json_t* request = json_object();
json_object_set_new(request, "msgtype", json_string("m.emote"));
json_object_set_new(request, "body", json_string(text.c_str()));
std::string eventId = sendMessage(roomId, request);
json_decref(request);
return eventId;
}
std::string Client::sendNotice(std::string roomId, std::string text) {
json_t* request = json_object();
json_object_set_new(request, "msgtype", json_string("m.notice"));
json_object_set_new(request, "body", json_string(text.c_str()));
std::string eventId = sendMessage(roomId, request);
json_decref(request);
return eventId;
}
std::string Client::sendText(std::string roomId, std::string text) {
2019-10-16 22:57:55 +02:00
json_t* request = json_object();
json_object_set_new(request, "msgtype", json_string("m.text"));
json_object_set_new(request, "body", json_string(text.c_str()));
std::string eventId = sendMessage(roomId, request);
json_decref(request);
return eventId;
}
2019-10-17 10:45:55 +02:00
std::string Client::sendMessage(std::string roomId, json_t* content) {
2019-10-16 22:57:55 +02:00
return sendEvent(roomId, "m.room.message", content);
}
2019-10-17 10:45:55 +02:00
std::string Client::sendEvent(std::string roomId, std::string eventType, json_t* content) {
2019-10-17 13:22:39 +02:00
roomId = resolveRoom(roomId);
2019-10-16 22:57:55 +02:00
std::string txid = std::to_string(time(NULL)) + "_REQ_" + std::to_string(requestId);
2019-10-17 10:45:55 +02:00
std::string path = "/_matrix/client/r0/rooms/" + urlencode(roomId) + "/send/" + urlencode(eventType) + "/" + urlencode(txid);
2019-10-16 22:57:55 +02:00
json_t* ret = doRequest("PUT", path, content);
if (!ret) {
return "";
}
json_t* eventId = json_object_get(ret, "event_id");
if (!eventId) {
json_decref(ret);
return "";
}
const char* eventIdStr = json_string_value(eventId);
json_decref(ret);
return eventIdStr;
2019-10-16 18:39:57 +02:00
}
2019-10-17 13:22:39 +02:00
std::string Client::sendStateEvent(std::string roomId, std::string type, std::string stateKey, json_t* content) {
roomId = resolveRoom(roomId);
std::string path = "/_matrix/client/r0/rooms/" + urlencode(roomId) + "/state/" + urlencode(type) + "/" + urlencode(stateKey);
json_t* ret = doRequest("PUT", path, content);
if (!ret) {
return "";
}
json_t* eventId = json_object_get(ret, "event_id");
if (!eventId) {
json_decref(ret);
return "";
}
const char* eventIdStr = json_string_value(eventId);
json_decref(ret);
return eventIdStr;
}
std::string Client::redactEvent(std::string roomId, std::string eventId, std::string reason) {
roomId = resolveRoom(roomId);
std::string txid = std::to_string(time(NULL)) + "_REQ_" + std::to_string(requestId);
json_t* content = json_object();
if (reason != "") {
json_object_set_new(content, "reason", json_string(reason.c_str()));
}
std::string path = "/_matrix/client/r0/rooms/" + urlencode(roomId) + "/redact/" + urlencode(eventId) + "/" + txid;
json_t* ret = doRequest("PUT", path, content);
json_decref(content);
if (!ret) {
return "";
}
json_t* retEventId = json_object_get(ret, "event_id");
if (!retEventId) {
json_decref(ret);
return "";
}
const char* eventIdStr = json_string_value(retEventId);
json_decref(ret);
return eventIdStr;
}
2019-10-17 12:54:49 +02:00
void startSyncLoopWithoutClass(void* arg) {
((Client*)arg)->startSync();
}
void Client::startSyncLoop() {
stopSyncLoop(); // first we stop an already running sync loop
2019-10-17 14:00:02 +02:00
isSyncing = true;
2019-10-17 12:54:49 +02:00
stopSyncing = false;
s32 prio = 0;
2019-10-17 20:31:00 +02:00
D printf("%lld\n", (u64)this);
2019-10-17 12:54:49 +02:00
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
syncThread = threadCreate(startSyncLoopWithoutClass, this, 8*1024, prio-1, -2, true);
}
void Client::stopSyncLoop() {
stopSyncing = true;
2019-10-17 14:00:02 +02:00
if (isSyncing) {
threadJoin(syncThread, U64_MAX);
threadFree(syncThread);
}
isSyncing = false;
2019-10-17 12:54:49 +02:00
}
2019-10-17 10:45:55 +02:00
void Client::processSync(json_t* sync) {
json_t* rooms = json_object_get(sync, "rooms");
if (!rooms) {
return; // nothing to do
}
json_t* leftRooms = json_object_get(rooms, "leave");
json_t* invitedRooms = json_object_get(rooms, "invite");
json_t* joinedRooms = json_object_get(rooms, "join");
const char* roomId;
json_t* room;
if (leftRooms) {
json_object_foreach(leftRooms, roomId, room) {
// rooms that we left
}
}
if (invitedRooms) {
json_object_foreach(invitedRooms, roomId, room) {
// rooms that we were invited to
}
}
if (joinedRooms) {
json_object_foreach(joinedRooms, roomId, room) {
// rooms that we are joined
2019-10-17 20:31:00 +02:00
D printf("%s:\n", roomId);
2019-10-17 10:45:55 +02:00
json_t* timeline = json_object_get(room, "timeline");
if (!timeline) {
2019-10-17 20:31:00 +02:00
D printf("no timeline\n");
2019-10-17 10:45:55 +02:00
continue;
}
json_t* events = json_object_get(timeline, "events");
if (!events) {
2019-10-17 20:31:00 +02:00
D printf("no events\n");
2019-10-17 10:45:55 +02:00
continue;
}
size_t index;
json_t* event;
json_array_foreach(events, index, event) {
json_t* eventType = json_object_get(event, "type");
2019-10-17 20:31:00 +02:00
D printf("%s\n", json_string_value(eventType));
2019-10-17 10:45:55 +02:00
}
}
}
}
void Client::startSync() {
2019-10-17 20:31:00 +02:00
while (true) {
std::string token = store->getSyncToken();
if (stopSyncing) {
return;
2019-10-17 10:45:55 +02:00
}
2019-10-17 20:31:00 +02:00
json_t* ret = doSync(token);
if (ret) {
// set the token for the next batch
json_t* token = json_object_get(ret, "next_batch");
if (token) {
D printf("Found next batch\n");
store->setSyncToken(json_string_value(token));
} else {
D printf("No next batch\n");
store->setSyncToken("");
}
processSync(ret);
json_decref(ret);
}
svcSleepThread((u64)1000000ULL * (u64)200);
2019-10-17 10:45:55 +02:00
}
}
json_t* Client::doSync(std::string token) {
2019-10-17 20:31:00 +02:00
D printf("Doing sync with token %s\n", token.c_str());
2019-10-17 10:45:55 +02:00
std::string query = "?full_state=false&timeout=" + std::to_string(SYNC_TIMEOUT);
if (token != "") {
query += "&since=" + token;
}
return doRequest("GET", "/_matrix/client/r0/sync" + query);
}
2019-10-16 21:26:06 +02:00
size_t DoRequestWriteCallback(char *contents, size_t size, size_t nmemb, void *userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
2019-10-16 18:39:57 +02:00
2019-10-17 10:45:55 +02:00
json_t* Client::doRequest(const char* method, std::string path, json_t* body) {
2019-10-16 21:26:06 +02:00
std::string url = hsUrl + path;
2019-10-16 22:57:55 +02:00
requestId++;
2019-10-16 21:26:06 +02:00
2019-10-17 20:31:00 +02:00
D printf("Opening Request %d\n%s\n", requestId, url.c_str());
2019-10-16 21:26:06 +02:00
if (!SOC_buffer) {
SOC_buffer = (u32*)memalign(0x1000, 0x100000);
if (!SOC_buffer) {
2019-10-16 22:57:55 +02:00
return NULL;
2019-10-16 18:39:57 +02:00
}
2019-10-16 21:26:06 +02:00
if (socInit(SOC_buffer, 0x100000) != 0) {
2019-10-16 22:57:55 +02:00
return NULL;
2019-10-16 18:39:57 +02:00
}
}
2019-10-16 21:26:06 +02:00
CURL* curl = curl_easy_init();
CURLcode res;
if (!curl) {
2019-10-17 20:31:00 +02:00
D printf("curl init failed\n");
2019-10-16 22:57:55 +02:00
return NULL;
2019-10-16 18:39:57 +02:00
}
2019-10-16 21:26:06 +02:00
std::string readBuffer;
2019-10-16 22:57:55 +02:00
struct curl_slist* headers = NULL;
2019-10-17 14:00:02 +02:00
if (token != "") {
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
}
2019-10-16 22:57:55 +02:00
if (body) {
headers = curl_slist_append(headers, "Content-Type: application/json");
const char* bodyStr = json_dumps(body, JSON_ENSURE_ASCII | JSON_ESCAPE_SLASH);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, bodyStr);
}
2019-10-16 21:26:06 +02:00
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "3ds");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
2019-10-16 22:57:55 +02:00
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
2019-10-16 21:26:06 +02:00
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DoRequestWriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
2019-10-17 10:45:55 +02:00
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
2019-10-16 21:26:06 +02:00
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// curl_easy_setopt(curl, CURLOPT_STDERR, stdout);
res = curl_easy_perform(curl);
2019-10-17 13:22:39 +02:00
curl_easy_cleanup(curl);
2019-10-16 21:26:06 +02:00
if (res != CURLE_OK) {
2019-10-17 20:31:00 +02:00
D printf("curl res not ok %d\n", res);
2019-10-16 22:57:55 +02:00
return NULL;
2019-10-16 18:39:57 +02:00
}
2019-10-17 20:31:00 +02:00
// D printf("%s\n", readBuffer.c_str());
2019-10-16 18:39:57 +02:00
json_error_t error;
2019-10-16 22:57:55 +02:00
json_t* content = json_loads(readBuffer.c_str(), 0, &error);
2019-10-16 18:39:57 +02:00
if (!content) {
2019-10-17 20:31:00 +02:00
D printf("Failed to parse json\n");
2019-10-16 22:57:55 +02:00
return NULL;
2019-10-16 18:39:57 +02:00
}
2019-10-16 22:57:55 +02:00
return content;
2019-10-16 18:39:57 +02:00
}
2019-10-17 10:45:55 +02:00
}; // namespace Matrix