// Copyright (c) 2014-2018 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Boolberry developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include "string_coding.h" #include "offers_services_helpers.h" #include "etc_custom_serialization.h" #include "misc_language.h" #include "currency_basic.h" namespace bc_services { bool order_offers_by_timestamp(const offer_details_ex* a, const offer_details_ex* b) { return a->timestamp < b->timestamp; } //------------------------------------------------------------------ bool order_offers_by_amount_primary(const offer_details_ex* a, const offer_details_ex* b) { return a->amount_primary < b->amount_primary; } //------------------------------------------------------------------ bool order_offers_by_amount_target(const offer_details_ex* a, const offer_details_ex* b) { return a->amount_target < b->amount_target; } //------------------------------------------------------------------ bool order_offers_by_rate(const offer_details_ex* a, const offer_details_ex* b) { return calculate_offer_rate(*a) < calculate_offer_rate(*b); } //------------------------------------------------------------------ bool order_offers_by_payment_types(const offer_details_ex* a, const offer_details_ex* b) { return a->payment_types < b->payment_types; } //------------------------------------------------------------------ bool order_offers_by_contacts(const offer_details_ex* a, const offer_details_ex* b) { return a->contacts < b->contacts; } //------------------------------------------------------------------ bool order_offers_by_location(const offer_details_ex* a, const offer_details_ex* b) { return a->location_city < b->location_city; } //------------------------------------------------------------------ bool order_offers_by_name(const offer_details_ex* a, const offer_details_ex* b) { #ifndef ANDROID_BUILD return currency::utf8_to_lower(a->target) < currency::utf8_to_lower(b->target); #else return false; #endif } //------------------------------------------------------------------ std::vector gsort_offers_predicates; bool init_vector = epee::misc_utils::static_initializer([](){ gsort_offers_predicates.resize(ORDER_CALLBACKS_COUNT); gsort_offers_predicates[ORDER_BY_TIMESTAMP] = &order_offers_by_timestamp; gsort_offers_predicates[ORDER_BY_AMOUNT_PRIMARY] = &order_offers_by_amount_primary; gsort_offers_predicates[ORDER_BY_AMOUNT_TARGET] = &order_offers_by_amount_target; gsort_offers_predicates[ORDER_BY_AMOUNT_RATE] = &order_offers_by_rate; gsort_offers_predicates[ORDER_BY_PAYMENT_TYPES] = &order_offers_by_payment_types; gsort_offers_predicates[ORDER_BY_CONTACTS] = &order_offers_by_contacts; gsort_offers_predicates[ORDER_BY_LOCATION] = &order_offers_by_location; gsort_offers_predicates[ORDER_BY_NAME] = &order_offers_by_name; return true; }); // std::locale locale_utf8; bool init_locale = epee::misc_utils::static_initializer([](){ setlocale(LC_ALL, ""); return true; }); to_low_case_ptr_type to_low_case_external = nullptr; void set_external_to_low_converter(to_low_case_ptr_type cb) { to_low_case_external = cb; } std::wstring to_lower_local_w(const std::string& s) { //std::u16string a; std::wstring ws = epee::string_encoding::utf8_to_wstring(s); if (to_low_case_external) return to_low_case_external(ws); //std::transform(ws.begin(), ws.end(), ws.begin(), ::towlower); boost::algorithm::to_lower(ws); return ws; } #define SECONDS_IN_ONE_DAY (60*60*24) bool is_offer_matched_by_filter(const offer_details_ex& o, const core_offers_filter& of, uint64_t current_time) { if (o.stopped) return false; //check offer type condition if (of.offer_type_mask) { switch (o.offer_type) { case OFFER_TYPE_PRIMARY_TO_TARGET: if (!(of.offer_type_mask & OFFER_TYPE_MASK_PRIMARY_TO_TARGET)) return false; break; case OFFER_TYPE_TARGET_TO_PRIMARY: if (!(of.offer_type_mask & OFFER_TYPE_MASK_TARGET_TO_PRIMARY)) return false; break; case OFFER_TYPE_GOODS_TO_PRIMARY: if (!(of.offer_type_mask & OFFER_TYPE_MASK_GOODS_TO_PRIMARY)) return false; break; case OFFER_TYPE_PRIMARY_TO_GOODS: if (!(of.offer_type_mask & OFFER_TYPE_MASK_PRIMARY_TO_GOODS)) return false; break; default: return false; } } //check timestamp condition if (of.timestamp_start && (o.timestamp < of.timestamp_start)) return false; if (of.timestamp_stop && (o.timestamp > of.timestamp_stop)) return false; //check bonus field if (of.bonus && o.bonus.empty()) return false; //check category if (!of.category.empty() && o.category.find(of.category) == std::string::npos) return false; #ifndef ANDROID_BUILD //check target condition if (of.target.size() && !currency::utf8_substring_test_case_insensitive(of.target, o.target)) return false; if (of.primary.size() && !currency::utf8_substring_test_case_insensitive(of.primary, o.primary)) return false; #endif //check payment_types condition (TODO: add more complicated algo here) if (of.payment_types.size()) { for (const auto& ot : of.payment_types) { if (std::string::npos == o.payment_types.find(ot)) return false; } } //check target condition if (of.location_country.size() && (of.location_country != o.location_country)) return false; #ifndef ANDROID_BUILD //check target condition if (of.location_city.size() && !currency::utf8_substring_test_case_insensitive(of.location_city, o.location_city)) return false; #endif //check amount if (of.amount_low_limit && (of.amount_low_limit > o.amount_primary)) return false; if (of.amount_up_limit && (of.amount_up_limit < o.amount_primary)) return false; double rate = ((static_cast(o.amount_target) / ETC_AMOUNT_DIVIDER)) / (static_cast(o.amount_primary) / COIN); //check rate if (of.rate_low_limit && (of.rate_low_limit > rate)) return false; if (of.rate_up_limit && (of.rate_up_limit < rate)) return false; //check keywords if (!of.keyword.empty()) { std::wstring all_in_apper; all_in_apper += to_lower_local_w(o.bonus); all_in_apper += to_lower_local_w(o.target); all_in_apper += to_lower_local_w(o.location_country); all_in_apper += to_lower_local_w(o.location_city); all_in_apper += to_lower_local_w(o.contacts); all_in_apper += to_lower_local_w(o.comment); all_in_apper += to_lower_local_w(o.payment_types); all_in_apper += to_lower_local_w(o.deal_option); all_in_apper += to_lower_local_w(o.category); std::wstring keyword_lowcase = to_lower_local_w(of.keyword); if (all_in_apper.find(keyword_lowcase) == std::wstring::npos) { return false; } } if (o.expiration_time && current_time > o.timestamp && current_time - o.timestamp > o.expiration_time*SECONDS_IN_ONE_DAY) return false; return true; } //-------------------------------------------------------------------------------- bool filter_offers_list(std::list& offers, const bc_services::core_offers_filter& filter, uint64_t current_core_time) { #ifndef ANDROID_BUILD //filter offers.remove_if([&](bc_services::offer_details_ex& o) -> bool { return !is_offer_matched_by_filter(o, filter, current_core_time); }); #endif //sort CHECK_AND_ASSERT_MES(filter.order_by < gsort_offers_predicates.size(), false, "Wrong cof.order_by value"); auto cb = *gsort_offers_predicates[static_cast(filter.order_by)]; //sort iterators in needed way if (!filter.reverse) { offers.sort([&](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b) { return cb(&a, &b); }); } else { offers.sort([&](const bc_services::offer_details_ex& a, const bc_services::offer_details_ex& b) { return cb(&b, &a); }); } //now cut what not needed auto it = std::next(offers.begin(), std::min(filter.offset, static_cast(offers.size()))); offers.erase(offers.begin(), it); // cut limit it = std::next(offers.begin(), std::min(filter.limit, static_cast(offers.size()))); offers.erase(it, offers.end()); return true; } //-------------------------------------------------------------------------------- crypto::hash offer_id_from_hash_and_index(const crypto::hash& tx_id, uint64_t index) { crypto::hash r = tx_id; //xor *(uint64_t*)&r ^= index; return r; } //--------------------------------------------------------------- crypto::hash offer_id_from_hash_and_index(const offer_id& oid) { return offer_id_from_hash_and_index(oid.tx_id, oid.index); } //--------------------------------------------------------------- const crypto::public_key get_offer_secure_key_by_index_from_tx(const currency::transaction& tx, size_t index) { size_t count = 0; for (const auto& a : tx.attachment) { if (a.type() == typeid(currency::tx_service_attachment)) { const currency::tx_service_attachment& txsa = boost::get(a); if (txsa.service_id == BC_OFFERS_SERVICE_ID) { if (count != index) { count++; continue; } if (txsa.security.empty()) return currency::null_pkey; return txsa.security.back(); } } } return currency::null_pkey; } } // namespace bc_services