void SubsonicService::onPingFinished() { QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { login_state_ = LoginState_BadServer; qLog(Error) << "Failed to connect (" << Utilities::EnumToString(QNetworkReply::staticMetaObject, "NetworkError", reply->error()) << "):" << reply->errorString(); } else { QXmlStreamReader reader(reply); reader.readNextStartElement(); QStringRef status = reader.attributes().value("status"); if (status == "ok") { login_state_ = LoginState_Loggedin; } else { reader.readNextStartElement(); int error = reader.attributes().value("code").toString().toInt(); qLog(Error) << "Subsonic error (" << Utilities::EnumToString(SubsonicService::staticMetaObject, "ApiError", error) << "):" << reader.attributes().value("message").toString(); switch (error) { // "Parameter missing" for "ping" is always blank username or password case ApiError_ParameterMissing: case ApiError_BadCredentials: login_state_ = LoginState_BadCredentials; break; case ApiError_OutdatedClient: login_state_ = LoginState_OutdatedClient; break; case ApiError_OutdatedServer: login_state_ = LoginState_OutdatedServer; break; case ApiError_Unlicensed: login_state_ = LoginState_Unlicensed; break; default: login_state_ = LoginState_OtherError; break; } } } qLog(Debug) << "Login state changed:" << Utilities::EnumToString(SubsonicService::staticMetaObject, "LoginState", login_state_); emit LoginStateChanged(login_state_); }
SubsonicService::SubsonicService(InternetModel *parent) : InternetService(kServiceName, parent, parent), network_(new QNetworkAccessManager(this)), http_url_handler_(new SubsonicUrlHandler(this, this)), https_url_handler_(new SubsonicHttpsUrlHandler(this, this)), login_state_(LoginState_OtherError), item_lookup_() { model()->player()->RegisterUrlHandler(http_url_handler_); model()->player()->RegisterUrlHandler(https_url_handler_); connect(this, SIGNAL(LoginStateChanged(SubsonicService::LoginState)), SLOT(onLoginStateChanged(SubsonicService::LoginState))); }
void SubsonicService::Login() { // Recreate fresh network state, otherwise old HTTPS settings seem to get reused network_->deleteLater(); network_ = new QNetworkAccessManager(this); network_->setCookieJar(new QNetworkCookieJar(network_)); // Forget login state whilst waiting login_state_ = LoginState_Unknown; if (IsConfigured()) { // Ping is enough to check credentials Ping(); } else { login_state_ = LoginState_IncompleteCredentials; emit LoginStateChanged(login_state_); } }
SubsonicService::SubsonicService(Application* app, InternetModel* parent) : InternetService(kServiceName, app, parent, parent), network_(new QNetworkAccessManager(this)), url_handler_(new SubsonicUrlHandler(this, this)), scanner_(new SubsonicLibraryScanner(this, this)), load_database_task_id_(0), context_menu_(NULL), root_(NULL), library_backend_(NULL), library_model_(NULL), library_filter_(NULL), library_sort_model_(new QSortFilterProxyModel(this)), total_song_count_(0), login_state_(LoginState_OtherError), redirect_count_(0) { app_->player()->RegisterUrlHandler(url_handler_); connect(scanner_, SIGNAL(ScanFinished()), SLOT(ReloadDatabaseFinished())); library_backend_ = new LibraryBackend; library_backend_->moveToThread(app_->database()->thread()); library_backend_->Init(app_->database(), kSongsTable, QString::null, QString::null, kFtsTable); connect(library_backend_, SIGNAL(TotalSongCountUpdated(int)), SLOT(UpdateTotalSongCount(int))); library_model_ = new LibraryModel(library_backend_, app_, this); library_model_->set_show_various_artists(false); library_model_->set_show_smart_playlists(false); library_filter_ = new LibraryFilterWidget(0); library_filter_->SetSettingsGroup(kSettingsGroup); library_filter_->SetLibraryModel(library_model_); library_filter_->SetFilterHint(tr("Search Subsonic")); library_filter_->SetAgeFilterEnabled(false); library_sort_model_->setSourceModel(library_model_); library_sort_model_->setSortRole(LibraryModel::Role_SortText); library_sort_model_->setDynamicSortFilter(true); library_sort_model_->setSortLocaleAware(true); library_sort_model_->sort(0); connect(this, SIGNAL(LoginStateChanged(SubsonicService::LoginState)), SLOT(OnLoginStateChanged(SubsonicService::LoginState))); context_menu_ = new QMenu; context_menu_->addActions(GetPlaylistActions()); context_menu_->addSeparator(); context_menu_->addAction( IconLoader::Load("view-refresh"), tr("Refresh catalogue"), this, SLOT(ReloadDatabase())); QAction* config_action = context_menu_->addAction( IconLoader::Load("configure"), tr("Configure Subsonic..."), this, SLOT(ShowConfig())); context_menu_->addSeparator(); context_menu_->addMenu(library_filter_->menu()); library_filter_->AddMenuAction(config_action); app_->global_search()->AddProvider(new LibrarySearchProvider( library_backend_, tr("Subsonic"), "subsonic", QIcon(":/providers/subsonic.png"), true, app_, this)); }
void SubsonicService::OnPingFinished(QNetworkReply* reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { switch(reply->error()) { case QNetworkReply::ConnectionRefusedError: login_state_ = LoginState_ConnectionRefused; break; case QNetworkReply::HostNotFoundError: login_state_ = LoginState_HostNotFound; break; case QNetworkReply::TimeoutError: login_state_ = LoginState_Timeout; break; case QNetworkReply::SslHandshakeFailedError: login_state_ = LoginState_SslError; break; default: //Treat uncaught error types here as generic login_state_ = LoginState_BadServer; break; } qLog(Error) << "Failed to connect (" << Utilities::EnumToString(QNetworkReply::staticMetaObject, "NetworkError", reply->error()) << "):" << reply->errorString(); } else { QXmlStreamReader reader(reply); reader.readNextStartElement(); QStringRef status = reader.attributes().value("status"); int http_status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (status == "ok") { login_state_ = LoginState_Loggedin; } else if (http_status_code >= 300 && http_status_code <= 399) { // Received a redirect status code, follow up on it. QUrl redirect_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (redirect_url.isEmpty()) { qLog(Debug) << "Received HTTP code " << http_status_code << ", but no URL"; login_state_ = LoginState_RedirectNoUrl; } else { redirect_count_++; qLog(Debug) << "Redirect receieved to " << redirect_url.toString(QUrl::RemoveQuery) << ", current redirect count is " << redirect_count_; if (redirect_count_ <= kMaxRedirects) { working_server_ = ScrubUrl(redirect_url).toString(QUrl::RemoveQuery); Ping(); // To avoid the LoginStateChanged, as it will come from the recursive request. return; } else { // Redirect limit exceeded login_state_ = LoginState_RedirectLimitExceeded; } } } else { reader.readNextStartElement(); int error = reader.attributes().value("code").toString().toInt(); qLog(Error) << "Subsonic error (" << Utilities::EnumToString(SubsonicService::staticMetaObject, "ApiError", error) << "):" << reader.attributes().value("message").toString(); switch (error) { // "Parameter missing" for "ping" is always blank username or password case ApiError_ParameterMissing: case ApiError_BadCredentials: login_state_ = LoginState_BadCredentials; break; case ApiError_OutdatedClient: login_state_ = LoginState_OutdatedClient; break; case ApiError_OutdatedServer: login_state_ = LoginState_OutdatedServer; break; case ApiError_Unlicensed: login_state_ = LoginState_Unlicensed; break; default: login_state_ = LoginState_OtherError; break; } } } qLog(Debug) << "Login state changed:" << Utilities::EnumToString(SubsonicService::staticMetaObject, "LoginState", login_state_); emit LoginStateChanged(login_state_); }