fix broken ovh bill urls

now with much more pain, you can login manually and we will hijack that session to download the bills.

huge yay to OVH for a great idea
This commit is contained in:
Pierre Ducroquet 2023-06-08 11:32:34 +02:00
parent 53ed1462b1
commit eb62fd694f
6 changed files with 196 additions and 22 deletions

View File

@ -26,6 +26,9 @@ set(PROJECT_SOURCES
dedicatedserverinfowidget.cpp
dedicatedserverinfowidget.h
dedicatedserverinfowidget.ui
ovhwebauthentication.h
ovhwebauthentication.cpp
ovhwebauthentication.ui
ovhapi.cpp
ovhapi.h
resources.qrc

View File

@ -200,82 +200,110 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="141"/>
<location filename="mainwindow.cpp" line="142"/>
<source>Validate credential</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="141"/>
<location filename="mainwindow.cpp" line="142"/>
<source>Have you validated the OVH credential request?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="183"/>
<location filename="mainwindow.cpp" line="184"/>
<source>Info %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="198"/>
<location filename="mainwindow.cpp" line="199"/>
<source>IPMI not activated</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="198"/>
<location filename="mainwindow.cpp" line="199"/>
<source>IPMI is not activated on this server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="207"/>
<location filename="mainwindow.cpp" line="208"/>
<source>IPMI KVM not available</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="207"/>
<location filename="mainwindow.cpp" line="208"/>
<source>IPMI KVM is not available on this server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="231"/>
<location filename="mainwindow.cpp" line="232"/>
<source>Waiting for task... %1</source>
<oldsource>Waiting for task...</oldsource>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="251"/>
<location filename="mainwindow.cpp" line="252"/>
<source>KVM %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="260"/>
<location filename="mainwindow.cpp" line="262"/>
<location filename="mainwindow.cpp" line="261"/>
<location filename="mainwindow.cpp" line="263"/>
<source>JNLP KVM</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="260"/>
<location filename="mainwindow.cpp" line="261"/>
<source>This server only supports a Java WebStart KVM, it will thus not be embedded in this window.
Java WebStart should be starting right now, wait until it finishes loading to close this dialog.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="262"/>
<location filename="mainwindow.cpp" line="263"/>
<source>Failed to launch javaws. Make sure it is properly installed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="361"/>
<location filename="mainwindow.cpp" line="362"/>
<source>Target archive</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="370"/>
<location filename="mainwindow.cpp" line="362"/>
<source>Zip files (*.zip)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="366"/>
<source>OVH...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="366"/>
<source>OVH decided that getting a bill require a &apos;true&apos; login. I&apos;m going to show you a web browser for that.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="377"/>
<source>Failed to open archive</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainwindow.cpp" line="370"/>
<location filename="mainwindow.cpp" line="377"/>
<source>Failed to open archive...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OvhWebAuthentication</name>
<message>
<location filename="ovhwebauthentication.ui" line="14"/>
<source>OVH Web authentication</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="ovhwebauthentication.ui" line="29"/>
<source>Please login on OVH website...</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -24,6 +24,7 @@
#include <KArchive>
#include <KZip>
#include "dedicatedserverinfowidget.h"
#include "ovhwebauthentication.h"
using namespace std::chrono_literals;
@ -358,10 +359,16 @@ QCoro::Task<> MainWindow::on_actionGraphPerMonth_triggered()
QCoro::Task<> MainWindow::on_actionBillsOfLastMonth_triggered()
{
// todo filters
auto fileName = QFileDialog::getSaveFileName(this, tr("Target archive"), QString());
auto fileName = QFileDialog::getSaveFileName(this, tr("Target archive"), QString(), tr("Zip files (*.zip)"));
if (fileName.isEmpty())
co_return;
QMessageBox::information(this, tr("OVH..."), tr("OVH decided that getting a bill require a 'true' login. I'm going to show you a web browser for that."));
auto ovhAuth = new OvhWebAuthentication(this);
if (ovhAuth->exec() != QDialog::Accepted) {
qDebug() << "Rejected login, ok, bye bye";
co_return;
}
auto start = QDateTime::currentMSecsSinceEpoch();
@ -383,8 +390,6 @@ QCoro::Task<> MainWindow::on_actionBillsOfLastMonth_triggered()
QString path = QString("/me/bill?date.to=%1&date.from=%2").arg(firstDayOfMonth.toString(Qt::ISODate))
.arg(oneMonthAgo.toString(Qt::ISODate));
double totalCost = 0;
QString currency;
auto billList = (co_await api->get(path, ten_years)).array();
// Ok, we can set the progress based on that list
@ -400,14 +405,13 @@ QCoro::Task<> MainWindow::on_actionBillsOfLastMonth_triggered()
auto billTime = QDateTime::fromString(billInfo["date"].toString(), Qt::ISODate);
qDebug() << "Downloading bill at " << billPdfUrl;
QByteArray pdfData = co_await api->download(QUrl{billPdfUrl});
QByteArray pdfData = co_await ovhAuth->download(QUrl{billPdfUrl});
targetArchive.writeFile(QString("%1.pdf").arg(billId), pdfData, 0100644, QString(), QString(), billTime, billTime, billTime);
// update progress dialog...
progressDialog->setValue(progressDialog->value() + 1);
}
targetArchive.close();
progressDialog->close();
@ -416,7 +420,7 @@ QCoro::Task<> MainWindow::on_actionBillsOfLastMonth_triggered()
auto end = QDateTime::currentMSecsSinceEpoch();
qDebug() << "Took ..." << end - start;
ovhAuth->deleteLater();
QDesktopServices::openUrl(QUrl::fromLocalFile(fileName));
}

65
ovhwebauthentication.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "ovhwebauthentication.h"
#include "ui_ovhwebauthentication.h"
#include <QTemporaryFile>
#include <QWebEngineProfile>
#include <QCoroSignal>
#include <QDir>
#include <QMessageBox>
OvhWebAuthentication::OvhWebAuthentication(QWidget *parent) :
QDialog(parent),
ui(new Ui::OvhWebAuthentication)
{
ui->setupUi(this);
// Ok, why this fake url ? because OVH limited the target urls, but did not document it in any way...
ui->webView->setUrl(QUrl{"https://www.ovh.com/auth/?onsuccess=https%3A%2F%2Fwww.ovh.com%2Ffake_url"});
connect(ui->webView, &QWebEngineView::urlChanged, [this](const QUrl &url) {
qDebug() << "Started loading url " << url;
if (url == QUrl("https://www.ovh.com/fake_url")) {
// SUCCESS !!!!
ui->webView->setUrl(QUrl{"about:blank"});
ui->webView->setHtml("<html><body><h1>SUCCESS !</h1></body></html>");
emit accept();
}
});
connect(ui->webView, &QWebEngineView::loadStarted, [this]() {
qDebug() << "Started loading";
});
connect(ui->webView, &QWebEngineView::loadFinished, [this]() {
qDebug() << "Finished loading";
});
}
OvhWebAuthentication::~OvhWebAuthentication()
{
delete ui;
}
QCoro::Task<QByteArray> OvhWebAuthentication::download(const QUrl &url)
{
QTemporaryFile tempFile;
if (tempFile.open()) {
ui->webView->page()->download(url, tempFile.fileName());
tempFile.close();
auto webProfile = ui->webView->page()->profile();
auto dlConn = connect(webProfile, &QWebEngineProfile::downloadRequested, this, [this](QWebEngineDownloadItem *download) {
qDebug() << "Accepting download";
download->accept();
QObject::connect(download, &QWebEngineDownloadItem::finished, this, [this, download]() {
qDebug() << "Download finished" << download->downloadFileName();
QDir dlDir{download->downloadDirectory()};
emit webEngineDownloadFinished(dlDir.absoluteFilePath(download->downloadFileName()));
});
});
const QString dlFileName = co_await qCoro(this, &OvhWebAuthentication::webEngineDownloadFinished);
qDebug() << "Ok, download finished, understood.";
disconnect(dlConn);
QFile dataFile(dlFileName);
dataFile.open(QIODevice::ReadOnly);
QByteArray data = dataFile.readAll();
dataFile.remove();
co_return data;
}
co_return "";
}

29
ovhwebauthentication.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef OVHWEBAUTHENTICATION_H
#define OVHWEBAUTHENTICATION_H
#include <QDialog>
#include <QCoroTask>
namespace Ui {
class OvhWebAuthentication;
}
class OvhWebAuthentication : public QDialog
{
Q_OBJECT
public:
explicit OvhWebAuthentication(QWidget *parent = nullptr);
~OvhWebAuthentication();
QCoro::Task<QByteArray> download(const QUrl &url);
signals:
void authenticated();
void webEngineDownloadFinished(const QString &dlFileName);
private:
Ui::OvhWebAuthentication *ui;
};
#endif // OVHWEBAUTHENTICATION_H

45
ovhwebauthentication.ui Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OvhWebAuthentication</class>
<widget class="QDialog" name="OvhWebAuthentication">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>OVH Web authentication</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QWebEngineView" name="webView" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Please login on OVH website...</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebEngineView</class>
<extends>QWidget</extends>
<header location="global">qwebengineview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>