IOS/KD: Implement friend registration

This commit is contained in:
Sketch 2023-12-11 17:52:15 -05:00 committed by Admiral H. Curtiss
parent 60f0ad501a
commit 015fbe4b5f
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
10 changed files with 118 additions and 74 deletions

View File

@ -21,20 +21,20 @@ MailParser::MailParser(const std::string& boundary, const u32 num_of_mail,
m_parser.onPartBegin = EmptyCallback; m_parser.onPartBegin = EmptyCallback;
m_parser.onHeaderField = [&](const char* buf, size_t start, size_t end, void* user_data) { m_parser.onHeaderField = [&](const char* buf, size_t start, size_t end, void* user_data) {
const Header header_key = std::make_pair(std::string(buf + start, end - start), std::string()); const Header header_key = std::make_pair(std::string(buf + start, end - start), std::string());
m_headers[current_index].push_back(header_key); m_headers[m_current_index].push_back(header_key);
}; };
m_parser.onHeaderValue = [&](const char* buf, size_t start, size_t end, void* user_data) { m_parser.onHeaderValue = [&](const char* buf, size_t start, size_t end, void* user_data) {
m_headers[current_index][current_header].second = std::string(buf + start, end - start); m_headers[m_current_index][m_current_header].second = std::string(buf + start, end - start);
}; };
m_parser.onHeaderEnd = [&](const char* buf, size_t start, size_t end, void* user_data) { m_parser.onHeaderEnd = [&](const char* buf, size_t start, size_t end, void* user_data) {
current_header++; m_current_header++;
}; };
m_parser.onPartData = [&](const char* buf, size_t start, size_t end, void* user_data) { m_parser.onPartData = [&](const char* buf, size_t start, size_t end, void* user_data) {
m_message_data[current_index].append(std::string(buf + start, end - start)); m_message_data[m_current_index].append(std::string(buf + start, end - start));
}; };
m_parser.onPartEnd = [&](const char* buf, size_t start, size_t end, void* user_data) { m_parser.onPartEnd = [&](const char* buf, size_t start, size_t end, void* user_data) {
current_index++; m_current_index++;
current_header = 0; m_current_header = 0;
}; };
m_parser.onEnd = EmptyCallback; m_parser.onEnd = EmptyCallback;
} }
@ -363,9 +363,8 @@ void MailParser::ParseDate(u32 index, u32 receive_index) const
m_receive_list->SetTime(receive_index, static_cast<u32>(std::floor(seconds_since_1900 / 60))); m_receive_list->SetTime(receive_index, static_cast<u32>(std::floor(seconds_since_1900 / 60)));
} }
ErrorCode MailParser::ParseFrom(u32 index, u32 receive_index, WC24FriendList& friend_list) const ErrorCode MailParser::ParseFrom(u32 index, u32 receive_index, WC24FriendList& friend_list)
{ {
u64 friend_code{};
u64 value{}; u64 value{};
const std::string str_friend = GetHeaderValue(index, "From"); const std::string str_friend = GetHeaderValue(index, "From");
if (str_friend.empty()) if (str_friend.empty())
@ -374,17 +373,17 @@ ErrorCode MailParser::ParseFrom(u32 index, u32 receive_index, WC24FriendList& fr
// Determine if this is a Wii sender or email. // Determine if this is a Wii sender or email.
if (std::regex_search(str_friend, m_wii_number_regex)) if (std::regex_search(str_friend, m_wii_number_regex))
{ {
friend_code = std::stoull(str_friend.substr(1, 16), nullptr, 10); m_sender = std::stoull(str_friend.substr(1, 16), nullptr, 10);
value = friend_code; value = m_sender;
} }
else else
{ {
// For emails, the friend code stored in the nwc24fl.bin differs from the value we need to set. // For emails, the friend code stored in the nwc24fl.bin differs from the value we need to set.
friend_code = WC24FriendList::ConvertEmailToFriendCode(str_friend); m_sender = WC24FriendList::ConvertEmailToFriendCode(str_friend);
value = u64{GetFullMessage(index).find("From") + 2} << 32 | static_cast<u64>(str_friend.size()); value = u64{GetFullMessage(index).find("From") + 2} << 32 | static_cast<u64>(str_friend.size());
} }
if (!friend_list.IsFriend(Common::swap64(friend_code)) && friend_code != NINTENDO_FRIEND_CODE) if (!friend_list.IsFriend(Common::swap64(m_sender)) && m_sender != NINTENDO_FRIEND_CODE)
{ {
WARN_LOG_FMT(IOS_WC24, "Received message from someone who is not a friend, discarding."); WARN_LOG_FMT(IOS_WC24, "Received message from someone who is not a friend, discarding.");
return WC24_ERR_NOT_FOUND; return WC24_ERR_NOT_FOUND;
@ -470,4 +469,9 @@ ErrorCode MailParser::ParseWiiCmd(u32 index, u32 receive_index) const
return WC24_OK; return WC24_OK;
} }
u64 MailParser::GetSender() const
{
return m_sender;
}
} // namespace IOS::HLE::NWC24::Mail } // namespace IOS::HLE::NWC24::Mail

View File

@ -51,7 +51,7 @@ public:
ErrorCode ParseContentType(u32 index, u32 receive_index, ErrorCode ParseContentType(u32 index, u32 receive_index,
IsMultipart is_multipart = IsMultipart{false}); IsMultipart is_multipart = IsMultipart{false});
void ParseDate(u32 index, u32 receive_index) const; void ParseDate(u32 index, u32 receive_index) const;
ErrorCode ParseFrom(u32 index, u32 receive_index, WC24FriendList& friend_list) const; ErrorCode ParseFrom(u32 index, u32 receive_index, WC24FriendList& friend_list);
void ParseSubject(u32 index, u32 receive_index) const; void ParseSubject(u32 index, u32 receive_index) const;
ErrorCode ParseTo(u32 index, u32 receive_index) const; ErrorCode ParseTo(u32 index, u32 receive_index) const;
ErrorCode ParseWiiAppId(u32 index, u32 receive_index) const; ErrorCode ParseWiiAppId(u32 index, u32 receive_index) const;
@ -63,6 +63,7 @@ public:
std::string GetHeaderValue(u32 index, std::string_view key, std::string GetHeaderValue(u32 index, std::string_view key,
IsMultipart is_multipart = IsMultipart{false}) const; IsMultipart is_multipart = IsMultipart{false}) const;
u32 GetHeaderLength(u32 index) const; u32 GetHeaderLength(u32 index) const;
u64 GetSender() const;
private: private:
static void EmptyCallback(const char* buffer, size_t start, size_t end, void* user_data){}; static void EmptyCallback(const char* buffer, size_t start, size_t end, void* user_data){};
@ -93,8 +94,9 @@ private:
std::vector<std::string> m_message_data; std::vector<std::string> m_message_data;
std::vector<Headers> m_headers; std::vector<Headers> m_headers;
std::regex m_wii_number_regex{"w\\d{16}"}; std::regex m_wii_number_regex{"w\\d{16}"};
u32 current_index{}; u32 m_current_index{};
u32 current_header{}; u32 m_current_header{};
u64 m_sender{};
std::string m_charset{}; std::string m_charset{};
std::string m_content_type_str{}; std::string m_content_type_str{};
ContentType m_content_type{}; ContentType m_content_type{};

View File

@ -27,6 +27,15 @@ class WC24FriendList final
{ {
public: public:
explicit WC24FriendList(std::shared_ptr<FS::FileSystem> fs); explicit WC24FriendList(std::shared_ptr<FS::FileSystem> fs);
enum class FriendStatus : u32
{
None,
Unconfirmed,
Confirmed,
Declined
};
static u64 ConvertEmailToFriendCode(std::string_view email); static u64 ConvertEmailToFriendCode(std::string_view email);
void ReadFriendList(); void ReadFriendList();
@ -34,7 +43,9 @@ public:
void WriteFriendList() const; void WriteFriendList() const;
bool IsFriend(u64 friend_id) const; bool IsFriend(u64 friend_id) const;
bool IsFriendEstablished(u64 code) const;
std::vector<u64> GetUnconfirmedFriends() const; std::vector<u64> GetUnconfirmedFriends() const;
void SetFriendStatus(u64 code, FriendStatus status);
private: private:
static constexpr u32 FRIEND_LIST_MAGIC = 0x5763466C; // WcFl static constexpr u32 FRIEND_LIST_MAGIC = 0x5763466C; // WcFl
@ -59,14 +70,6 @@ private:
Email Email
}; };
enum class FriendStatus : u32
{
None,
Unconfirmed,
Confirmed,
Declined
};
struct FriendListEntry final struct FriendListEntry final
{ {
u32 friend_type; u32 friend_type;

View File

@ -145,6 +145,12 @@ u16 WC24ReceiveList::GetAppGroup(u32 index) const
return Common::swap16(m_data.entries[index].app_group); return Common::swap16(m_data.entries[index].app_group);
} }
u32 WC24ReceiveList::GetWiiCmd(u32 index) const
{
ASSERT(!IsDisabled());
return Common::swap32(m_data.entries[index].wii_cmd);
}
void WC24ReceiveList::UpdateFlag(u32 index, u32 value, FlagOP op) void WC24ReceiveList::UpdateFlag(u32 index, u32 value, FlagOP op)
{ {
ASSERT(!IsDisabled()); ASSERT(!IsDisabled());

View File

@ -38,6 +38,7 @@ public:
u32 GetNextEntryIndex() const; u32 GetNextEntryIndex() const;
u32 GetAppID(u32 index) const; u32 GetAppID(u32 index) const;
u16 GetAppGroup(u32 index) const; u16 GetAppGroup(u32 index) const;
u32 GetWiiCmd(u32 index) const;
void FinalizeEntry(u32 index); void FinalizeEntry(u32 index);
void ClearEntry(u32 index); void ClearEntry(u32 index);

View File

@ -206,7 +206,8 @@ std::optional<u32> WC24SendList::GetNextFreeEntryIndex() const
return std::nullopt; return std::nullopt;
} }
ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender) void WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender,
std::string_view email)
{ {
ASSERT(!IsDisabled()); ASSERT(!IsDisabled());
// It is possible that the user composed a message before SendMail was called. // It is possible that the user composed a message before SendMail was called.
@ -222,7 +223,7 @@ ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_lis
const std::time_t t = std::time(nullptr); const std::time_t t = std::time(nullptr);
const std::string formatted_message = const std::string formatted_message =
fmt::format(MAIL_REGISTRATION_STRING, sender, code, fmt::gmtime(t)); fmt::format(MAIL_REGISTRATION_STRING, sender, code, email, fmt::gmtime(t));
const std::span message{reinterpret_cast<const u8*>(formatted_message.data()), const std::span message{reinterpret_cast<const u8*>(formatted_message.data()),
formatted_message.size()}; formatted_message.size()};
const ErrorCode reply = WriteToVFF(SEND_BOX_PATH, GetMailPath(entry_index), m_fs, message); const ErrorCode reply = WriteToVFF(SEND_BOX_PATH, GetMailPath(entry_index), m_fs, message);
@ -230,7 +231,7 @@ ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_lis
if (reply != WC24_OK) if (reply != WC24_OK)
{ {
ERROR_LOG_FMT(IOS_WC24, "Error writing registration message to VFF"); ERROR_LOG_FMT(IOS_WC24, "Error writing registration message to VFF");
return reply; return;
} }
NOTICE_LOG_FMT(IOS_WC24, "Issued registration message for Wii Friend: {}", code); NOTICE_LOG_FMT(IOS_WC24, "Issued registration message for Wii Friend: {}", code);
@ -256,7 +257,6 @@ ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_lis
// Only flush on success. // Only flush on success.
WriteSendList(); WriteSendList();
return WC24_OK;
} }
std::string_view WC24SendList::GetMailFlag() const std::string_view WC24SendList::GetMailFlag() const

View File

@ -50,7 +50,8 @@ public:
u32 GetNextEntryIndex() const; u32 GetNextEntryIndex() const;
std::optional<u32> GetNextFreeEntryIndex() const; std::optional<u32> GetNextFreeEntryIndex() const;
ErrorCode AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender); void AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender,
std::string_view email);
private: private:
static constexpr u32 MAX_ENTRIES = 127; static constexpr u32 MAX_ENTRIES = 127;
@ -62,12 +63,12 @@ private:
// just 128 bytes of base64 encoded 0 bytes. That file is supposed to be friend profile data which // just 128 bytes of base64 encoded 0 bytes. That file is supposed to be friend profile data which
// is written to nwc24fl.bin, although it has been observed to always be 0. // is written to nwc24fl.bin, although it has been observed to always be 0.
static constexpr char MAIL_REGISTRATION_STRING[] = static constexpr char MAIL_REGISTRATION_STRING[] =
"MAIL FROM: {0:016d}@wii.com\r\n" "MAIL FROM: w{0:016d}{2}\r\n"
"RCPT TO: {1:016d}wii.com\r\n" "RCPT TO: w{1:016d}{2}\r\n"
"DATA\r\n" "DATA\r\n"
"Date: {2:%a, %d %b %Y %X} GMT\r\n" "Date: {3:%a, %d %b %Y %X} GMT\r\n"
"From: {0:016d}@wii.com\r\n" "From: w{0:016d}{2}\r\n"
"To: {1:016d}@wii.com\r\n" "To: w{1:016d}{2}\r\n"
"Message-Id: <00002000B0DF6BB47FE0303E0DB0D@wii.com>\r\n" "Message-Id: <00002000B0DF6BB47FE0303E0DB0D@wii.com>\r\n"
"Subject: WC24 Cmd Message\r\n" "Subject: WC24 Cmd Message\r\n"
"X-Wii-AppId: 0-00000001-0001\r\n" "X-Wii-AppId: 0-00000001-0001\r\n"

View File

@ -206,9 +206,10 @@ void NWC24Config::SetId(u64 nwc24_id)
m_data.nwc24_id = Common::swap64(nwc24_id); m_data.nwc24_id = Common::swap64(nwc24_id);
} }
const char* NWC24Config::Email() const std::string_view NWC24Config::GetEmail() const
{ {
return m_data.email; const size_t size = strnlen(m_data.email, MAX_EMAIL_LENGTH);
return {m_data.email, size};
} }
void NWC24Config::SetEmail(const char* email) void NWC24Config::SetEmail(const char* email)

View File

@ -94,7 +94,7 @@ public:
u64 Id() const; u64 Id() const;
void SetId(u64 nwc24_id); void SetId(u64 nwc24_id);
const char* Email() const; std::string_view GetEmail() const;
void SetEmail(const char* email); void SetEmail(const char* email);
std::string GetAccountURL() const; std::string GetAccountURL() const;

View File

@ -275,7 +275,13 @@ void NetKDRequestDevice::SchedulerWorker(const SchedulerEvent event)
u32 mail_flag{}; u32 mail_flag{};
u32 interval{}; u32 interval{};
NWC24::ErrorCode code = KDCheckMail(&mail_flag, &interval); NWC24::ErrorCode code = KDSendMail();
if (code != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, code);
}
code = KDCheckMail(&mail_flag, &interval);
if (code != NWC24::WC24_OK) if (code != NWC24::WC24_OK)
{ {
LogError(ErrorType::CheckMail, code); LogError(ErrorType::CheckMail, code);
@ -290,12 +296,6 @@ void NetKDRequestDevice::SchedulerWorker(const SchedulerEvent event)
if (code != NWC24::WC24_OK) if (code != NWC24::WC24_OK)
LogError(ErrorType::ReceiveMail, code); LogError(ErrorType::ReceiveMail, code);
} }
code = KDSendMail();
if (code != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, code);
}
} }
} }
@ -545,6 +545,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDSendMail()
m_scheduler_buffer[4] = Common::swap32(static_cast<u32>(CurrentFunction::Send)); m_scheduler_buffer[4] = Common::swap32(static_cast<u32>(CurrentFunction::Send));
} }
m_send_list.AddRegistrationMessages(m_friend_list, m_config.Id(), m_config.GetEmail());
m_send_list.ReadSendList(); m_send_list.ReadSendList();
const std::string auth = const std::string auth =
fmt::format("mlid=w{}\r\npasswd={}", m_config.Id(), m_config.GetPassword()); fmt::format("mlid=w{}\r\npasswd={}", m_config.Id(), m_config.GetPassword());
@ -764,6 +765,37 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
m_receive_list.SetHeaderLength(entry_index, header_len); m_receive_list.SetHeaderLength(entry_index, header_len);
m_receive_list.SetMessageOffset(entry_index, header_len); m_receive_list.SetMessageOffset(entry_index, header_len);
reply = parser.ParseWiiAppId(i, entry_index);
if (reply != NWC24::WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Failed to parse Wii App ID.");
continue;
}
if (m_receive_list.GetAppID(entry_index) == 0 || m_receive_list.GetAppGroup(entry_index) == 0)
{
if (m_receive_list.GetAppID(entry_index) == 0)
{
m_receive_list.SetWiiCmd(entry_index, 0x44001);
}
else
{
m_receive_list.SetWiiCmd(entry_index, 0x80000);
}
m_receive_list.UpdateFlag(entry_index, 2, NWC24::Mail::FlagOP::Or);
}
else
{
m_receive_list.UpdateFlag(entry_index, 1, NWC24::Mail::FlagOP::Or);
}
reply = parser.ParseWiiCmd(i, entry_index);
if (reply != NWC24::WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Failed to parse command.");
continue;
}
reply = parser.ParseFrom(i, entry_index, m_friend_list); reply = parser.ParseFrom(i, entry_index, m_friend_list);
if (reply != NWC24::WC24_OK) if (reply != NWC24::WC24_OK)
{ {
@ -771,6 +803,30 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
continue; continue;
} }
// Handle registration is needed.
if (!m_friend_list.IsFriendEstablished(Common::swap64(parser.GetSender())))
{
// We use the parsed Wii Command to determine if this is a registration message.
const u32 wii_cmd = m_receive_list.GetWiiCmd(entry_index);
if (wii_cmd == 0x80010001 || wii_cmd == 0x80010002)
{
// Set the Wii as a registered friend.
m_friend_list.SetFriendStatus(parser.GetSender(),
NWC24::Mail::WC24FriendList::FriendStatus::Confirmed);
NOTICE_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Registered friend {}",
parser.GetSender());
}
else if (wii_cmd == 0x80010003)
{
// Wii declined the friend request
m_friend_list.SetFriendStatus(parser.GetSender(),
NWC24::Mail::WC24FriendList::FriendStatus::Declined);
WARN_LOG_FMT(IOS_WC24,
"NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Wii declined a friend request.");
}
continue;
}
reply = parser.ParseContentTransferEncoding(i, entry_index); reply = parser.ParseContentTransferEncoding(i, entry_index);
if (reply != NWC24::WC24_OK && reply != NWC24::WC24_ERR_NOT_FOUND) if (reply != NWC24::WC24_OK && reply != NWC24::WC24_ERR_NOT_FOUND)
{ {
@ -800,37 +856,6 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
continue; continue;
} }
reply = parser.ParseWiiAppId(i, entry_index);
if (reply != NWC24::WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Failed to parse Wii App ID.");
continue;
}
if (m_receive_list.GetAppID(entry_index) == 0 || m_receive_list.GetAppGroup(entry_index) == 0)
{
if (m_receive_list.GetAppID(entry_index) == 0)
{
m_receive_list.SetWiiCmd(entry_index, 0x44001);
}
else
{
m_receive_list.SetWiiCmd(entry_index, 0x80000);
}
m_receive_list.UpdateFlag(entry_index, 2, NWC24::Mail::FlagOP::Or);
}
else
{
m_receive_list.UpdateFlag(entry_index, 1, NWC24::Mail::FlagOP::Or);
}
reply = parser.ParseWiiCmd(i, entry_index);
if (reply != NWC24::WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SAVE_MAIL_NOW: Failed to parse command.");
continue;
}
reply = reply =
NWC24::WriteToVFF(NWC24::Mail::RECEIVE_BOX_PATH, NWC24::WriteToVFF(NWC24::Mail::RECEIVE_BOX_PATH,
NWC24::Mail::WC24ReceiveList::GetMailPath(msg_id), m_ios.GetFS(), data); NWC24::Mail::WC24ReceiveList::GetMailPath(msg_id), m_ios.GetFS(), data);
@ -846,6 +871,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
} }
m_receive_list.WriteReceiveList(); m_receive_list.WriteReceiveList();
m_friend_list.WriteFriendList();
m_scheduler_buffer[13] = Common::swap32(Common::swap32(m_scheduler_buffer[13]) + 1); m_scheduler_buffer[13] = Common::swap32(Common::swap32(m_scheduler_buffer[13]) + 1);
m_scheduler_buffer[4] = 0; m_scheduler_buffer[4] = 0;
return NWC24::WC24_OK; return NWC24::WC24_OK;