// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <utility>
#include "base/strings/strcat.h"
#include "chrome/browser/nearby_sharing/share_target.h"
#include "chrome/browser/nearby_sharing/text_attachment.h"
#include "components/drive/drive_api_util.h"
#include "url/gurl.h"
namespace {
// Tries to get a valid host name from the |text|. Returns nullopt otherwise.
std::optional<std::string> GetHostFromText(const std::string& text) {
GURL url(text);
if (!url.is_valid() || !url.has_host())
return std::nullopt;
return url.host();
}
// Masks the given |number| depending on the string length:
// - length <= 4: Masks all characters
// - 4 < length <= 6 Masks the last 4 characters
// - 6 < length <= 10: Skips the first 2 and masks the following 4 characters
// - length > 10: Skips the first 2 and last 4 characters. Masks the rest of
// the string
// Note: We're assuming a formatted phone number and won't try to reformat to
// E164 like on Android as there's no easy way of determining the intended
// region for the phone number.
std::string MaskPhoneNumber(const std::string& number) {
constexpr int kMinMaskedDigits = 4;
constexpr int kMaxLeadingDigits = 2;
constexpr int kMaxTailingDigits = 4;
constexpr int kLengthWithNoLeadingDigits = kMinMaskedDigits;
constexpr int kLengthWithNoTailingDigits =
kMinMaskedDigits + kMaxLeadingDigits;
if (number.empty())
return number;
std::string result = number;
bool has_plus = false;
if (number[0] == '+') {
result = number.substr(1);
has_plus = true;
}
// First calculate how many digits we would mask having exactly
// kMinMaskedDigits digits masked.
int leading_digits = 0;
if (result.length() > kLengthWithNoLeadingDigits)
leading_digits = result.length() - kLengthWithNoLeadingDigits;
int tailing_digits = 0;
if (result.length() > kLengthWithNoTailingDigits)
tailing_digits = result.length() - kLengthWithNoTailingDigits;
// Now limit resulting numbers of digits to maximally allowed values.
leading_digits = std::min(kMaxLeadingDigits, leading_digits);
tailing_digits = std::min(kMaxTailingDigits, tailing_digits);
int masked_digits = result.length() - leading_digits - tailing_digits;
return base::StrCat({(has_plus ? "+" : ""), result.substr(0, leading_digits),
std::string(masked_digits, 'x'),
result.substr(result.length() - tailing_digits)});
}
std::string GetTextTitle(const std::string& text_body,
TextAttachment::Type type) {
constexpr size_t kMaxPreviewTextLength = 32;
switch (type) {
case TextAttachment::Type::kUrl: {
std::optional<std::string> host = GetHostFromText(text_body);
if (host)
return *host;
break;
}
case TextAttachment::Type::kPhoneNumber:
return MaskPhoneNumber(text_body);
default:
break;
}
if (text_body.size() > kMaxPreviewTextLength)
return base::StrCat({text_body.substr(0, kMaxPreviewTextLength), "…"});
return text_body;
}
} // namespace
TextAttachment::TextAttachment(Type type,
std::string text_body,
std::optional<std::string> text_title,
std::optional<std::string> mime_type)
: Attachment(Attachment::Family::kText, text_body.size()),
type_(type),
text_title_(text_title && !text_title->empty()
? *text_title
: GetTextTitle(text_body, type)),
text_body_(std::move(text_body)),
mime_type_(mime_type ? *mime_type : std::string()) {}
TextAttachment::TextAttachment(int64_t id,
Type type,
std::string text_title,
int64_t size)
: Attachment(id, Attachment::Family::kText, size),
type_(type),
text_title_(std::move(text_title)) {}
TextAttachment::TextAttachment(const TextAttachment&) = default;
TextAttachment::TextAttachment(TextAttachment&&) = default;
TextAttachment& TextAttachment::operator=(const TextAttachment&) = default;
TextAttachment& TextAttachment::operator=(TextAttachment&&) = default;
TextAttachment::~TextAttachment() = default;
void TextAttachment::MoveToShareTarget(ShareTarget& share_target) {
share_target.text_attachments.push_back(std::move(*this));
}
const std::string& TextAttachment::GetDescription() const {
return text_title_;
}
nearby_share::mojom::ShareType TextAttachment::GetShareType() const {
switch (type()) {
case TextAttachment::Type::kUrl:
if (mime_type_ == drive::util::kGoogleDocumentMimeType) {
return nearby_share::mojom::ShareType::kGoogleDocsFile;
} else if (mime_type_ == drive::util::kGoogleSpreadsheetMimeType) {
return nearby_share::mojom::ShareType::kGoogleSheetsFile;
} else if (mime_type_ == drive::util::kGooglePresentationMimeType) {
return nearby_share::mojom::ShareType::kGoogleSlidesFile;
} else {
return nearby_share::mojom::ShareType::kUrl;
}
case TextAttachment::Type::kAddress:
return nearby_share::mojom::ShareType::kAddress;
case TextAttachment::Type::kPhoneNumber:
return nearby_share::mojom::ShareType::kPhone;
default:
return nearby_share::mojom::ShareType::kText;
}
}
void TextAttachment::set_text_body(std::string text_body) {
text_body_ = std::move(text_body);
text_title_ = GetTextTitle(text_body_, type_);
}