From c868229ed7f01d77d31cf062e4c90b330f855052 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Wilfried=20G=C3=B6esgens?= Date: Tue, 1 Jun 2010 22:27:02 +0000 Subject: [PATCH] * add StrBufSanitizeEmailRecipientVector(); it will qp encode plain names with utf-8 chars etc. * add CheckEncode() which scans for umlauts, whether qp encoding is neccesary * add tests --- libcitadel/lib/libcitadel.h | 5 + libcitadel/lib/stringbuf.c | 168 ++++++++++++++++++++ libcitadel/lib/tools.c | 17 ++ libcitadel/tests/email_recipientstrings.txt | 4 + libcitadel/tests/stringbuf_conversion.c | 100 ++++++++++-- 5 files changed, 283 insertions(+), 11 deletions(-) create mode 100644 libcitadel/tests/email_recipientstrings.txt diff --git a/libcitadel/lib/libcitadel.h b/libcitadel/lib/libcitadel.h index 9cd807e9d..92ab07f3e 100644 --- a/libcitadel/lib/libcitadel.h +++ b/libcitadel/lib/libcitadel.h @@ -266,6 +266,10 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* int StrBufDecodeBase64(StrBuf *Buf); int StrBufDecodeHex(StrBuf *Buf); int StrBufRFC2047encode(StrBuf **target, const StrBuf *source); +StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, + StrBuf *UserName, + StrBuf *EmailAddress, + StrBuf *EncBuf); int StrBufSanitizeAscii(StrBuf *Buf, const char Mute); #define LB (1) /* Internal escape chars */ #define RB (2) @@ -416,6 +420,7 @@ int HashLittle(const void *key, size_t length); void convert_spaces_to_underscores(char *str); +int CheckEncode(const char *pch, long len, const char *pche); /* * Convert 4 bytes char into an Integer. diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index 523733a7a..f4a43c303 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -2436,6 +2436,174 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source) return (*target)->BufUsed;; } + + +static void AddRecipient(StrBuf *Target, + StrBuf *UserName, + StrBuf *EmailAddress, + StrBuf *EncBuf) +{ + int QuoteMe = 0; + + if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0); + if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1; + + if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0); + StrBufRFC2047encode(&EncBuf, UserName); + StrBufAppendBuf(Target, EncBuf, 0); + if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0); + else StrBufAppendBufPlain(Target, HKEY(" "), 0); + + if (StrLength(EmailAddress) > 0){ + StrBufAppendBufPlain(Target, HKEY("<"), 0); + StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */ + StrBufAppendBufPlain(Target, HKEY(">"), 0); + } +} + + +/** + * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts + * \param Recp Source list of email recipients + * \param UserName Temporary buffer for internal use; Please provide valid buffer. + * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer. + * \param EncBuf Temporary buffer for internal use; Please provide valid buffer. + * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory. + */ +StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, + StrBuf *UserName, + StrBuf *EmailAddress, + StrBuf *EncBuf) +{ + StrBuf *Target; + int need_to_encode; + + const char *pch, *pche; + const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At; + + if ((Recp == NULL) || (StrLength(Recp) == 0)) + return NULL; + + need_to_encode = 0; + pch = ChrPtr(Recp); + pche = pch + StrLength(Recp); + + if (!CheckEncode(pch, -1, pche)) + return NewStrBufDup(Recp); + + Target = NewStrBufPlain(NULL, StrLength(Recp)); + + while ((pch != NULL) && (pch < pche)) + { + int ColonOk = 0; + + while (isspace(*pch)) pch++; + UserStart = UserEnd = EmailStart = EmailEnd = NULL; + + if ((*pch == '"') || (*pch == '\'')) { + UserStart = pch + 1; + + UserEnd = strchr(UserStart, *pch); + if (UserEnd == NULL) + break; ///TODO: Userfeedback?? + EmailStart = UserEnd + 1; + while (isspace(*EmailStart)) + EmailStart++; + if (UserEnd == UserStart) { + UserStart = UserEnd = NULL; + } + + if (*EmailStart == '<') { + EmailStart++; + EmailEnd = strchr(EmailStart, '>'); + if (EmailEnd == NULL) + EmailEnd = strchr(EmailStart, ','); + + } + else { + EmailEnd = strchr(EmailStart, ','); + } + if (EmailEnd == NULL) + EmailEnd = pche; + pch = EmailEnd + 1; + ColonOk = 1; + } + else { + int gt = 0; + UserStart = pch; + EmailEnd = strchr(UserStart, ','); + if (EmailEnd == NULL) { + EmailEnd = strchr(pch, '>'); + pch = NULL; + if (EmailEnd != NULL) { + gt = 1; + EmailEnd --; + } + else { + EmailEnd = pche; + } + } + else { + + pch = EmailEnd + 1; + while ((EmailEnd > UserStart) && + ((*EmailEnd == ',') || + (*EmailEnd == '>') || + (isspace(*EmailEnd)))) + { + if (*EmailEnd == '>') + gt = 1; + EmailEnd--; + } + if (EmailEnd == UserStart) + break; + } + if (gt) { + EmailStart = strchr(UserStart, '<'); + if ((EmailStart == NULL) || (EmailStart > EmailEnd)) + break; + UserEnd = EmailStart - 1; + EmailStart ++; + if (UserStart >= UserEnd) + UserStart = UserEnd = NULL; + At = strchr(EmailStart, '@'); + } + else { /* this is a local recipient... no domain, just a realname */ + At = strchr(EmailStart, '@'); + if (At == NULL) { + UserEnd = EmailEnd; + EmailEnd = NULL; + } + else { + EmailStart = UserStart; + UserStart = NULL; + } + } + } + + + if (UserStart != NULL) + StrBufPlain(UserName, UserStart, UserEnd - UserStart); + else + FlushStrBuf(UserName); + if (EmailStart != NULL) + StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart); + else + FlushStrBuf(EmailAddress); + + AddRecipient(Target, UserName, EmailAddress, EncBuf); + + + + if (*pch == ',') + pch ++; + while (isspace(*pch)) + pch ++; + } + return Target; +} + + /** * @ingroup StrBuf * @brief replaces all occurances of 'search' by 'replace' diff --git a/libcitadel/lib/tools.c b/libcitadel/lib/tools.c index 0166f0b70..da37f7f08 100644 --- a/libcitadel/lib/tools.c +++ b/libcitadel/lib/tools.c @@ -996,3 +996,20 @@ void convert_spaces_to_underscores(char *str) } +/* + * check whether the provided string needs to be qp encoded or not + */ +int CheckEncode(const char *pch, long len, const char *pche) +{ + if (pche == NULL) + pche = pch + len; + while (pch < pche) { + if (((unsigned char) *pch < 32) || + ((unsigned char) *pch > 126)) { + return 1; + } + pch++; + } + return 0; +} + diff --git a/libcitadel/tests/email_recipientstrings.txt b/libcitadel/tests/email_recipientstrings.txt new file mode 100644 index 000000000..7009b2388 --- /dev/null +++ b/libcitadel/tests/email_recipientstrings.txt @@ -0,0 +1,4 @@ +"Alexandra Weiz, Restless GmbH" , "NetIN" , " יריב ברקאי, מולטימדי" , "Завод ЖБ" +dothebart +dothebart@uncensored.citadel.org +Art Cancro , Art Cancro diff --git a/libcitadel/tests/stringbuf_conversion.c b/libcitadel/tests/stringbuf_conversion.c index 85ae46eec..e28416242 100644 --- a/libcitadel/tests/stringbuf_conversion.c +++ b/libcitadel/tests/stringbuf_conversion.c @@ -29,7 +29,7 @@ int fromstdin = 0; - +int parse_email = 0; static void TestRevalidateStrBuf(StrBuf *Buf) { CU_ASSERT(strlen(ChrPtr(Buf)) == StrLength(Buf)); @@ -136,6 +136,72 @@ static void TestRFC822DecodeStdin(void) } +static void TestEncodeEmail(void) +{ + StrBuf *Target; + StrBuf *Source; + StrBuf *UserName = NewStrBuf(); + StrBuf *EmailAddress = NewStrBuf(); + StrBuf *EncBuf = NewStrBuf(); + + Source = NewStrBuf(); + +// Source = NewStrBufPlain(HKEY("Art Cancro , Art Cancro ")); + + Source = NewStrBufPlain(HKEY("\"Alexandra Weiz, Restless GmbH\" , \"NetIN\" , \" יריב ברקאי, מולטימדי\" ")); + Target = StrBufSanitizeEmailRecipientVector( + Source, + UserName, + EmailAddress, + EncBuf + ); + + TestRevalidateStrBuf(Target); + printf("the source:>%s<\n", ChrPtr(Source)); + printf("the target:>%s<\n", ChrPtr(Target)); + FreeStrBuf(&Target); + FreeStrBuf(&UserName); + FreeStrBuf(&EmailAddress); + FreeStrBuf(&EncBuf); + + FreeStrBuf(&Source); +} + +static void TestEncodeEmailSTDIN(void) +{ + int fdin = 0;// STDIN + const char *Err; + StrBuf *Target; + StrBuf *Source; + StrBuf *UserName = NewStrBuf(); + StrBuf *EmailAddress = NewStrBuf(); + StrBuf *EncBuf = NewStrBuf(); + + Source = NewStrBuf(); + + while (fdin == 0) { + + StrBufTCP_read_line(Source, &fdin, 0, &Err); + printf("the source:>%s<\n", ChrPtr(Source)); + Target = StrBufSanitizeEmailRecipientVector( + Source, + UserName, + EmailAddress, + EncBuf + ); + + TestRevalidateStrBuf(Target); + printf("the target:>%s<\n", ChrPtr(Target)); + FreeStrBuf(&Target); + } + FreeStrBuf(&UserName); + FreeStrBuf(&EmailAddress); + FreeStrBuf(&EncBuf); + + FreeStrBuf(&Source); +} + + static void AddStrBufSimlpeTests(void) { @@ -143,14 +209,23 @@ static void AddStrBufSimlpeTests(void) CU_pTest pTest = NULL; pGroup = CU_add_suite("TestStringBufConversions", NULL, NULL); - if (!fromstdin) { - pTest = CU_add_test(pGroup, "testRFC822Decode", TestRFC822Decode); - pTest = CU_add_test(pGroup, "testRFC822Decode1", TestRFC822Decode); - pTest = CU_add_test(pGroup, "testRFC822Decode2", TestRFC822Decode); - pTest = CU_add_test(pGroup, "testRFC822Decode3", TestRFC822Decode); + if (!parse_email) { + if (!fromstdin) { + pTest = CU_add_test(pGroup, "testRFC822Decode", TestRFC822Decode); + pTest = CU_add_test(pGroup, "testRFC822Decode1", TestRFC822Decode); + pTest = CU_add_test(pGroup, "testRFC822Decode2", TestRFC822Decode); + pTest = CU_add_test(pGroup, "testRFC822Decode3", TestRFC822Decode); + } + else + pTest = CU_add_test(pGroup, "testRFC822DecodeSTDIN", TestRFC822DecodeStdin); + } + else { + if (!fromstdin) { + pTest = CU_add_test(pGroup, "TestParseEmailSTDIN", TestEncodeEmail); + } + else + pTest = CU_add_test(pGroup, "TestParseEmailSTDIN", TestEncodeEmailSTDIN); } - else - pTest = CU_add_test(pGroup, "testRFC822Decode3", TestRFC822DecodeStdin); } @@ -159,8 +234,11 @@ int main(int argc, char* argv[]) { int a; - while ((a = getopt(argc, argv, "i")) != EOF) + while ((a = getopt(argc, argv, "@i")) != EOF) switch (a) { + case '@': + parse_email = 1; + break; case 'i': fromstdin = 1; @@ -183,9 +261,9 @@ int main(int argc, char* argv[]) if (CU_TRUE == Run) { //CU_console_run_tests(); - printf("\nTests completed with return value %d.\n", CU_basic_run_tests()); + printf("\nTests completed with return value %d.\n", CU_basic_run_tests()); - ///CU_automated_run_tests(); + ///CU_automated_run_tests(); } CU_cleanup_registry(); -- 2.30.2