Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# changelog

## unreleased
- `setClientName()`: the name the receiver shows (Apple TV pairing dialog + the
AP2 source) is no longer hardcoded to "FXChainPlayer", defaults to
"airplay2-sender-cpp".
- provenance made precise: the crypto/wire-format core is clean-room; the
RAOP/AP2 transport in `raop_sender.cpp` is credited as a C++ port of pyatv
(MIT). pyatv's MIT notice now ships in `licenses/THIRD-PARTY-NOTICES.txt`.
Expand Down
13 changes: 9 additions & 4 deletions src/raop_sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,11 @@ void RaopSender::setNowPlaying(const QString& title, const QString& artist,
if (state_ == State::Streaming && changed) sendMetadata_();
}

void RaopSender::setClientName(const QString& name) {
clientName_ = name.isEmpty() ? QByteArrayLiteral("airplay2-sender-cpp")
: name.toUtf8();
}

namespace {
// DMAP tag: 4-char ASCII code + 4-byte big-endian length + payload.
QByteArray dmapTag(const char code[5], const QByteArray& payload) {
Expand Down Expand Up @@ -548,7 +553,7 @@ void RaopSender::sendRequest_(const QByteArray& method, const QByteArray& uri,
req += "DACP-ID: " + dacpId_ + "\r\n";
req += "Active-Remote: " + QByteArray::number(activeRemote_) + "\r\n";
req += "Client-Instance: " + dacpId_ + "\r\n";
req += "X-Apple-Client-Name: FXChainPlayer\r\n"; // AirPlay-bug fix, owntone parity
req += "X-Apple-Client-Name: " + clientName_ + "\r\n"; // AirPlay-bug fix, owntone parity
// v0.66.x, RTSP digest auth for pw=true receivers: once a 401 has
// told us realm+nonce, every subsequent request carries Authorization.
if (!digestNonce_.isEmpty() && !digestPassword_.isEmpty()) {
Expand Down Expand Up @@ -1041,7 +1046,7 @@ void RaopSender::httpPost_(const QByteArray& uri, const QByteArray& contentType,
// absence is a documented 403 cause. Mirror owntone (Client-Instance ==
// DACP-ID value).
req += "Client-Instance: " + dacpId_ + "\r\n";
req += "X-Apple-Client-Name: FXChainPlayer\r\n";
req += "X-Apple-Client-Name: " + clientName_ + "\r\n";
if (!contentType.isEmpty())
req += "Content-Type: " + contentType + "\r\n";
req += "Content-Length: " + QByteArray::number(body.size()) + "\r\n";
Expand Down Expand Up @@ -1602,7 +1607,7 @@ void RaopSender::sendAp2Rtsp_(const QByteArray& method, const QByteArray& uri,
req += "DACP-ID: " + dacpId_ + "\r\n";
req += "Active-Remote: " + QByteArray::number(activeRemote_) + "\r\n";
req += "Client-Instance: " + dacpId_ + "\r\n";
req += "X-Apple-Client-Name: FXChainPlayer\r\n";
req += "X-Apple-Client-Name: " + clientName_ + "\r\n";
if (method == "SETUP")
req += "X-Apple-StreamID: 1\r\n"; // owntone/pyatv parity
if (!contentType.isEmpty())
Expand Down Expand Up @@ -1635,7 +1640,7 @@ void RaopSender::sendAp2SetupSession_() {
d.emplace_back("groupContainsGroupLeader", Value::boolean(false));
d.emplace_back("macAddress", Value::str("AA:BB:CC:DD:EE:FF"));
d.emplace_back("model", Value::str("iPhone14,3"));
d.emplace_back("name", Value::str("FXChainPlayer"));
d.emplace_back("name", Value::str(clientName_.toStdString()));
d.emplace_back("osBuildVersion", Value::str("20F66"));
d.emplace_back("osName", Value::str("iPhone OS"));
d.emplace_back("osVersion", Value::str("16.5"));
Expand Down
4 changes: 4 additions & 0 deletions src/raop_sender.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class RaopSender : public QObject {
const QByteArray& cover = {},
const QByteArray& coverMime = {});

// Sender name the receiver shows (PIN dialog + AirPlay source); empty = default.
void setClientName(const QString& name);

signals:
void launched(bool ok, const QString& error); // RECORD accepted / failed
void closed(); // session ended
Expand Down Expand Up @@ -286,6 +289,7 @@ class RaopSender : public QObject {

QString npTitle_, npArtist_, npAlbum_; // now-playing metadata
QByteArray npCover_, npCoverMime_; // cover art bytes + MIME
QByteArray clientName_ = QByteArrayLiteral("airplay2-sender-cpp"); // X-Apple-Client-Name + AP2 name

// ── v0.66.x Phase 2/3, auth + AP2 state ─────────────────────────
// The pairing sub-state machine: which reply the next HTTP POST's
Expand Down