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.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());
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_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) {
current_header++;
m_current_header++;
};
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) {
current_index++;
current_header = 0;
m_current_index++;
m_current_header = 0;
};
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)));
}
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{};
const std::string str_friend = GetHeaderValue(index, "From");
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.
if (std::regex_search(str_friend, m_wii_number_regex))
{
friend_code = std::stoull(str_friend.substr(1, 16), nullptr, 10);
value = friend_code;
m_sender = std::stoull(str_friend.substr(1, 16), nullptr, 10);
value = m_sender;
}
else
{
// 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());
}
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.");
return WC24_ERR_NOT_FOUND;
@ -470,4 +469,9 @@ ErrorCode MailParser::ParseWiiCmd(u32 index, u32 receive_index) const
return WC24_OK;
}
u64 MailParser::GetSender() const
{
return m_sender;
}
} // namespace IOS::HLE::NWC24::Mail

View File

@ -51,7 +51,7 @@ public:
ErrorCode ParseContentType(u32 index, u32 receive_index,
IsMultipart is_multipart = IsMultipart{false});
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;
ErrorCode ParseTo(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,
IsMultipart is_multipart = IsMultipart{false}) const;
u32 GetHeaderLength(u32 index) const;
u64 GetSender() const;
private:
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<Headers> m_headers;
std::regex m_wii_number_regex{"w\\d{16}"};
u32 current_index{};
u32 current_header{};
u32 m_current_index{};
u32 m_current_header{};
u64 m_sender{};
std::string m_charset{};
std::string m_content_type_str{};
ContentType m_content_type{};

View File

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

View File

@ -145,6 +145,12 @@ u16 WC24ReceiveList::GetAppGroup(u32 index) const
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)
{
ASSERT(!IsDisabled());

View File

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

View File

@ -206,7 +206,8 @@ std::optional<u32> WC24SendList::GetNextFreeEntryIndex() const
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());
// 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::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()),
formatted_message.size()};
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)
{
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);
@ -256,7 +257,6 @@ ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_lis
// Only flush on success.
WriteSendList();
return WC24_OK;
}
std::string_view WC24SendList::GetMailFlag() const

View File

@ -50,7 +50,8 @@ public:
u32 GetNextEntryIndex() 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:
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
// is written to nwc24fl.bin, although it has been observed to always be 0.
static constexpr char MAIL_REGISTRATION_STRING[] =
"MAIL FROM: {0:016d}@wii.com\r\n"
"RCPT TO: {1:016d}wii.com\r\n"
"MAIL FROM: w{0:016d}{2}\r\n"
"RCPT TO: w{1:016d}{2}\r\n"
"DATA\r\n"
"Date: {2:%a, %d %b %Y %X} GMT\r\n"
"From: {0:016d}@wii.com\r\n"
"To: {1:016d}@wii.com\r\n"
"Date: {3:%a, %d %b %Y %X} GMT\r\n"
"From: w{0:016d}{2}\r\n"
"To: w{1:016d}{2}\r\n"
"Message-Id: <00002000B0DF6BB47FE0303E0DB0D@wii.com>\r\n"
"Subject: WC24 Cmd Message\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);
}
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)

View File

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

View File

@ -275,7 +275,13 @@ void NetKDRequestDevice::SchedulerWorker(const SchedulerEvent event)
u32 mail_flag{};
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)
{
LogError(ErrorType::CheckMail, code);
@ -290,12 +296,6 @@ void NetKDRequestDevice::SchedulerWorker(const SchedulerEvent event)
if (code != NWC24::WC24_OK)
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_send_list.AddRegistrationMessages(m_friend_list, m_config.Id(), m_config.GetEmail());
m_send_list.ReadSendList();
const std::string auth =
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.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);
if (reply != NWC24::WC24_OK)
{
@ -771,6 +803,30 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
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);
if (reply != NWC24::WC24_OK && reply != NWC24::WC24_ERR_NOT_FOUND)
{
@ -800,37 +856,6 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
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 =
NWC24::WriteToVFF(NWC24::Mail::RECEIVE_BOX_PATH,
NWC24::Mail::WC24ReceiveList::GetMailPath(msg_id), m_ios.GetFS(), data);
@ -846,6 +871,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDSaveMail()
}
m_receive_list.WriteReceiveList();
m_friend_list.WriteFriendList();
m_scheduler_buffer[13] = Common::swap32(Common::swap32(m_scheduler_buffer[13]) + 1);
m_scheduler_buffer[4] = 0;
return NWC24::WC24_OK;