2 * Copyright 2003-2018 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "QobuzClient.hxx"
22 #include "lib/gcrypt/MD5.hxx"
23 #include "util/ConstBuffer.hxx"
31 class QueryStringBuilder
{
35 QueryStringBuilder
&operator()(std::string
&dest
, const char *name
,
36 const char *value
) noexcept
{
37 dest
.push_back(first
? '?' : '&');
42 dest
+= value
; // TODO: escape
50 QobuzClient::QobuzClient(EventLoop
&event_loop
,
51 const char *_base_url
,
52 const char *_app_id
, const char *_app_secret
,
53 const char *_device_manufacturer_id
,
54 const char *_username
, const char *_email
,
55 const char *_password
,
56 const char *_format_id
)
57 :base_url(_base_url
), app_id(_app_id
), app_secret(_app_secret
),
58 device_manufacturer_id(_device_manufacturer_id
),
59 username(_username
), email(_email
), password(_password
),
60 format_id(_format_id
),
62 defer_invoke_handlers(event_loop
, BIND_THIS_METHOD(InvokeHandlers
))
67 QobuzClient::GetCurl() noexcept
73 QobuzClient::StartLogin()
75 assert(!session
.IsDefined());
76 assert(!login_request
);
77 assert(!handlers
.empty());
79 QobuzLoginHandler
&handler
= *this;
80 login_request
= std::make_unique
<QobuzLoginRequest
>(*curl
, base_url
,
84 device_manufacturer_id
,
86 login_request
->Start();
90 QobuzClient::AddLoginHandler(QobuzSessionHandler
&h
) noexcept
92 const std::lock_guard
<Mutex
> protect(mutex
);
93 assert(!h
.is_linked());
95 const bool was_empty
= handlers
.empty();
96 handlers
.push_front(h
);
98 if (!was_empty
|| login_request
)
101 if (session
.IsDefined()) {
102 ScheduleInvokeHandlers();
104 // TODO: throttle login attempts?
109 error
= std::current_exception();
110 ScheduleInvokeHandlers();
117 QobuzClient::GetSession() const
119 const std::lock_guard
<Mutex
> protect(mutex
);
122 std::rethrow_exception(error
);
124 if (!session
.IsDefined())
125 throw std::runtime_error("No session");
131 QobuzClient::OnQobuzLoginSuccess(QobuzSession
&&_session
) noexcept
134 const std::lock_guard
<Mutex
> protect(mutex
);
135 session
= std::move(_session
);
136 login_request
.reset();
139 ScheduleInvokeHandlers();
143 QobuzClient::OnQobuzLoginError(std::exception_ptr _error
) noexcept
146 const std::lock_guard
<Mutex
> protect(mutex
);
147 error
= std::move(_error
);
148 login_request
.reset();
151 ScheduleInvokeHandlers();
155 QobuzClient::InvokeHandlers() noexcept
157 const std::lock_guard
<Mutex
> protect(mutex
);
158 while (!handlers
.empty()) {
159 auto &h
= handlers
.front();
160 handlers
.pop_front();
162 const ScopeUnlock
unlock(mutex
);
168 QobuzClient::MakeUrl(const char *object
, const char *method
,
169 const std::multimap
<std::string
, std::string
> &query
) const noexcept
171 assert(!query
.empty());
173 std::string
uri(base_url
);
178 QueryStringBuilder q
;
179 for (const auto &i
: query
)
180 q(uri
, i
.first
.c_str(), i
.second
.c_str());
182 q(uri
, "app_id", app_id
);
187 QobuzClient::MakeSignedUrl(const char *object
, const char *method
,
188 const std::multimap
<std::string
, std::string
> &query
) const noexcept
190 assert(!query
.empty());
192 std::string
uri(base_url
);
197 QueryStringBuilder q
;
198 std::string
concatenated_query(object
);
199 concatenated_query
+= method
;
200 for (const auto &i
: query
) {
201 q(uri
, i
.first
.c_str(), i
.second
.c_str());
203 concatenated_query
+= i
.first
;
204 concatenated_query
+= i
.second
;
207 q(uri
, "app_id", app_id
);
209 const auto request_ts
= std::to_string(time(nullptr));
210 q(uri
, "request_ts", request_ts
.c_str());
211 concatenated_query
+= request_ts
;
213 concatenated_query
+= app_secret
;
215 const auto md5_hex
= MD5Hex({concatenated_query
.data(), concatenated_query
.size()});
216 q(uri
, "request_sig", &md5_hex
.front());