From 527ba25b14f13fee25e4f75501b3c1fd2294db07 Mon Sep 17 00:00:00 2001 From: berryplus Date: Sat, 8 Sep 2018 15:52:41 +0900 Subject: [PATCH 01/14] =?UTF-8?q?=E8=A8=98=E5=8F=B7=E9=A1=9E=E3=82=92wcsch?= =?UTF-8?q?r=E3=81=A7=E5=88=A4=E5=AE=9A=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E9=81=85=E3=81=84=E3=81=93=E3=81=A8=E3=81=AE=E5=AF=BE=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 42 ++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 242e2bc9c7..257a9fe2df 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -449,7 +449,26 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) +// || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + || (pszBuf[j] == L'!') + || (pszBuf[j] == L'#') + || (pszBuf[j] == L'$') + || (pszBuf[j] == L'%') + || (pszBuf[j] == L'&') + || (pszBuf[j] == L'\'') + || (pszBuf[j] == L'*') + || (pszBuf[j] == L'+') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'/') + || (pszBuf[j] == L'=') + || (pszBuf[j] == L'?') + || (pszBuf[j] == L'^') + || (pszBuf[j] == L'_') + || (pszBuf[j] == L'`') + || (pszBuf[j] == L'{') + || (pszBuf[j] == L'|') + || (pszBuf[j] == L'}') + || (pszBuf[j] == L'~') ){ j++; }else{ @@ -461,7 +480,26 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') || (pszBuf[j] == L'.') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) +// || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + || (pszBuf[j] == L'!') + || (pszBuf[j] == L'#') + || (pszBuf[j] == L'$') + || (pszBuf[j] == L'%') + || (pszBuf[j] == L'&') + || (pszBuf[j] == L'\'') + || (pszBuf[j] == L'*') + || (pszBuf[j] == L'+') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'/') + || (pszBuf[j] == L'=') + || (pszBuf[j] == L'?') + || (pszBuf[j] == L'^') + || (pszBuf[j] == L'_') + || (pszBuf[j] == L'`') + || (pszBuf[j] == L'{') + || (pszBuf[j] == L'|') + || (pszBuf[j] == L'}') + || (pszBuf[j] == L'~') ) ){ j++; From c180c73ccee6551aca38b59fe6a9ef97d0bb16b5 Mon Sep 17 00:00:00 2001 From: berryplus Date: Sat, 8 Sep 2018 23:34:32 +0900 Subject: [PATCH 02/14] =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=83=AB=E3=82=A2?= =?UTF-8?q?=E3=83=89=E3=83=AC=E3=82=B9=E5=89=8D=E5=8D=8A=E9=83=A8=E5=88=86?= =?UTF-8?q?=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=A6=8B?= =?UTF-8?q?=E7=9B=B4=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 153 +++++++++++++++++-------------- 1 file changed, 86 insertions(+), 67 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 257a9fe2df..878ee6227f 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -435,81 +435,31 @@ BOOL IsURL( return IsMailAddress(pszLine, nLineLen, pnMatchLen); } +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す @date 2016.04.27 記号類を許可 */ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) { - int j; - int nDotCount; - int nBgn; - + // メールアドレスには必ず@が含まれる + const wchar_t* pszAtmark; - j = 0; - if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') -// || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - || (pszBuf[j] == L'!') - || (pszBuf[j] == L'#') - || (pszBuf[j] == L'$') - || (pszBuf[j] == L'%') - || (pszBuf[j] == L'&') - || (pszBuf[j] == L'\'') - || (pszBuf[j] == L'*') - || (pszBuf[j] == L'+') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'/') - || (pszBuf[j] == L'=') - || (pszBuf[j] == L'?') - || (pszBuf[j] == L'^') - || (pszBuf[j] == L'_') - || (pszBuf[j] == L'`') - || (pszBuf[j] == L'{') - || (pszBuf[j] == L'|') - || (pszBuf[j] == L'}') - || (pszBuf[j] == L'~') - ){ - j++; - }else{ - return FALSE; - } - while( j < nBufLen - 2 && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'.') -// || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - || (pszBuf[j] == L'!') - || (pszBuf[j] == L'#') - || (pszBuf[j] == L'$') - || (pszBuf[j] == L'%') - || (pszBuf[j] == L'&') - || (pszBuf[j] == L'\'') - || (pszBuf[j] == L'*') - || (pszBuf[j] == L'+') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'/') - || (pszBuf[j] == L'=') - || (pszBuf[j] == L'?') - || (pszBuf[j] == L'^') - || (pszBuf[j] == L'_') - || (pszBuf[j] == L'`') - || (pszBuf[j] == L'{') - || (pszBuf[j] == L'|') - || (pszBuf[j] == L'}') - || (pszBuf[j] == L'~') - ) - ){ - j++; - } - if( j == 0 || j >= nBufLen - 2 ){ - return FALSE; - } - if( L'@' != pszBuf[j] ){ + // メールアドレス前半部分(@の手前)をチェックする + if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { return FALSE; } + assert(L'@' == *pszAtmark); + + int j = pszAtmark - pszBuf; + int nDotCount; + int nBgn; + // nAtPos = j; j++; nDotCount = 0; @@ -548,3 +498,72 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) } return TRUE; } + +/*! + * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する + * + * 高速化のため単純化した条件でチェックしている + * 参照する標準は RFC5321 + * @see http://srgia.com/docs/rfc5321j.html + */ +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept +{ + // RFC5321による local-part の最大文字数 + const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット + + // 関数仕様 + assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない + assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない + + // 出力値を初期化する + *ppszAtmark = nullptr; + + // 文字列が二重引用符で始まっているかチェックして結果を保存 + const bool quoted = (L'"' == *pszStart); + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszStart + (quoted ? 1 : 0); + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'@': + if (pszStart == pszScan) + { + return false; // local-partは1文字以上なのでNG + } + if (quoted) + { + return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG + } + *ppszAtmark = pszScan; + return true; // ここが正常終了 + case L'\\': // エスケープ記号 + if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) + { + return false; + } + pszScan++; // エスケープ記号の分1文字進める + break; + case L'"': // 二重引用符 + if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) + { + *ppszAtmark = &pszScan[1]; + return true; // ここは準正常終了。正常終了とはあえて区別しない。 + } + return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 + } + pszScan++; + if (MAX_LOCAL_PART < pszScan - pszStart) + { + return false; // 文字数オーバー + } + } + return false; +} From 876cc362cc1014b60b148d98fc66029a01d91f45 Mon Sep 17 00:00:00 2001 From: berryplus Date: Sun, 9 Sep 2018 01:29:57 +0900 Subject: [PATCH 03/14] =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=83=AB=E3=82=A2?= =?UTF-8?q?=E3=83=89=E3=83=AC=E3=82=B9=E5=BE=8C=E5=8D=8A=E9=83=A8=E5=88=86?= =?UTF-8?q?=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=A6=8B?= =?UTF-8?q?=E7=9B=B4=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 226 +++++++++++++++++++++++++------ 1 file changed, 188 insertions(+), 38 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 878ee6227f..529dee1279 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -442,11 +442,25 @@ inline static bool IsMailAddressLocalPart( _Out_ const wchar_t** ppszAtmark ) noexcept; +// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept; + /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す @date 2016.04.27 記号類を許可 + @date 2018.09.09 RFC準拠 */ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) { + // RFC5321による mailbox の最大文字数 + const ptrdiff_t MAX_MAILBOX = 255; //255オクテット + + // バカ避け + if (nBufLen < 1) return FALSE; + // メールアドレスには必ず@が含まれる const wchar_t* pszAtmark; @@ -456,45 +470,24 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) } assert(L'@' == *pszAtmark); - int j = pszAtmark - pszBuf; - int nDotCount; - int nBgn; - -// nAtPos = j; - j++; - nDotCount = 0; -// nAlphaCount = 0; - - - for (;;) { - nBgn = j; - while( j < nBufLen && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'_') - ) - ){ - j++; - } - if( 0 == j - nBgn ){ - return FALSE; - } - if( L'.' != pszBuf[j] ){ - if( 0 == nDotCount ){ - return FALSE; - }else{ - break; - } - }else{ - nDotCount++; - j++; - } + // メールアドレスの終了位置を受け取るポインタを宣言する + const wchar_t* pszEndOfMailBox; + + // メールアドレス後半部分(@の後ろ)をチェックする + if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) + { + return FALSE; + } + + // 全体の長さが制限を超えていないかチェックする + if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) + { + return FALSE; // 文字数オーバー } - if( NULL != pnAddressLenfth ){ - *pnAddressLenfth = j; + + if (pnAddressLenfth != nullptr) + { + *pnAddressLenfth = pszEndOfMailBox - pszBuf; } return TRUE; } @@ -567,3 +560,160 @@ inline static bool IsMailAddressLocalPart( } return false; } + +/*! + * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する + */ +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept +{ + // ccTLDの最小文字数 + const ptrdiff_t MIN_TLD = 2; + + // ドメインの最小文字数 + const ptrdiff_t MIN_DOMAIN = 3; + + // ドメインの最大文字数 + const ptrdiff_t MAX_DOMAIN = 63; + + // 関数仕様 + assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない + assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない + + // 出力値を初期化する + *ppszEndOfMailBox = nullptr; + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszAtmark + 1; + + auto dotCount = 0; + auto domainLength = 0; + auto prevHyphen = false; + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'.': // ドット記号 + if (dotCount == 0 && domainLength < MIN_DOMAIN) + { + return false; // ドメイン名の最小文字数は3なのでNG + } + if (0 < dotCount && domainLength < MIN_TLD) + { + // これはco.jpなどを正しく認識させるために必要。 + return false; // ドットで区切られる部分の最小文字数は2なのでNG + } + if (prevHyphen) + { + return false; // ハイフンに続くドットはNG + } + dotCount++; + domainLength = 0; + prevHyphen = false; + break; + case L'-': // ハイフン記号 + if (domainLength == 0) + { + return false; // ドットに続くハイフンはNG + } + if (prevHyphen) + { + return false; // 連続するハイフンはNG + } + domainLength++; + prevHyphen = true; + break; + default: + if (dotCount == 0) + { + return false; // ドメイン部には一つ以上のドット記号が必要なのでNG + } + if (domainLength == 0) + { + return false; // ドットで終わるドメインはNG + } + if (prevHyphen) + { + return false; // ハイフンで終わるドメインはNG + } + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + case L'A': + case L'B': + case L'C': + case L'D': + case L'E': + case L'F': + case L'G': + case L'H': + case L'I': + case L'J': + case L'K': + case L'L': + case L'M': + case L'N': + case L'O': + case L'P': + case L'Q': + case L'R': + case L'S': + case L'T': + case L'U': + case L'V': + case L'W': + case L'X': + case L'Y': + case L'Z': + case L'a': + case L'b': + case L'c': + case L'd': + case L'e': + case L'f': + case L'g': + case L'h': + case L'i': + case L'j': + case L'k': + case L'l': + case L'm': + case L'n': + case L'o': + case L'p': + case L'q': + case L'r': + case L's': + case L't': + case L'u': + case L'v': + case L'w': + case L'x': + case L'y': + case L'z': + domainLength++; + prevHyphen = false; + break; + } + pszScan++; + if (MAX_DOMAIN < domainLength) + { + return false; // 文字数オーバー + } + } + return false; +} From bfeb7360ed9e62366b0abfe6cd05adcb12f87ba3 Mon Sep 17 00:00:00 2001 From: berryplus Date: Sun, 9 Sep 2018 16:51:00 +0900 Subject: [PATCH 04/14] =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF?= =?UTF-8?q?=E5=AF=BE=E8=B1=A1=E3=81=8C=E8=8B=B1=E5=AD=97=E3=81=A7=E7=B5=82?= =?UTF-8?q?=E3=82=8F=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AB=E7=B5=82=E3=82=8F?= =?UTF-8?q?=E3=82=8A=E3=82=92=E6=A4=9C=E5=87=BA=E3=81=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=90=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sakura_core/parse/CWordParse.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 529dee1279..8c76af7632 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -442,6 +442,13 @@ inline static bool IsMailAddressLocalPart( _Out_ const wchar_t** ppszAtmark ) noexcept; +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + // 指定された文字列がメールアドレス後半部分の要件を満たすか判定する inline static bool IsMailAddressDomain( _In_z_ const wchar_t* pszAtmark, @@ -642,7 +649,7 @@ inline static bool IsMailAddressDomain( return false; // ハイフンで終わるドメインはNG } *ppszEndOfMailBox = pszScan; - return true; // ここが正常終了 + return true; // ここも正常終了 case L'0': case L'1': case L'2': @@ -710,6 +717,11 @@ inline static bool IsMailAddressDomain( break; } pszScan++; + if (pszScan == pszEnd) + { + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + } if (MAX_DOMAIN < domainLength) { return false; // 文字数オーバー From 14956cbd5964122d64476e64e5450533e5ee09f4 Mon Sep 17 00:00:00 2001 From: berryplus Date: Sun, 9 Sep 2018 16:51:21 +0900 Subject: [PATCH 05/14] =?UTF-8?q?=E5=8D=98=E4=BD=93=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=B3=E3=83=BC=E3=83=89=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unittests/test-is_mailaddress.cpp | 543 ++++++++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 tests/unittests/test-is_mailaddress.cpp diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp new file mode 100644 index 0000000000..82fd58b387 --- /dev/null +++ b/tests/unittests/test-is_mailaddress.cpp @@ -0,0 +1,543 @@ +#include + +#include + +#define NOMINMAX +#include +#include + +// 独自定義型 ACHAR を解決するために参照が必要 +#include "basis/primitive.h" + +// テスト対象関数があるヘッダファイル +#include "parse/CWordParse.h" + +// テスト対象関数(コピペで取込) +#pragma region ver20160427 + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 +*/ +BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +{ + int j; + int nDotCount; + int nBgn; + + + j = 0; + if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ){ + j++; + }else{ + return FALSE; + } + while( j < nBufLen - 2 && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'.') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ) + ){ + j++; + } + if( j == 0 || j >= nBufLen - 2 ){ + return FALSE; + } + if( L'@' != pszBuf[j] ){ + return FALSE; + } +// nAtPos = j; + j++; + nDotCount = 0; +// nAlphaCount = 0; + + + for (;;) { + nBgn = j; + while( j < nBufLen && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'_') + ) + ){ + j++; + } + if( 0 == j - nBgn ){ + return FALSE; + } + if( L'.' != pszBuf[j] ){ + if( 0 == nDotCount ){ + return FALSE; + }else{ + break; + } + }else{ + nDotCount++; + j++; + } + } + if( NULL != pnAddressLenfth ){ + *pnAddressLenfth = j; + } + return TRUE; +} + +#pragma endregion ver20160427 definition +#pragma region ver20180909 + +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + +// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept; + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 + @date 2018.09.09 RFC準拠 +*/ +BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +{ + // RFC5321による mailbox の最大文字数 + const ptrdiff_t MAX_MAILBOX = 255; //255オクテット + + // バカ避け + if (nBufLen < 1) return FALSE; + + // メールアドレスには必ず@が含まれる + const wchar_t* pszAtmark; + + // メールアドレス前半部分(@の手前)をチェックする + if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { + return FALSE; + } + assert(L'@' == *pszAtmark); + + // メールアドレスの終了位置を受け取るポインタを宣言する + const wchar_t* pszEndOfMailBox; + + // メールアドレス後半部分(@の後ろ)をチェックする + if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) + { + return FALSE; + } + + // 全体の長さが制限を超えていないかチェックする + if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) + { + return FALSE; // 文字数オーバー + } + + if (pnAddressLenfth != nullptr) + { + *pnAddressLenfth = pszEndOfMailBox - pszBuf; + } + return TRUE; +} + +/*! + * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する + * + * 高速化のため単純化した条件でチェックしている + * 参照する標準は RFC5321 + * @see http://srgia.com/docs/rfc5321j.html + */ +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept +{ + // RFC5321による local-part の最大文字数 + const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット + + // 関数仕様 + assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない + assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない + + // 出力値を初期化する + *ppszAtmark = nullptr; + + // 文字列が二重引用符で始まっているかチェックして結果を保存 + const bool quoted = (L'"' == *pszStart); + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszStart + (quoted ? 1 : 0); + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'@': + if (pszStart == pszScan) + { + return false; // local-partは1文字以上なのでNG + } + if (quoted) + { + return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG + } + *ppszAtmark = pszScan; + return true; // ここが正常終了 + case L'\\': // エスケープ記号 + if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) + { + return false; + } + pszScan++; // エスケープ記号の分1文字進める + break; + case L'"': // 二重引用符 + if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) + { + *ppszAtmark = &pszScan[1]; + return true; // ここは準正常終了。正常終了とはあえて区別しない。 + } + return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 + } + pszScan++; + if (MAX_LOCAL_PART < pszScan - pszStart) + { + return false; // 文字数オーバー + } + } + return false; +} + +/*! + * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する + */ +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept +{ + // ccTLDの最小文字数 + const ptrdiff_t MIN_TLD = 2; + + // ドメインの最小文字数 + const ptrdiff_t MIN_DOMAIN = 3; + + // ドメインの最大文字数 + const ptrdiff_t MAX_DOMAIN = 63; + + // 関数仕様 + assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない + assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない + + // 出力値を初期化する + *ppszEndOfMailBox = nullptr; + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszAtmark + 1; + + auto dotCount = 0; + auto domainLength = 0; + auto prevHyphen = false; + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'.': // ドット記号 + if (dotCount == 0 && domainLength < MIN_DOMAIN) + { + return false; // ドメイン名の最小文字数は3なのでNG + } + if (0 < dotCount && domainLength < MIN_TLD) + { + // これはco.jpなどを正しく認識させるために必要。 + return false; // ドットで区切られる部分の最小文字数は2なのでNG + } + if (prevHyphen) + { + return false; // ハイフンに続くドットはNG + } + dotCount++; + domainLength = 0; + prevHyphen = false; + break; + case L'-': // ハイフン記号 + if (domainLength == 0) + { + return false; // ドットに続くハイフンはNG + } + if (prevHyphen) + { + return false; // 連続するハイフンはNG + } + domainLength++; + prevHyphen = true; + break; + default: + if (dotCount == 0) + { + return false; // ドメイン部には一つ以上のドット記号が必要なのでNG + } + if (domainLength == 0) + { + return false; // ドットで終わるドメインはNG + } + if (prevHyphen) + { + return false; // ハイフンで終わるドメインはNG + } + *ppszEndOfMailBox = pszScan; + return true; // ここも正常終了 + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + case L'A': + case L'B': + case L'C': + case L'D': + case L'E': + case L'F': + case L'G': + case L'H': + case L'I': + case L'J': + case L'K': + case L'L': + case L'M': + case L'N': + case L'O': + case L'P': + case L'Q': + case L'R': + case L'S': + case L'T': + case L'U': + case L'V': + case L'W': + case L'X': + case L'Y': + case L'Z': + case L'a': + case L'b': + case L'c': + case L'd': + case L'e': + case L'f': + case L'g': + case L'h': + case L'i': + case L'j': + case L'k': + case L'l': + case L'm': + case L'n': + case L'o': + case L'p': + case L'q': + case L'r': + case L's': + case L't': + case L'u': + case L'v': + case L'w': + case L'x': + case L'y': + case L'z': + domainLength++; + prevHyphen = false; + break; + } + pszScan++; + if (pszScan == pszEnd) + { + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + } + if (MAX_DOMAIN < domainLength) + { + return false; // 文字数オーバー + } + } + return false; +} + +#pragma endregion ver20180909 definition + + +// テストマクロが他と被らないようにpushする +#pragma push_macro("TEST20180909") + +// このテストファイルローカルのテストモード切替フラグ +#if 0 +// 新旧動作比較用マクロ(TRUE, FALSE向け) +#define TEST20180909(pattern, buf, len, plen) \ + EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ + ASSERT_##pattern(IsMailAddress(buf, len, plen)); + +#else +// 新実装検証用マクロ(TRUE, FALSE向け) +#define TEST20180909(pattern, buf, len, plen) \ + ASSERT_##pattern(IsMailAddress(buf, len, plen)); +#endif + +TEST(testIsMailAddress, CheckBlank) +{ + wchar_t szTest[] = L""; //空文字 + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckExample) +{ + wchar_t szTest[] = L"test@example.com"; //標準的なサンプルメールアドレス + TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckExampleCoJp) +{ + wchar_t szTest[] = L"test@example.co.jp"; //標準的なサンプルメールアドレス + TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckTrailingSpace) +{ + wchar_t szTest[] = L"test@example.co.jp "; //標準的なサンプルメールアドレス + int mailboxLength; + TEST20180909(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_EQ(_countof(szTest) - 2, mailboxLength); +} + +TEST(testIsMailAddress, CheckPunctuation) +{ + wchar_t szTest[] = L"test!#$%&'*+-/=?^_`{|}~@example.com"; //記号類を含む + TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckMaxLocalPart) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szTest, _countof(szTest), L"%1$s%1$s%1$s%1$s@example.com", szSeed); //4個繋げて64文字にする + TEST20180909(TRUE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckExceedMaxLocalPart) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szTest, _countof(szTest), L"%1$s%1$s%1$s%1$s0@example.com", szSeed); //4個繋げて64文字 + 1 + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +TEST(testIsMailAddress, CheckMaxMailbox) +{ + wchar_t szTest[256]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$.63s.%1$.63s.%1$.58s.com", szSeed64); //最大255文字のチェック + int mailboxLength; + TEST20180909(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_EQ(255, mailboxLength); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckMaxExceedMailbox) +{ + wchar_t szTest[256 + 1]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$.63s.%1$.63s.%1$.58s0.com", szSeed64); //最大255文字オーバーのチェック + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooLongDomain) +{ + wchar_t szTest[256]; + wchar_t szSeed64[64 + 1]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$s.com", szSeed64); //63文字を超えるドメイン + TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooShortDomain) +{ + wchar_t szTest[] = L"yajim@my.me"; //ドメイン部は3文字以上 + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckTooShortCCTLD) +{ + wchar_t szTest[] = L"test@test.c.bak"; //CCTLD部は2文字以上 + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckDomainIncludesUnderScore) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test_domain.com", szSeed); //_を含むドメイン + TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); +} + +TEST(testIsMailAddress, CheckDomainIncludesSingleHyphen) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test-domain.com", szSeed); //途中に-を含むドメイン + TEST20180909(TRUE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckDomainIncludesDoubleHyphen) +{ + wchar_t szTest[256]; + wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 + ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test--domain.com", szSeed); //途中に-を含むドメイン + TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckQuotedLocalPart) +{ + wchar_t szTest[] = L"\"test\\@c\"@test.com"; + TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); +} + +// 動作変更あり。新実装では条件を厳しくして高速化している +TEST(testIsMailAddress, CheckBadQuotedLocalPart) +{ + wchar_t szTest[] = L"\"test@test.com"; + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + + +#pragma pop_macro("TEST20180909") + From ab699a7885fc00febcc0b70b8cf84d25dd794117 Mon Sep 17 00:00:00 2001 From: berryplus Date: Mon, 10 Sep 2018 04:33:34 +0900 Subject: [PATCH 06/14] =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=82=92util/string=5F?= =?UTF-8?q?ex=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 変更対象の関数を、ビルドできるファイルに移動し、コードを直接テストできるようにする --- sakura_core/parse/CWordParse.cpp | 412 ------------------------ sakura_core/parse/CWordParse.h | 4 - sakura_core/util/string_ex.cpp | 412 ++++++++++++++++++++++++ sakura_core/util/string_ex.h | 4 + sakura_core/view/CEditView.cpp | 1 + sakura_core/view/CEditView_Mouse.cpp | 1 + sakura_core/view/colors/CColor_Url.cpp | 1 + tests/unittests/test-is_mailaddress.cpp | 335 ++----------------- 8 files changed, 451 insertions(+), 719 deletions(-) diff --git a/sakura_core/parse/CWordParse.cpp b/sakura_core/parse/CWordParse.cpp index 8c76af7632..8ced37b366 100644 --- a/sakura_core/parse/CWordParse.cpp +++ b/sakura_core/parse/CWordParse.cpp @@ -317,415 +317,3 @@ bool CWordParse::SearchNextWordPosition4KW( } return false; } - - -//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 -uchar_t wc_to_c(wchar_t wc) -{ -#if 0 -//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 - char buf[3]={0,0,0}; - int ret=wctomb(buf,wc); - if(ret==-1)return 0; //エラー - if(buf[1]!=0)return 0; //エラー扱い - return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 -#endif - // 2011.12.15 wctombを使わない版 - if(wc <= 0x7F){ - return (uchar_t)wc; - } - return 0; -} - -//@@@ 2002.01.24 Start by MIK -/*! - 文字列がURLかどうかを検査する。 - - @retval TRUE URLである - @retval FALSE URLでない - - @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 - url_char の値は url_table の配列番号+1 になっています。 - 新しい URL を追加する場合は #define 値を修正してください。 - url_table は頭文字がアルファベット順になるように並べてください。 - - 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 -*/ -BOOL IsURL( - const wchar_t* pszLine, //!< [in] 文字列 - int nLineLen, //!< [in] 文字列の長さ - int* pnMatchLen //!< [out] URLの長さ -) -{ - struct _url_table_t { - wchar_t name[12]; - int length; - bool is_mail; - }; - static const struct _url_table_t url_table[] = { - /* アルファベット順 */ - { L"file://", 7, false }, /* 1 */ - { L"ftp://", 6, false }, /* 2 */ - { L"gopher://", 9, false }, /* 3 */ - { L"http://", 7, false }, /* 4 */ - { L"https://", 8, false }, /* 5 */ - { L"mailto:", 7, true }, /* 6 */ - { L"news:", 5, false }, /* 7 */ - { L"nntp://", 7, false }, /* 8 */ - { L"prospero://", 11, false }, /* 9 */ - { L"telnet://", 9, false }, /* 10 */ - { L"tp://", 5, false }, /* 11 */ //2004.02.02 - { L"ttp://", 6, false }, /* 12 */ //2004.02.02 - { L"wais://", 7, false }, /* 13 */ - { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ - }; - -/* テーブルの保守性を高めるための定義 */ - const char urF = 1; - const char urG = 3; - const char urH = 4; - const char urM = 6; - const char urN = 7; - const char urP = 9; - const char urT = 10; - const char urW = 13; //2004.02.02 - - static const char url_char[] = { - /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ - 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ - 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ - urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ - /* あと128バイト犠牲にすればif文を2箇所削除できる */ - /* 0 : not url char - * -1 : url char - * other: url head char --> url_table array number + 1 - */ - }; - - const wchar_t *p = pszLine; - const struct _url_table_t *urlp; - int i; - - if( wc_to_c(*p)==0 ) return FALSE; /* 2バイト文字 */ - if( 0 < url_char[wc_to_c(*p)] ){ /* URL開始文字 */ - for(urlp = &url_table[url_char[wc_to_c(*p)]-1]; urlp->name[0] == wc_to_c(*p); urlp++){ /* URLテーブルを探索 */ - if( (urlp->length <= nLineLen) && (auto_memcmp(urlp->name, pszLine, urlp->length) == 0) ){ /* URLヘッダは一致した */ - p += urlp->length; /* URLヘッダ分をスキップする */ - if( urlp->is_mail ){ /* メール専用の解析へ */ - if( IsMailAddress(p, nLineLen - urlp->length, pnMatchLen) ){ - *pnMatchLen = *pnMatchLen + urlp->length; - return TRUE; - } - return FALSE; - } - for(i = urlp->length; i < nLineLen; i++, p++){ /* 通常の解析へ */ - if( wc_to_c(*p)==0 || (!(url_char[wc_to_c(*p)])) ) break; /* 終端に達した */ - } - if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ - *pnMatchLen = i; - return TRUE; - } - } - } - return IsMailAddress(pszLine, nLineLen, pnMatchLen); -} - -// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept; - -// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept; - -// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept; - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 - @date 2018.09.09 RFC準拠 -*/ -BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) -{ - // RFC5321による mailbox の最大文字数 - const ptrdiff_t MAX_MAILBOX = 255; //255オクテット - - // バカ避け - if (nBufLen < 1) return FALSE; - - // メールアドレスには必ず@が含まれる - const wchar_t* pszAtmark; - - // メールアドレス前半部分(@の手前)をチェックする - if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { - return FALSE; - } - assert(L'@' == *pszAtmark); - - // メールアドレスの終了位置を受け取るポインタを宣言する - const wchar_t* pszEndOfMailBox; - - // メールアドレス後半部分(@の後ろ)をチェックする - if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) - { - return FALSE; - } - - // 全体の長さが制限を超えていないかチェックする - if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) - { - return FALSE; // 文字数オーバー - } - - if (pnAddressLenfth != nullptr) - { - *pnAddressLenfth = pszEndOfMailBox - pszBuf; - } - return TRUE; -} - -/*! - * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する - * - * 高速化のため単純化した条件でチェックしている - * 参照する標準は RFC5321 - * @see http://srgia.com/docs/rfc5321j.html - */ -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept -{ - // RFC5321による local-part の最大文字数 - const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット - - // 関数仕様 - assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない - assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない - - // 出力値を初期化する - *ppszAtmark = nullptr; - - // 文字列が二重引用符で始まっているかチェックして結果を保存 - const bool quoted = (L'"' == *pszStart); - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszStart + (quoted ? 1 : 0); - - // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) - { - switch (*pszScan) - { - case L'@': - if (pszStart == pszScan) - { - return false; // local-partは1文字以上なのでNG - } - if (quoted) - { - return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG - } - *ppszAtmark = pszScan; - return true; // ここが正常終了 - case L'\\': // エスケープ記号 - if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) - { - return false; - } - pszScan++; // エスケープ記号の分1文字進める - break; - case L'"': // 二重引用符 - if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) - { - *ppszAtmark = &pszScan[1]; - return true; // ここは準正常終了。正常終了とはあえて区別しない。 - } - return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 - } - pszScan++; - if (MAX_LOCAL_PART < pszScan - pszStart) - { - return false; // 文字数オーバー - } - } - return false; -} - -/*! - * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する - */ -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept -{ - // ccTLDの最小文字数 - const ptrdiff_t MIN_TLD = 2; - - // ドメインの最小文字数 - const ptrdiff_t MIN_DOMAIN = 3; - - // ドメインの最大文字数 - const ptrdiff_t MAX_DOMAIN = 63; - - // 関数仕様 - assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない - assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない - - // 出力値を初期化する - *ppszEndOfMailBox = nullptr; - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszAtmark + 1; - - auto dotCount = 0; - auto domainLength = 0; - auto prevHyphen = false; - - // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) - { - switch (*pszScan) - { - case L'.': // ドット記号 - if (dotCount == 0 && domainLength < MIN_DOMAIN) - { - return false; // ドメイン名の最小文字数は3なのでNG - } - if (0 < dotCount && domainLength < MIN_TLD) - { - // これはco.jpなどを正しく認識させるために必要。 - return false; // ドットで区切られる部分の最小文字数は2なのでNG - } - if (prevHyphen) - { - return false; // ハイフンに続くドットはNG - } - dotCount++; - domainLength = 0; - prevHyphen = false; - break; - case L'-': // ハイフン記号 - if (domainLength == 0) - { - return false; // ドットに続くハイフンはNG - } - if (prevHyphen) - { - return false; // 連続するハイフンはNG - } - domainLength++; - prevHyphen = true; - break; - default: - if (dotCount == 0) - { - return false; // ドメイン部には一つ以上のドット記号が必要なのでNG - } - if (domainLength == 0) - { - return false; // ドットで終わるドメインはNG - } - if (prevHyphen) - { - return false; // ハイフンで終わるドメインはNG - } - *ppszEndOfMailBox = pszScan; - return true; // ここも正常終了 - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - case L'A': - case L'B': - case L'C': - case L'D': - case L'E': - case L'F': - case L'G': - case L'H': - case L'I': - case L'J': - case L'K': - case L'L': - case L'M': - case L'N': - case L'O': - case L'P': - case L'Q': - case L'R': - case L'S': - case L'T': - case L'U': - case L'V': - case L'W': - case L'X': - case L'Y': - case L'Z': - case L'a': - case L'b': - case L'c': - case L'd': - case L'e': - case L'f': - case L'g': - case L'h': - case L'i': - case L'j': - case L'k': - case L'l': - case L'm': - case L'n': - case L'o': - case L'p': - case L'q': - case L'r': - case L's': - case L't': - case L'u': - case L'v': - case L'w': - case L'x': - case L'y': - case L'z': - domainLength++; - prevHyphen = false; - break; - } - pszScan++; - if (pszScan == pszEnd) - { - *ppszEndOfMailBox = pszScan; - return true; // ここが正常終了 - } - if (MAX_DOMAIN < domainLength) - { - return false; // 文字数オーバー - } - } - return false; -} diff --git a/sakura_core/parse/CWordParse.h b/sakura_core/parse/CWordParse.h index 0c152b2eba..6983472c9c 100644 --- a/sakura_core/parse/CWordParse.h +++ b/sakura_core/parse/CWordParse.h @@ -125,10 +125,6 @@ class CWordParse{ static bool _match_charlist( const WCHAR c, const WCHAR *pszList ); }; -BOOL IsURL( const wchar_t*, int, int* );/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ -BOOL IsMailAddress( const wchar_t*, int, int* ); /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ - - // ACHAR 版 inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList ) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index 637db506eb..fda2ca3be0 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -899,3 +899,415 @@ int skr_towlower( int c ) #endif return towlower( (wchar_t)c ); } + + +//! wcがasciiなら0-127のまま返す。それ以外は0を返す。 +uchar_t wc_to_c(wchar_t wc) +{ +#if 0 +//! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 + char buf[3]={0,0,0}; + int ret=wctomb(buf,wc); + if(ret==-1)return 0; //エラー + if(buf[1]!=0)return 0; //エラー扱い + return buf[0] <= 0x7F ? buf[0]: 0; //1バイトで表せたので、これを返す 2011.12.17 バッファオーバーランの修正 +#endif + // 2011.12.15 wctombを使わない版 + if(wc <= 0x7F){ + return (uchar_t)wc; + } + return 0; +} + +//@@@ 2002.01.24 Start by MIK +/*! + 文字列がURLかどうかを検査する。 + + @retval TRUE URLである + @retval FALSE URLでない + + @note 関数内に定義したテーブルは必ず static const 宣言にすること(性能に影響します)。 + url_char の値は url_table の配列番号+1 になっています。 + 新しい URL を追加する場合は #define 値を修正してください。 + url_table は頭文字がアルファベット順になるように並べてください。 + + 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 +*/ +BOOL IsURL( + const wchar_t* pszLine, //!< [in] 文字列 + int nLineLen, //!< [in] 文字列の長さ + int* pnMatchLen //!< [out] URLの長さ +) +{ + struct _url_table_t { + wchar_t name[12]; + int length; + bool is_mail; + }; + static const struct _url_table_t url_table[] = { + /* アルファベット順 */ + { L"file://", 7, false }, /* 1 */ + { L"ftp://", 6, false }, /* 2 */ + { L"gopher://", 9, false }, /* 3 */ + { L"http://", 7, false }, /* 4 */ + { L"https://", 8, false }, /* 5 */ + { L"mailto:", 7, true }, /* 6 */ + { L"news:", 5, false }, /* 7 */ + { L"nntp://", 7, false }, /* 8 */ + { L"prospero://", 11, false }, /* 9 */ + { L"telnet://", 9, false }, /* 10 */ + { L"tp://", 5, false }, /* 11 */ //2004.02.02 + { L"ttp://", 6, false }, /* 12 */ //2004.02.02 + { L"wais://", 7, false }, /* 13 */ + { L"{", 0, false } /* 14 */ /* '{' is 'z'+1 : terminate */ + }; + +/* テーブルの保守性を高めるための定義 */ + const char urF = 1; + const char urG = 3; + const char urH = 4; + const char urM = 6; + const char urN = 7; + const char urP = 9; + const char urT = 10; + const char urW = 13; //2004.02.02 + + static const char url_char[] = { + /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +00: */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* +10: */ + 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, /* +20: " !"#$%&'()*+,-./" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, -1, /* +30: "0123456789:;<=>?" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* +40: "@ABCDEFGHIJKLMNO" */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, 0, 0, -1, /* +50: "PQRSTUVWXYZ[\]^_" */ + 0, -1, -1, -1, -1, -1,urF,urG,urH, -1, -1, -1, -1,urM,urN, -1, /* +60: "`abcdefghijklmno" */ + urP, -1, -1, -1,urT, -1, -1,urW, -1, -1, -1, 0, 0, 0, -1, 0, /* +70: "pqrstuvwxyz{|}~ " */ + /* あと128バイト犠牲にすればif文を2箇所削除できる */ + /* 0 : not url char + * -1 : url char + * other: url head char --> url_table array number + 1 + */ + }; + + const wchar_t *p = pszLine; + const struct _url_table_t *urlp; + int i; + + if( wc_to_c(*p)==0 ) return FALSE; /* 2バイト文字 */ + if( 0 < url_char[wc_to_c(*p)] ){ /* URL開始文字 */ + for(urlp = &url_table[url_char[wc_to_c(*p)]-1]; urlp->name[0] == wc_to_c(*p); urlp++){ /* URLテーブルを探索 */ + if( (urlp->length <= nLineLen) && (auto_memcmp(urlp->name, pszLine, urlp->length) == 0) ){ /* URLヘッダは一致した */ + p += urlp->length; /* URLヘッダ分をスキップする */ + if( urlp->is_mail ){ /* メール専用の解析へ */ + if( IsMailAddress(p, nLineLen - urlp->length, pnMatchLen) ){ + *pnMatchLen = *pnMatchLen + urlp->length; + return TRUE; + } + return FALSE; + } + for(i = urlp->length; i < nLineLen; i++, p++){ /* 通常の解析へ */ + if( wc_to_c(*p)==0 || (!(url_char[wc_to_c(*p)])) ) break; /* 終端に達した */ + } + if( i == urlp->length ) return FALSE; /* URLヘッダだけ */ + *pnMatchLen = i; + return TRUE; + } + } + } + return IsMailAddress(pszLine, nLineLen, pnMatchLen); +} + +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + +// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept; + +// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept; + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 + @date 2018.09.09 RFC準拠 +*/ +BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +{ + // RFC5321による mailbox の最大文字数 + const ptrdiff_t MAX_MAILBOX = 255; //255オクテット + + // バカ避け + if (nBufLen < 1) return FALSE; + + // メールアドレスには必ず@が含まれる + const wchar_t* pszAtmark; + + // メールアドレス前半部分(@の手前)をチェックする + if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { + return FALSE; + } + assert(L'@' == *pszAtmark); + + // メールアドレスの終了位置を受け取るポインタを宣言する + const wchar_t* pszEndOfMailBox; + + // メールアドレス後半部分(@の後ろ)をチェックする + if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) + { + return FALSE; + } + + // 全体の長さが制限を超えていないかチェックする + if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) + { + return FALSE; // 文字数オーバー + } + + if (pnAddressLenfth != nullptr) + { + *pnAddressLenfth = pszEndOfMailBox - pszBuf; + } + return TRUE; +} + +/*! + * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する + * + * 高速化のため単純化した条件でチェックしている + * 参照する標準は RFC5321 + * @see http://srgia.com/docs/rfc5321j.html + */ +inline static bool IsMailAddressLocalPart( + _In_z_ const wchar_t* pszStart, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszAtmark +) noexcept +{ + // RFC5321による local-part の最大文字数 + const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット + + // 関数仕様 + assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない + assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない + + // 出力値を初期化する + *ppszAtmark = nullptr; + + // 文字列が二重引用符で始まっているかチェックして結果を保存 + const bool quoted = (L'"' == *pszStart); + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszStart + (quoted ? 1 : 0); + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'@': + if (pszStart == pszScan) + { + return false; // local-partは1文字以上なのでNG + } + if (quoted) + { + return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG + } + *ppszAtmark = pszScan; + return true; // ここが正常終了 + case L'\\': // エスケープ記号 + if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) + { + return false; + } + pszScan++; // エスケープ記号の分1文字進める + break; + case L'"': // 二重引用符 + if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) + { + *ppszAtmark = &pszScan[1]; + return true; // ここは準正常終了。正常終了とはあえて区別しない。 + } + return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 + } + pszScan++; + if (MAX_LOCAL_PART < pszScan - pszStart) + { + return false; // 文字数オーバー + } + } + return false; +} + +/*! + * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する + */ +inline static bool IsMailAddressDomain( + _In_z_ const wchar_t* pszAtmark, + _In_ const wchar_t* pszEnd, + _Out_ const wchar_t** ppszEndOfMailBox +) noexcept +{ + // ccTLDの最小文字数 + const ptrdiff_t MIN_TLD = 2; + + // ドメインの最小文字数 + const ptrdiff_t MIN_DOMAIN = 3; + + // ドメインの最大文字数 + const ptrdiff_t MAX_DOMAIN = 63; + + // 関数仕様 + assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない + assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない + + // 出力値を初期化する + *ppszEndOfMailBox = nullptr; + + // ループ中にスキャンする文字位置を設定する + auto pszScan = pszAtmark + 1; + + auto dotCount = 0; + auto domainLength = 0; + auto prevHyphen = false; + + // スキャン位置が終端に達するまでループ + while (pszScan < pszEnd) + { + switch (*pszScan) + { + case L'.': // ドット記号 + if (dotCount == 0 && domainLength < MIN_DOMAIN) + { + return false; // ドメイン名の最小文字数は3なのでNG + } + if (0 < dotCount && domainLength < MIN_TLD) + { + // これはco.jpなどを正しく認識させるために必要。 + return false; // ドットで区切られる部分の最小文字数は2なのでNG + } + if (prevHyphen) + { + return false; // ハイフンに続くドットはNG + } + dotCount++; + domainLength = 0; + prevHyphen = false; + break; + case L'-': // ハイフン記号 + if (domainLength == 0) + { + return false; // ドットに続くハイフンはNG + } + if (prevHyphen) + { + return false; // 連続するハイフンはNG + } + domainLength++; + prevHyphen = true; + break; + default: + if (dotCount == 0) + { + return false; // ドメイン部には一つ以上のドット記号が必要なのでNG + } + if (domainLength == 0) + { + return false; // ドットで終わるドメインはNG + } + if (prevHyphen) + { + return false; // ハイフンで終わるドメインはNG + } + *ppszEndOfMailBox = pszScan; + return true; // ここも正常終了 + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + case L'A': + case L'B': + case L'C': + case L'D': + case L'E': + case L'F': + case L'G': + case L'H': + case L'I': + case L'J': + case L'K': + case L'L': + case L'M': + case L'N': + case L'O': + case L'P': + case L'Q': + case L'R': + case L'S': + case L'T': + case L'U': + case L'V': + case L'W': + case L'X': + case L'Y': + case L'Z': + case L'a': + case L'b': + case L'c': + case L'd': + case L'e': + case L'f': + case L'g': + case L'h': + case L'i': + case L'j': + case L'k': + case L'l': + case L'm': + case L'n': + case L'o': + case L'p': + case L'q': + case L'r': + case L's': + case L't': + case L'u': + case L'v': + case L'w': + case L'x': + case L'y': + case L'z': + domainLength++; + prevHyphen = false; + break; + } + pszScan++; + if (pszScan == pszEnd) + { + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + } + if (MAX_DOMAIN < domainLength) + { + return false; // 文字数オーバー + } + } + return false; +} diff --git a/sakura_core/util/string_ex.h b/sakura_core/util/string_ex.h index 4fe4e485a6..05f19e9f79 100644 --- a/sakura_core/util/string_ex.h +++ b/sakura_core/util/string_ex.h @@ -337,5 +337,9 @@ inline int wcsncmp_auto(const wchar_t* strData1, const wchar_t* szData2) #define _tcsncmp_literal strncmp_literal #endif +BOOL IsURL( const wchar_t*, int, int* );/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ +BOOL IsMailAddress( const wchar_t*, int, int* ); /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ + + #endif /* SAKURA_STRING_EX_29EB1DD7_7259_4D6C_A651_B9174E5C3D3C9_H_ */ /*[EOF]*/ diff --git a/sakura_core/view/CEditView.cpp b/sakura_core/view/CEditView.cpp index 4e632388b7..061fb13ce9 100644 --- a/sakura_core/view/CEditView.cpp +++ b/sakura_core/view/CEditView.cpp @@ -56,6 +56,7 @@ #include "types/CTypeSupport.h" #include "convert/CConvert.h" #include "util/RegKey.h" +#include "util/string_ex.h" // IsURL #include "util/string_ex2.h" #include "util/os.h" //WM_MOUSEWHEEL,IMR_RECONVERTSTRING,WM_XBUTTON*,IMR_CONFIRMRECONVERTSTRING #include "util/module.h" diff --git a/sakura_core/view/CEditView_Mouse.cpp b/sakura_core/view/CEditView_Mouse.cpp index a25487a84f..4d54d2ca22 100644 --- a/sakura_core/view/CEditView_Mouse.cpp +++ b/sakura_core/view/CEditView_Mouse.cpp @@ -36,6 +36,7 @@ #include "uiparts/CWaitCursor.h" #include "uiparts/HandCursor.h" #include "util/input.h" +#include "util/string_ex.h" // IsMailAddress #include "util/os.h" #include "sakura_rc.h" diff --git a/sakura_core/view/colors/CColor_Url.cpp b/sakura_core/view/colors/CColor_Url.cpp index 5c31ba2f50..8de9c78660 100644 --- a/sakura_core/view/colors/CColor_Url.cpp +++ b/sakura_core/view/colors/CColor_Url.cpp @@ -5,6 +5,7 @@ #include "doc/CEditDoc.h" #include "doc/layout/CLayout.h" #include "types/CTypeSupport.h" +#include "util/string_ex.h" // IsMailAddress // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 82fd58b387..93fa381eee 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -6,13 +6,43 @@ #include #include +// リンク依存関係を取り込まないためにダミーのマクロ定義をしておく +//#include "debug/Debug2.cpp" +#define debug_output(str, ...) +#define debug_exit2(file, line, exp) + + +// 独自シンボル USE_STRICT_INT を解決するために参照が必要 +#include "config/build_config.h" + // 独自定義型 ACHAR を解決するために参照が必要 #include "basis/primitive.h" -// テスト対象関数があるヘッダファイル +// 独自定義型 CLogicInt を解決するために参照が必要 +// これを先にインクルードしておく必要がある +#include "basis/SakuraBasis.h" + +// テスト対象関数が元々あったヘッダファイル #include "parse/CWordParse.h" -// テスト対象関数(コピペで取込) +// テスト対象関数があるcppファイルを埋め込みで取り込む +// 他ファイルで同じファイルを取り込んではいけない +#include "util/string_ex.cpp" + +// テストマクロが他と被らないようにpushする +#pragma push_macro("TEST20180909") + +// このテストファイルローカルのテストモード切替フラグ +// 0 で新旧比較モード +// 1 で新の検証モード +#if 0 +// 新旧動作比較用マクロ(TRUE, FALSE向け) +#define TEST20180909(pattern, buf, len, plen) \ + EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ + ASSERT_##pattern(IsMailAddress(buf, len, plen)); + + +// テスト対象の旧関数(コピペで埋め込み) #pragma region ver20160427 /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す @@ -92,308 +122,7 @@ BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressL } #pragma endregion ver20160427 definition -#pragma region ver20180909 - -// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept; - -// 指定された文字列がメールアドレス後半部分の要件を満たすか判定する -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept; - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 - @date 2018.09.09 RFC準拠 -*/ -BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) -{ - // RFC5321による mailbox の最大文字数 - const ptrdiff_t MAX_MAILBOX = 255; //255オクテット - - // バカ避け - if (nBufLen < 1) return FALSE; - - // メールアドレスには必ず@が含まれる - const wchar_t* pszAtmark; - - // メールアドレス前半部分(@の手前)をチェックする - if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { - return FALSE; - } - assert(L'@' == *pszAtmark); - - // メールアドレスの終了位置を受け取るポインタを宣言する - const wchar_t* pszEndOfMailBox; - - // メールアドレス後半部分(@の後ろ)をチェックする - if (!IsMailAddressDomain(pszAtmark, pszBuf + nBufLen, &pszEndOfMailBox)) - { - return FALSE; - } - - // 全体の長さが制限を超えていないかチェックする - if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) - { - return FALSE; // 文字数オーバー - } - - if (pnAddressLenfth != nullptr) - { - *pnAddressLenfth = pszEndOfMailBox - pszBuf; - } - return TRUE; -} -/*! - * 指定された文字列がメールアドレス前半部分の要件を満たすか判定する - * - * 高速化のため単純化した条件でチェックしている - * 参照する標準は RFC5321 - * @see http://srgia.com/docs/rfc5321j.html - */ -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept -{ - // RFC5321による local-part の最大文字数 - const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット - - // 関数仕様 - assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない - assert(pszStart < pszEnd); // 開始位置と終了位置は逆転してはならない - - // 出力値を初期化する - *ppszAtmark = nullptr; - - // 文字列が二重引用符で始まっているかチェックして結果を保存 - const bool quoted = (L'"' == *pszStart); - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszStart + (quoted ? 1 : 0); - - // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) - { - switch (*pszScan) - { - case L'@': - if (pszStart == pszScan) - { - return false; // local-partは1文字以上なのでNG - } - if (quoted) - { - return false; // 二重引用符で始まる場合、終端にも二重引用符が必要なのでNG - } - *ppszAtmark = pszScan; - return true; // ここが正常終了 - case L'\\': // エスケープ記号 - if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) - { - return false; - } - pszScan++; // エスケープ記号の分1文字進める - break; - case L'"': // 二重引用符 - if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) - { - *ppszAtmark = &pszScan[1]; - return true; // ここは準正常終了。正常終了とはあえて区別しない。 - } - return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 - } - pszScan++; - if (MAX_LOCAL_PART < pszScan - pszStart) - { - return false; // 文字数オーバー - } - } - return false; -} - -/*! - * 指定された文字列がメールアドレス後半部分の要件を満たすか判定する - */ -inline static bool IsMailAddressDomain( - _In_z_ const wchar_t* pszAtmark, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszEndOfMailBox -) noexcept -{ - // ccTLDの最小文字数 - const ptrdiff_t MIN_TLD = 2; - - // ドメインの最小文字数 - const ptrdiff_t MIN_DOMAIN = 3; - - // ドメインの最大文字数 - const ptrdiff_t MAX_DOMAIN = 63; - - // 関数仕様 - assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない - assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない - - // 出力値を初期化する - *ppszEndOfMailBox = nullptr; - - // ループ中にスキャンする文字位置を設定する - auto pszScan = pszAtmark + 1; - - auto dotCount = 0; - auto domainLength = 0; - auto prevHyphen = false; - - // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) - { - switch (*pszScan) - { - case L'.': // ドット記号 - if (dotCount == 0 && domainLength < MIN_DOMAIN) - { - return false; // ドメイン名の最小文字数は3なのでNG - } - if (0 < dotCount && domainLength < MIN_TLD) - { - // これはco.jpなどを正しく認識させるために必要。 - return false; // ドットで区切られる部分の最小文字数は2なのでNG - } - if (prevHyphen) - { - return false; // ハイフンに続くドットはNG - } - dotCount++; - domainLength = 0; - prevHyphen = false; - break; - case L'-': // ハイフン記号 - if (domainLength == 0) - { - return false; // ドットに続くハイフンはNG - } - if (prevHyphen) - { - return false; // 連続するハイフンはNG - } - domainLength++; - prevHyphen = true; - break; - default: - if (dotCount == 0) - { - return false; // ドメイン部には一つ以上のドット記号が必要なのでNG - } - if (domainLength == 0) - { - return false; // ドットで終わるドメインはNG - } - if (prevHyphen) - { - return false; // ハイフンで終わるドメインはNG - } - *ppszEndOfMailBox = pszScan; - return true; // ここも正常終了 - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'8': - case L'9': - case L'A': - case L'B': - case L'C': - case L'D': - case L'E': - case L'F': - case L'G': - case L'H': - case L'I': - case L'J': - case L'K': - case L'L': - case L'M': - case L'N': - case L'O': - case L'P': - case L'Q': - case L'R': - case L'S': - case L'T': - case L'U': - case L'V': - case L'W': - case L'X': - case L'Y': - case L'Z': - case L'a': - case L'b': - case L'c': - case L'd': - case L'e': - case L'f': - case L'g': - case L'h': - case L'i': - case L'j': - case L'k': - case L'l': - case L'm': - case L'n': - case L'o': - case L'p': - case L'q': - case L'r': - case L's': - case L't': - case L'u': - case L'v': - case L'w': - case L'x': - case L'y': - case L'z': - domainLength++; - prevHyphen = false; - break; - } - pszScan++; - if (pszScan == pszEnd) - { - *ppszEndOfMailBox = pszScan; - return true; // ここが正常終了 - } - if (MAX_DOMAIN < domainLength) - { - return false; // 文字数オーバー - } - } - return false; -} - -#pragma endregion ver20180909 definition - - -// テストマクロが他と被らないようにpushする -#pragma push_macro("TEST20180909") - -// このテストファイルローカルのテストモード切替フラグ -#if 0 -// 新旧動作比較用マクロ(TRUE, FALSE向け) -#define TEST20180909(pattern, buf, len, plen) \ - EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ - ASSERT_##pattern(IsMailAddress(buf, len, plen)); #else // 新実装検証用マクロ(TRUE, FALSE向け) From ad186ce995fd61b1f8949cfd7402bc10917f3832 Mon Sep 17 00:00:00 2001 From: berryplus Date: Mon, 10 Sep 2018 05:30:01 +0900 Subject: [PATCH 07/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ローカルでしか使わない関数をstatic化 const → constexpr TODO: コメント追記 --- sakura_core/util/string_ex.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index fda2ca3be0..3994cb6806 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -902,7 +902,7 @@ int skr_towlower( int c ) //! wcがasciiなら0-127のまま返す。それ以外は0を返す。 -uchar_t wc_to_c(wchar_t wc) +inline static uchar_t wc_to_c(wchar_t wc) { #if 0 //! wcがSJIS1バイト文字ならcharに変換して0~255を返す。SJIS2バイト文字なら0を返す。 @@ -919,7 +919,6 @@ uchar_t wc_to_c(wchar_t wc) return 0; } -//@@@ 2002.01.24 Start by MIK /*! 文字列がURLかどうかを検査する。 @@ -931,6 +930,7 @@ uchar_t wc_to_c(wchar_t wc) 新しい URL を追加する場合は #define 値を修正してください。 url_table は頭文字がアルファベット順になるように並べてください。 + 2002.01.24 MIK 2007.10.23 kobake UNICODE対応。//$ wchar_t専用のテーブル(または判定ルーチン)を用意したほうが効率は上がるはずです。 */ BOOL IsURL( @@ -939,6 +939,7 @@ BOOL IsURL( int* pnMatchLen //!< [out] URLの長さ ) { + // TODO: この関数は、UNCアドレスも含めて見直した方がよさげ by berryzplus struct _url_table_t { wchar_t name[12]; int length; @@ -963,14 +964,14 @@ BOOL IsURL( }; /* テーブルの保守性を高めるための定義 */ - const char urF = 1; - const char urG = 3; - const char urH = 4; - const char urM = 6; - const char urN = 7; - const char urP = 9; - const char urT = 10; - const char urW = 13; //2004.02.02 + constexpr char urF = 1; + constexpr char urG = 3; + constexpr char urH = 4; + constexpr char urM = 6; + constexpr char urN = 7; + constexpr char urP = 9; + constexpr char urT = 10; + constexpr char urW = 13; //2004.02.02 static const char url_char[] = { /* +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F */ @@ -1014,7 +1015,7 @@ BOOL IsURL( } } } - return IsMailAddress(pszLine, nLineLen, pnMatchLen); + return IsMailAddress(pszLine, nLineLen, pnMatchLen); // TODO: FALSE じゃなくて? } // 指定された文字列がメールアドレス前半部分の要件を満たすか判定する From 7c0fce002c2007b2594aca66ee45a89777d56abe Mon Sep 17 00:00:00 2001 From: berryplus Date: Mon, 10 Sep 2018 06:33:00 +0900 Subject: [PATCH 08/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ループ外で判定できる判定条件は先に判定しておく ヘッダにある公開関数に仮引数を書いておく 変数名のtypo修正 --- sakura_core/util/string_ex.cpp | 29 +++++++++++++---------------- sakura_core/util/string_ex.h | 7 +++++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index 3994cb6806..a413cb842e 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -1043,7 +1043,7 @@ inline static bool IsMailAddressDomain( @date 2016.04.27 記号類を許可 @date 2018.09.09 RFC準拠 */ -BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) { // RFC5321による mailbox の最大文字数 const ptrdiff_t MAX_MAILBOX = 255; //255オクテット @@ -1075,9 +1075,9 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) return FALSE; // 文字数オーバー } - if (pnAddressLenfth != nullptr) + if (pnAddressLength != nullptr) { - *pnAddressLenfth = pszEndOfMailBox - pszBuf; + *pnAddressLength = pszEndOfMailBox - pszBuf; } return TRUE; } @@ -1110,9 +1110,10 @@ inline static bool IsMailAddressLocalPart( // ループ中にスキャンする文字位置を設定する auto pszScan = pszStart + (quoted ? 1 : 0); + auto pszScanEnd = std::min(pszStart + MAX_LOCAL_PART + 1, pszEnd); // スキャン位置が終端に達するまでループ - while (pszScan < pszEnd) + while (pszScan < pszScanEnd) { switch (*pszScan) { @@ -1128,14 +1129,14 @@ inline static bool IsMailAddressLocalPart( *ppszAtmark = pszScan; return true; // ここが正常終了 case L'\\': // エスケープ記号 - if (pszScan + 1 == pszEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) + if (pszScan + 1 == pszScanEnd || pszScan[1] < L'\x20' || L'\x7E' < pszScan[1]) { return false; } pszScan++; // エスケープ記号の分1文字進める break; case L'"': // 二重引用符 - if (quoted && pszScan + 1 < pszEnd && L'@' == pszScan[1]) + if (quoted && pszScan + 1 < pszScanEnd && L'@' == pszScan[1]) { *ppszAtmark = &pszScan[1]; return true; // ここは準正常終了。正常終了とはあえて区別しない。 @@ -1143,12 +1144,8 @@ inline static bool IsMailAddressLocalPart( return false; // 末尾以外に現れるエスケープされてない二重引用符は不正 } pszScan++; - if (MAX_LOCAL_PART < pszScan - pszStart) - { - return false; // 文字数オーバー - } } - return false; + return false; // 文字数オーバー } /*! @@ -1300,15 +1297,15 @@ inline static bool IsMailAddressDomain( break; } pszScan++; - if (pszScan == pszEnd) - { - *ppszEndOfMailBox = pszScan; - return true; // ここが正常終了 - } if (MAX_DOMAIN < domainLength) { return false; // 文字数オーバー } } + if (pszScan == pszEnd) + { + *ppszEndOfMailBox = pszScan; + return true; // ここが正常終了 + } return false; } diff --git a/sakura_core/util/string_ex.h b/sakura_core/util/string_ex.h index 05f19e9f79..bc646340e6 100644 --- a/sakura_core/util/string_ex.h +++ b/sakura_core/util/string_ex.h @@ -337,8 +337,11 @@ inline int wcsncmp_auto(const wchar_t* strData1, const wchar_t* szData2) #define _tcsncmp_literal strncmp_literal #endif -BOOL IsURL( const wchar_t*, int, int* );/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ -BOOL IsMailAddress( const wchar_t*, int, int* ); /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ +/* 指定アドレスがURLの先頭ならばTRUEとその長さを返す */ +BOOL IsURL(const wchar_t* pszLine, int nLineLen, int* pnMatchLen); + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す */ +BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLength); #endif /* SAKURA_STRING_EX_29EB1DD7_7259_4D6C_A651_B9174E5C3D3C9_H_ */ From f08cbe1efc1921d8811741e4d3814a4365b0f28d Mon Sep 17 00:00:00 2001 From: berryplus Date: Wed, 12 Sep 2018 02:29:29 +0900 Subject: [PATCH 09/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "a@"を正しく判定できないバグの修正 テストファイル構成変更(テスト対象とテストコードを分ける) テストコード中のモード切替をシンボルの「定義/未定義」で行うように修正 --- sakura_core/util/string_ex.cpp | 6 +- tests/unittests/CMakeLists.txt | 17 ++- .../unittests/code-IsMailAddress_20160427.cpp | 78 ++++++++++ tests/unittests/code-util_string_ex.cpp | 28 ++++ tests/unittests/test-is_mailaddress.cpp | 142 ++++-------------- 5 files changed, 147 insertions(+), 124 deletions(-) create mode 100644 tests/unittests/code-IsMailAddress_20160427.cpp create mode 100644 tests/unittests/code-util_string_ex.cpp diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index a413cb842e..e885e3485b 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -1167,7 +1167,7 @@ inline static bool IsMailAddressDomain( const ptrdiff_t MAX_DOMAIN = 63; // 関数仕様 - assert(pszAtmark + 1 < pszEnd); // @位置と終了位置は逆転してはならない + assert(pszAtmark < pszEnd); // @位置と終了位置は逆転してはならない assert(L'@' == *pszAtmark); // @位置にある文字は@でなければならない // 出力値を初期化する @@ -1175,6 +1175,10 @@ inline static bool IsMailAddressDomain( // ループ中にスキャンする文字位置を設定する auto pszScan = pszAtmark + 1; + if (pszScan == pszEnd) + { + return false; // @の後ろが0文字、長さが足りない + } auto dotCount = 0; auto domainLength = 0; diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index 8d7a71dff2..5e664c3a24 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -5,16 +5,17 @@ include(${CMAKE_SOURCE_DIR}/runtime.cmake) set(project_name tests1) # define a variable SRC with file GLOB -file(GLOB SRC ${CMAKE_CURRENT_LIST_DIR}/test*.cpp) +file(GLOB TEST_SRC ${CMAKE_CURRENT_LIST_DIR}/test*.cpp) +file(GLOB TARGET_SRC ${CMAKE_CURRENT_LIST_DIR}/code*.cpp) -# add include directories -include_directories(${googletest_SOURCE_DIR}/include) +# define sources files of an executable +add_executable(${project_name} ${TEST_SRC} ${TARGET_SRC}) -# add include directories for sakura_core -include_directories(${CMAKE_SOURCE_DIR}/../sakura_core) +# add definitions +target_compile_definitions(${project_name} PUBLIC _CONSOLE UNICODE _UNICODE) -# define sources files of an executable -add_executable(${project_name} ${SRC}) +# add include directories for sakura_core +target_include_directories(${project_name} PRIVATE ${CMAKE_SOURCE_DIR}/../sakura_core) # use shared library version # this is required when using parameterized test @@ -30,4 +31,4 @@ endif (BUILD_SHARED_LIBS) # link libraries target_link_libraries(${project_name} gtest) -target_link_libraries(${project_name} gtest_main) +target_link_libraries(${project_name} gtest_main) \ No newline at end of file diff --git a/tests/unittests/code-IsMailAddress_20160427.cpp b/tests/unittests/code-IsMailAddress_20160427.cpp new file mode 100644 index 0000000000..59efa7ebe5 --- /dev/null +++ b/tests/unittests/code-IsMailAddress_20160427.cpp @@ -0,0 +1,78 @@ +// テスト対象の旧関数(コピペで埋め込み) +#include "StdAfx.h" + +/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す + @date 2016.04.27 記号類を許可 +*/ +BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) +{ + int j; + int nDotCount; + int nBgn; + + + j = 0; + if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ){ + j++; + }else{ + return FALSE; + } + while( j < nBufLen - 2 && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'.') + || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) + ) + ){ + j++; + } + if( j == 0 || j >= nBufLen - 2 ){ + return FALSE; + } + if( L'@' != pszBuf[j] ){ + return FALSE; + } +// nAtPos = j; + j++; + nDotCount = 0; +// nAlphaCount = 0; + + + for (;;) { + nBgn = j; + while( j < nBufLen && + ( + (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') + || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') + || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') + || (pszBuf[j] == L'-') + || (pszBuf[j] == L'_') + ) + ){ + j++; + } + if( 0 == j - nBgn ){ + return FALSE; + } + if( L'.' != pszBuf[j] ){ + if( 0 == nDotCount ){ + return FALSE; + }else{ + break; + } + }else{ + nDotCount++; + j++; + } + } + if( NULL != pnAddressLenfth ){ + *pnAddressLenfth = j; + } + return TRUE; +} diff --git a/tests/unittests/code-util_string_ex.cpp b/tests/unittests/code-util_string_ex.cpp new file mode 100644 index 0000000000..7d9744ff7c --- /dev/null +++ b/tests/unittests/code-util_string_ex.cpp @@ -0,0 +1,28 @@ +// code-util_string_ex.cpp +// util/string_ex.cpp 取込用スタブファイル + +// リンク依存関係を取り込まないためにダミーのマクロ定義をしておく +//#include "debug/Debug2.cpp" +#define debug_output(str, ...) +#define debug_exit2(file, line, exp) + + +// sakura_core/StdAfx.h にある前提インクルードを取り込む +#define NOMINMAX +#include +#include + +// 独自シンボル USE_STRICT_INT を解決するために参照が必要 +#include "config/build_config.h" + +// 独自定義型 ACHAR を解決するために参照が必要 +#include "basis/primitive.h" + +// 独自定義型 CLogicInt を解決するために参照が必要 +// util/string_ex.hを読み込む前に、これをインクルードしておく必要がある +#include "basis/SakuraBasis.h" + + +// cppファイルを埋め込みで取り込む +// 他ファイルで同じファイルを取り込んではいけない +#include "util/string_ex.cpp" diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 93fa381eee..49dcba07d9 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -1,135 +1,40 @@ #include -#include - #define NOMINMAX #include #include -// リンク依存関係を取り込まないためにダミーのマクロ定義をしておく -//#include "debug/Debug2.cpp" -#define debug_output(str, ...) -#define debug_exit2(file, line, exp) - - -// 独自シンボル USE_STRICT_INT を解決するために参照が必要 -#include "config/build_config.h" +// テスト対象関数のヘッダファイル +//#include "util/string_ex.h" //依存関係が多いのでテスト対象の関数定義のみ抜き出し +BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); -// 独自定義型 ACHAR を解決するために参照が必要 -#include "basis/primitive.h" -// 独自定義型 CLogicInt を解決するために参照が必要 -// これを先にインクルードしておく必要がある -#include "basis/SakuraBasis.h" +// 変更前実装の定義 +// ※関数定義は IsMailAddress_20160427.cpp を参照。 +BOOL IsMailAddress_20160427(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); -// テスト対象関数が元々あったヘッダファイル -#include "parse/CWordParse.h" - -// テスト対象関数があるcppファイルを埋め込みで取り込む -// 他ファイルで同じファイルを取り込んではいけない -#include "util/string_ex.cpp" // テストマクロが他と被らないようにpushする #pragma push_macro("TEST20180909") -// このテストファイルローカルのテストモード切替フラグ -// 0 で新旧比較モード -// 1 で新の検証モード -#if 0 -// 新旧動作比較用マクロ(TRUE, FALSE向け) -#define TEST20180909(pattern, buf, len, plen) \ - EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ - ASSERT_##pattern(IsMailAddress(buf, len, plen)); - - -// テスト対象の旧関数(コピペで埋め込み) -#pragma region ver20160427 - -/* 現在位置がメールアドレスならば、NULL以外と、その長さを返す - @date 2016.04.27 記号類を許可 -*/ -BOOL IsMailAddress_20160427( const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth ) -{ - int j; - int nDotCount; - int nBgn; - - - j = 0; - if( (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - ){ - j++; - }else{ - return FALSE; - } - while( j < nBufLen - 2 && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'.') - || NULL != wcschr(L"!#$%&'*+-/=?^_`{|}~", pszBuf[j]) - ) - ){ - j++; - } - if( j == 0 || j >= nBufLen - 2 ){ - return FALSE; - } - if( L'@' != pszBuf[j] ){ - return FALSE; - } -// nAtPos = j; - j++; - nDotCount = 0; -// nAlphaCount = 0; - - - for (;;) { - nBgn = j; - while( j < nBufLen && - ( - (pszBuf[j] >= L'a' && pszBuf[j] <= L'z') - || (pszBuf[j] >= L'A' && pszBuf[j] <= L'Z') - || (pszBuf[j] >= L'0' && pszBuf[j] <= L'9') - || (pszBuf[j] == L'-') - || (pszBuf[j] == L'_') - ) - ){ - j++; - } - if( 0 == j - nBgn ){ - return FALSE; - } - if( L'.' != pszBuf[j] ){ - if( 0 == nDotCount ){ - return FALSE; - }else{ - break; - } - }else{ - nDotCount++; - j++; - } - } - if( NULL != pnAddressLenfth ){ - *pnAddressLenfth = j; - } - return TRUE; -} - -#pragma endregion ver20160427 definition - +// ローカルテストモードの定義 +// ↓をコメントインすると現新比較テストを実行できます。 +//#define REGRESSION_TEST +// モードによってテストマクロの定義内容を変える +#ifdef REGRESSION_TEST + // 新旧動作比較用マクロ(TRUE, FALSE向け) + #define TEST20180909(pattern, buf, len, plen) \ + EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ + ASSERT_##pattern(IsMailAddress(buf, len, plen)); #else -// 新実装検証用マクロ(TRUE, FALSE向け) -#define TEST20180909(pattern, buf, len, plen) \ - ASSERT_##pattern(IsMailAddress(buf, len, plen)); + // 新実装検証用マクロ(TRUE, FALSE向け) + #define TEST20180909(pattern, buf, len, plen) \ + ASSERT_##pattern(IsMailAddress(buf, len, plen)); #endif +////////////////////////////////////////////////////////////////////// + TEST(testIsMailAddress, CheckBlank) { wchar_t szTest[] = L""; //空文字 @@ -267,6 +172,13 @@ TEST(testIsMailAddress, CheckBadQuotedLocalPart) TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); } +// レビューコメントにより追試 +TEST(testIsMailAddress, CheckAwithAtmark) +{ + wchar_t szTest[] = L"a@"; + TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); +} + #pragma pop_macro("TEST20180909") From 020289ea2bfc13f77a6234582071609738a866e8 Mon Sep 17 00:00:00 2001 From: berryplus Date: Wed, 12 Sep 2018 02:39:58 +0900 Subject: [PATCH 10/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E7=82=B9=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ファイル末尾の改行を削ってしまっていたので戻す --- tests/unittests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index 5e664c3a24..43df6806b8 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -31,4 +31,4 @@ endif (BUILD_SHARED_LIBS) # link libraries target_link_libraries(${project_name} gtest) -target_link_libraries(${project_name} gtest_main) \ No newline at end of file +target_link_libraries(${project_name} gtest_main) From 55f474ee5899a9033a27fd0a34b293770e26d116 Mon Sep 17 00:00:00 2001 From: berryplus Date: Wed, 12 Sep 2018 23:59:08 +0900 Subject: [PATCH 11/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新旧関数の結果の期待値が見えるように修正 pragmaの使い方を間違っていたので#define-#undefに差し替え インクルードコメントの書き間違いを訂正 --- sakura_core/view/colors/CColor_Url.cpp | 2 +- tests/unittests/test-is_mailaddress.cpp | 84 +++++++++++++------------ 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/sakura_core/view/colors/CColor_Url.cpp b/sakura_core/view/colors/CColor_Url.cpp index 8de9c78660..c876bbf2f4 100644 --- a/sakura_core/view/colors/CColor_Url.cpp +++ b/sakura_core/view/colors/CColor_Url.cpp @@ -5,7 +5,7 @@ #include "doc/CEditDoc.h" #include "doc/layout/CLayout.h" #include "types/CTypeSupport.h" -#include "util/string_ex.h" // IsMailAddress +#include "util/string_ex.h" // IsURL // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // diff --git a/tests/unittests/test-is_mailaddress.cpp b/tests/unittests/test-is_mailaddress.cpp index 49dcba07d9..a47cb6040c 100644 --- a/tests/unittests/test-is_mailaddress.cpp +++ b/tests/unittests/test-is_mailaddress.cpp @@ -14,57 +14,59 @@ BOOL IsMailAddress(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); BOOL IsMailAddress_20160427(const wchar_t* pszBuf, int nBufLen, int* pnAddressLenfth); -// テストマクロが他と被らないようにpushする -#pragma push_macro("TEST20180909") - -// ローカルテストモードの定義 -// ↓をコメントインすると現新比較テストを実行できます。 -//#define REGRESSION_TEST - -// モードによってテストマクロの定義内容を変える -#ifdef REGRESSION_TEST - // 新旧動作比較用マクロ(TRUE, FALSE向け) - #define TEST20180909(pattern, buf, len, plen) \ - EXPECT_##pattern(IsMailAddress_20160427(buf, len, plen)); \ - ASSERT_##pattern(IsMailAddress(buf, len, plen)); -#else - // 新実装検証用マクロ(TRUE, FALSE向け) - #define TEST20180909(pattern, buf, len, plen) \ - ASSERT_##pattern(IsMailAddress(buf, len, plen)); -#endif +////////////////////////////////////////////////////////////////////// +// テストマクロ + +// 新旧動作比較用マクロ1(TRUE, FALSE向け) +// ASSERT_SAME: 旧実装と新実装で動作が変わらないことを期待 +#define ASSERT_SAME(expected, szTarget, cchTarget, pchMatchedLen) \ + EXPECT_##expected(_OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ + ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); + +// 新旧動作比較用マクロ2(TRUE, FALSE向け) +// ASSERT_CHANGE: 旧実装と新実装で動作が変わることを期待 +#define ASSERT_CHANGE(expected, szTarget, cchTarget, pchMatchedLen) \ + EXPECT_NE(expected, _OLD_IMPL(szTarget, cchTarget, pchMatchedLen)); \ + ASSERT_##expected(_NEW_IMPL(szTarget, cchTarget, pchMatchedLen)); + +// テスト対象の新旧関数をマクロに当てる +#define _OLD_IMPL IsMailAddress_20160427 +#define _NEW_IMPL IsMailAddress + ////////////////////////////////////////////////////////////////////// +// テストコード TEST(testIsMailAddress, CheckBlank) { wchar_t szTest[] = L""; //空文字 - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); } TEST(testIsMailAddress, CheckExample) { wchar_t szTest[] = L"test@example.com"; //標準的なサンプルメールアドレス - TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); } TEST(testIsMailAddress, CheckExampleCoJp) { wchar_t szTest[] = L"test@example.co.jp"; //標準的なサンプルメールアドレス - TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); } TEST(testIsMailAddress, CheckTrailingSpace) { wchar_t szTest[] = L"test@example.co.jp "; //標準的なサンプルメールアドレス int mailboxLength; - TEST20180909(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); ASSERT_EQ(_countof(szTest) - 2, mailboxLength); } TEST(testIsMailAddress, CheckPunctuation) { wchar_t szTest[] = L"test!#$%&'*+-/=?^_`{|}~@example.com"; //記号類を含む - TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, NULL); } TEST(testIsMailAddress, CheckMaxLocalPart) @@ -72,7 +74,7 @@ TEST(testIsMailAddress, CheckMaxLocalPart) wchar_t szTest[256]; wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szTest, _countof(szTest), L"%1$s%1$s%1$s%1$s@example.com", szSeed); //4個繋げて64文字にする - TEST20180909(TRUE, szTest, ::wcslen(szTest), NULL); + ASSERT_SAME(TRUE, szTest, ::wcslen(szTest), NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している @@ -81,7 +83,7 @@ TEST(testIsMailAddress, CheckExceedMaxLocalPart) wchar_t szTest[256]; wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szTest, _countof(szTest), L"%1$s%1$s%1$s%1$s0@example.com", szSeed); //4個繋げて64文字 + 1 - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); } TEST(testIsMailAddress, CheckMaxMailbox) @@ -92,7 +94,7 @@ TEST(testIsMailAddress, CheckMaxMailbox) ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$.63s.%1$.63s.%1$.58s.com", szSeed64); //最大255文字のチェック int mailboxLength; - TEST20180909(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); + ASSERT_SAME(TRUE, szTest, _countof(szTest) - 1, &mailboxLength); ASSERT_EQ(255, mailboxLength); } @@ -104,7 +106,7 @@ TEST(testIsMailAddress, CheckMaxExceedMailbox) wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$.63s.%1$.63s.%1$.58s0.com", szSeed64); //最大255文字オーバーのチェック - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している @@ -115,21 +117,21 @@ TEST(testIsMailAddress, CheckTooLongDomain) wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szSeed64, _countof(szSeed64), L"%1$s%1$s%1$s%1$s", szSeed); //4個繋げて64文字にする ::_swprintf_p(szTest, _countof(szTest), L"%1$s@%1$s.com", szSeed64); //63文字を超えるドメイン - TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している TEST(testIsMailAddress, CheckTooShortDomain) { wchar_t szTest[] = L"yajim@my.me"; //ドメイン部は3文字以上 - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している TEST(testIsMailAddress, CheckTooShortCCTLD) { wchar_t szTest[] = L"test@test.c.bak"; //CCTLD部は2文字以上 - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_CHANGE(FALSE, szTest, _countof(szTest) - 1, NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している @@ -138,7 +140,7 @@ TEST(testIsMailAddress, CheckDomainIncludesUnderScore) wchar_t szTest[256]; wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test_domain.com", szSeed); //_を含むドメイン - TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); } TEST(testIsMailAddress, CheckDomainIncludesSingleHyphen) @@ -146,7 +148,7 @@ TEST(testIsMailAddress, CheckDomainIncludesSingleHyphen) wchar_t szTest[256]; wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test-domain.com", szSeed); //途中に-を含むドメイン - TEST20180909(TRUE, szTest, ::wcslen(szTest), NULL); + ASSERT_SAME(TRUE, szTest, ::wcslen(szTest), NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している @@ -155,30 +157,34 @@ TEST(testIsMailAddress, CheckDomainIncludesDoubleHyphen) wchar_t szTest[256]; wchar_t szSeed[] = L"0123456789ABCDEF"; // 16文字の素片 ::_swprintf_p(szTest, _countof(szTest), L"%1$s@test--domain.com", szSeed); //途中に-を含むドメイン - TEST20180909(FALSE, szTest, ::wcslen(szTest), NULL); + ASSERT_CHANGE(FALSE, szTest, ::wcslen(szTest), NULL); } // 動作変更あり。新実装では条件を厳しくして高速化している TEST(testIsMailAddress, CheckQuotedLocalPart) { wchar_t szTest[] = L"\"test\\@c\"@test.com"; - TEST20180909(TRUE, szTest, _countof(szTest) - 1, NULL); + ASSERT_CHANGE(TRUE, szTest, _countof(szTest) - 1, NULL); } -// 動作変更あり。新実装では条件を厳しくして高速化している TEST(testIsMailAddress, CheckBadQuotedLocalPart) { wchar_t szTest[] = L"\"test@test.com"; - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); } -// レビューコメントにより追試 +// レビューコメントにより追試。動作変えていない部分だが、誤って動作が変わっていた。 TEST(testIsMailAddress, CheckAwithAtmark) { wchar_t szTest[] = L"a@"; - TEST20180909(FALSE, szTest, _countof(szTest) - 1, NULL); + ASSERT_SAME(FALSE, szTest, _countof(szTest) - 1, NULL); } +////////////////////////////////////////////////////////////////////// +// テストマクロの後始末 -#pragma pop_macro("TEST20180909") +#undef _OLD_IMPL +#undef _NEW_IMPL +#undef ASSERT_SAME +#undef ASSERT_CHANGE From 4d41b0bf251abcf92d7917f812af7d9f4749672f Mon Sep 17 00:00:00 2001 From: berryplus Date: Thu, 13 Sep 2018 01:33:08 +0900 Subject: [PATCH 12/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NULLチェック追加 コメント修正 使いまわす差分値を一時変数に入れる assert除去 --- sakura_core/util/string_ex.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index e885e3485b..ca01376e72 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -1048,8 +1048,8 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) // RFC5321による mailbox の最大文字数 const ptrdiff_t MAX_MAILBOX = 255; //255オクテット - // バカ避け - if (nBufLen < 1) return FALSE; + // 想定しないパラメータは前半チェックの前に弾く + if (pszBuf == nullptr || nBufLen < 1) return FALSE; // メールアドレスには必ず@が含まれる const wchar_t* pszAtmark; @@ -1058,7 +1058,6 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) if (!IsMailAddressLocalPart(pszBuf, pszBuf + nBufLen, &pszAtmark)) { return FALSE; } - assert(L'@' == *pszAtmark); // メールアドレスの終了位置を受け取るポインタを宣言する const wchar_t* pszEndOfMailBox; @@ -1070,14 +1069,15 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) } // 全体の長さが制限を超えていないかチェックする - if (MAX_MAILBOX < pszEndOfMailBox - pszBuf) + auto cchAddressLength = pszEndOfMailBox - pszBuf; + if (MAX_MAILBOX < cchAddressLength) { return FALSE; // 文字数オーバー } if (pnAddressLength != nullptr) { - *pnAddressLength = pszEndOfMailBox - pszBuf; + *pnAddressLength = cchAddressLength; } return TRUE; } From 16b0d48b34b871f88d77e78f9b659012e33bbe34 Mon Sep 17 00:00:00 2001 From: berryplus Date: Fri, 14 Sep 2018 00:58:11 +0900 Subject: [PATCH 13/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit constexprを活用する メールアドレスの最小文字数は1じゃないことを考慮する 備忘コメントを具体的にする コンフリクト解消 --- sakura_core/util/string_ex.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index ca01376e72..e8c67f31ca 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -1016,6 +1016,10 @@ BOOL IsURL( } } return IsMailAddress(pszLine, nLineLen, pnMatchLen); // TODO: FALSE じゃなくて? + // この関数の作成目的は「指定した文字列がURLかどうかを判定すること」だったと推定される。 + // 先行する「mailto:」のないメールアドレスはURLではないのでここはFALSEを返すべき。 + // 「URLとメールアドレスにリンクを付けたい」を叶えるための措置と考えられるが、もっといい解決策がありそうに思う。 + // とはいえ、すぐに着手できそうな状況ではないので、備忘目的でコメントだけ残しておく。 by berryzplus } // 指定された文字列がメールアドレス前半部分の要件を満たすか判定する @@ -1025,13 +1029,6 @@ inline static bool IsMailAddressLocalPart( _Out_ const wchar_t** ppszAtmark ) noexcept; -// 指定された文字列がメールアドレス前半部分の要件を満たすか判定する -inline static bool IsMailAddressLocalPart( - _In_z_ const wchar_t* pszStart, - _In_ const wchar_t* pszEnd, - _Out_ const wchar_t** ppszAtmark -) noexcept; - // 指定された文字列がメールアドレス後半部分の要件を満たすか判定する inline static bool IsMailAddressDomain( _In_z_ const wchar_t* pszAtmark, @@ -1039,6 +1036,7 @@ inline static bool IsMailAddressDomain( _Out_ const wchar_t** ppszEndOfMailBox ) noexcept; + /* 現在位置がメールアドレスならば、NULL以外と、その長さを返す @date 2016.04.27 記号類を許可 @date 2018.09.09 RFC準拠 @@ -1046,10 +1044,14 @@ inline static bool IsMailAddressDomain( BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) { // RFC5321による mailbox の最大文字数 - const ptrdiff_t MAX_MAILBOX = 255; //255オクテット + constexpr ptrdiff_t MAX_MAILBOX = 255; //255オクテット + + // 論理的なメールアドレス長の下限文字数 + // 1(@手前) + 1(@) + 3(ドメイン最小文字数) + 1(.) + 3(TLD) = 9 + constexpr ptrdiff_t MIN_MAILBOX = 9; - // 想定しないパラメータは前半チェックの前に弾く - if (pszBuf == nullptr || nBufLen < 1) return FALSE; + // 想定しないパラメータは前半チェックの前に弾く + if (pszBuf == nullptr || nBufLen < MIN_MAILBOX) return FALSE; // メールアドレスには必ず@が含まれる const wchar_t* pszAtmark; @@ -1069,7 +1071,7 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) } // 全体の長さが制限を超えていないかチェックする - auto cchAddressLength = pszEndOfMailBox - pszBuf; + const auto cchAddressLength = pszEndOfMailBox - pszBuf; if (MAX_MAILBOX < cchAddressLength) { return FALSE; // 文字数オーバー @@ -1096,7 +1098,7 @@ inline static bool IsMailAddressLocalPart( ) noexcept { // RFC5321による local-part の最大文字数 - const ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット + constexpr ptrdiff_t MAX_LOCAL_PART = 64; //64オクテット // 関数仕様 assert(pszStart != pszEnd); // 長さ0の文字列をチェックしてはならない @@ -1158,13 +1160,13 @@ inline static bool IsMailAddressDomain( ) noexcept { // ccTLDの最小文字数 - const ptrdiff_t MIN_TLD = 2; + constexpr ptrdiff_t MIN_TLD = 2; // ドメインの最小文字数 - const ptrdiff_t MIN_DOMAIN = 3; + constexpr ptrdiff_t MIN_DOMAIN = 3; // ドメインの最大文字数 - const ptrdiff_t MAX_DOMAIN = 63; + constexpr ptrdiff_t MAX_DOMAIN = 63; // 関数仕様 assert(pszAtmark < pszEnd); // @位置と終了位置は逆転してはならない From 9e3d55b84b9567e44c34bb176c9a5085f84d620c Mon Sep 17 00:00:00 2001 From: berryplus Date: Fri, 14 Sep 2018 07:45:59 +0900 Subject: [PATCH 14/14] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 定数値の誤りを修正 コメントの誤りを修正 --- sakura_core/util/string_ex.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sakura_core/util/string_ex.cpp b/sakura_core/util/string_ex.cpp index e8c67f31ca..64770b7428 100644 --- a/sakura_core/util/string_ex.cpp +++ b/sakura_core/util/string_ex.cpp @@ -1046,11 +1046,12 @@ BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength ) // RFC5321による mailbox の最大文字数 constexpr ptrdiff_t MAX_MAILBOX = 255; //255オクテット - // 論理的なメールアドレス長の下限文字数 - // 1(@手前) + 1(@) + 3(ドメイン最小文字数) + 1(.) + 3(TLD) = 9 - constexpr ptrdiff_t MIN_MAILBOX = 9; + // mailboxの最小文字数(これより短いと構成要素を含めなくなる) + // 例) a@z.jp + // 1(@手前) + 1(@) + 1(ドメイン) + 1(.) + 2(TLD/ccTLD) = 6 + constexpr ptrdiff_t MIN_MAILBOX = 6; - // 想定しないパラメータは前半チェックの前に弾く + // 想定しないパラメータは前半チェックの前に弾く if (pszBuf == nullptr || nBufLen < MIN_MAILBOX) return FALSE; // メールアドレスには必ず@が含まれる