libemailfunctions

email.cpp
1/*
2 This file is part of tdepim.
3 Copyright (c) 2004 TDEPIM developers
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20#include "email.h"
21
22#include <kdebug.h>
23#include <tdelocale.h>
24#include <kidna.h>
25#include <kmime_util.h>
26
27#include <tqregexp.h>
28
29//-----------------------------------------------------------------------------
30TQStringList KPIM::splitEmailAddrList(const TQString& aStr)
31{
32 // Features:
33 // - always ignores quoted characters
34 // - ignores everything (including parentheses and commas)
35 // inside quoted strings
36 // - supports nested comments
37 // - ignores everything (including double quotes and commas)
38 // inside comments
39
40 TQStringList list;
41
42 if (aStr.isEmpty())
43 return list;
44
45 TQString addr;
46 uint addrstart = 0;
47 int commentlevel = 0;
48 bool insidequote = false;
49
50 for (uint index=0; index<aStr.length(); index++) {
51 // the following conversion to latin1 is o.k. because
52 // we can safely ignore all non-latin1 characters
53 switch (aStr[index].latin1()) {
54 case '"' : // start or end of quoted string
55 if (commentlevel == 0)
56 insidequote = !insidequote;
57 break;
58 case '(' : // start of comment
59 if (!insidequote)
60 commentlevel++;
61 break;
62 case ')' : // end of comment
63 if (!insidequote) {
64 if (commentlevel > 0)
65 commentlevel--;
66 else {
67 kdDebug(5300) << "Error in address splitting: Unmatched ')'"
68 << endl;
69 return list;
70 }
71 }
72 break;
73 case '\\' : // quoted character
74 index++; // ignore the quoted character
75 break;
76 case ',' :
77 case ';' :
78 if (!insidequote && (commentlevel == 0)) {
79 addr = aStr.mid(addrstart, index-addrstart);
80 if (!addr.isEmpty())
81 list += addr.simplifyWhiteSpace();
82 addrstart = index+1;
83 }
84 break;
85 }
86 }
87 // append the last address to the list
88 if (!insidequote && (commentlevel == 0)) {
89 addr = aStr.mid(addrstart, aStr.length()-addrstart);
90 if (!addr.isEmpty())
91 list += addr.simplifyWhiteSpace();
92 }
93 else
94 kdDebug(5300) << "Error in address splitting: "
95 << "Unexpected end of address list"
96 << endl;
97
98 return list;
99}
100
101//-----------------------------------------------------------------------------
102// Used by KPIM::splitAddress(...) and KPIM::getFirstEmailAddress(...).
103KPIM::EmailParseResult splitAddressInternal( const TQCString& address,
104 TQCString & displayName,
105 TQCString & addrSpec,
106 TQCString & comment,
107 bool allowMultipleAddresses )
108{
109// kdDebug() << "KMMessage::splitAddress( " << address << " )" << endl;
110
111 displayName = "";
112 addrSpec = "";
113 comment = "";
114
115 // these strings are later copied to displayName resp. addrSpec resp. comment
116 // we don't operate directly on those variables, since as ByteArray deriverates
117 // they have a miserable performance on operator+
118 TQString dName;
119 TQString aSpec;
120 TQString cmmt;
121
122 if ( address.isEmpty() )
123 return KPIM::AddressEmpty;
124
125 // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
126 // The purpose is to extract a displayable string from the mailboxes.
127 // Comments in the addr-spec are not handled. No error checking is done.
128
129 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
130 bool inQuotedString = false;
131 int commentLevel = 0;
132 bool stop = false;
133
134 for ( const char* p = address.data(); *p && !stop; ++p ) {
135 switch ( context ) {
136 case TopLevel : {
137 switch ( *p ) {
138 case '"' : inQuotedString = !inQuotedString;
139 dName += *p;
140 break;
141 case '(' : if ( !inQuotedString ) {
142 context = InComment;
143 commentLevel = 1;
144 }
145 else
146 dName += *p;
147 break;
148 case '<' : if ( !inQuotedString ) {
149 context = InAngleAddress;
150 }
151 else
152 dName += *p;
153 break;
154 case '\\' : // quoted character
155 dName += *p;
156 ++p; // skip the '\'
157 if ( *p )
158 dName += *p;
159 else
160 return KPIM::UnexpectedEnd;
161 break;
162 case ',' :
163 case ';' : if ( !inQuotedString ) {
164 if ( allowMultipleAddresses )
165 stop = true;
166 else
167 return KPIM::UnexpectedComma;
168 }
169 else
170 dName += *p;
171 break;
172 default : dName += *p;
173 }
174 break;
175 }
176 case InComment : {
177 switch ( *p ) {
178 case '(' : ++commentLevel;
179 cmmt += *p;
180 break;
181 case ')' : --commentLevel;
182 if ( commentLevel == 0 ) {
183 context = TopLevel;
184 cmmt += ' '; // separate the text of several comments
185 }
186 else
187 cmmt += *p;
188 break;
189 case '\\' : // quoted character
190 cmmt += *p;
191 ++p; // skip the '\'
192 if ( *p )
193 cmmt += *p;
194 else
195 return KPIM::UnexpectedEnd;
196 break;
197 default : cmmt += *p;
198 }
199 break;
200 }
201 case InAngleAddress : {
202 switch ( *p ) {
203 case '"' : inQuotedString = !inQuotedString;
204 aSpec += *p;
205 break;
206 case '>' : if ( !inQuotedString ) {
207 context = TopLevel;
208 }
209 else
210 aSpec += *p;
211 break;
212 case '\\' : // quoted character
213 aSpec += *p;
214 ++p; // skip the '\'
215 if ( *p )
216 aSpec += *p;
217 else
218 return KPIM::UnexpectedEnd;
219 break;
220 default : aSpec += *p;
221 }
222 break;
223 }
224 } // switch ( context )
225 }
226 // check for errors
227 if ( inQuotedString )
228 return KPIM::UnbalancedQuote;
229 if ( context == InComment )
230 return KPIM::UnbalancedParens;
231 if ( context == InAngleAddress )
232 return KPIM::UnclosedAngleAddr;
233
234
235 displayName = dName.stripWhiteSpace().latin1();
236 comment = cmmt.stripWhiteSpace().latin1();
237 addrSpec = aSpec.stripWhiteSpace().latin1();
238
239 if ( addrSpec.isEmpty() ) {
240 if ( displayName.isEmpty() )
241 return KPIM::NoAddressSpec;
242 else {
243 addrSpec = displayName;
244 displayName.truncate( 0 );
245 }
246 }
247/*
248 kdDebug() << "display-name : \"" << displayName << "\"" << endl;
249 kdDebug() << "comment : \"" << comment << "\"" << endl;
250 kdDebug() << "addr-spec : \"" << addrSpec << "\"" << endl;
251*/
252 return KPIM::AddressOk;
253}
254
255
256//-----------------------------------------------------------------------------
258 TQCString & displayName,
259 TQCString & addrSpec,
260 TQCString & comment )
261{
262 return splitAddressInternal( address, displayName, addrSpec, comment,
263 false /* don't allow multiple addresses */ );
264}
265
266
267//-----------------------------------------------------------------------------
269 TQString & displayName,
270 TQString & addrSpec,
271 TQString & comment )
272{
273 TQCString d, a, c;
274 KPIM::EmailParseResult result = splitAddress( address.utf8(), d, a, c );
275 if ( result == AddressOk ) {
276 displayName = TQString::fromUtf8( d );
277 addrSpec = TQString::fromUtf8( a );
278 comment = TQString::fromUtf8( c );
279 }
280 return result;
281}
282
283
284//-----------------------------------------------------------------------------
286{
287 // If we are passed an empty string bail right away no need to process further
288 // and waste resources
289 if ( aStr.isEmpty() ) {
290 return AddressEmpty;
291 }
292
293 // count how many @'s are in the string that is passed to us
294 // if 0 or > 1 take action
295 // at this point to many @'s cannot bail out right away since
296 // @ is allowed in qoutes, so we use a bool to keep track
297 // and then make a judgement further down in the parser
298 // FIXME count only @ not in double quotes
299
300 bool tooManyAtsFlag = false;
301
302 int atCount = aStr.contains('@');
303 if ( atCount > 1 ) {
304 tooManyAtsFlag = true;;
305 } else if ( atCount == 0 ) {
306 return TooFewAts;
307 }
308
309 // The main parser, try and catch all weird and wonderful
310 // mistakes users and/or machines can create
311
312 enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
313 bool inQuotedString = false;
314 int commentLevel = 0;
315
316 unsigned int strlen = aStr.length();
317
318 for ( unsigned int index=0; index < strlen; index++ ) {
319 switch ( context ) {
320 case TopLevel : {
321 switch ( aStr[index].latin1() ) {
322 case '"' : inQuotedString = !inQuotedString;
323 break;
324 case '(' :
325 if ( !inQuotedString ) {
326 context = InComment;
327 commentLevel = 1;
328 }
329 break;
330 case '[' :
331 if ( !inQuotedString ) {
332 return InvalidDisplayName;
333 }
334 break;
335 case ']' :
336 if ( !inQuotedString ) {
337 return InvalidDisplayName;
338 }
339 break;
340 case ':' :
341 if ( !inQuotedString ) {
342 return DisallowedChar;
343 }
344 break;
345 case '<' :
346 if ( !inQuotedString ) {
347 context = InAngleAddress;
348 }
349 break;
350 case '\\' : // quoted character
351 ++index; // skip the '\'
352 if (( index + 1 )> strlen ) {
353 return UnexpectedEnd;
354 }
355 break;
356 case ',' :
357 case ';' :
358 if ( !inQuotedString )
359 return UnexpectedComma;
360 break;
361 case ')' :
362 if ( !inQuotedString )
363 return UnbalancedParens;
364 break;
365 case '>' :
366 if ( !inQuotedString )
367 return UnopenedAngleAddr;
368 break;
369 case '@' :
370 if ( !inQuotedString ) {
371 if ( index == 0 ) { // Missing local part
372 return MissingLocalPart;
373 } else if( index == strlen-1 ) {
374 return MissingDomainPart;
375 }
376 } else if ( inQuotedString ) {
377 --atCount;
378 if ( atCount == 1 ) {
379 tooManyAtsFlag = false;
380 }
381 }
382 break;
383 }
384 break;
385 }
386 case InComment : {
387 switch ( aStr[index] ) {
388 case '(' : ++commentLevel;
389 break;
390 case ')' : --commentLevel;
391 if ( commentLevel == 0 ) {
392 context = TopLevel;
393 }
394 break;
395 case '\\' : // quoted character
396 ++index; // skip the '\'
397 if (( index + 1 )> strlen ) {
398 return UnexpectedEnd;
399 }
400 break;
401 }
402 break;
403 }
404
405 case InAngleAddress : {
406 switch ( aStr[index] ) {
407 case ',' :
408 case ';' :
409 if ( !inQuotedString ) {
410 return UnexpectedComma;
411 }
412 break;
413 case '"' : inQuotedString = !inQuotedString;
414 break;
415 case '@' :
416 if ( inQuotedString ) {
417 --atCount;
418 if ( atCount == 1 ) {
419 tooManyAtsFlag = false;
420 }
421 }
422 break;
423 case '>' :
424 if ( !inQuotedString ) {
425 context = TopLevel;
426 break;
427 }
428 break;
429 case '\\' : // quoted character
430 ++index; // skip the '\'
431 if (( index + 1 )> strlen ) {
432 return UnexpectedEnd;
433 }
434 break;
435 }
436 break;
437 }
438 }
439 }
440
441 if ( atCount == 0 && !inQuotedString )
442 return TooFewAts;
443
444 if ( inQuotedString )
445 return UnbalancedQuote;
446
447 if ( context == InComment )
448 return UnbalancedParens;
449
450 if ( context == InAngleAddress )
451 return UnclosedAngleAddr;
452
453 if ( tooManyAtsFlag ) {
454 return TooManyAts;
455 }
456 return AddressOk;
457}
458
459//-----------------------------------------------------------------------------
461{
462 switch ( errorCode ) {
463 case TooManyAts :
464 return i18n("The email address you entered is not valid because it "
465 "contains more than one @. "
466 "You will not create valid messages if you do not "
467 "change your address.");
468 case TooFewAts :
469 return i18n("The email address you entered is not valid because it "
470 "does not contain a @."
471 "You will not create valid messages if you do not "
472 "change your address.");
473 case AddressEmpty :
474 return i18n("You have to enter something in the email address field.");
475 case MissingLocalPart :
476 return i18n("The email address you entered is not valid because it "
477 "does not contain a local part.");
478 case MissingDomainPart :
479 return i18n("The email address you entered is not valid because it "
480 "does not contain a domain part.");
481 case UnbalancedParens :
482 return i18n("The email address you entered is not valid because it "
483 "contains unclosed comments/brackets.");
484 case AddressOk :
485 return i18n("The email address you entered is valid.");
486 case UnclosedAngleAddr :
487 return i18n("The email address you entered is not valid because it "
488 "contains an unclosed anglebracket.");
489 case UnopenedAngleAddr :
490 return i18n("The email address you entered is not valid because it "
491 "contains an unopened anglebracket.");
492 case UnexpectedComma :
493 return i18n("The email address you have entered is not valid because it "
494 "contains an unexpected comma.");
495 case UnexpectedEnd :
496 return i18n("The email address you entered is not valid because it ended "
497 "unexpectedly, this probably means you have used an escaping type "
498 "character like an \\ as the last character in your email "
499 "address.");
500 case UnbalancedQuote :
501 return i18n("The email address you entered is not valid because it "
502 "contains quoted text which does not end.");
503 case NoAddressSpec :
504 return i18n("The email address you entered is not valid because it "
505 "does not seem to contain an actual email address, i.e. "
506 "something of the form joe@kde.org.");
507 case DisallowedChar :
508 return i18n("The email address you entered is not valid because it "
509 "contains an illegal character.");
510 case InvalidDisplayName :
511 return i18n("The email address you have entered is not valid because it "
512 "contains an invalid displayname.");
513 }
514 return i18n("Unknown problem with email address");
515}
516
517//-----------------------------------------------------------------------------
518bool KPIM::isValidSimpleEmailAddress( const TQString& aStr )
519{
520 // If we are passed an empty string bail right away no need to process further
521 // and waste resources
522 if ( aStr.isEmpty() ) {
523 return false;
524 }
525
526 int atChar = aStr.findRev( '@' );
527 TQString domainPart = aStr.mid( atChar + 1);
528 TQString localPart = aStr.left( atChar );
529 bool tooManyAtsFlag = false;
530 bool inQuotedString = false;
531 int atCount = localPart.contains( '@' );
532
533 unsigned int strlen = localPart.length();
534 for ( unsigned int index=0; index < strlen; index++ ) {
535 switch( localPart[ index ].latin1() ) {
536 case '"' : inQuotedString = !inQuotedString;
537 break;
538 case '@' :
539 if ( inQuotedString ) {
540 --atCount;
541 if ( atCount == 0 ) {
542 tooManyAtsFlag = false;
543 }
544 }
545 break;
546 }
547 }
548
549 TQString addrRx = "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@";
550 if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
551 addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
552 }
553 if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
554 addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
555 } else {
556 addrRx += "[\\w-]+(\\.[\\w-]+)*";
557 }
558 TQRegExp rx( addrRx );
559 return rx.exactMatch( aStr ) && !tooManyAtsFlag;
560}
561
562//-----------------------------------------------------------------------------
564{
565 return i18n("The email address you entered is not valid because it "
566 "does not seem to contain an actual email address, i.e. "
567 "something of the form joe@kde.org.");
568}
569//-----------------------------------------------------------------------------
570TQCString KPIM::getEmailAddress( const TQCString & address )
571{
572 TQCString dummy1, dummy2, addrSpec;
574 splitAddressInternal( address, dummy1, addrSpec, dummy2,
575 false /* don't allow multiple addresses */ );
576 if ( result != AddressOk ) {
577 addrSpec = TQCString();
578 kdDebug() // << k_funcinfo << "\n"
579 << "Input: aStr\nError:"
580 << emailParseResultToString( result ) << endl;
581 }
582
583 return addrSpec;
584}
585
586
587//-----------------------------------------------------------------------------
588TQString KPIM::getEmailAddress( const TQString & address )
589{
590 return TQString::fromUtf8( getEmailAddress( address.utf8() ) );
591}
592
593
594//-----------------------------------------------------------------------------
595TQCString KPIM::getFirstEmailAddress( const TQCString & addresses )
596{
597 TQCString dummy1, dummy2, addrSpec;
599 splitAddressInternal( addresses, dummy1, addrSpec, dummy2,
600 true /* allow multiple addresses */ );
601 if ( result != AddressOk ) {
602 addrSpec = TQCString();
603 kdDebug() // << k_funcinfo << "\n"
604 << "Input: aStr\nError:"
605 << emailParseResultToString( result ) << endl;
606 }
607
608 return addrSpec;
609}
610
611
612//-----------------------------------------------------------------------------
613TQString KPIM::getFirstEmailAddress( const TQString & addresses )
614{
615 return TQString::fromUtf8( getFirstEmailAddress( addresses.utf8() ) );
616}
617
618
619//-----------------------------------------------------------------------------
620bool KPIM::getNameAndMail(const TQString& aStr, TQString& name, TQString& mail)
621{
622 name = TQString();
623 mail = TQString();
624
625 const int len=aStr.length();
626 const char cQuotes = '"';
627
628 bool bInComment = false;
629 bool bInQuotesOutsideOfEmail = false;
630 int i=0, iAd=0, iMailStart=0, iMailEnd=0;
631 TQChar c;
632 unsigned int commentstack = 0;
633
634 // Find the '@' of the email address
635 // skipping all '@' inside "(...)" comments:
636 while( i < len ){
637 c = aStr[i];
638 if( '(' == c ) commentstack++;
639 if( ')' == c ) commentstack--;
640 bInComment = commentstack != 0;
641 if( '"' == c && !bInComment )
642 bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail;
643
644 if( !bInComment && !bInQuotesOutsideOfEmail ){
645 if( '@' == c ){
646 iAd = i;
647 break; // found it
648 }
649 }
650 ++i;
651 }
652
653 if ( !iAd ) {
654 // We suppose the user is typing the string manually and just
655 // has not finished typing the mail address part.
656 // So we take everything that's left of the '<' as name and the rest as mail
657 for( i = 0; len > i; ++i ) {
658 c = aStr[i];
659 if( '<' != c )
660 name.append( c );
661 else
662 break;
663 }
664 mail = aStr.mid( i+1 );
665 if ( mail.endsWith( ">" ) )
666 mail.truncate( mail.length() - 1 );
667
668 } else {
669 // Loop backwards until we find the start of the string
670 // or a ',' that is outside of a comment
671 // and outside of quoted text before the leading '<'.
672 bInComment = false;
673 bInQuotesOutsideOfEmail = false;
674 for( i = iAd-1; 0 <= i; --i ) {
675 c = aStr[i];
676 if( bInComment ) {
677 if( '(' == c ) {
678 if( !name.isEmpty() )
679 name.prepend( ' ' );
680 bInComment = false;
681 } else {
682 name.prepend( c ); // all comment stuff is part of the name
683 }
684 }else if( bInQuotesOutsideOfEmail ){
685 if( cQuotes == c )
686 bInQuotesOutsideOfEmail = false;
687 else if ( c != '\\' )
688 name.prepend( c );
689 }else{
690 // found the start of this addressee ?
691 if( ',' == c )
692 break;
693 // stuff is before the leading '<' ?
694 if( iMailStart ){
695 if( cQuotes == c )
696 bInQuotesOutsideOfEmail = true; // end of quoted text found
697 else {
698 name.prepend( c );
699 }
700 }else{
701 switch( c ){
702 case '<':
703 iMailStart = i;
704 break;
705 case ')':
706 if( !name.isEmpty() )
707 name.prepend( ' ' );
708 bInComment = true;
709 break;
710 default:
711 if( ' ' != c )
712 mail.prepend( c );
713 }
714 }
715 }
716 }
717
718 name = name.simplifyWhiteSpace();
719 mail = mail.simplifyWhiteSpace();
720
721 if( mail.isEmpty() )
722 return false;
723
724 mail.append('@');
725
726 // Loop forward until we find the end of the string
727 // or a ',' that is outside of a comment
728 // and outside of quoted text behind the trailing '>'.
729 bInComment = false;
730 bInQuotesOutsideOfEmail = false;
731 int parenthesesNesting = 0;
732 for( i = iAd+1; len > i; ++i ) {
733 c = aStr[i];
734 if( bInComment ){
735 if( ')' == c ){
736 if ( --parenthesesNesting == 0 ) {
737 bInComment = false;
738 if( !name.isEmpty() )
739 name.append( ' ' );
740 } else {
741 // nested ")", add it
742 name.append( ')' ); // name can't be empty here
743 }
744 } else {
745 if( '(' == c ) {
746 // nested "("
747 ++parenthesesNesting;
748 }
749 name.append( c ); // all comment stuff is part of the name
750 }
751 }else if( bInQuotesOutsideOfEmail ){
752 if( cQuotes == c )
753 bInQuotesOutsideOfEmail = false;
754 else if ( c != '\\' )
755 name.append( c );
756 }else{
757 // found the end of this addressee ?
758 if( ',' == c )
759 break;
760 // stuff is behind the trailing '>' ?
761 if( iMailEnd ){
762 if( cQuotes == c )
763 bInQuotesOutsideOfEmail = true; // start of quoted text found
764 else
765 name.append( c );
766 }else{
767 switch( c ){
768 case '>':
769 iMailEnd = i;
770 break;
771 case '(':
772 if( !name.isEmpty() )
773 name.append( ' ' );
774 if ( ++parenthesesNesting > 0 )
775 bInComment = true;
776 break;
777 default:
778 if( ' ' != c )
779 mail.append( c );
780 }
781 }
782 }
783 }
784 }
785
786 name = name.simplifyWhiteSpace();
787 mail = mail.simplifyWhiteSpace();
788
789 return ! (name.isEmpty() || mail.isEmpty());
790}
791
792
793//-----------------------------------------------------------------------------
794bool KPIM::compareEmail( const TQString& email1, const TQString& email2,
795 bool matchName )
796{
797 TQString e1Name, e1Email, e2Name, e2Email;
798
799 getNameAndMail( email1, e1Name, e1Email );
800 getNameAndMail( email2, e2Name, e2Email );
801
802 return e1Email == e2Email &&
803 ( !matchName || ( e1Name == e2Name ) );
804}
805
806
807//-----------------------------------------------------------------------------
808TQString KPIM::normalizedAddress( const TQString & displayName,
809 const TQString & addrSpec,
810 const TQString & comment )
811{
812 TQString realDisplayName = displayName;
813 realDisplayName.remove( TQChar( 0x202D ) );
814 realDisplayName.remove( TQChar( 0x202E ) );
815 realDisplayName.remove( TQChar( 0x202A ) );
816 realDisplayName.remove( TQChar( 0x202B ) );
817
818 if ( realDisplayName.isEmpty() && comment.isEmpty() )
819 return addrSpec;
820 else if ( comment.isEmpty() )
821 return quoteNameIfNecessary( realDisplayName ) + " <" + addrSpec + ">";
822 else if ( realDisplayName.isEmpty() ) {
823 TQString commentStr = comment;
824 return quoteNameIfNecessary( commentStr ) + " <" + addrSpec + ">";
825 }
826 else
827 return realDisplayName + " (" + comment + ") <" + addrSpec + ">";
828}
829
830
831//-----------------------------------------------------------------------------
832TQString KPIM::decodeIDN( const TQString & addrSpec )
833{
834 const int atPos = addrSpec.findRev( '@' );
835 if ( atPos == -1 )
836 return addrSpec;
837
838 TQString idn = KIDNA::toUnicode( addrSpec.mid( atPos + 1 ) );
839 if ( idn.isEmpty() )
840 return TQString();
841
842 return addrSpec.left( atPos + 1 ) + idn;
843}
844
845
846//-----------------------------------------------------------------------------
847TQString KPIM::encodeIDN( const TQString & addrSpec )
848{
849 const int atPos = addrSpec.findRev( '@' );
850 if ( atPos == -1 )
851 return addrSpec;
852
853 TQString idn = KIDNA::toAscii( addrSpec.mid( atPos + 1 ) );
854 if ( idn.isEmpty() )
855 return addrSpec;
856
857 return addrSpec.left( atPos + 1 ) + idn;
858}
859
860
861//-----------------------------------------------------------------------------
862TQString KPIM::normalizeAddressesAndDecodeIDNs( const TQString & str )
863{
864// kdDebug() << "KPIM::normalizeAddressesAndDecodeIDNs( \""
865// << str << "\" )" << endl;
866 if( str.isEmpty() )
867 return str;
868
869 const TQStringList addressList = KPIM::splitEmailAddrList( str );
870 TQStringList normalizedAddressList;
871
872 TQCString displayName, addrSpec, comment;
873
874 for( TQStringList::ConstIterator it = addressList.begin();
875 ( it != addressList.end() );
876 ++it ) {
877 if( !(*it).isEmpty() ) {
878 if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment )
879 == AddressOk ) {
880
881 displayName = KMime::decodeRFC2047String(displayName).utf8();
882 comment = KMime::decodeRFC2047String(comment).utf8();
883
884 normalizedAddressList <<
885 normalizedAddress( TQString::fromUtf8( displayName ),
886 decodeIDN( TQString::fromUtf8( addrSpec ) ),
887 TQString::fromUtf8( comment ) );
888 }
889 else {
890 kdDebug() << "splitting address failed: " << *it << endl;
891 }
892 }
893 }
894/*
895 kdDebug() << "normalizedAddressList: \""
896 << normalizedAddressList.join( ", " )
897 << "\"" << endl;
898*/
899 return normalizedAddressList.join( ", " );
900}
901
902//-----------------------------------------------------------------------------
903TQString KPIM::normalizeAddressesAndEncodeIDNs( const TQString & str )
904{
905 //kdDebug() << "KPIM::normalizeAddressesAndEncodeIDNs( \""
906 // << str << "\" )" << endl;
907 if( str.isEmpty() )
908 return str;
909
910 const TQStringList addressList = KPIM::splitEmailAddrList( str );
911 TQStringList normalizedAddressList;
912
913 TQCString displayName, addrSpec, comment;
914
915 for( TQStringList::ConstIterator it = addressList.begin();
916 ( it != addressList.end() );
917 ++it ) {
918 if( !(*it).isEmpty() ) {
919 if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment )
920 == AddressOk ) {
921
922 normalizedAddressList <<
923 normalizedAddress( TQString::fromUtf8( displayName ),
924 encodeIDN( TQString::fromUtf8( addrSpec ) ),
925 TQString::fromUtf8( comment ) );
926 }
927 else {
928 kdDebug() << "splitting address failed: " << *it << endl;
929 }
930 }
931 }
932
933 /*
934 kdDebug() << "normalizedAddressList: \""
935 << normalizedAddressList.join( ", " )
936 << "\"" << endl;
937 */
938 return normalizedAddressList.join( ", " );
939}
940
941
942//-----------------------------------------------------------------------------
943// Escapes unescaped doublequotes in str.
944static TQString escapeQuotes( const TQString & str )
945{
946 if ( str.isEmpty() )
947 return TQString();
948
949 TQString escaped;
950 // reserve enough memory for the worst case ( """..."" -> \"\"\"...\"\" )
951 escaped.reserve( 2*str.length() );
952 unsigned int len = 0;
953 for ( unsigned int i = 0; i < str.length(); ++i, ++len ) {
954 if ( str[i] == '"' ) { // unescaped doublequote
955 escaped[len] = '\\';
956 ++len;
957 }
958 else if ( str[i] == '\\' ) { // escaped character
959 escaped[len] = '\\';
960 ++len;
961 ++i;
962 if ( i >= str.length() ) // handle trailing '\' gracefully
963 break;
964 }
965 escaped[len] = str[i];
966 }
967 escaped.truncate( len );
968 return escaped;
969}
970
971//-----------------------------------------------------------------------------
972TQString KPIM::quoteNameIfNecessary( const TQString &str )
973{
974 TQString quoted = str;
975
976 TQRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
977 // avoid double quoting
978 if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
979 quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
980 }
981 else if ( quoted.find( needQuotes ) != -1 ) {
982 quoted = "\"" + escapeQuotes( quoted ) + "\"";
983 }
984
985 return quoted;
986}
987
TDE_EXPORT TQString normalizeAddressesAndEncodeIDNs(const TQString &str)
Normalizes all email addresses in the given list and encodes all IDNs in punycode.
Definition: email.cpp:903
TDE_EXPORT TQString encodeIDN(const TQString &addrSpec)
Encodes the domain part of the given addr-spec in punycode if it's an IDN.
Definition: email.cpp:847
TDE_EXPORT TQString simpleEmailAddressErrorMsg()
Returns a i18n string to be used in msgboxes this allows for error messages to be the same across the...
Definition: email.cpp:563
TDE_EXPORT TQString quoteNameIfNecessary(const TQString &str)
Add quote characters around the given string if it contains a character that makes that necessary,...
Definition: email.cpp:972
TDE_EXPORT TQStringList splitEmailAddrList(const TQString &aStr)
Split a comma separated list of email addresses.
Definition: email.cpp:30
TDE_EXPORT bool compareEmail(const TQString &email1, const TQString &email2, bool matchName)
Compare two email addresses.
Definition: email.cpp:794
TDE_EXPORT EmailParseResult splitAddress(const TQCString &address, TQCString &displayName, TQCString &addrSpec, TQCString &comment)
Splits the given address into display name, email address and comment.
Definition: email.cpp:257
TDE_EXPORT TQString normalizeAddressesAndDecodeIDNs(const TQString &addresses)
Normalizes all email addresses in the given list and decodes all IDNs.
Definition: email.cpp:862
TDE_EXPORT TQString decodeIDN(const TQString &addrSpec)
Decodes the punycode domain part of the given addr-spec if it's an IDN.
Definition: email.cpp:832
TDE_EXPORT bool getNameAndMail(const TQString &aStr, TQString &name, TQString &mail)
Return email address and name from string.
Definition: email.cpp:620
TDE_EXPORT TQString normalizedAddress(const TQString &displayName, const TQString &addrSpec, const TQString &comment)
Returns a normalized address built from the given parts.
Definition: email.cpp:808
TDE_EXPORT TQCString getFirstEmailAddress(const TQCString &addresses)
Returns the pure email address (addr-spec in RFC2822) of the first email address of a list of address...
Definition: email.cpp:595
TDE_EXPORT TQString emailParseResultToString(EmailParseResult errorCode)
Translate the enum errorcodes from emailParseResult into i18n'd strings that can be used for msg boxe...
Definition: email.cpp:460
TDE_EXPORT TQCString getEmailAddress(const TQCString &address)
Returns the pure email address (addr-spec in RFC2822) of the given address (mailbox in RFC2822).
Definition: email.cpp:570
TDE_EXPORT bool isValidSimpleEmailAddress(const TQString &aStr)
Validates an email address in the form of joe@example.org.
Definition: email.cpp:518
EmailParseResult
Result type for splitAddress, isValidEmailAddress.
Definition: email.h:43
TDE_EXPORT EmailParseResult isValidEmailAddress(const TQString &aStr)
Validates an email address in the form of "Joe User" joe@example.org.
Definition: email.cpp:285