libtld 2.0.14
A library to determine the Top-Level Domain name of any Internet URI.
tld_emails.cpp
Go to the documentation of this file.
1/* TLD library -- TLD, emails extractions
2 * Copyright (c) 2013-2025 Made to Order Software Corp. All Rights Reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include "libtld/tld.h"
24
25// C
26//
27#include <stdio.h>
28#include <string.h>
29
30// C++
31//
32#include <memory>
33#include <iostream>
34#include <algorithm>
35
36
285namespace
286{
297void trim(std::string& value)
298{
299 if(!value.empty())
300 {
301 size_t i(value.length());
302 for(; i > 0; --i)
303 {
304 const char c(value[i - 1]);
305 if(c != ' ' && c != '\r' && c != '\n' && c != '\t')
306 {
307 break;
308 }
309 }
310 value.resize(i);
311 }
312}
313
323bool is_quoted_char(char c)
324{
325 // 0x7F is the Delete key which is viewed as a control
326 // here we accept all characters over 127 in case the user
327 // has UTF-8 as input data
328 return c == '\t' || c >= ' ' && c != 0x7F;
329}
330
341bool is_atom_char(char c)
342{
343 return (c >= 'A' && c <= 'Z')
344 || (c >= 'a' && c <= 'z')
345 || (c >= '0' && c <= '9')
346 || c == '!' || c == '#'
347 || c == '$' || c == '%'
348 || c == '&' || c == '\''
349 || c == '*' || c == '+'
350 || c == '-' || c == '/'
351 || c == '=' || c == '?'
352 || c == '^' || c == '_'
353 || c == '`' || c == '{'
354 || c == '|' || c == '}'
355 || c == '~';
356}
357} // no name namespace
358
359
368 : f_flags(0)
369 , f_result(TLD_RESULT_SUCCESS)
370 , f_pos(0)
371{
372}
373
392tld_result tld_email_list::parse(std::string const & emails, int flags)
393{
394 f_input = emails;
395 f_flags = flags;
397 f_last_group.clear();
398 f_pos = 0; // always rewind too
399 f_email_list.clear();
400
403 {
404 f_email_list.clear();
405 }
406
407 return f_result;
408}
409
416{
417 // old emails supposedly accepted \0 in headers!
418 // we actually do not even support control characters as
419 // defined in the newest version of the Internet Message
420 // (RFC 5322); the following loop, though, does not check
421 // all the characters, only those necessary to cut all the
422 // email elements properly
423
424 char const * start(f_input.c_str());
425 bool group(true);
426 char const * s(start);
427 for(; *s != '\0'; ++s)
428 {
429 switch(*s)
430 {
431 case ' ':
432 case '\n':
433 case '\r':
434 case '\t':
435 // skip leading spaces immediately
436 if(start == s)
437 {
438 start = s + 1;
439 }
440 break;
441
442 case ';':
443 // end of this group
444 {
445 // trim ending spaces
446 char const * end(s);
447 for(; end > start; --end)
448 {
449 char const c(end[-1]);
450 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
451 {
452 break;
453 }
454 }
455 if(end - start > 0)
456 {
457 std::string const e(start, end - start);
458 tld_email_t email;
459 email.f_group = f_last_group;
460 f_result = email.parse(e);
462 {
463 return;
464 }
465 f_email_list.push_back(email);
466 }
467 }
468 f_last_group = "";
469 group = true;
470 start = s + 1;
471 break;
472
473 case ':':
474 // group label
475 if(!group)
476 {
477 // wrong place for this ':' character
479 return;
480 }
481 {
482 // trim ending spaces
483 char const * end(s);
484 for(; end > start; --end)
485 {
486 char const c(end[-1]);
487 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
488 {
489 break;
490 }
491 }
492 if(end - start <= 0)
493 {
494 // An explicitly empty group name is not legal
496 return;
497 }
498 std::string const last_group(start, end - start);
499 // always add the group with an empty email (in case there
500 // is no email; and it clearly delimit each group.)
501 tld_email_t email;
502 f_result = email.parse_group(last_group);
504 {
505 // this happens if the group name is invalid
506 // (i.e. include controls or is empty)
507 return;
508 }
509 f_last_group = email.f_group;
510 f_email_list.push_back(email);
511 }
512 start = s + 1;
513 group = false; // cannot get another legal ':' until we find the ';'
514 break;
515
516 case ',':
517 // email separation
518 {
519 // trim ending spaces
520 char const * end(s);
521 for(; end > start; --end)
522 {
523 char const c(end[-1]);
524 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
525 {
526 break;
527 }
528 }
529 if(end - start > 0)
530 {
531 std::string const e(start, end - start);
532 tld_email_t email;
533 email.f_group = f_last_group;
534 f_result = email.parse(e);
536 {
537 return;
538 }
539 f_email_list.push_back(email);
540 }
541 }
542 start = s + 1;
543 break;
544
545 case '"':
546 // quoted strings may include escaped characters so it is a
547 // special case, also it could include a comma
548 for(++s; *s != '\0' && *s != '"'; ++s)
549 {
550 if(*s == '\\')
551 {
552 if(!is_quoted_char(s[1]))
553 {
554 // "\NUL" is never considered valid
556 return;
557 }
558 ++s;
559 }
560 }
561 if(*s == '\0')
562 {
563 // unterminated quoted string
565 return;
566 }
567 break;
568
569 case '(':
570 {
571 // comments may include other comments
572 int comment_count(1);
573 for(++s; *s != '\0'; ++s)
574 {
575 if(*s == '\\')
576 {
577 if(!is_quoted_char(s[1]))
578 {
579 // "\NUL" is never considered valid
581 return;
582 }
583 ++s;
584 }
585 else if(*s == '(')
586 {
587 ++comment_count;
588 }
589 else if(*s == ')')
590 {
591 --comment_count;
592 if(comment_count <= 0)
593 {
594 break;
595 }
596 }
597 }
598 if(*s == '\0')
599 {
600 // unterminated comment
602 return;
603 }
604 }
605 break;
606
607 case '[':
608 for(++s; *s != ']'; ++s)
609 {
610 if(*s == '\0' || *s == '[' || *s == '\\')
611 {
612 // domain literal cannot include '[', ']', or '\'
613 // and it must end with ']'
614 //
616 return;
617 }
618 }
619 break;
620
621 }
622 }
623
624 if(!group)
625 {
626 // the ';' to end a group is missing
628 return;
629 }
630
631 {
632 // trim ending spaces
633 char const * end(s);
634 for(; end > start; --end)
635 {
636 char const c(end[-1]);
637 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
638 {
639 break;
640 }
641 }
642 if(end - start > 0)
643 {
644 std::string const e(start, end - start);
645 tld_email_t email;
646 email.f_group = f_last_group;
647 f_result = email.parse(e);
649 {
650 return;
651 }
652 f_email_list.push_back(email);
653 }
654 }
655}
656
713std::string tld_email_list::quote_string(const std::string& str, char quote)
714{
715 bool apply_quotes(false);
716 char open(quote);
717 char close('"');
718 char const * extra("");
719 char const * escape("");
720 switch(quote)
721 {
722 case '(':
723 close = ')';
724 apply_quotes = true;
725 escape = "()";
726 break;
727
728 case '"':
729 extra = " \t";
730 escape = "\"";
731 break;
732
733 case '\'':
734 open = '"';
735 close = '"';
736 extra = ".";
737 escape = "\"";
738 break;
739
740 case '[':
741 close = ']';
742 extra = ".";
743 break;
744
745 }
746 if(!apply_quotes)
747 {
748 // check whether quotes are required
749 char const * s(str.c_str());
750 for(; *s != '\0'; ++s)
751 {
752 if(!is_atom_char(*s) && strchr(extra, *s) == nullptr)
753 {
754 break;
755 }
756 }
757 apply_quotes = *s != '\0';
758 }
759 if(apply_quotes)
760 {
761 std::string result;
762 result += open;
763 for(const char *s(str.c_str()); *s != '\0'; ++s)
764 {
765 if(strchr(escape, *s) != nullptr)
766 {
767 result += '\\';
768 }
769 result += *s;
770 }
771 result += close;
772 return result;
773 }
774 return str;
775}
776
789{
790 return static_cast<int>(f_email_list.size());
791}
792
802{
803 f_pos = 0;
804}
805
819{
820 if(f_pos >= static_cast<int>(f_email_list.size()))
821 {
822 return false;
823 }
824
825 e = f_email_list[f_pos];
826 ++f_pos;
827
828 return true;
829}
830
850{
851 if(f_pos >= static_cast<int>(f_email_list.size()))
852 {
853 return false;
854 }
855
856 e->f_group = f_email_list[f_pos].f_group.c_str();
857 e->f_original_email = f_email_list[f_pos].f_original_email.c_str();
858 e->f_fullname = f_email_list[f_pos].f_fullname.c_str();
859 e->f_username = f_email_list[f_pos].f_username.c_str();
860 e->f_domain = f_email_list[f_pos].f_domain.c_str();
861 e->f_email_only = f_email_list[f_pos].f_email_only.c_str();
862 e->f_canonicalized_email = f_email_list[f_pos].f_canonicalized_email.c_str();
863 ++f_pos;
864
865 return true;
866}
867
890{
891 std::string uname;
892 for(const char *u(name.c_str()); *u != '\0' && *u != ':'; ++u)
893 {
894 if(*u >= 'a' && *u <= 'z')
895 {
896 uname += *u & 0x5F;
897 }
898 else if((*u >= 'A' && *u <= 'Z')
899 || (*u >= '0' && *u <= '9')
900 || *u == '-')
901 {
902 uname += *u;
903 }
904 else
905 {
907 }
908 }
909 // the field must start with a letter and it cannot be empty
910 if(uname.empty() || uname[0] < 'A' || uname[0] > 'Z')
911 {
913 }
914
915 if(uname == "FROM"
916 || uname == "RESENT-FROM")
917 {
919 }
920 if(uname == "SENDER"
921 || uname == "RESENT-SENDER")
922 {
924 }
925 if(uname == "TO"
926 || uname == "CC"
927 || uname == "REPLY-TO"
928 || uname == "RESENT-TO"
929 || uname == "RESENT-CC")
930 {
932 }
933 if(uname == "BCC"
934 || uname == "RESENT-BCC")
935 {
937 }
938
940}
941
973{
974 // The following is parsing ONE email since we already removed the
975 // groups, commas, semi-colons, leading and ending spaces.
976 //
977 std::string value;
978 value.reserve(email.length());
979 std::string fullname;
980 std::string username;
981 std::string domain;
982 uint32_t count = 0;
983 bool has_angle(false);
984 bool found_at(false);
985 bool found_dot(false);
986 bool done(false);
987 char const * start(email.c_str());
988 char const * s(start);
989 for(; *s != '\0'; ++s)
990 {
991 switch(*s)
992 {
993 case '"':
994 if(done)
995 {
996 return TLD_RESULT_INVALID;
997 }
998 for(++s; *s != '"'; ++s)
999 {
1000 if(*s == '\0')
1001 {
1002 throw std::logic_error("somehow we found a \\0 in a quoted string in tld_email_t which should not happen since it was already checked validity in tld_email_t::parse()");
1003 }
1004 if(*s == '\\')
1005 {
1006 // the backslash is not part of the result
1007 ++s;
1008 if(*s == '\0')
1009 {
1010 // this cannot actually happen because we are
1011 // expected to capture those at the previous
1012 // level
1013 throw std::logic_error("somehow we found a \\0 in a quoted string after a backslash in tld_email_t which should not happen since it was already checked validity in tld_email_t::parse()"); // LCOV_EXCL_LINE
1014 }
1015 }
1016 if((static_cast<unsigned char>(*s) < ' ' && *s != '\t') || *s == 0x7F)
1017 {
1018 // do not accept any control characters
1019 // (note that this is sufficient to check all characters
1020 // after the \ character)
1021 //
1022 return TLD_RESULT_INVALID;
1023 }
1024 value += *s;
1025 }
1026 // on entry of this loop, *s == '"'
1027 do
1028 {
1029 ++s;
1030 }
1031 while(*s == ' ');
1032 if( *s != '<' && *s != '@' )
1033 {
1034 // A space afterwards is allowed, but '<' is expected
1035 //
1036 return TLD_RESULT_INVALID;
1037 }
1038 --s;
1039 break;
1040
1041 case '(':
1042 // comments are completely ignored
1043 count = 1;
1044 for(++s; count > 0; ++s)
1045 {
1046 char c(*s);
1047 switch(c)
1048 {
1049 case '\0':
1050 throw std::logic_error("somehow we found a \\0 in a comment in tld_email_t which should not happen since it was already checked in tld_email_t::parse()");
1051
1052 case '(':
1053 ++count;
1054 break;
1055
1056 case ')':
1057 --count;
1058 break;
1059
1060 case '\n':
1061 case '\r':
1062 case '\t':
1063 c = ' ';
1064 break;
1065
1066 case '\\':
1067 ++s;
1068 if(!is_quoted_char(*s))
1069 {
1070 throw std::logic_error("somehow we found a non-quotable character after a backslash (\\) in tld_email_t which should not happen since it was already checked in tld_email_t::parse()");
1071 }
1072 c = *s;
1073 break;
1074
1075 }
1076 if(static_cast<unsigned char>(c) < ' ')
1077 {
1078 // do not accept any control characters in comments
1079 // (except \r, \n, and \t)
1080 return TLD_RESULT_INVALID;
1081 }
1082 }
1083 --s;
1084 break;
1085
1086 case '[':
1087 if(!found_at || done || !value.empty() || !domain.empty())
1088 {
1089 // domain before the '@'
1090 //
1091 return TLD_RESULT_INVALID;
1092 }
1093 // trim spaces after the '['
1094 //
1095 for(++s; *s != ']'; ++s)
1096 {
1097 char const c(*s);
1098 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
1099 {
1100 break;
1101 }
1102 }
1103 for(; *s != '[' && *s != '\\' && *s != ']' && *s != ' ' && *s != '\n' && *s != '\r' && *s != '\t'; ++s)
1104 {
1105 if(*s == '\0')
1106 {
1107 throw std::logic_error("somehow we found a \\0 in a literal domain in tld_email_t which should not happen since it was already checked in tld_email_t::parse()");
1108 }
1109 // spaces are forbidden in domain names (see test above)
1110 //
1111 if(static_cast<unsigned char>(*s) < ' ' || *s == 0x7F)
1112 {
1113 // do not accept any control characters
1114 //
1115 return TLD_RESULT_INVALID;
1116 }
1117 value += *s;
1118 }
1119 // we can have spaces at the end, but those must be followed by ']'
1120 //
1121 for(; *s != '[' && *s != '\\' && *s != ']'; ++s)
1122 {
1123 char const c(*s);
1124 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
1125 {
1126 break;
1127 }
1128 }
1129 if(*s != ']' || value.empty())
1130 {
1131 // domain literal cannot include a space
1132 // nor can it be empty
1133 //
1134 return TLD_RESULT_NULL;
1135 }
1136 if(value[0] == '.'
1137 || *value.rbegin() == '.'
1138 || value.find("..") != std::string::npos)
1139 {
1140 // a domain cannot start or end with "."
1141 // a domain cannot include ".."
1142 //
1143 return TLD_RESULT_INVALID;
1144 }
1145 domain = value;
1146 value.clear();
1147 break;
1148
1149 case '<':
1150 if(has_angle || found_at || found_dot || done)
1151 {
1152 // found two '<' or the '<' after the '@'
1153 // or we had a dot before meaning that we already have a dotted username
1154 // or we are done (a.k.a. found the '>')
1155 //
1156 return TLD_RESULT_INVALID;
1157 }
1158
1159 // if we have an angle email address, whatever we found so far
1160 // is the user name; although it can be empty
1161 //
1162 trim(value);
1163 if(!value.empty())
1164 {
1165 fullname = value;
1166 value.clear();
1167 }
1168 has_angle = true;
1169 break;
1170
1171 case '>':
1172 if(!has_angle || !found_at || done)
1173 {
1174 // missing '<' and/or '@'
1175 //
1176 return TLD_RESULT_INVALID;
1177 }
1178 if(domain.empty())
1179 {
1180 trim(value);
1181 if(value.empty())
1182 {
1183 // an empty domain name is not valid, apparently
1184 //
1185 return TLD_RESULT_NULL;
1186 }
1187 // we are done, we can only find spaces and comments
1188 //
1189 domain = value;
1190 }
1191 else
1192 {
1193 if(!value.empty())
1194 {
1195 return TLD_RESULT_INVALID;
1196 }
1197 }
1198 done = true;
1199 has_angle = false;
1200 value.clear();
1201 break;
1202
1203 case '@':
1204 // Note: if done is true, found_at is also true here
1205 if(found_at || done)
1206 {
1207 // found two '@' characters
1208 return TLD_RESULT_INVALID;
1209 }
1210 found_at = true;
1211 found_dot = false; // reset this flag
1212 trim(value);
1213 if(value.empty())
1214 {
1215 // no username is not a valid entry
1216 //
1217 return TLD_RESULT_NULL;
1218 }
1219 username = value;
1220 value.clear();
1221 break;
1222
1223 case ' ':
1224 case '\n':
1225 case '\r':
1226 case '\t':
1227 //
1228 // keep just one space
1229 //
1230 if( !value.empty() )
1231 {
1232 value += ' ';
1233 }
1234 // and skip all the others
1235 // (as far as I know this is not allowed in the RFC, only one space
1236 // between items; however, after a new-line / carriage return, you
1237 // could get many spaces and tabs and that's legal)
1238 //
1239 for(++s; *s != '\0'; ++s)
1240 {
1241 char const c(*s);
1242 if(c != ' ' && c != '\n' && c != '\r' && c != '\t')
1243 {
1244 break;
1245 }
1246 }
1247 --s; // the main loop will skip that last character (again)
1248 break;
1249
1250 case '.':
1251 if(value.empty() // cannot start with a dot
1252 || (!value.empty() && *value.rbegin() == '.') // cannot include two dots one after the other
1253 || s[1] == '@' || s[1] == '>') // cannot end with a dot
1254 {
1255 return TLD_RESULT_INVALID;
1256 }
1257 found_dot = true;
1258 value += '.';
1259 break;
1260
1261 default:
1262 // here we must have a valid atom character ([-A-Za-z0-9!#$%&'*+/=?^_`{|}~])
1263 //
1264 if(!is_atom_char(*s))
1265 {
1266 // not a valid atom character
1267 //
1268 return TLD_RESULT_INVALID;
1269 }
1270 value += *s;
1271 break;
1272
1273 }
1274 }
1275
1276 if(username.empty() || has_angle)
1277 {
1278 // no username means the '@' is missing
1279 // angle bracket was not closed ('>' missing)
1280 //
1281 return TLD_RESULT_NULL;
1282 }
1283
1284 if(done)
1285 {
1286 if(!value.empty())
1287 {
1288 // nothing of substance can appear after the domain
1289 //
1290 return TLD_RESULT_INVALID;
1291 }
1292 }
1293 else
1294 {
1295 trim(value);
1296 if(value.empty())
1297 {
1298 if(domain.empty())
1299 {
1300 // domain is missing
1301 //
1302 return TLD_RESULT_NULL;
1303 }
1304 }
1305 else
1306 {
1307 if(!domain.empty())
1308 {
1309 // domain "defined twice"
1310 //
1311 return TLD_RESULT_INVALID;
1312 }
1313 domain = value;
1314 }
1315 }
1316
1317 // finally, verify that the domain is indeed valid
1318 // (i.e. proper characters, structure, and TLD)
1319 // for that step we use the lowercase version
1320 //
1321 struct tld_info info;
1322 std::unique_ptr<char, void(*)(char *)> lowercase_domain(tld_domain_to_lowercase(domain.c_str()), reinterpret_cast<void(*)(char *)>(&::free));
1323 tld_result result(tld(lowercase_domain.get(), &info));
1324 if(result != TLD_RESULT_SUCCESS)
1325 {
1326 return result;
1327 }
1328
1329 // EX-193 and EX-185: email must not have whitespace in it!
1330 //
1331 auto has_whitespace = [&]( char c )
1332 {
1333 return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
1334 };
1335 if( std::find_if( std::begin(username), std::end(username), has_whitespace ) != std::end(username) )
1336 {
1337 return TLD_RESULT_INVALID;
1338 }
1339 //
1340 if( std::find_if( std::begin(domain), std::end(domain), has_whitespace ) != std::end(domain) )
1341 {
1342 return TLD_RESULT_INVALID;
1343 }
1344
1345 f_original_email = email;
1346 f_fullname = fullname;
1347 f_username = username;
1348 f_domain = domain;
1349 f_email_only = quote_string(username, '\'') + "@" + quote_string(domain, '['); // TODO protect characters...
1350
1351 // the canonicalized version uses the domain name in lowercase
1352 //
1353 std::string canonicalized_email(quote_string(username, '\'') + "@" + quote_string(lowercase_domain.get(), '[')); // TODO protect characters...
1354 if(fullname.empty())
1355 {
1356 f_canonicalized_email = canonicalized_email;
1357 }
1358 else
1359 {
1360 f_canonicalized_email = quote_string(fullname, '"') + " <" + canonicalized_email + ">"; // TODO protect characters...
1361 }
1362
1363 return TLD_RESULT_SUCCESS;
1364}
1365
1390{
1391 char const * s(group.c_str());
1392 std::string g;
1393 uint32_t count = 0;
1394
1395 for(; *s != '\0'; ++s)
1396 {
1397 switch(*s)
1398 {
1399 case ' ':
1400 case '\n':
1401 case '\r':
1402 case '\t':
1403 if(!g.empty())
1404 {
1405 g += ' ';
1406 }
1407 for(++s; *s == ' ' || *s == '\n' || *s == '\r' || *s == '\t'; ++s);
1408 --s;
1409 break;
1410
1411 case '(':
1412 count = 1;
1413#pragma GCC diagnostic push
1414#pragma GCC diagnostic ignored "-Wstrict-overflow"
1415 for(++s; count > 0; ++s)
1416#pragma GCC diagnostic pop
1417 {
1418 if(*s == '\0')
1419 {
1420 throw std::logic_error("somehow we found a \\0 in a quoted string in tld_email_t which should not happen since it was already checked in tld_email_t::parse()");
1421 }
1422 switch(*s)
1423 {
1424 case '(':
1425 ++count;
1426 break;
1427
1428 case ')':
1429 --count;
1430 break;
1431
1432 case '\\':
1433 if(!is_quoted_char(s[1]))
1434 {
1435 throw std::logic_error("somehow we found a non-quotable character in tld_email_t which should not happen since it was already checked in tld_email_t::parse()");
1436 }
1437 ++s;
1438 break;
1439
1440 // controls, etc. were already checked
1441 }
1442 }
1443 // come back on the ')' since the main for will do a ++s
1444 --s;
1445 break;
1446
1447 default:
1448 if(static_cast<unsigned char>(*s) < ' ' || *s == 0x7F)
1449 {
1450 return TLD_RESULT_INVALID;
1451 }
1452 g += *s;
1453 break;
1454
1455 }
1456 }
1457 if(g.empty())
1458 {
1459 return TLD_RESULT_INVALID;
1460 }
1461
1462 f_group = g;
1463
1464 return TLD_RESULT_SUCCESS;
1465}
1466
1481{
1482 return new tld_email_list;
1483}
1484
1494{
1495 delete list;
1496}
1497
1511tld_result tld_email_parse(struct tld_email_list * list, char const * emails, int flags)
1512{
1513 return list->parse(emails, flags);
1514}
1515
1526{
1527 return list->count();
1528}
1529
1539{
1540 list->rewind();
1541}
1542
1560{
1561 return list->next(e) ? 1 : 0;
1562}
1563
1939/* vim: ts=4 sw=4 et
1940 */
Parts of one email.
Definition tld.h:226
std::string f_email_only
The complete email address without display name.
Definition tld.h:235
tld_result parse(const std::string &email)
Parse one email to a tld_email_t object.
std::string f_canonicalized_email
The email including the display name.
Definition tld.h:236
std::string f_domain
The domain part of the email address.
Definition tld.h:234
tld_result parse_group(const std::string &group)
Parse a group including comments.
std::string f_username
The user being named in this email address.
Definition tld.h:233
std::string f_fullname
The user full or display name.
Definition tld.h:232
std::string f_group
The group this emails was defined in.
Definition tld.h:230
std::string f_original_email
The email as read from the source.
Definition tld.h:231
The C++ side of the email list implementation.
Definition tld.h:223
tld_result f_result
The result of the parse() function.
Definition tld.h:255
int f_flags
The flags as passed to the parse() function.
Definition tld.h:254
void rewind() const
Rewind the reader to the start of the list.
tld_result parse(const std::string &emails, int flags)
Parse a new list of emails.
static tld_email_field_type email_field_type(const std::string &name)
Check whether a name represents a field with a list of emails.
int count() const
Return the number of emails recorded.
tld_email_list_t f_email_list
The list of emails.
Definition tld.h:258
std::string f_last_group
The last group read in the input.
Definition tld.h:256
std::string f_input
The input string of the last call to parse().
Definition tld.h:253
void parse_all_emails()
Parse all the emails in f_input.
tld_email_list()
Initialize the tld_email_list object.
int f_pos
The current position reading the emails.
Definition tld.h:257
bool next(tld_email_t &e) const
Retrieve a copy of the next email information.
static std::string quote_string(const std::string &name, char quote)
Transform a name if it requires quotation.
Parts of one email.
Definition tld.h:151
const char * f_group
The group this emails was defined in.
Definition tld.h:152
const char * f_canonicalized_email
The email including the display name.
Definition tld.h:158
const char * f_original_email
The email as read from the source.
Definition tld.h:153
const char * f_email_only
The complete email address without display name.
Definition tld.h:157
const char * f_username
The user being named in this email address.
Definition tld.h:155
const char * f_domain
The domain part of the email address.
Definition tld.h:156
const char * f_fullname
The user full or display name.
Definition tld.h:154
Set of information returned by the tld() function.
Definition tld.h:102
The public header of the libtld library.
LIBTLD_EXPORT char * tld_domain_to_lowercase(const char *domain)
Transform a domain with a TLD to lowercase before processing.
tld_email_field_type
Definition tld.h:162
@ TLD_EMAIL_FIELD_TYPE_UNKNOWN
The input does not represent valid emails.
Definition tld.h:164
@ TLD_EMAIL_FIELD_TYPE_ADDRESS_LIST
The input represents a mandatory list of mailboxes.
Definition tld.h:167
@ TLD_EMAIL_FIELD_TYPE_MAILBOX
The input represents a mailbox.
Definition tld.h:166
@ TLD_EMAIL_FIELD_TYPE_ADDRESS_LIST_OPT
The input represents an optional list of email addresses.
Definition tld.h:168
@ TLD_EMAIL_FIELD_TYPE_INVALID
The input of email_field_type() was not valid.
Definition tld.h:163
@ TLD_EMAIL_FIELD_TYPE_MAILBOX_LIST
The input represents a mailbox list.
Definition tld.h:165
LIBTLD_EXPORT void tld_email_free(struct tld_email_list *list)
Free the list of emails.
LIBTLD_EXPORT enum tld_result tld(const char *uri, struct tld_info *info)
Get information about the TLD for the specified URI.
Definition tld.cpp:1113
LIBTLD_EXPORT void tld_email_rewind(struct tld_email_list *list)
Rewind the reading of the emails.
LIBTLD_EXPORT struct tld_email_list * tld_email_alloc()
Allocate a list of emails object.
LIBTLD_EXPORT int tld_email_count(struct tld_email_list *list)
Return the number of emails found after a parse.
LIBTLD_EXPORT enum tld_result tld_email_parse(struct tld_email_list *list, const char *emails, int flags)
Parse a list of emails in the email list object.
tld_result
Definition tld.h:92
@ TLD_RESULT_SUCCESS
Success! The TLD of the specified URI is valid.
Definition tld.h:93
@ TLD_RESULT_INVALID
The TLD was found, but it is marked as invalid.
Definition tld.h:94
@ TLD_RESULT_NULL
The input URI is empty.
Definition tld.h:95
LIBTLD_EXPORT int tld_email_next(struct tld_email_list *list, struct tld_email *e)
Retrieve the next email.
void list()
List the default schemes accepted.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.