diff --git a/Source/Core/Core/IOS/Network/KD/Mail/MailParser.cpp b/Source/Core/Core/IOS/Network/KD/Mail/MailParser.cpp index 3a1bc09a8c..15c99c5fcf 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/MailParser.cpp +++ b/Source/Core/Core/IOS/Network/KD/Mail/MailParser.cpp @@ -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(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(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 diff --git a/Source/Core/Core/IOS/Network/KD/Mail/MailParser.h b/Source/Core/Core/IOS/Network/KD/Mail/MailParser.h index b0f7c9f649..971d1256fb 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/MailParser.h +++ b/Source/Core/Core/IOS/Network/KD/Mail/MailParser.h @@ -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 m_message_data; std::vector 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{}; diff --git a/Source/Core/Core/IOS/Network/KD/Mail/WC24FriendList.h b/Source/Core/Core/IOS/Network/KD/Mail/WC24FriendList.h index d533040bc9..eb2eba6c13 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/WC24FriendList.h +++ b/Source/Core/Core/IOS/Network/KD/Mail/WC24FriendList.h @@ -27,6 +27,15 @@ class WC24FriendList final { public: explicit WC24FriendList(std::shared_ptr 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 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; diff --git a/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.cpp b/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.cpp index d015aaae57..2f4431f145 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.cpp +++ b/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.cpp @@ -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()); diff --git a/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.h b/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.h index 756b3bcb20..79983419ae 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.h +++ b/Source/Core/Core/IOS/Network/KD/Mail/WC24Receive.h @@ -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); diff --git a/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.cpp b/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.cpp index 5cd8d7adba..6af2fe757b 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.cpp +++ b/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.cpp @@ -206,7 +206,8 @@ std::optional 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(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 diff --git a/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.h b/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.h index d839b2b102..56740b68a3 100644 --- a/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.h +++ b/Source/Core/Core/IOS/Network/KD/Mail/WC24Send.h @@ -50,7 +50,8 @@ public: u32 GetNextEntryIndex() const; std::optional 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" diff --git a/Source/Core/Core/IOS/Network/KD/NWC24Config.cpp b/Source/Core/Core/IOS/Network/KD/NWC24Config.cpp index 16ddb537cc..6faf1e9267 100644 --- a/Source/Core/Core/IOS/Network/KD/NWC24Config.cpp +++ b/Source/Core/Core/IOS/Network/KD/NWC24Config.cpp @@ -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) diff --git a/Source/Core/Core/IOS/Network/KD/NWC24Config.h b/Source/Core/Core/IOS/Network/KD/NWC24Config.h index cea17f2c66..535780b082 100644 --- a/Source/Core/Core/IOS/Network/KD/NWC24Config.h +++ b/Source/Core/Core/IOS/Network/KD/NWC24Config.h @@ -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; diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp index b8da501e14..406ece409c 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp @@ -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(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;