diff --git a/libcommsy.hpp b/libcommsy.hpp index fb6999a..3f0b4f4 100644 --- a/libcommsy.hpp +++ b/libcommsy.hpp @@ -32,13 +32,11 @@ class parsingNoSuchTagError : public std::exception {}; class descDownloadError : public std::exception {}; - static std::string server_url; static std::string server_sid; static std::string room; - std::string ltrim(const std::string& s) { static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended}; return std::regex_replace(s, lws, ""); @@ -90,6 +88,7 @@ void gumbo_search_by_attr(std::vector &elemvect, GumboNode* node, s if (node->type != GUMBO_NODE_ELEMENT) { return; } + GumboAttribute* hclass; if (node->v.element.tag == expectedtag && (hclass = gumbo_get_attribute(&node->v.element.attributes, attrname.c_str()))) { @@ -97,14 +96,17 @@ void gumbo_search_by_attr(std::vector &elemvect, GumboNode* node, s elemvect.push_back(node); } } + GumboVector* children = &node->v.element.children; for (unsigned int i = 0; i < children->length; ++i) { gumbo_search_by_attr(elemvect, static_cast(children->data[i]), attrname, searchword, expectedtag); } } + void gumbo_search_by_class(std::vector &elemvect, GumboNode* node, std::string searchword, GumboTag expectedtag) { return gumbo_search_by_attr(elemvect, node, "class", searchword, expectedtag); } + GumboNode *gumbo_search_by_id(GumboNode* node, std::string searchword, GumboTag expectedtag) { std::vector elemvect; gumbo_search_by_attr(elemvect, node, "id", searchword, expectedtag); @@ -121,9 +123,11 @@ void gumbo_search_by_tag(std::vector &elemvect, GumboNode* node, Gu if (node->type != GUMBO_NODE_ELEMENT) { return; } + if (node->v.element.tag == searchedtag) { elemvect.push_back(node); } + GumboVector* children = &node->v.element.children; for (unsigned int i = 0; i < children->length; ++i) { gumbo_search_by_tag(elemvect, static_cast(children->data[i]), searchedtag); @@ -132,22 +136,27 @@ void gumbo_search_by_tag(std::vector &elemvect, GumboNode* node, Gu static std::string gumbo_cleantext(GumboNode* node) { if (node->type == GUMBO_NODE_TEXT) { - return std::string(node->v.text.text); + return std::string(node->v.text.text); + } else if (node->type == GUMBO_NODE_ELEMENT && node->v.element.tag != GUMBO_TAG_SCRIPT && node->v.element.tag != GUMBO_TAG_STYLE) { + std::string contents = ""; GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { - const std::string text = gumbo_cleantext(reinterpret_cast (children->data[i])); - if (i != 0 && !text.empty()) { - contents.append(" "); - } - contents.append(text); + const std::string text = gumbo_cleantext(reinterpret_cast (children->data[i])); + if (i != 0 && !text.empty()) { + contents.append(" "); + } + contents.append(text); } + return contents; + } else { - return ""; + return ""; } } @@ -156,15 +165,18 @@ std::vector gumbo_get_attr(GumboNode *node, std::string attrkey, Gu GumboNode *childnode; GumboVector* children = &node->v.element.children; std::vector toappend; + // Check if current element is already the right one if (node->v.element.tag == expected_tag) { // Return this elements wanted attribute key return {gumbo_get_attribute(&node->v.element.attributes, attrkey.c_str())->value}; } + // Check if This is a node element else if (node->type != GUMBO_NODE_ELEMENT) { return {}; } + // Iterate through child nodes for (unsigned int it = 0; it < children->length; ++it) { childnode = reinterpret_cast (children->data[it]); @@ -175,6 +187,7 @@ std::vector gumbo_get_attr(GumboNode *node, std::string attrkey, Gu attrvals = merge_strvects(attrvals, toappend); } } + // Return the final result return attrvals; } @@ -182,6 +195,7 @@ std::vector gumbo_get_attr(GumboNode *node, std::string attrkey, Gu std::string gumbo_find_text_by_tag(GumboNode *node, GumboTag searchtag) { GumboNode *childnode; GumboVector* children = &node->v.element.children; + // Iterate through childs for (unsigned int it = 0; it < children->length; ++it) { childnode = reinterpret_cast (children->data[it]); @@ -189,6 +203,7 @@ std::string gumbo_find_text_by_tag(GumboNode *node, GumboTag searchtag) { return trim(gumbo_cleantext(childnode)); } } + throw parsingNoSuchTagError(); return ""; } @@ -228,20 +243,24 @@ std::vector> get_post_files(GumboNode *node) std::vector filenames; std::vector> filenameurlmap; std::map tmpmap; + // Get meta nodes gumbo_search_by_class(metanodes, node, "uk-comment-meta", GUMBO_TAG_DIV); // Get URLs fileurls = gumbo_get_attr(metanodes[2], "href", GUMBO_TAG_A); // Get filenames filenames = gumbo_get_attr(metanodes[2], "title", GUMBO_TAG_A); + // Generate map auto urlit = fileurls.begin(); auto nameit = filenames.begin(); while (true) { + // Break if last item was reached if (urlit == fileurls.end() or nameit == filenames.end()) { break; } + // Generate temporary map tmpmap = {}; tmpmap[*nameit] = *urlit; @@ -259,6 +278,7 @@ std::string get_post_desc(std::string post_url) { GumboOutput *post_document; GumboNode *desc_node; std::vector results; + // Get material ID material_id = get_filename(post_url); // Download post @@ -267,17 +287,20 @@ std::string get_post_desc(std::string post_url) { if (statuscode != 200) { throw descDownloadError(); } + // Parse post post_document = gumbo_parse(httpcontent.str().c_str()); // Get description element desc_node = gumbo_search_by_id(post_document->root, "description" + material_id, GUMBO_TAG_DIV); // Extract description gumbo_search_by_tag(results, desc_node, GUMBO_TAG_P); + // Cencenate occurencies std::string result_string; for (auto it = results.begin(); it != results.end(); it++) { result_string.append(trim(gumbo_cleantext(*it)) + "\n"); } + // Return first occurence return result_string; } @@ -316,6 +339,7 @@ public: std::string *getDescription(unsigned long postID) { // Get post commsyPost *thispost = getPost(postID); + // Check if post description was downloaded already if (thispost->description.empty()) { // Download post @@ -335,6 +359,7 @@ public: GumboOutput *document; long statuscode; numposts = 0; + while (1) { // Check connection and download feed try { @@ -349,15 +374,18 @@ public: } else if (statuscode != 200) { throw connectionFailError(); } - // Do some stuff + + // Do some stuff XD document = gumbo_parse(httpcontent.str().c_str()); httpcontent.str(std::string()); // Clear buffer just in case we need it later + // Get posts auto gumboPosts = get_posts(document->root); if (gumboPosts.size() == 0) { // Stop fetching more data break; } + // Map posts and their corresponding URL to a number for (auto it = gumboPosts.begin(); it != gumboPosts.end(); it++) { // Create post struct @@ -377,6 +405,7 @@ public: thispost.files.insert(filemap.begin(), filemap.end()); } } + // Append to posts vector posts.push_back(thispost); // Increment post counter diff --git a/libcommsyauth.hpp b/libcommsyauth.hpp index 40e3c29..bd0c771 100644 --- a/libcommsyauth.hpp +++ b/libcommsyauth.hpp @@ -1,4 +1,6 @@ #include +#include + #include #include #include @@ -13,27 +15,28 @@ class authFailureError : public std::exception {}; -class libCommsyAuth : QObject { +class libCommsyAuth : public QObject { Q_OBJECT public: + Q_SIGNALS: void reEmit(QNetworkReply *r); public: - QNetworkAccessManager *mNetMan = nullptr; - QNetworkRequest *mNetReq = nullptr; - - libCommsyAuth() { + explicit libCommsyAuth(QObject *p = nullptr) : QObject (p){ mNetMan = new QNetworkAccessManager(this); mNetReq = new QNetworkRequest(); + connect(mNetMan, &QNetworkAccessManager::finished, this, &libCommsyAuth::reEmit); } + QNetworkAccessManager *mNetMan = nullptr; + QNetworkRequest *mNetReq = nullptr; + void connectInit(const QUrl& serverBaseUrl) { // Initialise request //mNetReq->setTransferTimeout(15000); mNetReq->setUrl(serverBaseUrl); mNetReq->setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - connect(mNetMan, &QNetworkAccessManager::finished, this, &libCommsyAuth::reEmit); mNetMan->get(*mNetReq); } @@ -43,25 +46,32 @@ public: void sendAuth(const QString& authUrl, const QString& username, const QString& password) { QUrlQuery mNetQuery; + // Set auth credentials mNetQuery.addQueryItem("user_id", username); mNetQuery.addQueryItem("password", password); + // Create request mNetReq->setUrl(authUrl + "&mod=context&fct=login"); mNetReq->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - // Perform the request return - connect(mNetMan, &QNetworkAccessManager::finished, this, &libCommsyAuth::reEmit); + + // Perform the request mNetMan->post(*mNetReq, mNetQuery.toString(QUrl::FullyEncoded).toUtf8()); } QString getSID(QNetworkReply *reply) { auto cookies = QNetworkCookie::parseCookies(reply->rawHeader("Set-Cookie")); + for (const auto& cookie : cookies) { if (cookie.name() == "SID") { std::cout << cookie.value().toStdString() << std::endl; return cookie.value(); } } + throw authFailureError(); } }; + + +void libCommsyAuth::reEmit(QNetworkReply *) {} diff --git a/main.cpp b/main.cpp index be1c19f..522281f 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,12 @@ #include "ui_login.h" #include "ui_failure.h" #include "ui_offline.h" + +#include "libcommsy.hpp" +#include "libcommsyauth.hpp" + +#include + #include #include #include @@ -13,10 +19,6 @@ #include #include #include -#include - -#include "libcommsy.hpp" -#include "libcommsyauth.hpp" #define QCommsy_NAME "QCommsy" #define QCommsy_VERSION "0.1-alpha" @@ -61,15 +63,18 @@ public: void _UILoader::infoWindow(QMainWindow *w) { Ui::infoWindow *thisui = new Ui::infoWindow; + // Show initial UI thisui->setupUi(w); w->show(); + // Update version info thisui->versionInfo->setText( "App frontend: " QCommsy_NAME " version " QCommsy_VERSION "
" "Authentication backend: " libCommsyAuth_NAME " version " libCommsyAuth_VERSION "
" "Scrapping backend: " libCommsy_NAME " version " libCommsy_VERSION ); + // Add button handlers w->connect(thisui->backButton, &QPushButton::pressed, [this, w] () { loginWindow(w); @@ -79,68 +84,74 @@ void _UILoader::infoWindow(QMainWindow *w) { void _UILoader::failureWindow(QMainWindow *w) { Ui::failureWindow *thisui = new Ui::failureWindow; QString reason; + // Show initial UI thisui->setupUi(w); w->show(); + // Get error message reason = "Ein interner Fehler ist aufgetreten
" "Dies ist sehr wahrscheinlich ein Programmfehler. Sollte das zu häufig passieren, kontaktiere bitte den Entwickler"; + // Show error message thisui->descriptionText->setText("Verbindung fehlgeschlagen
" + reason); + // Add button handlers - w->connect(thisui->retryButton, &QPushButton::pressed, [this, w] () { - loginWindow(w); - }); - w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { - logout(); - loginWindow(w); - }); + w->connect(thisui->retryButton, &QPushButton::pressed, [this, w] () { loginWindow(w); }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { logout(); loginWindow(w); }); } + void _UILoader::offlineWindow(QMainWindow *w) { Ui::offlineWindow *thisui = new Ui::offlineWindow; QString reason; + // Show initial UI thisui->setupUi(w); w->show(); // Add button handlers - w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { - logout(); - loginWindow(w); - }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { logout(); loginWindow(w); }); } void _UILoader::loginWindow(QMainWindow *w, const QString& failure) { Ui::loginWindow *thisui = new Ui::loginWindow; + // Check if logged in already if (settings->contains("server_sid")) { return overviewWindow(w); } + // Show initial UI thisui->setupUi(w); w->show(); + // Restore login data if wanted bool restoreForm = settings->value("server_save", false).toBool(); if (restoreForm) { thisui->saveBox->setCheckState(Qt::Checked); //... TODO } + // Set failure text thisui->failureText->setText(failure); + // Add button handlers w->connect(thisui->loginButton, &QPushButton::pressed, [this, thisui, w] () { // Check input lines if (thisui->adressLine->text().isEmpty() or thisui->usernameLine->text().isEmpty() or thisui->roomLine->text().isEmpty() or thisui->passwordLine->text().isEmpty()) { return; } + // Save basic server credentials settings->setValue("server_save", thisui->saveBox->checkState() == Qt::Checked); settings->setValue("server_url", thisui->adressLine->text()); settings->setValue("server_room", thisui->roomLine->text()); + // Connect - w->connect(libCommsyAuth, &libCommsyAuth::reEmit, [this, thisui, w] (QNetworkReply *netReply) { + w->connect(auther, &libCommsyAuth::reEmit, [this, thisui, w] (QNetworkReply *netReply) { std::cout << auther->getAuthUrl(netReply).toStdString() << std::endl; // Get SID - w->connect(libCommsyAuth, &libCommsyAuth::reEmit, [this, thisui, w] (QNetworkReply *netReply) { + + w->connect(auther, &libCommsyAuth::reEmit, [this, thisui, w] (QNetworkReply *netReply) { // Login try { settings->setValue("server_sid", auther->getSID(netReply)); @@ -152,14 +163,17 @@ void _UILoader::loginWindow(QMainWindow *w, const QString& failure) { thisui->failureText->setText("Der angegebene Raum existiert nicht"); } }); + auther->sendAuth(auther->getAuthUrl(netReply), thisui->usernameLine->text(), thisui->passwordLine->text()); }); + auther->connectInit(thisui->adressLine->text()); }); } void _UILoader::overviewWindow(QMainWindow *w, bool catchInvalidRoomError) { Ui::overviewWindow *thisui = new Ui::overviewWindow; + // Connect if required try { reconnect(); @@ -167,37 +181,38 @@ void _UILoader::overviewWindow(QMainWindow *w, bool catchInvalidRoomError) { logout(); return loginWindow(w, "Die Sitzung ist abgelaufen - bitte erneut anmelden"); } catch (invalidRoomError& exception) { + if (catchInvalidRoomError) { logout(); return loginWindow(w, "Der Raum existiert nicht mehr"); } else { throw exception; } + } catch (connectionFailError&) { return offlineWindow(w); } catch (std::exception&) { return failureWindow(w); } + // Show initial UI thisui->setupUi(w); w->show(); + // Refresh view thisui->postCounter->setText(QString::number(connector->numposts) + " Einträge"); thisui->postList->clear(); for (const auto& thispost : connector->posts) { thisui->postList->addItem(QString::fromStdString(thispost.name)); } + // Add button handlers w->connect(thisui->postList, &QListWidget::activated, [this, thisui] (QModelIndex index) { QDesktopServices::openUrl(QUrl(settings->value("server_url").toString() + QString::fromStdString(connector->getPost(index.row())->url))); }); - w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { - logout(); - loginWindow(w); - }); - w->connect(thisui->infoButton, &QPushButton::pressed, [this, w] () { - infoWindow(w); - }); + + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { logout(); loginWindow(w); }); + w->connect(thisui->infoButton, &QPushButton::pressed, [this, w] () { infoWindow(w); }); }