| 794 | } |
| 795 | |
| 796 | QString Backend::readAccessToken() const |
| 797 | { |
| 798 | QString tokenPath; |
| 799 | if (m_providerName == "gdrive") { |
| 800 | tokenPath = m_providerPath.isEmpty() ? defaultTokenPath("gdrive") : m_providerPath; |
| 801 | } else if (m_providerName == "onedrive") { |
| 802 | tokenPath = m_providerPath.isEmpty() ? defaultTokenPath("onedrive") : m_providerPath; |
| 803 | } else { |
| 804 | return QString(); |
| 805 | } |
| 806 | |
| 807 | QFile f(tokenPath); |
| 808 | if (!f.open(QIODevice::ReadOnly)) return QString(); |
| 809 | |
| 810 | QJsonDocument doc = QJsonDocument::fromJson(f.readAll()); |
| 811 | f.close(); |
| 812 | QJsonObject obj = doc.object(); |
| 813 | |
| 814 | // Check if token is expired and refresh if needed |
| 815 | qint64 expiresAt = obj.value("expires_at").toInteger(0); |
| 816 | if (expiresAt > 0 && QDateTime::currentSecsSinceEpoch() >= expiresAt - 60) { |
| 817 | // Thread-safe re-entrancy guard |
| 818 | static QAtomicInt s_refreshing{0}; |
| 819 | if (!s_refreshing.testAndSetAcquire(0, 1)) return QString(); |
| 820 | struct RefreshGuard { |
| 821 | QAtomicInt& flag; |
| 822 | RefreshGuard(QAtomicInt& f) : flag(f) {} |
| 823 | ~RefreshGuard() { flag.storeRelease(0); } |
| 824 | } guard(s_refreshing); |
| 825 | |
| 826 | QString refreshToken = obj.value("refresh_token").toString(); |
| 827 | if (refreshToken.isEmpty()) return QString(); |
| 828 | |
| 829 | // Synchronous refresh with reduced timeout |
| 830 | QNetworkAccessManager nam; |
| 831 | QUrlQuery body; |
| 832 | body.addQueryItem("client_id", m_providerName == "onedrive" ? ONEDRIVE_CLIENT_ID : GDRIVE_CLIENT_ID); |
| 833 | body.addQueryItem("client_secret", m_providerName == "onedrive" ? ONEDRIVE_CLIENT_SECRET : GDRIVE_CLIENT_SECRET); |
| 834 | body.addQueryItem("refresh_token", refreshToken); |
| 835 | body.addQueryItem("grant_type", "refresh_token"); |
| 836 | if (m_providerName == "onedrive") |
| 837 | body.addQueryItem("scope", "Files.ReadWrite offline_access"); |
| 838 | |
| 839 | QNetworkRequest req(QUrl(m_providerName == "onedrive" |
| 840 | ? "https://login.microsoftonline.com/common/oauth2/v2.0/token" |
| 841 | : "https://oauth2.googleapis.com/token")); |
| 842 | req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); |
| 843 | |
| 844 | // Retry up to 2 times (broken IPv6 fails instantly, retry gives IPv4 a chance) |
| 845 | QNetworkReply *reply = nullptr; |
| 846 | for (int attempt = 0; attempt < 3; ++attempt) { |
| 847 | QEventLoop loop; |
| 848 | QTimer timeout; |
| 849 | timeout.setSingleShot(true); |
| 850 | reply = nam.post(req, body.toString(QUrl::FullyEncoded).toUtf8()); |
| 851 | QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); |
| 852 | QObject::connect(&timeout, &QTimer::timeout, reply, &QNetworkReply::abort); |
| 853 | timeout.start(10000); |
nothing calls this directly
no outgoing calls
no test coverage detected