From 7cf31cd5100fe4e9d056d3f8a2e12acf6bb21349 Mon Sep 17 00:00:00 2001 From: Rushfan Date: Sat, 7 Nov 2020 11:51:13 -0800 Subject: [PATCH] Added EmailTest class * Added test for fixup_user_entered_email * Fixed a couple of issues in fixup_user_entered_email * Added wwiv::strings::contains(haystack, needle) since it seems generally useful. * cleaned up a few other things here and there. --- bbs/email.cpp | 23 ++++++------- bbs/email.h | 4 +-- bbs_test/CMakeLists.txt | 1 + bbs_test/email_test.cpp | 34 +++++++++++++++++++ core/strings.cpp | 43 ++++++++++++------------ core/strings.h | 73 ++++++++++++++++++++++------------------- 6 files changed, 110 insertions(+), 68 deletions(-) create mode 100644 bbs_test/email_test.cpp diff --git a/bbs/email.cpp b/bbs/email.cpp index 67378acef..c75345f69 100644 --- a/bbs/email.cpp +++ b/bbs/email.cpp @@ -727,27 +727,28 @@ void delmail(File& f, size_t loc) { f.Write(&m, sizeof(mailrec)); } -std::string fixup_user_entered_email(const std::string& s) { - if (s.empty()) { +std::string fixup_user_entered_email(const std::string& user_input) { + if (user_input.empty()) { return{}; } - auto user_input{s}; const auto at_pos = user_input.find('@'); if (at_pos != std::string::npos && at_pos < user_input.size() - 1 && isalpha(user_input.at(at_pos + 1))) { - if (user_input.find(INTERNET_EMAIL_FAKE_OUTBOUND_ADDRESS) != std::string::npos) { - StringLowerCase(&user_input); - user_input += INTERNET_EMAIL_FAKE_OUTBOUND_ADDRESS; + if (!contains(user_input, INTERNET_EMAIL_FAKE_OUTBOUND_ADDRESS)) { + return StrCat(ToStringLowerCase(user_input), " ", INTERNET_EMAIL_FAKE_OUTBOUND_ADDRESS); } - } else if (user_input.find('(') != std::string::npos && user_input.find(')') != std::string::npos) { + return user_input; + } + + const auto first = user_input.find_last_of('('); + const auto last = user_input.find_last_of(')'); + if (first != std::string::npos && last != std::string::npos) { // This is where we'd check for (NNNN) and add in the @NNN for the FTN networks. - const auto first = user_input.find_last_of('('); - const auto last = user_input.find_last_of(')'); if (last > first) { const auto inner = user_input.substr(first + 1, last - first - 1); - if (inner.find('/') != std::string::npos) { + if (wwiv::stl::contains(inner, '/') && !contains(user_input, FTN_FAKE_OUTBOUND_ADDRESS)) { // At least need a FTN address. - user_input += StrCat(" ", FTN_FAKE_OUTBOUND_ADDRESS); + return StrCat(user_input, " ", FTN_FAKE_OUTBOUND_ADDRESS); //bout << "\r\n|#9Sending to FTN Address: |#2" << inner << wwiv::endl; } } diff --git a/bbs/email.h b/bbs/email.h index 6c0543794..ab35e1423 100644 --- a/bbs/email.h +++ b/bbs/email.h @@ -58,6 +58,6 @@ void email(const std::string& title, uint16_t user_number, uint16_t system_numbe void imail(const std::string& title, uint16_t user_number, uint16_t system_number); void delmail(wwiv::core::File& pFile, size_t loc); -[[nodiscard]] std::string fixup_user_entered_email(const std::string& s); +[[nodiscard]] std::string fixup_user_entered_email(const std::string& user_input); -#endif // __INCLUDED_BBS_MSGBASE_H__ \ No newline at end of file +#endif diff --git a/bbs_test/CMakeLists.txt b/bbs_test/CMakeLists.txt index df309728c..3ac5667c3 100644 --- a/bbs_test/CMakeLists.txt +++ b/bbs_test/CMakeLists.txt @@ -10,6 +10,7 @@ set(test_sources bputch_test.cpp datetime_test.cpp dsz_test.cpp + email_test.cpp input_test.cpp make_abs_test.cpp msgbase1_test.cpp diff --git a/bbs_test/email_test.cpp b/bbs_test/email_test.cpp new file mode 100644 index 000000000..0df2da1e3 --- /dev/null +++ b/bbs_test/email_test.cpp @@ -0,0 +1,34 @@ +/**************************************************************************/ +/* */ +/* WWIV Version 5.x */ +/* Copyright (C)2014-2020, WWIV Software Services */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, */ +/* software distributed under the License is distributed on an */ +/* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, */ +/* either express or implied. See the License for the specific */ +/* language governing permissions and limitations under the License. */ +/* */ +/**************************************************************************/ +#include "gtest/gtest.h" + +#include "bbs/email.h" + + + +TEST(EmailTest, Smoke) { + EXPECT_EQ("1", fixup_user_entered_email("1")); + EXPECT_EQ("1@1", fixup_user_entered_email("1@1")); + EXPECT_EQ("1@1.WWIVNET", fixup_user_entered_email("1@1.WWIVNET")); + + EXPECT_EQ("rushfan@wwiv.us @32767", fixup_user_entered_email("rushfan@wwiv.us @32767")); + EXPECT_EQ("rushfan@wwiv.us @32767", fixup_user_entered_email("rushfan@wwiv.us")); + EXPECT_EQ("rushfan (1:1/100) @32765", fixup_user_entered_email("rushfan (1:1/100)")); + EXPECT_EQ("rushfan (1:1/100) @32765", fixup_user_entered_email("rushfan (1:1/100) @32765")); +} diff --git a/core/strings.cpp b/core/strings.cpp index c9083fc90..59cc09643 100644 --- a/core/strings.cpp +++ b/core/strings.cpp @@ -123,7 +123,7 @@ void SplitString(const string& original_string, const string& delims, vector* out) { - string s(original_string); + auto s(original_string); for (auto found = s.find_first_of(delims); found != string::npos; s = s.substr(found + 1), found = s.find_first_of(delims)) { if (found > 0) { @@ -150,13 +150,11 @@ bool ends_with(const std::string& input, const std::string& match) { /** * Returns a string justified and padded with "bg". + * * @param s The text to justify * @param length the length of the text * @param bg the character to use as the background - * @param just_type one of the following: - * LEFT - * RIGHT - * @return the justified text. + * @param just_type one of the following: LEFT, RIGHT */ void StringJustify(string* s, int length, char bg, JustificationType just_type) { if (ssize(*s) > length) { @@ -184,8 +182,8 @@ void StringJustify(string* s, int length, char bg, JustificationType just_type) /** * Removes spaces from the beginning and the end of the string s. + * * @param str the string from which to remove spaces - * @return str with spaces removed. */ void StringTrim(char* str) { string s(str); @@ -195,11 +193,11 @@ void StringTrim(char* str) { /** * Removes spaces from the beginning and the end of the string s. +* * @param s the string from which to remove spaces -* @return s with spaces removed. */ void StringTrim(string* s) { - string::size_type pos = s->find_first_not_of(DELIMS_WHITE); + auto pos = s->find_first_not_of(DELIMS_WHITE); s->erase(0, pos); pos = s->find_last_not_of(DELIMS_WHITE); @@ -208,8 +206,8 @@ void StringTrim(string* s) { /** * Removes CF and LF from the beginning and the end of the string s. +* * @param s the string from which to remove spaces -* @return s with spaces removed. */ void StringTrimCRLF(string* s) { auto pos = s->find_first_not_of(DELIMS_CRLF); @@ -222,12 +220,13 @@ void StringTrimCRLF(string* s) { /** * Removes spaces from the beginning and the end of the string s and * returns it as a new string +* * @param orig the string from which to remove spaces * @return orig with spaces removed. */ string StringTrim(const string& orig) { - string s(orig); - string::size_type pos = s.find_first_not_of(DELIMS_WHITE); + auto s(orig); + auto pos = s.find_first_not_of(DELIMS_WHITE); s.erase(0, pos); pos = s.find_last_not_of(DELIMS_WHITE); @@ -375,7 +374,17 @@ static bool IsColorCode(char c) { if (!c) { return false; } - return (c == '#' || isdigit(c)); + return c == '#' || isdigit(c); +} + +bool wwiv::strings::contains(const std::string& haystack, const std::string_view& needle) noexcept { + try { + return haystack.find(needle) != std::string::npos; + } catch (...) { + DLOG(FATAL) << "Caught exeption in wwiv::strings::contains: '" + << haystack << "' : '" << needle << "'"; + return false; + } } char* stripcolors(const char* str) { @@ -461,6 +470,7 @@ unsigned char upcase(unsigned char ch) { * @return The lowercase version of the character */ unsigned char locase(unsigned char ch) { + // ReSharper disable once CppCStyleCast auto* ss = (unsigned char*)strchr((const char*)translate_letters[1], ch); if (ss) { ch = translate_letters[0][ss - translate_letters[1]]; @@ -531,15 +541,6 @@ char *strupr(char *s) { return s; } -/** Converts string to lowercase */ -char *strlwr(char *s) { - for (int i = 0; s[i] != 0; i++) { - s[i] = wwiv::strings::to_lower_case(s[i]); - } - - return s; -} - // Reverses a string char *strrev(char *s) { CHECK(s != nullptr); diff --git a/core/strings.h b/core/strings.h index fbfff508e..6a9d2f11d 100644 --- a/core/strings.h +++ b/core/strings.h @@ -16,8 +16,8 @@ /* language governing permissions and limitations under the License. */ /* */ /**************************************************************************/ -#ifndef __INCLUDED_STRINGS_H__ -#define __INCLUDED_STRINGS_H__ +#ifndef INCLUDED_CORE_STRINGS_H +#define INCLUDED_CORE_STRINGS_H // ReSharper disable once CppUnusedIncludeDirective #include // strncpy @@ -34,8 +34,7 @@ #undef StrCat #endif // StrCat -namespace wwiv { -namespace strings { +namespace wwiv::strings { enum class JustificationType { LEFT, RIGHT }; @@ -86,80 +85,80 @@ template std::string StrCat(const A& a, const Arg } // Comparisons - bool IsEquals(const char* str1, const char* str2); - bool iequals(const char* str1, const char* str2); - bool iequals(const std::string& s1, const std::string& s2); - int StringCompareIgnoreCase(const char* str1, const char* str2); - int StringCompare(const char* str1, const char* str2); + [[nodiscard]] bool IsEquals(const char* str1, const char* str2); + [[nodiscard]] bool iequals(const char* str1, const char* str2); + [[nodiscard]] bool iequals(const std::string& s1, const std::string& s2); + [[nodiscard]] int StringCompareIgnoreCase(const char* str1, const char* str2); + [[nodiscard]] int StringCompare(const char* str1, const char* str2); const std::string& StringReplace(std::string* orig, const std::string& old_string, const std::string& new_string); std::vector SplitString(const std::string& original_string, const std::string& delims); - std::vector SplitString(const std::string& original_string, + [[nodiscard]] std::vector SplitString(const std::string& original_string, const std::string& delims, bool skip_empty); void SplitString(const std::string& original_string, const std::string& delims, std::vector* out); void SplitString(const std::string& original_string, const std::string& delims, bool skip_empty, std::vector* out); - bool starts_with(const std::string& input, const std::string& match); - bool ends_with(const std::string& input, const std::string& match); + [[nodiscard]] bool starts_with(const std::string& input, const std::string& match); + [[nodiscard]] bool ends_with(const std::string& input, const std::string& match); void StringJustify(std::string* s, int length, char bg, JustificationType just_type); void StringTrim(char* str); void StringTrim(std::string* s); - std::string StringTrim(const std::string& orig); + [[nodiscard]] std::string StringTrim(const std::string& orig); void StringTrimCRLF(std::string* s); void StringTrimEnd(std::string* s); void StringTrimEnd(char* str); void StringTrimBegin(std::string* s); void StringUpperCase(std::string* s); - std::string ToStringUpperCase(const std::string& s); + [[nodiscard]] std::string ToStringUpperCase(const std::string& s); void StringLowerCase(std::string* s); - std::string ToStringLowerCase(const std::string& s); + [[nodiscard]] std::string ToStringLowerCase(const std::string& s); -// Strips the string from the first occurence of ch +// Strips the string from the first occurrence of ch // Doesn't seem to be used anywhere. Maybe it should be removed. char* StringRemoveChar(const char* str, char ch); /** * Joints the strings in lines, using end_of_line in between each line. */ - std::string JoinStrings(const std::vector& lines, const std::string& end_of_line); + [[nodiscard]] std::string JoinStrings(const std::vector& lines, const std::string& end_of_line); // String length without colors - int size_without_colors(const std::string& s); + [[nodiscard]] int size_without_colors(const std::string& s); /** returns a copy of orig trimmed to size, excluding colors. */ - std::string trim_to_size_ignore_colors(const std::string& orig, int size); + [[nodiscard]] std::string trim_to_size_ignore_colors(const std::string& orig, int size); /** * Returns orig padded to size, excluding color codes. */ - std::string pad_to_ignore_colors(const std::string& orig, int size); + [[nodiscard]] std::string pad_to_ignore_colors(const std::string& orig, int size); // String length - std::string::size_type size(const std::string& s); + [[nodiscard]] std::string::size_type size(const std::string& s); // String length - std::string::size_type size(const char* s); + [[nodiscard]] std::string::size_type size(const char* s); // String length as an int - int ssize(const char* s); + [[nodiscard]] int ssize(const char* s); // String length as an int - int ssize(const unsigned char* s); + [[nodiscard]] int ssize(const unsigned char* s); // String length as an int - int ssize(const std::string& s); + [[nodiscard]] int ssize(const std::string& s); /** returns a copy of orig trimmed to size, excluding colors. */ std::string trim_to_size(const std::string& orig, int size); - /** Typesafe version of toupper */ + /** Type-safe version of toupper */ template, char>> T to_upper_case(const T a) { return static_cast(::toupper(a)); } @@ -204,23 +203,30 @@ template std::string StrCat(const A& a, const Arg s, b); } - } // namespace strings + /** + * Return true if haystack contains needed as a substring. + * + * Like boost::contains. + * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1679r3.html + * for proposal to add as std::string::contains. + */ + bool contains(const std::string& haystack, const std::string_view& needle) noexcept; } // namespace wwiv // Function Prototypes - char* stripcolors(const char* pszOrig); - std::string stripcolors(const std::string& orig); - unsigned char upcase(unsigned char ch); - unsigned char locase(unsigned char ch); + [[nodiscard]] char* stripcolors(const char* pszOrig); + [[nodiscard]] std::string stripcolors(const std::string& orig); + [[nodiscard]] unsigned char upcase(unsigned char ch); + [[nodiscard]] unsigned char locase(unsigned char ch); void properize(char* text); - std::string properize(const std::string& text); + [[nodiscard]] std::string properize(const std::string& text); extern const char* DELIMS_WHITE; /** returns true if needle is found in haystack ,ignoring case */ - bool ifind_first(const std::string& haystack, const std::string& needle); + [[nodiscard]] bool ifind_first(const std::string& haystack, const std::string& needle); #ifdef _WIN32 @@ -231,7 +237,6 @@ template std::string StrCat(const A& a, const Arg #else // _WIN32 char* strupr(char* s); - char* strlwr(char* s); char* strrev(char* s); #endif // _WIN32