You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kshowmail/kshowmail/showrecordelem.cpp

633 lines
17 KiB

/***************************************************************************
showrecord.cpp - description
-------------------
begin : Thu Dec 28 2000
copyright : (C) 2000-2001 by Eggert Ehmke
email : eggert.ehmke@berlin.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "showrecordelem.h"
int const ShowRecordElem::continueShowHeaders( 0 );
int const ShowRecordElem::cancelShowHeaders( 1 );
ShowRecordElem::ShowRecordElem ()
{
//set default values
m_from = "???";
m_subject = "???";
m_size = 0;
m_pItem = NULL;
m_new = false;
markAtViewRefresh = false;
}
ShowRecordElem::ShowRecordElem( int number, TQString& uid, bool isNew )
{
//set default values
m_from = "???";
m_subject = "???";
m_size = 0;
m_pItem = NULL;
markAtViewRefresh = false;
//set given values
m_nNumber = number;
m_uid = uid;
m_new = isNew;
}
TQCString ShowRecordElem::scanHeader( const TQString& item ) const
{
TQCString headerline( "" ); //found header line
//get e.g. the "From:" line, starting with cr,lf,"From:" and ending
//with a carriage return
//build the search string
TQString searchstring( TQString( "\r\n%1:" ).arg( item ) );
//searching...
int pos1 = m_header.find( searchstring, 0, FALSE );
int pos2 = m_header.find( '\r', pos1 + 2 );
//cut out the interesting content, if we have found a matching line
//if we have found nothing, the returned string will be ""
if( pos1 >= 0 )
{
headerline = m_header.mid( pos1 + searchstring.length(), pos2 - pos1 - searchstring.length() );
}
return headerline;
}
void ShowRecordElem::setHeader( const TQString& header )
{
//store given header
m_header = header.ascii();
//extract sender and store it
TQCString from = scanHeader( "From" );
from = from.simplifyWhiteSpace();
setFrom( from );
//extract addressee and store it
TQCString to = scanHeader( "To" );
to = to.simplifyWhiteSpace();
setTo (to);
//extract subject and store it
TQCString subject = scanHeader( "Subject" );
subject = subject.simplifyWhiteSpace();
setSubject( subject );
//extract date and store it
TQCString date = scanHeader( "Date" );
setDate( date );
//extract content type
TQCString content = scanHeader( "Content-Type" );
content = content.simplifyWhiteSpace ();
//remove the stuff after the content type; see RFC 2045
int posSemicolon = content.find( ';' );
if( posSemicolon != -1 )
{
content.remove( posSemicolon, content.length() - posSemicolon );
}
//store content type
setContent (content);
}
void ShowRecordElem::setDate( const TQCString& date )
{
DwDateTime dwDate; //this class represents an RFC-822 date-time;
//see mimelib/datetime.h
//convert and store the date-time
dwDate.FromString( date );
dwDate.Parse();
m_unixDate.setTime_t( dwDate.AsUnixTime() );
}
TQString ShowRecordElem::from() const
{
return Codecs::decodeRFC2047( m_from );
}
TQString ShowRecordElem::to() const
{
return Codecs::decodeRFC2047( m_to );
}
TQString ShowRecordElem::subject() const
{
return Codecs::decodeRFC2047( m_subject );
}
TQString ShowRecordElem::date() const
{
return TDEGlobal::locale()->formatDateTime( m_unixDate, true, true );
}
TQString ShowRecordElem::strUnixTime() const
{
return m_unixDate.toString( TQt::ISODate );
}
TQString ShowRecordElem::strSize() const
{
return TQString( "%1" ).arg( m_size, 8 );
}
TQString ShowRecordElem::state() const
{
if( m_new )
return i18n( "new" );
else
return i18n( "old" );
}
void ShowRecordElem::saveOptions( TQDomDocument& doc, TQDomElement& parent )
{
//build item tag of this mail( with mail number)
TQString hdr = TQString( ITEM_MESSAGE );
hdr.append( "%1" );
hdr = hdr.arg( m_nNumber );
//create a new element and store the mail meta data in it
TQDomElement elem = doc.createElement( hdr );
elem.setAttribute( ATTRIBUTE_MAIL_NUMBER, m_nNumber );
elem.setAttribute( ATTRIBUTE_MAIL_SIZE, m_size );
elem.setAttribute( ATTRIBUTE_MAIL_UID, m_uid );
//create a sub element for the mail header in store the header in it
TQDomElement subelem = doc.createElement( ITEM_MAIL_HEADER );
subelem.appendChild( doc.createTextNode( m_header ) );
//add header element to the mail element
elem.appendChild( subelem );
//add mail element to the account (parent) element
parent.appendChild( elem );
}
void ShowRecordElem::readOptions( TQDomElement& elem )
{
//get number, size and uid
setNumber( elem.attribute( ATTRIBUTE_MAIL_NUMBER ).toInt() );
setSize( elem.attribute( ATTRIBUTE_MAIL_SIZE ).toInt() );
setUIDL( elem.attribute( ATTRIBUTE_MAIL_UID ) );
//search for the header item and read it
TQDomElement subelem = elem.namedItem( ITEM_MAIL_HEADER ).toElement();
setHeader( subelem.text() );
//the mail is not new
setNew( false );
}
void ShowRecordElem::setFrom( const TQCString & from )
{
m_from = from;
}
void ShowRecordElem::setTo( const TQCString & to )
{
m_to = to;
}
void ShowRecordElem::setSubject( const TQCString & subject )
{
m_subject = subject;
}
void ShowRecordElem::setContent( const TQCString& content )
{
m_content = content;
}
TQString ShowRecordElem::header( ) const
{
return TQString( m_header );
}
void ShowRecordElem::setUIDL( const TQString & uid )
{
m_uid = uid;
}
TQString ShowRecordElem::uidl( ) const
{
return m_uid;
}
void ShowRecordElem::setSize( int size )
{
m_size = size;
}
int ShowRecordElem::size( ) const
{
return m_size;
}
void ShowRecordElem::setNew( bool isnew )
{
m_new = isnew;
}
bool ShowRecordElem::isNew( ) const
{
return m_new;
}
void ShowRecordElem::setNumber( int n )
{
m_nNumber = n;
}
int ShowRecordElem::number( ) const
{
return m_nNumber;
}
TQString ShowRecordElem::content( ) const
{
return m_content;
}
void ShowRecordElem::setViewItem( ShowListViewItem* item )
{
m_pItem = item;
//marks the new entry if recommend by the filter
if( markAtViewRefresh )
{
//mark entry
item->setSelected( true );
//delete flag
markAtViewRefresh = false;
}
}
ShowListViewItem * ShowRecordElem::viewItem( ) const
{
return m_pItem;
}
bool ShowRecordElem::isSelected( ) const
{
if( m_pItem != NULL )
return m_pItem->isSelected();
else
return false;
}
TQString ShowRecordElem::strSizePrefix( ) const
{
TQString size;
if( m_size >= 1024 * 1024 )
{
//prefix is mega
size = TQString( "%L1M" ).arg( ( (double)m_size / ( 1024 * 1024 ) ), 0, 'f', 1 );
}
else if( m_size >= 1024 )
{
//prefix is kilo
size = TQString( "%L1K" ).arg( ( (double)m_size / 1024 ), 0, 'f', 1 );
}
else
//no prefix
size = TQString( "%L1" ).arg( m_size );
return size;
}
TQString ShowRecordElem::decodeMailBody( TQByteArray body, bool preferHTML ) const
{
TQString charset; //charset of the content
TQString encoding; //content transfer encoding
//cast given body to a TQCString
//class TQCString needs a null terminated char array to create
//an object. Therefore we append an null byte to the given mail body
body.resize( body.size() + 1 );
body[ body.size() - 1 ] = '\0';
TQCString strBody( (char *)body.data() );
//normalize line ends; remove all \r characters
for( uint i = 0; i < strBody.size(); i++ )
if( strBody[ i ] == '\r' )
strBody.remove( i, 1 );
//get boundary that is separating the parts of a multipart message
//if the header doesn't contain a boundary attribute, this messsage
//has just one part
TQString boundary = getBoundary();
//process body subject to it is a multipart messsage or not
if( boundary == "" )
{
//the message has just one body part
//get the position of the first blank line
int posBlankLine = strBody.find( "\n\n" );
//truncate body; the found blank line is separating the
//header from the message
strBody = strBody.mid( posBlankLine + 2 );
if( !strBody.isEmpty() ) //fixed bug 1773636
while( strBody[ 0 ] == '\n')
strBody.remove( 0, 1 );
//get charset of the message; it is behind the
//content type attribute in the header
charset = getCharset();
//get transfer encoding type from the header
encoding = getTransferEncoding();
}
else
{
//the message has multiple parts
//get positions of a plain text and html flag (value of the content type attribute)
int posPlainFlag = strBody.find( "text/plain", 0, false );
int posHTMLFlag = strBody.find( "text/html", 0, false );
//just decode the body, if a plain text or a HTML part is available
if( posPlainFlag != -1 || posHTMLFlag != -1 )
{
//do we want to take the HTML part?
bool hasHTML = posHTMLFlag != -1;
bool takeHTML = ( hasHTML && preferHTML ) || posPlainFlag == -1;
//now we want to extract the designated part
//While the (truncated) mail text (or the header at the first pass)
//contains a boundary attribute we will extract the designated part
//between the boundaries
int posInside; //a position inside the designated part
while( boundary != "" )
{
//get a position inside the designated part
if( takeHTML )
posInside = strBody.find( "text/html", 0, false );
else
posInside = strBody.find( "text/plain", 0, false );
//get length of the boundary
int lengthBoundary = boundary.length();
//calculate the begin and end of the part to extract
int beginPart = strBody.findRev( boundary.ascii(), posInside ) + lengthBoundary + 1;
int lengthPart = strBody.findRev( '\n', strBody.find( boundary.ascii(), posInside ) ) - beginPart;
strBody = strBody.mid( beginPart, lengthPart );
//looking for a further boundary attribute
//get the position of the first occurance of "boundary="
int posBoundary = strBody.find( "boundary=", 0, false );
if( posBoundary >= 0 )
{
//calculate positon of the first quote
int posFirstQuote = posBoundary + 9;
//get the position of closing quote
int posSecondQuote = strBody.find( '"', posFirstQuote + 1 );
//get boundary string
boundary.append( strBody.mid( posFirstQuote + 1, posSecondQuote - posFirstQuote - 1 ) );
}
else
boundary = "";
}
//now we get charset and transfer encoding if available in the extracted
//part
//get the position of the first occurance of "charset="
int posCharset = strBody.find( "charset=", 0, false );
//continue, if a charset attribute was found
if( posCharset >= 0 )
{
//calculate positon of the value
int posBeginValue = posCharset + 8;
//get end of the value
int posEndValue = strBody.find( '\n', posBeginValue ) - 1;
//get charset
charset.append( strBody.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
//remove quotes
charset.remove( '"' );
//remove all content after the first semicolon (inclusive)
int posSemicolon = charset.find( ';' );
charset = charset.left( posSemicolon );
}
//get the position of the first occurance of "charset="
int posEncoding = strBody.find( "Content-Transfer-Encoding:", 0, false );
//continue, if a charset attribute was found
if( posEncoding >= 0 )
{
//calculate positon of the value
int posBeginValue = posEncoding + 26;
//get end of the value
int posEndValue = strBody.find( '\n', posBeginValue ) - 1;
//get charset
encoding.append( strBody.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
//remove quotes and spaces
encoding = encoding.stripWhiteSpace();
encoding.remove( '"' );
}
//cut off the part header; the found blank line is separating the
//part header from the message
if( posCharset != -1 || posEncoding != -1 )
{
int posBlankLine = strBody.find( "\n\n" );
strBody = strBody.mid( posBlankLine + 2 );
if( !strBody.isEmpty() ) //fixed bug 1773636
while( strBody[ 0 ] == '\n')
strBody.remove( 0, 1 );
}
}
}
//Good things come to those who wait. We have extract the message.
//Now we have to decode the message, if it is encoded
if( encoding == "quoted-printable" && !strBody.isEmpty() ) //fixed bug 1773636
{
strBody = KCodecs::quotedPrintableDecode( strBody );
}
return TQString( strBody );
}
TQString ShowRecordElem::getBoundary( ) const
{
TQString boundary;
//check, whether it is a multipart message
if( m_content.contains( "multipart", false ) )
{
//it is a multipart message
//get the position of the first occurance of "boundary="
int posBoundary = m_header.find( "boundary=", 0, false );
//continue, if a boundary attribute was found
if( posBoundary >= 0 )
{
//calculate positon of the first quote
int posFirstQuote = posBoundary + 9;
//get the position of closing quote
int posSecondQuote = m_header.find( '"', posFirstQuote + 1 );
//get boundary string
boundary.append( m_header.mid( posFirstQuote + 1, posSecondQuote - posFirstQuote - 1 ) );
}
}
return boundary;
}
TQString ShowRecordElem::getCharset( ) const
{
TQString charset;
//get the position of the first occurance of "charset="
int posCharset = m_header.find( "charset=", 0, false );
//continue, if a charset attribute was found
if( posCharset >= 0 )
{
//calculate positon of the value
int posBeginValue = posCharset + 8;
//get end of the value
int posEndValue = m_header.find( '\r', posBeginValue ) - 1;
//get charset
charset.append( m_header.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
//remove quotes
charset.remove( '"' );
//remove all content after the first semicolon (inclusive)
int posSemicolon = charset.find( ';' );
charset = charset.left( posSemicolon );
}
return TQString( charset );
}
TQString ShowRecordElem::getTransferEncoding( ) const
{
TQString encoding;
//get the position of the first occurance of "charset="
int posEncoding = m_header.find( "Content-Transfer-Encoding:", 0, false );
//continue, if a charset attribute was found
if( posEncoding >= 0 )
{
//calculate positon of the value
int posBeginValue = posEncoding + 26;
//get end of the value
int posEndValue = m_header.find( '\r', posBeginValue ) - 1;
//get charset
encoding.append( m_header.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
//remove quotes and spaces
encoding = encoding.stripWhiteSpace();
encoding.remove( '"' );
}
return TQString( encoding );
}
int ShowRecordElem::showHeader( TQString& account )
{
//show header
TQString tsubject = subject();
TQString tmailheader = header();
//create and open the window
ShowHeaderDialog dlg( kapp->mainWidget(), account, tsubject, tmailheader );
int ret = dlg.exec();
//returns the matching value
return ret == TQDialog::Accepted ? ShowRecordElem::continueShowHeaders : ShowRecordElem::cancelShowHeaders;
}
FilterAction_Type ShowRecordElem::applyHeaderFilter( HeaderFilter* filter, TQString account, TQString& mailbox, FilterLog* log )
{
FilterAction_Type action = filter->check( from(), to(), size(), subject(), header(), account, mailbox );
//if the action is MARK, the related view entry shall be marked at the next view refresh
if( action == FActMark ) markAtViewRefresh = true;
//if the action is DELETE, we add a entry to the log
if( log == NULL )
kdError( "ShowRecordElem::applyHeaderFilter: Pointer to the filter log is NULL. Can't write to log." );
if( action == FActDelete && log != NULL )
log->addDeletedMail( sentDateTime(), from(), account, subject() );
if( action == FActMove && log != NULL )
log->addMovedMail( sentDateTime(), from(), account, subject(), mailbox );
return action;
}
TQDateTime ShowRecordElem::sentDateTime() const
{
return m_unixDate;
}
void ShowRecordElem::writeToMoveLog( FilterLog * log, TQString account, TQString mailbox )
{
log->addMovedMail( sentDateTime(), from(), account, subject(), mailbox );
}
void ShowRecordElem::writeToDeleteLog( FilterLog * log, TQString account )
{
log->addDeletedMail( sentDateTime(), from(), account, subject() );
}
void ShowRecordElem::setMarkAtNextViewRefresh( )
{
markAtViewRefresh = true;
}