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.
633 lines
17 KiB
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
|
|
TQCString searchstring( TQString( "\r\n%1:" ).arg( item ).utf8() );
|
|
|
|
//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;
|
|
}
|
|
|
|
|