• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "core/fpdftext/cpdf_linkextract.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 
9 // Class to help test functions in CPDF_LinkExtract class.
10 class CPDF_TestLinkExtract final : public CPDF_LinkExtract {
11  public:
CPDF_TestLinkExtract()12   CPDF_TestLinkExtract() : CPDF_LinkExtract(nullptr) {}
13 
14  private:
15   // Add test cases as friends to access protected member functions.
16   // Access CheckMailLink and CheckWebLink.
17   FRIEND_TEST(CPDF_LinkExtractTest, CheckMailLink);
18   FRIEND_TEST(CPDF_LinkExtractTest, CheckWebLink);
19 };
20 
TEST(CPDF_LinkExtractTest,CheckMailLink)21 TEST(CPDF_LinkExtractTest, CheckMailLink) {
22   CPDF_TestLinkExtract extractor;
23   // Check cases that fail to extract valid mail link.
24   const wchar_t* const invalid_strs[] = {
25       L"",
26       L"peter.pan",       // '@' is required.
27       L"abc@server",      // Domain name needs at least one '.'.
28       L"abc.@gmail.com",  // '.' can not immediately precede '@'.
29       L"abc@xyz&q.org",   // Domain name should not contain '&'.
30       L"abc@.xyz.org",    // Domain name should not start with '.'.
31       L"fan@g..com"       // Domain name should not have consecutive '.'
32   };
33   for (size_t i = 0; i < FX_ArraySize(invalid_strs); ++i) {
34     const wchar_t* const input = invalid_strs[i];
35     WideString text_str(input);
36     EXPECT_FALSE(extractor.CheckMailLink(&text_str)) << input;
37   }
38 
39   // Check cases that can extract valid mail link.
40   // An array of {input_string, expected_extracted_email_address}.
41   const wchar_t* const valid_strs[][2] = {
42       {L"peter@abc.d", L"peter@abc.d"},
43       {L"red.teddy.b@abc.com", L"red.teddy.b@abc.com"},
44       {L"abc_@gmail.com", L"abc_@gmail.com"},  // '_' is ok before '@'.
45       {L"dummy-hi@gmail.com",
46        L"dummy-hi@gmail.com"},                  // '-' is ok in user name.
47       {L"a..df@gmail.com", L"df@gmail.com"},    // Stop at consecutive '.'.
48       {L".john@yahoo.com", L"john@yahoo.com"},  // Remove heading '.'.
49       {L"abc@xyz.org?/", L"abc@xyz.org"},       // Trim ending invalid chars.
50       {L"fan{abc@xyz.org", L"abc@xyz.org"},     // Trim beginning invalid chars.
51       {L"fan@g.com..", L"fan@g.com"},           // Trim the ending periods.
52       {L"CAP.cap@Gmail.Com", L"CAP.cap@Gmail.Com"},  // Keep the original case.
53   };
54   for (size_t i = 0; i < FX_ArraySize(valid_strs); ++i) {
55     const wchar_t* const input = valid_strs[i][0];
56     WideString text_str(input);
57     WideString expected_str(L"mailto:");
58     expected_str += valid_strs[i][1];
59     EXPECT_TRUE(extractor.CheckMailLink(&text_str)) << input;
60     EXPECT_STREQ(expected_str.c_str(), text_str.c_str());
61   }
62 }
63 
TEST(CPDF_LinkExtractTest,CheckWebLink)64 TEST(CPDF_LinkExtractTest, CheckWebLink) {
65   CPDF_TestLinkExtract extractor;
66   // Check cases that fail to extract valid web link.
67   // The last few are legit web addresses that we don't handle now.
68   const wchar_t* const invalid_cases[] = {
69       L"", L"http", L"www.", L"https-and-www",
70       L"http:/abc.com",             // Missing slash.
71       L"http://((()),",             // Only invalid chars in host name.
72       L"ftp://example.com",         // Ftp scheme is not supported.
73       L"http:example.com",          // Missing slashes.
74       L"http//[example.com",        // Invalid IPv6 address.
75       L"http//[00:00:00:00:00:00",  // Invalid IPv6 address.
76       L"http//[]",                  // Empty IPv6 address.
77       // Web addresses that in correct format that we don't handle.
78       L"abc.example.com",  // URL without scheme.
79   };
80   const int32_t DEFAULT_VALUE = -42;
81   for (size_t i = 0; i < FX_ArraySize(invalid_cases); ++i) {
82     const wchar_t* const input = invalid_cases[i];
83     WideString text_str(input);
84     int32_t start_offset = DEFAULT_VALUE;
85     int32_t count = DEFAULT_VALUE;
86     EXPECT_FALSE(extractor.CheckWebLink(&text_str, &start_offset, &count))
87         << input;
88     EXPECT_EQ(DEFAULT_VALUE, start_offset) << input;
89     EXPECT_EQ(DEFAULT_VALUE, count) << input;
90   }
91 
92   // Check cases that can extract valid web link.
93   // An array of {input_string, expected_extracted_web_link}.
94   struct ValidCase {
95     const wchar_t* const input_string;
96     const wchar_t* const url_extracted;
97     const int32_t start_offset;
98     const int32_t count;
99   };
100   const ValidCase valid_cases[] = {
101       {L"http://www.example.com", L"http://www.example.com", 0,
102        22},  // standard URL.
103       {L"http://www.example.com:88", L"http://www.example.com:88", 0,
104        25},  // URL with port number.
105       {L"http://test@www.example.com", L"http://test@www.example.com", 0,
106        27},  // URL with username.
107       {L"http://test:test@example.com", L"http://test:test@example.com", 0,
108        28},  // URL with username and password.
109       {L"http://example", L"http://example", 0,
110        14},  // URL with short domain name.
111       {L"http////www.server", L"http://www.server", 8,
112        10},  // URL starts with "www.".
113       {L"http:/www.abc.com", L"http://www.abc.com", 6,
114        11},                                       // URL starts with "www.".
115       {L"www.a.b.c", L"http://www.a.b.c", 0, 9},  // URL starts with "www.".
116       {L"https://a.us", L"https://a.us", 0, 12},  // Secure http URL.
117       {L"https://www.t.us", L"https://www.t.us", 0, 16},  // Secure http URL.
118       {L"www.example-test.com", L"http://www.example-test.com", 0,
119        20},  // '-' in host is ok.
120       {L"www.example.com,", L"http://www.example.com", 0,
121        15},  // Trim ending invalid chars.
122       {L"www.example.com;(", L"http://www.example.com", 0,
123        15},  // Trim ending invalid chars.
124       {L"test:www.abc.com", L"http://www.abc.com", 5,
125        11},  // Trim chars before URL.
126       {L"(http://www.abc.com)", L"http://www.abc.com", 1,
127        18},  // Trim external brackets.
128       {L"0(http://www.abc.com)0", L"http://www.abc.com", 2,
129        18},  // Trim chars outside brackets as well.
130       {L"0(www.abc.com)0", L"http://www.abc.com", 2,
131        11},  // Links without http should also have brackets trimmed.
132       {L"http://www.abc.com)0", L"http://www.abc.com)0", 0,
133        20},  // Do not trim brackets that were not opened.
134       {L"{(<http://www.abc.com>)}", L"http://www.abc.com", 3,
135        18},  // Trim chars with multiple levels of brackets.
136       {L"[http://www.abc.com/z(1)]", L"http://www.abc.com/z(1)", 1,
137        23},  // Brackets opened inside the URL should not be trimmed.
138       {L"(http://www.abc.com/z(1))", L"http://www.abc.com/z(1)", 1,
139        23},  // Brackets opened inside the URL should not be trimmed.
140       {L"\"http://www.abc.com\"", L"http://www.abc.com", 1,
141        18},  // External quotes can also be escaped
142       {L"www.g.com..", L"http://www.g.com..", 0, 11},  // Leave ending periods.
143 
144       // Web links can contain IP addresses too.
145       {L"http://192.168.0.1", L"http://192.168.0.1", 0, 18},  // IPv4 address.
146       {L"http://192.168.0.1:80", L"http://192.168.0.1:80", 0,
147        21},  // IPv4 address with port.
148       {L"http://[aa::00:bb::00:cc:00]", L"http://[aa::00:bb::00:cc:00]", 0,
149        28},  // IPv6 reference.
150       {L"http://[aa::00:bb::00:cc:00]:12", L"http://[aa::00:bb::00:cc:00]:12",
151        0, 31},  // IPv6 reference with port.
152       {L"http://[aa]:12", L"http://[aa]:12", 0,
153        14},  // Not validate IP address.
154       {L"http://[aa]:12abc", L"http://[aa]:12", 0,
155        14},                                      // Trim for IPv6 address.
156       {L"http://[aa]:", L"http://[aa]", 0, 11},  // Trim for IPv6 address.
157 
158       // Path and query parts can be anything.
159       {L"www.abc.com/#%%^&&*(", L"http://www.abc.com/#%%^&&*(", 0, 20},
160       {L"www.a.com/#a=@?q=rr&r=y", L"http://www.a.com/#a=@?q=rr&r=y", 0, 23},
161       {L"http://a.com/1/2/3/4\5\6", L"http://a.com/1/2/3/4\5\6", 0, 22},
162       {L"http://www.example.com/foo;bar", L"http://www.example.com/foo;bar", 0,
163        30},
164 
165       // Invalid chars inside host name are ok as we don't validate them.
166       {L"http://ex[am]ple", L"http://ex[am]ple", 0, 16},
167       {L"http://:example.com", L"http://:example.com", 0, 19},
168       {L"http://((())/path?", L"http://((())/path?", 0, 18},
169       {L"http:////abc.server", L"http:////abc.server", 0, 19},
170 
171       // Non-ASCII chars are not validated either.
172       {L"www.测试.net", L"http://www.测试.net", 0, 10},
173       {L"www.测试。net。", L"http://www.测试。net。", 0, 11},
174       {L"www.测试.net;", L"http://www.测试.net;", 0, 11},
175   };
176   for (size_t i = 0; i < FX_ArraySize(valid_cases); ++i) {
177     const wchar_t* const input = valid_cases[i].input_string;
178     WideString text_str(input);
179     int32_t start_offset = DEFAULT_VALUE;
180     int32_t count = DEFAULT_VALUE;
181     EXPECT_TRUE(extractor.CheckWebLink(&text_str, &start_offset, &count))
182         << input;
183     EXPECT_STREQ(valid_cases[i].url_extracted, text_str.c_str());
184     EXPECT_EQ(valid_cases[i].start_offset, start_offset) << input;
185     EXPECT_EQ(valid_cases[i].count, count) << input;
186   }
187 }
188