IOS/KD: Implement Send Mail

This commit is contained in:
Sketch 2023-09-18 13:48:25 -04:00 committed by Admiral H. Curtiss
parent 2c3d05423d
commit b46fcf9032
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
11 changed files with 591 additions and 2 deletions

View File

@ -383,6 +383,8 @@ add_library(core
IOS/Network/KD/VFF/VFFUtil.h
IOS/Network/KD/WC24File.h
IOS/Network/KD/Mail/MailCommon.h
IOS/Network/KD/Mail/WC24FriendList.cpp
IOS/Network/KD/Mail/WC24FriendList.h
IOS/Network/KD/Mail/WC24Send.cpp
IOS/Network/KD/Mail/WC24Send.h
IOS/Network/MACUtils.cpp

View File

@ -11,6 +11,11 @@ namespace IOS::HLE::NWC24::Mail
{
constexpr u32 MAIL_LIST_MAGIC = 0x57635466; // WcTf
inline u32 CalculateFileOffset(u32 index)
{
return Common::swap32(128 + (index * 128));
}
#pragma pack(push, 1)
struct MailListHeader final
{

View File

@ -0,0 +1,93 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/IOS/Network/KD/Mail/WC24FriendList.h"
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/Uids.h"
namespace IOS::HLE::NWC24::Mail
{
WC24FriendList::WC24FriendList(std::shared_ptr<FS::FileSystem> fs) : m_fs{std::move(fs)}
{
ReadFriendList();
}
void WC24FriendList::ReadFriendList()
{
const auto file = m_fs->OpenFile(PID_KD, PID_KD, FRIEND_LIST_PATH, FS::Mode::Read);
if (!file || !file->Read(&m_data, 1))
return;
const bool success = CheckFriendList();
if (!success)
ERROR_LOG_FMT(IOS_WC24, "There is an error in the Receive List for WC24 mail");
}
void WC24FriendList::WriteFriendList() const
{
constexpr FS::Modes public_modes{FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite};
m_fs->CreateFullPath(PID_KD, PID_KD, FRIEND_LIST_PATH, 0, public_modes);
const auto file = m_fs->CreateAndOpenFile(PID_KD, PID_KD, FRIEND_LIST_PATH, public_modes);
if (!file || !file->Write(&m_data, 1))
ERROR_LOG_FMT(IOS_WC24, "Failed to open or write WC24 Receive list file");
}
bool WC24FriendList::CheckFriendList() const
{
// 'WcFl' magic
if (Common::swap32(m_data.header.magic) != FRIEND_LIST_MAGIC)
{
ERROR_LOG_FMT(IOS_WC24, "Receive List magic mismatch ({} != {})",
Common::swap32(m_data.header.magic), FRIEND_LIST_MAGIC);
return false;
}
return true;
}
bool WC24FriendList::DoesFriendExist(u64 friend_id) const
{
return std::any_of(m_data.friend_codes.cbegin(), m_data.friend_codes.cend(),
[&friend_id](const u64 v) { return v == friend_id; });
}
std::vector<u64> WC24FriendList::GetUnconfirmedFriends() const
{
std::vector<u64> friends{};
for (u32 i = 0; i < MAX_ENTRIES; i++)
{
if (static_cast<FriendStatus>(Common::swap32(m_data.entries[i].status)) ==
FriendStatus::Unconfirmed &&
static_cast<FriendType>(Common::swap32(m_data.entries[i].friend_type)) == FriendType::Wii)
{
friends.push_back(Common::swap64(m_data.friend_codes.at(i)));
}
}
return friends;
}
u64 WC24FriendList::ConvertEmailToFriendCode(std::string_view email)
{
u32 upper = 0x80;
u32 lower{};
u32 idx{};
for (char chr : email)
{
if (idx == 7)
{
upper = upper | (email.size() & 0x1f);
break;
}
lower = (upper | chr) >> 0x18 | (lower | lower >> 0x1f) << 8;
upper = (upper | chr) * 0x100;
idx++;
}
return u64{lower} << 32 | upper;
}
} // namespace IOS::HLE::NWC24::Mail

View File

@ -0,0 +1,98 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <string>
#include <vector>
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/Swap.h"
#include "Core/IOS/Network/KD/NWC24Config.h"
namespace IOS::HLE
{
namespace FS
{
class FileSystem;
}
namespace NWC24::Mail
{
constexpr const char FRIEND_LIST_PATH[] = "/" WII_WC24CONF_DIR "/nwc24fl.bin";
class WC24FriendList final
{
public:
explicit WC24FriendList(std::shared_ptr<FS::FileSystem> fs);
static u64 ConvertEmailToFriendCode(std::string_view email);
void ReadFriendList();
bool CheckFriendList() const;
void WriteFriendList() const;
bool DoesFriendExist(u64 friend_id) const;
std::vector<u64> GetUnconfirmedFriends() const;
private:
static constexpr u32 FRIEND_LIST_MAGIC = 0x5763466C; // WcFl
static constexpr u32 MAX_ENTRIES = 100;
#pragma pack(push, 1)
struct FriendListHeader final
{
u32 magic; // 'WcFl' 0x5763466C
u32 version;
u32 max_friend_entries;
u32 number_of_friends;
char padding[48];
};
static_assert(sizeof(FriendListHeader) == 64);
static_assert(std::is_trivially_copyable_v<FriendListHeader>);
enum class FriendType : u32
{
None,
Wii,
Email
};
enum class FriendStatus : u32
{
None,
Unconfirmed,
Confirmed,
Declined
};
struct FriendListEntry final
{
u32 friend_type;
u32 status;
char nickname[24];
u32 mii_id;
u32 system_id;
char reserved[24];
char email_or_code[96];
char padding[160];
};
static_assert(sizeof(FriendListEntry) == 320);
static_assert(std::is_trivially_copyable_v<FriendListEntry>);
struct FriendList final
{
FriendListHeader header;
std::array<u64, MAX_ENTRIES> friend_codes;
std::array<FriendListEntry, MAX_ENTRIES> entries;
};
static_assert(sizeof(FriendList) == 32864);
static_assert(std::is_trivially_copyable_v<FriendList>);
#pragma pack(pop)
FriendList m_data;
std::shared_ptr<FS::FileSystem> m_fs;
};
} // namespace NWC24::Mail
} // namespace IOS::HLE

View File

@ -3,8 +3,10 @@
#include "Core/IOS/Network/KD/Mail/WC24Send.h"
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/Network/KD/VFF/VFFUtil.h"
#include "Core/IOS/Uids.h"
#include <fmt/chrono.h>
#include "Common/Assert.h"
namespace IOS::HLE::NWC24::Mail
@ -44,7 +46,28 @@ bool WC24SendList::ReadSendList()
return false;
}
return CheckSendList();
// Make sure that next_entry_offset is not out of bounds.
if (m_data.header.next_entry_offset % 128 != 0 ||
m_data.header.next_entry_offset >
sizeof(MailListEntry) * (MAX_ENTRIES - 1) + sizeof(MailListEntry))
{
const std::optional<u32> next_entry_index = GetNextFreeEntryIndex();
if (!next_entry_index)
{
// If there are no free entries, we will have to overwrite an entry.
m_data.header.next_entry_offset = Common::swap32(128);
}
else
{
m_data.header.next_entry_offset = CalculateFileOffset(next_entry_index.value());
}
}
const s32 file_error = CheckSendList();
if (!file_error)
ERROR_LOG_FMT(IOS_WC24, "There is an error in the Send List for WC24 mail");
return true;
}
bool WC24SendList::IsDisabled() const
@ -82,6 +105,147 @@ bool WC24SendList::CheckSendList() const
return true;
}
u32 WC24SendList::GetNumberOfMail() const
{
ASSERT(!IsDisabled());
return Common::swap32(m_data.header.number_of_mail);
}
u32 WC24SendList::GetEntryId(u32 entry_index) const
{
ASSERT(!IsDisabled());
return Common::swap32(m_data.entries[entry_index].id);
}
u32 WC24SendList::GetMailSize(u32 index) const
{
ASSERT(!IsDisabled());
return Common::swap32(m_data.entries[index].msg_size);
}
ErrorCode WC24SendList::DeleteMessage(u32 index)
{
ASSERT(!IsDisabled());
ErrorCode error = NWC24::DeleteFileFromVFF(NWC24::Mail::SEND_BOX_PATH, GetMailPath(index), m_fs);
if (error != WC24_OK)
return error;
// Fix up the header then clear the entry.
m_data.header.number_of_mail = Common::swap32(Common::swap32(m_data.header.number_of_mail) - 1);
m_data.header.next_entry_id = Common::swap32(GetEntryId(index));
m_data.header.next_entry_offset = CalculateFileOffset(index);
m_data.header.total_size_of_messages =
Common::swap32(m_data.header.total_size_of_messages) - GetMailSize(index);
std::memset(&m_data.entries[index], 0, sizeof(MailListEntry));
return WC24_OK;
}
std::string WC24SendList::GetMailPath(u32 index) const
{
return fmt::format("mb/s{:07d}.msg", GetEntryId(index));
}
u32 WC24SendList::GetNextEntryId() const
{
ASSERT(!IsDisabled());
return Common::swap32(m_data.header.next_entry_id);
}
u32 WC24SendList::GetNextEntryIndex() const
{
ASSERT(!IsDisabled());
return (Common::swap32(m_data.header.next_entry_offset) - 128) / 128;
}
std::vector<u32> WC24SendList::GetMailToSend() const
{
ASSERT(!IsDisabled());
// The list is not guaranteed to have all entries consecutively.
// As such we must find the populated entries for the specified number of mails.
const u32 mail_count = std::min(GetNumberOfMail(), 16U);
u32 found{};
std::vector<u32> mails{};
for (u32 index = 0; index < MAX_ENTRIES; index++)
{
if (found == mail_count)
break;
if (GetEntryId(index) != 0)
{
mails.emplace_back(index);
found++;
}
}
return mails;
}
std::optional<u32> WC24SendList::GetNextFreeEntryIndex() const
{
for (u32 index = 0; index < MAX_ENTRIES; index++)
{
if (GetEntryId(index) == 0)
return index;
}
return std::nullopt;
}
ErrorCode WC24SendList::AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender)
{
ASSERT(!IsDisabled());
// It is possible that the user composed a message before SendMail was called.
ReadSendList();
const std::vector<u64> unconfirmed_friends = friend_list.GetUnconfirmedFriends();
for (const u64 code : unconfirmed_friends)
{
const u32 entry_index = GetNextEntryIndex();
const u32 msg_id = GetNextEntryId();
m_data.entries[entry_index].id = Common::swap32(msg_id);
std::time_t t = std::time(nullptr);
const std::string formatted_message =
fmt::format(MAIL_REGISTRATION_STRING, sender, code, fmt::gmtime(t));
std::vector<u8> message{formatted_message.begin(), formatted_message.end()};
NWC24::ErrorCode reply =
NWC24::WriteToVFF(NWC24::Mail::SEND_BOX_PATH, GetMailPath(entry_index), m_fs, message);
if (reply != WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "Error writing registration message to VFF");
return reply;
}
NOTICE_LOG_FMT(IOS_WC24, "Issued registration message for Wii Friend: {}", code);
// Update the header and some fields in the body
m_data.entries[entry_index].msg_size = Common::swap32(static_cast<u32>(message.size()));
m_data.header.number_of_mail = Common::swap32(GetNumberOfMail() + 1);
m_data.header.next_entry_id = Common::swap32(msg_id + 1);
m_data.header.total_size_of_messages =
Common::swap32(m_data.header.total_size_of_messages) + static_cast<u32>(message.size());
const std::optional<u32> next_entry_index = GetNextFreeEntryIndex();
if (!next_entry_index)
{
// If there are no free entries, we overwrite the first entry.
m_data.header.next_entry_offset = Common::swap32(128);
}
else
{
m_data.header.next_entry_offset = CalculateFileOffset(next_entry_index.value());
}
}
// Only flush on success.
WriteSendList();
return WC24_OK;
}
std::string_view WC24SendList::GetMailFlag() const
{
ASSERT(!IsDisabled());

View File

@ -3,13 +3,16 @@
#pragma once
#include <optional>
#include <string_view>
#include <vector>
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/Swap.h"
#include "Core/IOS/Network/KD/Mail/MailCommon.h"
#include "Core/IOS/Network/KD/Mail/WC24FriendList.h"
#include "Core/IOS/Network/KD/NWC24Config.h"
namespace IOS::HLE
@ -34,11 +37,56 @@ public:
bool IsDisabled() const;
std::string_view GetMailFlag() const;
u32 GetNumberOfMail() const;
std::vector<u32> GetMailToSend() const;
u32 GetEntryId(u32 entry_index) const;
u32 GetMailSize(u32 index) const;
ErrorCode DeleteMessage(u32 index);
std::string GetMailPath(u32 index) const;
u32 GetNextEntryId() const;
u32 GetNextEntryIndex() const;
std::optional<u32> GetNextFreeEntryIndex() const;
ErrorCode AddRegistrationMessages(const WC24FriendList& friend_list, u64 sender);
private:
static constexpr u32 MAX_ENTRIES = 127;
static constexpr u32 SEND_LIST_SIZE = 16384;
// Format for the message Wii Mail sends when trying to register a Wii Friend.
// Most fields can be static such as the X-Wii-AppId which is the Wii Menu,
// X-Wii-Cmd which is the registration command, and the attached file which is
// 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"
"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"
"Message-Id: <00002000B0DF6BB47FE0303E0DB0D@wii.com>\r\n"
"Subject: WC24 Cmd Message\r\n"
"X-Wii-AppId: 0-00000001-0001\r\n"
"X-Wii-Cmd: 80010001\r\n"
"MIME-Version: 1.0\r\n"
"Content-Type: multipart/mixed;\r\n "
"boundary=\"Boundary-NWC24-041B6CE500012\"\r\n"
"--Boundary-NWC24-041B6CE500012\r\n"
"Content-Type: text/plain; charset=us-ascii\r\n"
"Content-Transfer-Encoding: 7bit\r\n"
"WC24 Cmd Message\r\n"
"--Boundary-NWC24-041B6CE500012\r\n"
"Content-Type: application/octet-stream;\r\n "
"name=a0000018.dat\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-Disposition: attachment;\r\n "
"filename=a0000018.dat\r\n\r\n "
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
"\r\n\r\n"
"--Boundary-NWC24-041B6CE500012--";
#pragma pack(push, 1)
struct SendList final
{

View File

@ -246,4 +246,16 @@ void NWC24Config::SetPassword(std::string_view password)
std::strncpy(m_data.paswd, password.data(), std::size(m_data.paswd));
m_data.paswd[MAX_PASSWORD_LENGTH - 1] = '\0';
}
std::string NWC24Config::GetSendURL() const
{
const size_t size = strnlen(m_data.http_urls[4], MAX_URL_LENGTH);
return {m_data.http_urls[4], size};
}
std::string_view NWC24Config::GetPassword() const
{
const size_t size = strnlen(m_data.paswd, MAX_PASSWORD_LENGTH);
return {m_data.paswd, size};
}
} // namespace IOS::HLE::NWC24

View File

@ -34,6 +34,8 @@ enum ErrorCode : s32
WC24_ERR_ID_REGISTERED = -36,
WC24_ERR_DISABLED = -39,
WC24_ERR_ID_NOT_REGISTERED = -44,
WC24_MSG_DAMAGED = -71,
WC24_MSG_TOO_BIG = -72
};
enum class NWC24CreationStage : u32
@ -72,6 +74,8 @@ public:
std::string_view GetMlchkid() const;
std::string GetCheckURL() const;
std::string GetSendURL() const;
std::string_view GetPassword() const;
NWC24CreationStage CreationStage() const;
void SetCreationStage(NWC24CreationStage creation_stage);

View File

@ -155,7 +155,7 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h
NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name)
: EmulationDevice(ios, device_name), m_config{ios.GetFS()}, m_dl_list{ios.GetFS()},
m_send_list{ios.GetFS()}
m_send_list{ios.GetFS()}, m_friend_list{ios.GetFS()}
{
// Enable all NWC24 permissions
m_scheduler_buffer[1] = Common::swap32(-1);
@ -269,6 +269,12 @@ void NetKDRequestDevice::SchedulerWorker(const SchedulerEvent event)
{
LogError(ErrorType::CheckMail, code);
}
code = KDSendMail();
if (code != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, code);
}
}
}
@ -309,6 +315,15 @@ void NetKDRequestDevice::LogError(ErrorType error_type, s32 error_code)
case ErrorType::CheckMail:
new_code = -(102200 - error_code);
break;
case ErrorType::SendMail:
new_code = -(105000 - error_code);
break;
case ErrorType::ReceiveMail:
new_code = -(100300 - error_code);
break;
case ErrorType::CGI:
new_code = -(error_code + 110000);
break;
}
std::lock_guard lg(m_scheduler_buffer_lock);
@ -492,6 +507,133 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineSubtask(u16 entry_index,
return NWC24::WC24_ERR_INVALID_VALUE;
}
NWC24::ErrorCode NetKDRequestDevice::KDSendMail()
{
bool success = false;
Common::ScopeGuard exit_guard([&] {
std::lock_guard lg(m_scheduler_buffer_lock);
if (success)
{
// m_scheduler_buffer[11] contains the amount of times we have sent for mail this IOS
// session.
m_scheduler_buffer[14] = Common::swap32(Common::swap32(m_scheduler_buffer[14]) + 1);
}
m_scheduler_buffer[4] = static_cast<u32>(CurrentFunction::None);
m_send_list.WriteSendList();
});
{
std::lock_guard lg(m_scheduler_buffer_lock);
m_scheduler_buffer[4] = Common::swap32(static_cast<u32>(CurrentFunction::Send));
}
m_send_list.ReadSendList();
const std::string auth =
fmt::format("mlid=w{}\r\npasswd={}", m_config.Id(), m_config.GetPassword());
std::vector<Common::HttpRequest::Multiform> multiform = {{"mlid", auth}};
std::vector<u32> mails = m_send_list.GetMailToSend();
for (const u32 file_index : mails)
{
const u32 entry_id = m_send_list.GetEntryId(file_index);
const u32 mail_size = m_send_list.GetMailSize(file_index);
if (mail_size > MAX_MAIL_SIZE)
{
WARN_LOG_FMT(IOS_WC24,
"NET_KD_REQ: IOCTL_NWC24_SEND_MAIL_NOW: Mail at index {} was too large to send.",
entry_id);
LogError(ErrorType::SendMail, NWC24::WC24_MSG_TOO_BIG);
NWC24::ErrorCode res = m_send_list.DeleteMessage(file_index);
if (res != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, res);
}
mails.erase(std::remove(mails.begin(), mails.end(), file_index), mails.end());
continue;
}
std::vector<u8> mail_data(mail_size);
NWC24::ErrorCode res = NWC24::ReadFromVFF(
NWC24::Mail::SEND_BOX_PATH, m_send_list.GetMailPath(file_index), m_ios.GetFS(), mail_data);
if (res != NWC24::WC24_OK)
{
ERROR_LOG_FMT(IOS_WC24, "Reading mail at index {} failed with error code {}.", entry_id,
static_cast<s32>(res));
LogError(ErrorType::SendMail, NWC24::WC24_MSG_DAMAGED);
res = m_send_list.DeleteMessage(file_index);
if (res != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, res);
}
mails.erase(std::remove(mails.begin(), mails.end(), file_index), mails.end());
continue;
}
const std::string mail_str = {mail_data.begin(), mail_data.end()};
multiform.push_back({fmt::format("m{}", entry_id), mail_str});
}
const Common::HttpRequest::Response response =
m_http.PostMultiform(m_config.GetSendURL(), multiform);
if (!response)
{
ERROR_LOG_FMT(IOS_WC24, "NET_KD_REQ: IOCTL_NWC24_SEND_MAIL_NOW: Failed to request data at {}.",
m_config.GetSendURL());
return NWC24::WC24_ERR_SERVER;
}
// Now check if any mail failed to save to the server.
const std::string response_str = {response->begin(), response->end()};
const std::string code = GetValueFromCGIResponse(response_str, "cd");
if (code != "100")
{
ERROR_LOG_FMT(
IOS_WC24,
"NET_KD_REQ: IOCTL_NWC24_CHECK_MAIL_NOW: Mail server returned non-success code: {}", code);
return NWC24::WC24_ERR_SERVER;
}
// Reverse in order to delete from bottom to top of the send list.
// We do this to ensure that new entries can be written as close to the beginning of the file as
// possible.
for (auto it = mails.rbegin(); it != mails.rend(); ++it)
{
const u32 entry_id = m_send_list.GetEntryId(*it);
Common::ScopeGuard delete_guard([&] {
NWC24::ErrorCode res = m_send_list.DeleteMessage(*it);
if (res != NWC24::WC24_OK)
{
LogError(ErrorType::SendMail, res);
}
});
const std::string value = GetValueFromCGIResponse(response_str, fmt::format("cd{}", entry_id));
s32 cgi_code{};
const bool did_parse = TryParse(value, &cgi_code);
if (!did_parse)
{
ERROR_LOG_FMT(IOS_WC24, "Mail server returned invalid CGI response code.");
LogError(ErrorType::CGI, NWC24::WC24_ERR_SERVER);
break;
}
if (cgi_code != 100)
{
ERROR_LOG_FMT(IOS_WC24, "Mail server failed to save mail at index {}", entry_id);
LogError(ErrorType::CGI, cgi_code);
}
}
success = true;
return NWC24::WC24_OK;
}
NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
const std::optional<u8> subtask_id)
{
@ -651,6 +793,13 @@ IPCReply NetKDRequestDevice::HandleNWC24CheckMailNow(const IOCtlRequest& request
return IPCReply(IPC_SUCCESS);
}
IPCReply NetKDRequestDevice::HandleNWC24SendMailNow(const IOCtlRequest& request)
{
const NWC24::ErrorCode reply = KDSendMail();
WriteReturnValue(reply, request.buffer_out);
return IPCReply(IPC_SUCCESS);
}
IPCReply NetKDRequestDevice::HandleNWC24DownloadNowEx(const IOCtlRequest& request)
{
if (m_dl_list.IsDisabled() || !m_dl_list.ReadDlList())
@ -975,6 +1124,9 @@ std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
case IOCTL_NWC24_DOWNLOAD_NOW_EX:
return LaunchAsyncTask(&NetKDRequestDevice::HandleNWC24DownloadNowEx, request);
case IOCTL_NWC24_SEND_MAIL_NOW:
return LaunchAsyncTask(&NetKDRequestDevice::HandleNWC24SendMailNow, request);
case IOCTL_NWC24_REQUEST_SHUTDOWN:
{
if (request.buffer_in == 0 || request.buffer_in % 4 != 0 || request.buffer_in_size < 8 ||

View File

@ -13,6 +13,7 @@
#include "Common/HttpRequest.h"
#include "Common/WorkQueueThread.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/Network/KD/Mail/WC24FriendList.h"
#include "Core/IOS/Network/KD/Mail/WC24Send.h"
#include "Core/IOS/Network/KD/NWC24Config.h"
#include "Core/IOS/Network/KD/NWC24DL.h"
@ -72,6 +73,9 @@ private:
Client,
Server,
CheckMail,
SendMail,
ReceiveMail,
CGI,
};
enum class SchedulerEvent
@ -80,14 +84,18 @@ private:
Download,
};
IPCReply HandleNWC24SendMailNow(const IOCtlRequest& request);
NWC24::ErrorCode KDCheckMail(u32* mail_flag, u32* interval);
IPCReply HandleRequestRegisterUserId(const IOCtlRequest& request);
NWC24::ErrorCode KDSendMail();
void LogError(ErrorType error_type, s32 error_code);
void SchedulerTimer();
void SchedulerWorker(SchedulerEvent event);
NWC24::ErrorCode DetermineDownloadTask(u16* entry_index, std::optional<u8>* subtask_id) const;
NWC24::ErrorCode DetermineSubtask(u16 entry_index, std::optional<u8>* subtask_id) const;
static constexpr u32 MAX_MAIL_SIZE = 208952;
static std::string GetValueFromCGIResponse(const std::string& response, const std::string& key);
static constexpr std::array<u8, 20> MAIL_CHECK_KEY = {0xce, 0x4c, 0xf2, 0x9a, 0x3d, 0x6b, 0xe1,
0xc2, 0x61, 0x91, 0x72, 0xb5, 0xcb, 0x29,
@ -96,6 +104,7 @@ private:
NWC24::NWC24Config m_config;
NWC24::NWC24Dl m_dl_list;
NWC24::Mail::WC24SendList m_send_list;
NWC24::Mail::WC24FriendList m_friend_list;
Common::WorkQueueThread<AsyncTask> m_work_queue;
Common::WorkQueueThread<std::function<void()>> m_scheduler_work_queue;
std::mutex m_async_reply_lock;

View File

@ -366,6 +366,7 @@
<ClInclude Include="Core\IOS\Network\KD\VFF\VFFUtil.h" />
<ClInclude Include="Core\IOS\Network\KD\WC24File.h" />
<ClInclude Include="Core\IOS\Network\KD\Mail\MailCommon.h" />
<ClInclude Include="Core\IOS\Network\KD\Mail\WC24FriendList.h" />
<ClInclude Include="Core\IOS\Network\KD\Mail\WC24Send.h" />
<ClInclude Include="Core\IOS\Network\MACUtils.h" />
<ClInclude Include="Core\IOS\Network\NCD\Manage.h" />
@ -1017,6 +1018,7 @@
<ClCompile Include="Core\IOS\Network\KD\NWC24Config.cpp" />
<ClCompile Include="Core\IOS\Network\KD\NWC24DL.cpp" />
<ClCompile Include="Core\IOS\Network\KD\VFF\VFFUtil.cpp" />
<ClCompile Include="Core\IOS\Network\KD\Mail\WC24FriendList.cpp" />
<ClCompile Include="Core\IOS\Network\KD\Mail\WC24Send.cpp" />
<ClCompile Include="Core\IOS\Network\MACUtils.cpp" />
<ClCompile Include="Core\IOS\Network\NCD\Manage.cpp" />