mirror of
https://github.com/monero-project/monero.git
synced 2024-10-01 11:49:47 -04:00
Merge pull request #2210
cb0b5594
Move OpenAlias console input back from libs (moneromooo-monero)
This commit is contained in:
commit
f5be5b7e7b
@ -26,12 +26,9 @@
|
|||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "common/command_line.h"
|
|
||||||
#include "common/i18n.h"
|
|
||||||
#include "common/dns_utils.h"
|
#include "common/dns_utils.h"
|
||||||
|
#include "common/i18n.h"
|
||||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||||
#include <cstring>
|
|
||||||
#include <sstream>
|
|
||||||
// check local first (in the event of static or in-source compilation of libunbound)
|
// check local first (in the event of static or in-source compilation of libunbound)
|
||||||
#include "unbound.h"
|
#include "unbound.h"
|
||||||
|
|
||||||
@ -401,7 +398,7 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec
|
|||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm)
|
std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm)
|
||||||
{
|
{
|
||||||
// attempt to get address from dns query
|
// attempt to get address from dns query
|
||||||
auto addresses = addresses_from_url(url, dnssec_valid);
|
auto addresses = addresses_from_url(url, dnssec_valid);
|
||||||
@ -410,44 +407,7 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn
|
|||||||
LOG_ERROR("wrong address: " << url);
|
LOG_ERROR("wrong address: " << url);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
// for now, move on only if one address found
|
return dns_confirm(url, addresses, dnssec_valid);
|
||||||
if (addresses.size() > 1)
|
|
||||||
{
|
|
||||||
LOG_ERROR("not yet supported: Multiple Monero addresses found for given URL: " << url);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (!cli_confirm)
|
|
||||||
return addresses[0];
|
|
||||||
// prompt user for confirmation.
|
|
||||||
// inform user of DNSSEC validation status as well.
|
|
||||||
std::string dnssec_str;
|
|
||||||
if (dnssec_valid)
|
|
||||||
{
|
|
||||||
dnssec_str = tr("DNSSEC validation passed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
|
|
||||||
}
|
|
||||||
std::stringstream prompt;
|
|
||||||
prompt << tr("For URL: ") << url
|
|
||||||
<< ", " << dnssec_str << std::endl
|
|
||||||
<< tr(" Monero Address = ") << addresses[0]
|
|
||||||
<< std::endl
|
|
||||||
<< tr("Is this OK? (Y/n) ")
|
|
||||||
;
|
|
||||||
// prompt the user for confirmation given the dns query and dnssec status
|
|
||||||
std::string confirm_dns_ok = command_line::input_line(prompt.str());
|
|
||||||
if (std::cin.eof())
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (!command_line::is_yes(confirm_dns_ok))
|
|
||||||
{
|
|
||||||
std::cout << tr("you have cancelled the transfer request") << std::endl;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return addresses[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -163,7 +163,7 @@ namespace dns_utils
|
|||||||
std::string address_from_txt_record(const std::string& s);
|
std::string address_from_txt_record(const std::string& s);
|
||||||
std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid);
|
std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid);
|
||||||
|
|
||||||
std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm = true);
|
std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> confirm_dns);
|
||||||
|
|
||||||
bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls);
|
bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls);
|
||||||
|
|
||||||
|
@ -308,13 +308,13 @@ namespace cryptonote {
|
|||||||
, crypto::hash8& payment_id
|
, crypto::hash8& payment_id
|
||||||
, bool testnet
|
, bool testnet
|
||||||
, const std::string& str_or_url
|
, const std::string& str_or_url
|
||||||
, bool cli_confirm
|
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url))
|
if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url))
|
||||||
return true;
|
return true;
|
||||||
bool dnssec_valid;
|
bool dnssec_valid;
|
||||||
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, cli_confirm);
|
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, dns_confirm);
|
||||||
return !address_str.empty() &&
|
return !address_str.empty() &&
|
||||||
get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str);
|
get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str);
|
||||||
}
|
}
|
||||||
@ -323,12 +323,12 @@ namespace cryptonote {
|
|||||||
cryptonote::account_public_address& address
|
cryptonote::account_public_address& address
|
||||||
, bool testnet
|
, bool testnet
|
||||||
, const std::string& str_or_url
|
, const std::string& str_or_url
|
||||||
, bool cli_confirm
|
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, cli_confirm);
|
return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, dns_confirm);
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) {
|
bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) {
|
||||||
|
@ -67,6 +67,15 @@ namespace cryptonote {
|
|||||||
};
|
};
|
||||||
#pragma pack (pop)
|
#pragma pack (pop)
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string return_first_address(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)
|
||||||
|
{
|
||||||
|
if (addresses.empty())
|
||||||
|
return {};
|
||||||
|
return addresses[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Cryptonote helper functions */
|
/* Cryptonote helper functions */
|
||||||
@ -109,14 +118,14 @@ namespace cryptonote {
|
|||||||
, crypto::hash8& payment_id
|
, crypto::hash8& payment_id
|
||||||
, bool testnet
|
, bool testnet
|
||||||
, const std::string& str_or_url
|
, const std::string& str_or_url
|
||||||
, bool cli_confirm = true
|
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address
|
||||||
);
|
);
|
||||||
|
|
||||||
bool get_account_address_from_str_or_url(
|
bool get_account_address_from_str_or_url(
|
||||||
cryptonote::account_public_address& address
|
cryptonote::account_public_address& address
|
||||||
, bool testnet
|
, bool testnet
|
||||||
, const std::string& str_or_url
|
, const std::string& str_or_url
|
||||||
, bool cli_confirm = true
|
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address
|
||||||
);
|
);
|
||||||
|
|
||||||
bool is_coinbase(const transaction& tx);
|
bool is_coinbase(const transaction& tx);
|
||||||
|
@ -256,7 +256,8 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
|||||||
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, args.front()))
|
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, args.front()))
|
||||||
{
|
{
|
||||||
bool dnssec_valid;
|
bool dnssec_valid;
|
||||||
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid);
|
std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid,
|
||||||
|
[](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid){return addresses[0];});
|
||||||
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, address_str))
|
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, address_str))
|
||||||
{
|
{
|
||||||
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, address_str))
|
if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, address_str))
|
||||||
|
@ -281,6 +281,42 @@ namespace
|
|||||||
{
|
{
|
||||||
return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
|
return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string oa_prompter(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)
|
||||||
|
{
|
||||||
|
if (addresses.empty())
|
||||||
|
return {};
|
||||||
|
// prompt user for confirmation.
|
||||||
|
// inform user of DNSSEC validation status as well.
|
||||||
|
std::string dnssec_str;
|
||||||
|
if (dnssec_valid)
|
||||||
|
{
|
||||||
|
dnssec_str = tr("DNSSEC validation passed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
|
||||||
|
}
|
||||||
|
std::stringstream prompt;
|
||||||
|
prompt << tr("For URL: ") << url
|
||||||
|
<< ", " << dnssec_str << std::endl
|
||||||
|
<< tr(" Monero Address = ") << addresses[0]
|
||||||
|
<< std::endl
|
||||||
|
<< tr("Is this OK? (Y/n) ")
|
||||||
|
;
|
||||||
|
// prompt the user for confirmation given the dns query and dnssec status
|
||||||
|
std::string confirm_dns_ok = command_line::input_line(prompt.str());
|
||||||
|
if (std::cin.eof())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!command_line::is_yes(confirm_dns_ok))
|
||||||
|
{
|
||||||
|
std::cout << tr("you have cancelled the transfer request") << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return addresses[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2215,7 +2251,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
|||||||
cryptonote::tx_destination_entry de;
|
cryptonote::tx_destination_entry de;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 new_payment_id;
|
crypto::hash8 new_payment_id;
|
||||||
if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i]))
|
if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -2714,7 +2750,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
|
|||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 new_payment_id;
|
crypto::hash8 new_payment_id;
|
||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0]))
|
if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -3312,7 +3348,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1]))
|
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -3414,7 +3450,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2]))
|
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -3566,7 +3602,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1]))
|
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -4084,7 +4120,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id8;
|
crypto::hash8 payment_id8;
|
||||||
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), args[1]))
|
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), args[1], oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
@ -4275,7 +4311,7 @@ bool simple_wallet::verify(const std::vector<std::string> &args)
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), address_string))
|
if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), address_string, oa_prompter))
|
||||||
{
|
{
|
||||||
fail_msg_writer() << tr("failed to parse address");
|
fail_msg_writer() << tr("failed to parse address");
|
||||||
return true;
|
return true;
|
||||||
|
@ -352,9 +352,24 @@ namespace tools
|
|||||||
cryptonote::tx_destination_entry de;
|
cryptonote::tx_destination_entry de;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 new_payment_id;
|
crypto::hash8 new_payment_id;
|
||||||
if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false))
|
er.message = "";
|
||||||
|
if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address,
|
||||||
|
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
|
||||||
|
if (!dnssec_valid)
|
||||||
|
{
|
||||||
|
er.message = std::string("Invalid DNSSEC for ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (addresses.empty())
|
||||||
|
{
|
||||||
|
er.message = std::string("No Monero address found at ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return addresses[0];
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
||||||
|
if (er.message.empty())
|
||||||
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
|
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1049,10 +1064,23 @@ namespace tools
|
|||||||
cryptonote::account_public_address address;
|
cryptonote::account_public_address address;
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id;
|
crypto::hash8 payment_id;
|
||||||
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false))
|
er.message = "";
|
||||||
|
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address,
|
||||||
|
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
|
||||||
|
if (!dnssec_valid)
|
||||||
|
{
|
||||||
|
er.message = std::string("Invalid DNSSEC for ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (addresses.empty())
|
||||||
|
{
|
||||||
|
er.message = std::string("No Monero address found at ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return addresses[0];
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
||||||
er.message = "";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,9 +1471,24 @@ namespace tools
|
|||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 payment_id8;
|
crypto::hash8 payment_id8;
|
||||||
crypto::hash payment_id = cryptonote::null_hash;
|
crypto::hash payment_id = cryptonote::null_hash;
|
||||||
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false))
|
er.message = "";
|
||||||
|
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address,
|
||||||
|
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
|
||||||
|
if (!dnssec_valid)
|
||||||
|
{
|
||||||
|
er.message = std::string("Invalid DNSSEC for ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (addresses.empty())
|
||||||
|
{
|
||||||
|
er.message = std::string("No Monero address found at ") + url;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return addresses[0];
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
|
||||||
|
if (er.message.empty())
|
||||||
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
|
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user