#include "archive_parser.h" #include "src/list_item.h" #include "src/types.h" #include #include #include Archive::Archive(QString outbox_filename, ArchiveType archive_type) : outbox_filename(outbox_filename), archive_type(archive_type) {} Archive::InitError Archive::init() { QFile outbox_file(outbox_filename); if (!outbox_file.open(QIODevice::ReadOnly | QIODevice::Text)) return FailedOpeningFile; QJsonParseError json_error; QJsonDocument outbox_json_document = QJsonDocument::fromJson(outbox_file.readAll(), &json_error); outbox_file.close(); if (json_error.error != QJsonParseError::NoError) return JsonParseError; if (outbox_json_document.isEmpty()) return JsonEmpty; if (outbox_json_document.isNull()) return JsonNull; if (outbox_json_document.isObject()) outbox_json = new QJsonObject (outbox_json_document.object()); else return JsonNotObject; // Do some more throughful checks to make sure that the JSON is actually valid. if (not (outbox_json->contains("@context") and outbox_json->value("@context").isString() and (outbox_json->value("@context").toString() == "https://www.w3.org/ns/activitystreams"))) return JsonNotActivityStream; if (outbox_json->contains("orderedItems") and outbox_json->value("orderedItems").isArray()) { outbox_items = new QJsonArray(outbox_json->value("orderedItems").toArray()); // we'll need it during Archive's lifetime } return NoError; } Archive::~Archive() { delete outbox_json; outbox_json = nullptr; delete outbox_items; outbox_items = nullptr; } bool is_status_type_allowed(StatusType status_type, ViewStatusTypes allowed_types) { switch (status_type) { case PUBLIC: return allowed_types.includePublic; case UNLISTED: return allowed_types.includeUnlisted; case PRIVATE: return allowed_types.includePrivate; case DIRECT: return allowed_types.includeDirect; default: return true; } } StatusType get_status_type(QJsonObject obj) { /* * public: * to: #Public * unlisted: * to: /followers * cc: #Public * followers: * to: /followers * direct: * to: the Actors mentioned */ StatusType status_type = UNKNOWN; bool is_private_or_unlisted = false; QJsonArray to; if (obj.value("to").isArray()) { to = obj.value("to").toArray(); // see https://www.w3.org/TR/activitypub/#public-addressing for (auto collection : to) { QString col = collection.toString(); if (col == "https://www.w3.org/ns/activitystreams#Public") { return PUBLIC; // status' privacy can only be promoted to a higher level } else if (col.endsWith("/followers")) // at least for Mastodon… is_private_or_unlisted = true; // private or unlisted } } if (obj.value("cc").isArray()) { QJsonArray cc = obj.value("cc").toArray(); for (auto collection : cc) { QString col = collection.toString(); if (col == "https://www.w3.org/ns/activitystreams#Public" and is_private_or_unlisted) { return UNLISTED; // status' privacy can only be promoted to a higher level } } if (is_private_or_unlisted) return PRIVATE; else if (to.size() > 0) return DIRECT; else if (to.size() == 0 and cc.size() == 0) // sending a direct message to yourself or to another actor that doesn't exist anymore return DIRECT; } return status_type; } void Archive::update_status_list(ViewStatusTypes allowed_types, QListWidget *parent) { int i = 0; for (auto&& item : *outbox_items) { if (item.isObject()){ QJsonObject obj = item.toObject(); if (not obj.value("type").isString()) // this shouldn't happen, but you never know goto next_item; if (obj.value("type").toString() != "Create") // ignoring everything that isn't a Create activity for now goto next_item; // Determine the status' type StatusType status_type = get_status_type(obj); if (not is_status_type_allowed(status_type, allowed_types)) goto next_item; if (obj.value("object").isObject()) { QJsonObject activity = obj.value("object").toObject(); if (activity.value("content").isString()) { // Strip HTML for display in list, according to https://stackoverflow.com/a/12157835 QTextDocument strip_html; strip_html.setHtml(activity.value("content").toString()); ListItem *item = new ListItem(strip_html.toPlainText(), status_type, parent, i); } } } next_item: ++i; } }