Music Hub  ..
A session-wide music playback service
track_list_skeleton.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2015 Canonical Ltd.
3  * Copyright © 2022 UBports Foundation.
4  *
5  * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License version 3,
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY {} without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authored by: Thomas Voß <thomas.voss@canonical.com>
20  */
21 
22 #include "track_list_skeleton.h"
23 
24 #include "apparmor/lomiri.h"
25 #include "logging.h"
27 
28 #include "mpris.h"
29 
30 #include "util/uri_check.h"
31 
32 #include <QDBusConnection>
33 #include <QDBusMessage>
34 
35 #include <limits>
36 #include <cstdint>
37 
38 namespace media = lomiri::MediaHubService;
39 
40 using namespace media;
41 
42 namespace lomiri {
43 namespace MediaHubService {
44 
46 {
47  Q_DECLARE_PUBLIC(TrackListSkeleton)
48 
49 public:
50  TrackListSkeletonPrivate(const QDBusConnection &bus,
51  const apparmor::lomiri::RequestContextResolver::Ptr& request_context_resolver,
52  const media::apparmor::lomiri::RequestAuthenticator::Ptr& request_authenticator,
55  m_connection(bus),
56  request_context_resolver(request_context_resolver),
57  request_authenticator(request_authenticator),
58  m_impl(impl),
59  q_ptr(q)
60  {
61  }
62 
63 private:
64  QDBusConnection m_connection;
65  media::apparmor::lomiri::RequestContextResolver::Ptr request_context_resolver;
66  media::apparmor::lomiri::RequestAuthenticator::Ptr request_authenticator;
68  TrackListSkeleton *q_ptr;
69 };
70 
71 }} // namespace
72 
74  const media::apparmor::lomiri::RequestContextResolver::Ptr& request_context_resolver,
75  const media::apparmor::lomiri::RequestAuthenticator::Ptr& request_authenticator,
77  QObject *parent):
78  QObject(parent),
79  d_ptr(new TrackListSkeletonPrivate(bus,
80  request_context_resolver,
81  request_authenticator,
82  impl, this))
83 {
84  QObject::connect(impl, &TrackListImplementation::trackListReplaced,
85  this, [this](const QVector<media::Track::Id> &tracks,
86  const Track::Id &currentTrack) {
87  QStringList trackList;
88  for (const auto id: tracks) {
89  trackList.append(id);
90  }
91  Q_EMIT TrackListReplaced(trackList, currentTrack);
92  });
93  QObject::connect(impl, &TrackListImplementation::trackAdded,
95  QObject::connect(impl, &TrackListImplementation::tracksAdded,
96  this, [this](const QVector<QUrl> &tracks) {
97  QStringList trackList;
98  for (const auto uri: tracks) {
99  trackList.append(uri.toString());
100  }
101  Q_EMIT TracksAdded(trackList);
102  });
103  QObject::connect(impl, &TrackListImplementation::trackRemoved,
105  QObject::connect(impl, &TrackListImplementation::trackMoved,
107  QObject::connect(impl, &TrackListImplementation::trackChanged,
109  QObject::connect(impl, &TrackListImplementation::trackListReset,
111  // FIXME TrackMetadataChanged is never invoked
112 }
113 
115 {
116 }
117 
118 QStringList TrackListSkeleton::tracks() const
119 {
120  Q_D(const TrackListSkeleton);
121  QStringList trackList;
122  const auto tracks = d->m_impl->tracks();
123  for (const auto id: tracks) {
124  trackList.append(id);
125  }
126  return trackList;
127 }
128 
130 {
131  Q_D(const TrackListSkeleton);
132  return d->m_impl->canEditTracks();
133 }
134 
135 QMap<QString,QString> TrackListSkeleton::GetTracksMetadata(const QString &track)
136 {
137  /* FIXME: We should return the metadata, but since the old
138  * implementation always returned an empty map, let's continue
139  * doing the same. We need to fix the signature anyway.
140  Q_D(TrackListSkeleton);
141  Track::MetaData md = d->m_impl->query_meta_data_for_track(track);
142  */
143  Q_UNUSED(track);
144  return QMap<QString,QString>();
145 }
146 
147 QString TrackListSkeleton::GetTracksUri(const QString &track)
148 {
149  Q_D(TrackListSkeleton);
150  return d->m_impl->query_uri_for_track(track).toString();
151 }
152 
153 void TrackListSkeleton::AddTrack(const QString &uri, const QString &after,
154  bool makeCurrent)
155 {
156  Q_D(TrackListSkeleton);
157 
158  MH_TRACE("");
159  QDBusMessage in = message();
160  QDBusConnection bus = connection();
161  in.setDelayedReply(true);
162 
163  struct Params {
164  QString uri;
165  QString after;
166  bool makeCurrent;
167  } params = { uri, after, makeCurrent };
168 
169  d->request_context_resolver->resolve_context_for_dbus_name_async
170  (in.service(), [this, in, bus, params](const media::apparmor::lomiri::Context& context)
171  {
172  Q_D(TrackListSkeleton);
173  QUrl uri = QUrl::fromUserInput(params.uri);
174  media::Track::Id after = params.after;
175  bool makeCurrent = params.makeCurrent;
176 
177  // Make sure the client has adequate apparmor permissions to open the URI
178  const auto result = d->request_authenticator->authenticate_open_uri_request(context, uri);
179 
180  UriCheck uri_check(uri);
181  const bool valid_uri = !uri_check.is_local_file() or
182  (uri_check.is_local_file() and uri_check.file_exists());
183  QDBusMessage reply = in.createReply();
184  if (!valid_uri)
185  {
186  const QString err_str = {"Warning: Not adding track " + uri.toString() +
187  " to TrackList because it can't be found."};
188  MH_WARNING() << err_str;
189  reply = in.createErrorReply(
190  mpris::Player::Error::UriNotFound::name,
191  err_str);
192  }
193  else
194  {
195  // Only add the track to the TrackList if it passes the apparmor permissions check
196  if (std::get<0>(result))
197  {
198  d->m_impl->add_track_with_uri_at(uri, after, makeCurrent);
199  }
200  else
201  {
202  const QString err_str = {"Warning: Not adding track " + uri.toString() +
203  " to TrackList because of inadequate client apparmor permissions."};
204  MH_WARNING() << err_str;
205  reply = in.createErrorReply(
207  err_str);
208  }
209  }
210 
211  bus.send(reply);
212  });
213 }
214 
215 void TrackListSkeleton::AddTracks(const QStringList &uris,
216  const QString &after)
217 {
218  Q_D(TrackListSkeleton);
219  MH_TRACE("");
220  QDBusMessage in = message();
221  QDBusConnection bus = connection();
222  in.setDelayedReply(true);
223 
224  struct Params {
225  QStringList uris;
226  QString after;
227  } params = { uris, after };
228 
229  d->request_context_resolver->resolve_context_for_dbus_name_async
230  (in.service(), [this, in, bus, params](const media::apparmor::lomiri::Context& context)
231  {
232  Q_D(TrackListSkeleton);
233  const QStringList &uris = params.uris;
234  const media::Track::Id after = params.after;
235 
236  bool valid_uri = false;
237  media::apparmor::lomiri::RequestAuthenticator::Result result;
238  QString uri_err_str, err_str;
239  QVector<QUrl> trackUris;
240  QDBusMessage reply;
241  for (const auto uri : uris)
242  {
243  UriCheck uri_check(uri);
244  valid_uri = !uri_check.is_local_file() or
245  (uri_check.is_local_file() and uri_check.file_exists());
246  if (!valid_uri)
247  {
248  uri_err_str = {"Warning: Not adding track " + uri +
249  " to TrackList because it can't be found."};
250  MH_WARNING() << uri_err_str;
251  reply = in.createErrorReply(
252  mpris::Player::Error::UriNotFound::name,
253  err_str);
254  }
255 
256  // Make sure the client has adequate apparmor permissions to open the URI
257  result = d->request_authenticator->authenticate_open_uri_request(context, uri);
258  if (not std::get<0>(result))
259  {
260  err_str = {"Warning: Not adding track " + uri +
261  " to TrackList because of inadequate client apparmor permissions."};
262  break;
263  }
264 
265  trackUris.append(QUrl::fromUserInput(uri));
266  }
267 
268  // Only add the track to the TrackList if it passes the apparmor permissions check
269  if (std::get<0>(result))
270  {
271  reply = in.createReply();
272  d->m_impl->add_tracks_with_uri_at(trackUris, after);
273  }
274  else
275  {
276  MH_WARNING() << err_str;
277  reply = in.createErrorReply(
279  err_str);
280  }
281 
282  bus.send(reply);
283  });
284 }
285 
286 void TrackListSkeleton::MoveTrack(const QString &id, const QString &to)
287 {
288  Q_D(TrackListSkeleton);
289  try {
290  const bool ret = d->m_impl->move_track(id, to);
291  if (!ret)
292  {
293  const QString err_str = {"Error: Not moving track " + id +
294  " to destination " + to};
295  MH_WARNING() << err_str;
296  sendErrorReply(
298  err_str);
299  }
300  } catch(media::TrackList::Errors::FailedToMoveTrack& e) {
301  sendErrorReply(
303  e.what());
304  } catch(media::TrackList::Errors::FailedToFindMoveTrackSource& e) {
305  sendErrorReply(
307  e.what());
308  } catch(media::TrackList::Errors::FailedToFindMoveTrackDest& e) {
309  sendErrorReply(
311  e.what());
312  }
313 }
314 
315 void TrackListSkeleton::RemoveTrack(const QString &id)
316 {
317  Q_D(TrackListSkeleton);
318  try {
319  d->m_impl->remove_track(id);
320  } catch(media::TrackList::Errors::TrackNotFound& e) {
321  sendErrorReply(
323  e.what());
324  }
325 }
326 
327 void TrackListSkeleton::GoTo(const QString &id)
328 {
329  Q_D(TrackListSkeleton);
330  d->m_impl->go_to(id);
331 }
332 
333 void TrackListSkeleton::Reset()
334 {
335  Q_D(TrackListSkeleton);
336  d->m_impl->reset();
337 }
mpris.h
QObject
lomiri::MediaHubService::TrackListSkeleton::GetTracksMetadata
QMap< QString, QString > GetTracksMetadata(const QString &id)
Definition: track_list_skeleton.cpp:135
lomiri::MediaHubService::TrackListSkeleton::canEditTracks
bool canEditTracks() const
Definition: track_list_skeleton.cpp:129
lomiri::MediaHubService::TrackListImplementation::trackListReplaced
void trackListReplaced(const QVector< Track::Id > &tracks, const Track::Id &currentTrack)
track_list_skeleton.h
lomiri::MediaHubService::Track::Id
QString Id
Definition: track.h:34
lomiri::MediaHubService::TrackListSkeletonPrivate::TrackListSkeletonPrivate
TrackListSkeletonPrivate(const QDBusConnection &bus, const apparmor::lomiri::RequestContextResolver::Ptr &request_context_resolver, const media::apparmor::lomiri::RequestAuthenticator::Ptr &request_authenticator, TrackListImplementation *impl, TrackListSkeleton *q)
Definition: track_list_skeleton.cpp:50
mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name
static constexpr const char * name
Definition: mpris.h:227
lomiri::MediaHubService::TrackListSkeleton::TrackChanged
Q_SCRIPTABLE void TrackChanged(const QString &id)
lomiri::MediaHubService::TrackListSkeleton::TrackListSkeleton
TrackListSkeleton(const QDBusConnection &bus, const lomiri::MediaHubService::apparmor::lomiri::RequestContextResolver::Ptr &request_context_resolver, const lomiri::MediaHubService::apparmor::lomiri::RequestAuthenticator::Ptr &request_authenticator, TrackListImplementation *impl, QObject *parent=nullptr)
Definition: track_list_skeleton.cpp:73
lomiri::MediaHubService::TrackListSkeletonPrivate
Definition: track_list_skeleton.cpp:45
lomiri::MediaHubService::TrackListSkeleton::tracks
QStringList tracks() const
Definition: track_list_skeleton.cpp:118
lomiri::MediaHubService::TrackListSkeleton::AddTrack
void AddTrack(const QString &uri, const QString &after, bool makeCurrent)
Definition: track_list_skeleton.cpp:153
lomiri::MediaHubService::TrackListSkeleton
Definition: track_list_skeleton.h:44
lomiri::MediaHubService::TrackListImplementation::trackRemoved
void trackRemoved(const Track::Id &id)
lomiri::MediaHubService::TrackListSkeleton::TrackListReset
Q_SCRIPTABLE void TrackListReset()
lomiri::MediaHubService::TrackListImplementation::trackChanged
void trackChanged(const Track::Id &id)
MH_TRACE
#define MH_TRACE(...)
Definition: logging.h:37
lomiri::MediaHubService
Definition: context.h:28
lomiri::MediaHubService::TrackListSkeleton::TrackListReplaced
Q_SCRIPTABLE void TrackListReplaced(const QStringList &tracks, const QString &currentTrack)
lomiri::MediaHubService::TrackListSkeleton::TrackMoved
Q_SCRIPTABLE void TrackMoved(const QString &id, const QString &to)
lomiri::MediaHubService::TrackListSkeleton::TrackRemoved
Q_SCRIPTABLE void TrackRemoved(const QString &id)
mpris::TrackList::Error::FailedToFindMoveTrackDest::name
static constexpr const char * name
Definition: mpris.h:251
lomiri::MediaHubService::TrackListSkeleton::TrackAdded
Q_SCRIPTABLE void TrackAdded(const QString &id)
lomiri::MediaHubService::TrackListImplementation::trackAdded
void trackAdded(const Track::Id &id)
lomiri.h
lomiri::MediaHubService::apparmor::lomiri::RequestContextResolver::Ptr
QSharedPointer< RequestContextResolver > Ptr
Definition: lomiri.h:85
mpris::TrackList::Error::FailedToMoveTrack::name
static constexpr const char * name
Definition: mpris.h:235
track_list_implementation.h
mpris::TrackList::Error::FailedToFindMoveTrackSource::name
static constexpr const char * name
Definition: mpris.h:243
lomiri::MediaHubService::TrackListSkeleton::GetTracksUri
QString GetTracksUri(const QString &id)
Definition: track_list_skeleton.cpp:147
lomiri
Definition: dbus_utils.h:24
uri_check.h
lomiri::MediaHubService::TrackListSkeleton::TracksAdded
Q_SCRIPTABLE void TracksAdded(const QStringList &trackURIs)
MH_WARNING
#define MH_WARNING(...)
Definition: logging.h:40
lomiri::MediaHubService::TrackListImplementation::trackMoved
void trackMoved(const Track::Id &id, const Track::Id &to)
logging.h
lomiri::MediaHubService::TrackListImplementation::trackListReset
void trackListReset()
lomiri::MediaHubService::TrackListImplementation
Definition: track_list_implementation.h:74
mpris::TrackList::Error::TrackNotFound::name
static constexpr const char * name
Definition: mpris.h:259
lomiri::MediaHubService::TrackListSkeleton::~TrackListSkeleton
~TrackListSkeleton()
Definition: track_list_skeleton.cpp:114
lomiri::MediaHubService::TrackListImplementation::tracksAdded
void tracksAdded(const QVector< QUrl > &tracks)