# showrecordelem.cpp

    00001 /***************************************************************************
    00002                           showrecord.cpp  -  description
    00003                              -------------------
    00004     begin                : Thu Dec 28 2000
    00005     copyright            : (C) 2000-2001 by Eggert Ehmke
    00006     email                : eggert.ehmke@berlin.de
    00007  ***************************************************************************/
    00008
    00009 /***************************************************************************
    00010  *                                                                         *
    00011  *   This program is free software; you can redistribute it and/or modify  *
    00012  *   it under the terms of the GNU General Public License as published by  *
    00013  *   the Free Software Foundation; either version 2 of the License, or     *
    00014  *   (at your option) any later version.                                   *
    00015  *                                                                         *
    00016  ***************************************************************************/
    00017
    00018
    00019
    00020
    00021 #include "showrecordelem.h"
    00022
    00023 int const ShowRecordElem::continueShowHeaders( 0 );
    00024 int const ShowRecordElem::cancelShowHeaders( 1 );
    00025
    00026 ShowRecordElem::ShowRecordElem ()
    00027 {
    00028   //set default values
    00029   m_from = "???";
    00030   m_subject = "???";
    00031   m_size = 0;
    00032   m_pItem = NULL;
    00033   m_new = false;
    00034   m_pFilter = NULL;
    00035 }
    00036
    00037 ShowRecordElem::ShowRecordElem( int number, TQString& uid, bool isNew )
    00038 {
    00039   //set default values
    00040   m_from = "???";
    00041   m_subject = "???";
    00042   m_size = 0;
    00043   m_pItem = NULL;
    00044   m_pFilter = NULL;
    00045
    00046   //set given values
    00047   m_nNumber = number;
    00048   m_uid = uid;
    00049   m_new = isNew;
    00050 }
    00051
    00052
    00053 TQCString ShowRecordElem::scanHeader( const TQString& item ) const
    00054 {
    00055   TQCString headerline( "" );      //found header line
    00056
    00057   //get e.g. the "From:" line, starting with cr,lf,"From:" and ending
    00058   //with a carriage return
    00059
    00060   //build the search string
    00061   TQString searchstring( TQString( "\r\n%1:" ).arg( item ) );
    00062
    00063   //searching...
    00064   int pos1 = m_header.find( searchstring, 0, FALSE );
    00065   int pos2 = m_header.find( '\r', pos1 + 2 );
    00066
    00067   //cut out the interesting content, if we have found a matching line
    00068   //if we have found nothing, the returned string will be ""
    00069   if( pos1 >= 0 )
    00070   {
    00071     headerline = m_header.mid( pos1 + searchstring.length(), pos2 - pos1 - searchstring.length() );
    00072   }
    00073
    00074   return headerline;
    00075 }
    00076
    00077 void ShowRecordElem::setHeader( const TQString& header )
    00078 {
    00079   //store given header
    00080   m_header = header.ascii();
    00081
    00082   //extract sender and store it
    00083   TQCString from = scanHeader( "From" );
    00084   from = from.simplifyWhiteSpace();
    00085   setFrom( from );
    00086
    00087   //extract addressee and store it
    00088   TQCString to = scanHeader( "To" );
    00089   to = to.simplifyWhiteSpace();
    00090   setTo (to);
    00091
    00092   //extract subject and store it
    00093   TQCString subject = scanHeader( "Subject" );
    00094   subject = subject.simplifyWhiteSpace();
    00095   setSubject( subject );
    00096
    00097   //extract date and store it
    00098   TQCString date = scanHeader( "Date" );
    00099   setDate( date );
    00100
    00101   //extract content type
    00102   TQCString content = scanHeader( "Content-Type" );
    00103   content = content.simplifyWhiteSpace ();
    00104
    00105   //remove the stuff after the content type; see RFC 2045
    00106   int posSemicolon = content.find( ';' );
    00107   if( posSemicolon != -1 )
    00108   {
    00109     content.remove( posSemicolon, content.length() - posSemicolon );
    00110   }
    00111
    00112   //store content type
    00113   setContent (content);
    00114 }
    00115
    00116 void ShowRecordElem::setDate( const TQCString& date )
    00117 {
    00118   DwDateTime dwDate;      //this class represents an RFC-822 date-time;
    00119                           //see mimelib/datetime.h
    00120
    00121   //convert and store the date-time
    00122   dwDate.FromString( date );
    00123   dwDate.Parse();
    00124   m_unixDate.setTime_t( dwDate.AsUnixTime() );
    00125 }
    00126
    00127 TQString ShowRecordElem::from() const
    00128 {
    00129   return Codecs::decodeRFC2047( m_from );
    00130 }
    00131
    00132 TQString ShowRecordElem::to() const
    00133 {
    00134   return Codecs::decodeRFC2047( m_to );
    00135 }
    00136
    00137 TQString ShowRecordElem::subject() const
    00138 {
    00139   return Codecs::decodeRFC2047( m_subject );
    00140 }
    00141
    00142 TQString ShowRecordElem::date() const
    00143 {
    00144   return TDEGlobal::locale()->formatDateTime( m_unixDate, true, true );
    00145 }
    00146
    00147 TQString ShowRecordElem::strUnixTime() const
    00148 {
    00149   return m_unixDate.toString( TQt::ISODate );
    00150 }
    00151
    00152 TQString ShowRecordElem::strSize() const
    00153 {
    00154   return TQString( "%1" ).arg( m_size, 8 );
    00155 }
    00156
    00157 TQString ShowRecordElem::state() const
    00158 {
    00159   if( m_new )
    00160     return i18n( "new" );
    00161   else
    00162     return i18n( "old" );
    00163 }
    00164
    00165 void ShowRecordElem::saveOptions( TQDomDocument& doc, TQDomElement& parent )
    00166 {
    00167   //build item tag of this mail( with mail number)
    00168   TQString hdr = TQString( ITEM_MESSAGE );
    00169   hdr.append( "%1" );
    00170   hdr = hdr.arg( m_nNumber );
    00171
    00172   //create a new element and store the mail meta data in it
    00173   TQDomElement elem = doc.createElement( hdr );
    00174   elem.setAttribute( ATTRIBUTE_MAIL_NUMBER, m_nNumber );
    00175   elem.setAttribute( ATTRIBUTE_MAIL_SIZE, m_size );
    00176   elem.setAttribute( ATTRIBUTE_MAIL_UID, m_uid );
    00177
    00178   //create a sub element for the mail header in store the header in it
    00179   TQDomElement subelem = doc.createElement( ITEM_MAIL_HEADER );
    00180   subelem.appendChild( doc.createTextNode( m_header ) );
    00181
    00182   //add header element to the mail element
    00183   elem.appendChild( subelem );
    00184
    00185   //add mail element to the account (parent) element
    00186   parent.appendChild( elem );
    00187 }
    00188
    00189 void ShowRecordElem::readOptions( TQDomElement& elem )
    00190 {
    00191   //get number, size and uid
    00192   setNumber( elem.attribute( ATTRIBUTE_MAIL_NUMBER ).toInt() );
    00193   setSize( elem.attribute( ATTRIBUTE_MAIL_SIZE ).toInt() );
    00194   setUIDL( elem.attribute( ATTRIBUTE_MAIL_UID ) );
    00195
    00196   //search for the header item and read it
    00197   TQDomElement subelem = elem.namedItem( ITEM_MAIL_HEADER ).toElement();
    00198   setHeader( subelem.text() );
    00199
    00200   //the mail is not new
    00201   setNew( false );
    00202 }
    00203
    00204 void ShowRecordElem::applyFilters ()
    00205 {
    00206   //search for a matching filter
    00207   FilterElem* filter = Filter::matches( this );
    00208
    00209   if( filter != NULL )
    00210   {
    00211     //if we have found a matching filter, highlight the corresponding
    00212     //list view entry
    00213     m_pItem->setSelected (true);
    00214
    00215     //store a pointer to the filter
    00216     setFilter( filter );
    00217   }
    00218 }
    00219
    00220 void ShowRecordElem::setFrom( const TQCString & from )
    00221 {
    00222   m_from = from;
    00223 }
    00224
    00225 void ShowRecordElem::setTo( const TQCString & to )
    00226 {
    00227   m_to = to;
    00228 }
    00229
    00230 void ShowRecordElem::setSubject( const TQCString & subject )
    00231 {
    00232   m_subject = subject;
    00233 }
    00234
    00235 void ShowRecordElem::setContent( const TQCString& content )
    00236 {
    00237   m_content = content;
    00238 }
    00239
    00240 TQString ShowRecordElem::header( ) const
    00241 {
    00242   return TQString( m_header );
    00243 }
    00244
    00245 void ShowRecordElem::setUIDL( const TQString & uid )
    00246 {
    00247   m_uid = uid;
    00248 }
    00249
    00250 TQString ShowRecordElem::uidl( ) const
    00251 {
    00252   return m_uid;
    00253 }
    00254
    00255 void ShowRecordElem::setSize( int size )
    00256 {
    00257   m_size = size;
    00258 }
    00259
    00260 int ShowRecordElem::size( ) const
    00261 {
    00262   return m_size;
    00263 }
    00264
    00265 void ShowRecordElem::setNew( bool isnew )
    00266 {
    00267   m_new = isnew;
    00268 }
    00269
    00270 bool ShowRecordElem::isNew( ) const
    00271 {
    00272   return m_new;
    00273 }
    00274
    00275 void ShowRecordElem::setNumber( int n )
    00276 {
    00277   m_nNumber = n;
    00278 }
    00279
    00280 int ShowRecordElem::number( ) const
    00281 {
    00282   return m_nNumber;
    00283 }
    00284
    00285 TQString ShowRecordElem::content( ) const
    00286 {
    00287   return m_content;
    00288 }
    00289
    00290 void ShowRecordElem::setViewItem( ShowListViewItem* item )
    00291 {
    00292   m_pItem = item;
    00293 }
    00294
    00295 ShowListViewItem * ShowRecordElem::viewItem( ) const
    00296 {
    00297   return m_pItem;
    00298 }
    00299
    00300 void ShowRecordElem::setFilter( FilterElem* filter )
    00301 {
    00302   m_pFilter = filter;
    00303 }
    00304
    00305 bool ShowRecordElem::isSelected( ) const
    00306 {
    00307   if( m_pItem != NULL )
    00308     return m_pItem->isSelected();
    00309   else
    00310     return false;
    00311 }
    00312
    00313 TQString ShowRecordElem::strSizePrefix( ) const
    00314 {
    00315   TQString size;
    00316
    00317   if( m_size >= 1024 * 1024 )
    00318   {
    00319     //prefix is mega
    00320     size = TQString( "%L1M" ).arg( ( (double)m_size / ( 1024 * 1024 ) ), 0, 'f', 1 );
    00321   }
    00322   else if( m_size >= 1024 )
    00323   {
    00324     //prefix is kilo
    00325     size = TQString( "%L1K" ).arg( ( (double)m_size / 1024 ), 0, 'f', 1 );
    00326   }
    00327   else
    00328     //no prefix
    00329     size = TQString( "%L1" ).arg( m_size );
    00330
    00331   return size;
    00332 }
    00333
    00334 TQString ShowRecordElem::decodeMailBody( TQByteArray body, bool preferHTML ) const
    00335 {
    00336   TQString charset;    //charset of the content
    00337   TQString encoding;   //content transfer encoding
    00338
    00339   //cast given body to a TQCString
    00340   //class TQCString needs a null terminated char array to create
    00341   //an object. Therefore we append an null byte to the given mail body
    00342   body.resize( body.size() + 1 );
    00343   body[ body.size() - 1 ] = '\0';
    00344   TQCString strBody( (char *)body.data() );
    00345
    00346   //normalize line ends; remove all \r characters
    00347   for( uint i = 0; i < strBody.size(); i++ )
    00348     if( strBody[ i ] == '\r' )
    00349       strBody.remove( i, 1 );
    00350
    00351   //get boundary that is separating the parts of a multipart message
    00352   //if the header doesn't contain a boundary attribute, this messsage
    00353   //has just one part
    00354   TQString boundary = getBoundary();
    00355
    00356   //process body subject to it is a multipart messsage or not
    00357   if( boundary == "" )
    00358   {
    00359     //the message has just one body part
    00360
    00361     //get the position of the first blank line
    00362     int posBlankLine = strBody.find( "\n\n" );
    00363
    00364     //truncate body; the found blank line is separating the
    00365     //header from the message
    00366     strBody = strBody.mid( posBlankLine + 2 );
    00367     while( strBody[ 0 ] == '\n')
    00368       strBody.remove( 0, 1 );
    00369
    00370     //get charset of the message; it is behind the
    00371     //content type attribute in the header
    00372     charset = getCharset();
    00373
    00374     //get transfer encoding type from the header
    00375     encoding = getTransferEncoding();
    00376   }
    00377   else
    00378   {
    00379     //the message has multiple parts
    00380
    00381     //get positions of a plain text and html flag (value of the content type attribute)
    00382     int posPlainFlag = strBody.find( "text/plain", 0, false );
    00383     int posHTMLFlag = strBody.find( "text/html", 0, false );
    00384
    00385     //just decode the body, if a plain text or a HTML part is available
    00386     if( posPlainFlag != -1 || posHTMLFlag != -1 )
    00387     {
    00388       //do we want to take the HTML part?
    00389       bool hasHTML = posHTMLFlag != -1;
    00390       bool takeHTML = ( hasHTML && preferHTML ) || posPlainFlag == -1;
    00391
    00392       //now we want to extract the designated part
    00393       //While the (truncated) mail text (or the header at the first pass)
    00394       //contains a boundary attribute we will extract the designated part
    00395       //between the boundaries
    00396       int posInside;    //a position inside the designated part
    00397       while( boundary != "" )
    00398       {
    00399         //get a position inside the designated part
    00400         if( takeHTML )
    00401           posInside = strBody.find( "text/html", 0, false );
    00402         else
    00403           posInside = strBody.find( "text/plain", 0, false );
    00404
    00405         //get length of the boundary
    00406         int lengthBoundary = boundary.length();
    00407
    00408         //calculate the begin and end of the part to extract
    00409         int beginPart = strBody.findRev( boundary.ascii(), posInside ) + lengthBoundary + 1;
    00410         int lengthPart = strBody.findRev( '\n', strBody.find( boundary.ascii(), posInside ) ) - beginPart;
    00411
    00412         strBody = strBody.mid( beginPart, lengthPart );
    00413
    00414         //looking for a further boundary attribute
    00415         //get the position of the first occurance of "boundary="
    00416         int posBoundary = strBody.find( "boundary=", 0, false );
    00417
    00418         if( posBoundary >= 0 )
    00419         {
    00420           //calculate positon of the first quote
    00421           int posFirstQuote = posBoundary + 9;
    00422
    00423           //get the position of closing quote
    00424           int posSecondQuote = strBody.find( '"', posFirstQuote + 1 );
    00425
    00426           //get boundary string
    00427           boundary.append( strBody.mid( posFirstQuote + 1, posSecondQuote - posFirstQuote - 1 ) );
    00428         }
    00429         else
    00430           boundary = "";
    00431       }
    00432
    00433       //now we get charset and transfer encoding if available in the extracted
    00434       //part
    00435
    00436       //get the position of the first occurance of "charset="
    00437       int posCharset = strBody.find( "charset=", 0, false );
    00438
    00439       //continue, if a charset attribute was found
    00440       if( posCharset >= 0 )
    00441       {
    00442         //calculate positon of the value
    00443         int posBeginValue = posCharset + 8;
    00444
    00445         //get end of the value
    00446         int posEndValue = strBody.find( '\n', posBeginValue ) - 1;
    00447
    00448         //get charset
    00449         charset.append( strBody.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
    00450
    00451         //remove quotes
    00452         charset.remove( '"' );
    00453         //remove all content after the first semicolon (inclusive)
    00454         int posSemicolon = charset.find( ';' );
    00455         charset = charset.left( posSemicolon );
    00456       }
    00457
    00458       //get the position of the first occurance of "charset="
    00459       int posEncoding = strBody.find( "Content-Transfer-Encoding:", 0, false );
    00460
    00461       //continue, if a charset attribute was found
    00462       if( posEncoding >= 0 )
    00463       {
    00464         //calculate positon of the value
    00465         int posBeginValue = posEncoding + 26;
    00466
    00467         //get end of the value
    00468         int posEndValue = strBody.find( '\n', posBeginValue ) - 1;
    00469
    00470         //get charset
    00471         encoding.append( strBody.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
    00472
    00473         //remove quotes and spaces
    00474         encoding = encoding.stripWhiteSpace();
    00475         encoding.remove( '"' );
    00476       }
    00477
    00478       //cut off the part header; the found blank line is separating the
    00479       //part header from the message
    00480       if( posCharset != -1 || posEncoding != -1 )
    00481       {
    00482         int posBlankLine = strBody.find( "\n\n" );
    00483         strBody = strBody.mid( posBlankLine + 2 );
    00484         while( strBody[ 0 ] == '\n')
    00485           strBody.remove( 0, 1 );
    00486       }
    00487     }
    00488   }
    00489
    00490   //Good things come to those who wait. We have extract the message.
    00491   //Now we have to decode the message, if it is encoded
    00492   if( encoding == "quoted-printable" )
    00493   {
    00494     strBody = KCodecs::quotedPrintableDecode( strBody );
    00495   }
    00496
    00497   return TQString( strBody );
    00498 }
    00499
    00500 TQString ShowRecordElem::getBoundary( ) const
    00501 {
    00502   TQString boundary;
    00503
    00504   //check, whether it is a multipart message
    00505   if( m_content.contains( "multipart", false ) )
    00506   {
    00507     //it is a multipart message
    00508
    00509     //get the position of the first occurance of "boundary="
    00510     int posBoundary = m_header.find( "boundary=", 0, false );
    00511
    00512     //continue, if a boundary attribute was found
    00513     if( posBoundary >= 0 )
    00514     {
    00515       //calculate positon of the first quote
    00516       int posFirstQuote = posBoundary + 9;
    00517
    00518       //get the position of closing quote
    00519       int posSecondQuote = m_header.find( '"', posFirstQuote + 1 );
    00520
    00521       //get boundary string
    00522       boundary.append( m_header.mid( posFirstQuote + 1, posSecondQuote - posFirstQuote - 1 ) );
    00523     }
    00524   }
    00525
    00526   return boundary;
    00527 }
    00528
    00529 TQString ShowRecordElem::getCharset( ) const
    00530 {
    00531   TQString charset;
    00532
    00533   //get the position of the first occurance of "charset="
    00534   int posCharset = m_header.find( "charset=", 0, false );
    00535
    00536   //continue, if a charset attribute was found
    00537   if( posCharset >= 0 )
    00538   {
    00539     //calculate positon of the value
    00540     int posBeginValue = posCharset + 8;
    00541
    00542     //get end of the value
    00543     int posEndValue = m_header.find( '\r', posBeginValue ) - 1;
    00544
    00545     //get charset
    00546     charset.append( m_header.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
    00547
    00548     //remove quotes
    00549     charset.remove( '"' );
    00550     //remove all content after the first semicolon (inclusive)
    00551     int posSemicolon = charset.find( ';' );
    00552     charset = charset.left( posSemicolon );
    00553   }
    00554
    00555   return TQString( charset );
    00556 }
    00557
    00558 TQString ShowRecordElem::getTransferEncoding( ) const
    00559 {
    00560   TQString encoding;
    00561
    00562   //get the position of the first occurance of "charset="
    00563   int posEncoding = m_header.find( "Content-Transfer-Encoding:", 0, false );
    00564
    00565   //continue, if a charset attribute was found
    00566   if( posEncoding >= 0 )
    00567   {
    00568     //calculate positon of the value
    00569     int posBeginValue = posEncoding + 26;
    00570
    00571     //get end of the value
    00572     int posEndValue = m_header.find( '\r', posBeginValue ) - 1;
    00573
    00574     //get charset
    00575     encoding.append( m_header.mid( posBeginValue, posEndValue - posBeginValue + 1 ) );
    00576
    00577     //remove quotes and spaces
    00578     encoding = encoding.stripWhiteSpace();
    00579     encoding.remove( '"' );
    00580   }
    00581
    00582   return TQString( encoding );
    00583
    00584 }
    00585
    00586 int ShowRecordElem::showHeader( TQString& account )
    00587 {
    00588   //show header
    00589   TQString tsubject = subject();
    00590   TQString tmailheader = header();
    00591
    00592   //create and open the window
    00593   ShowHeaderDialog dlg( kapp->mainWidget(), account, tsubject, tmailheader );
    00594   int ret = dlg.exec();
    00595
    00596   //returns the matching value
    00597   return ret == TQDialog::Accepted ? ShowRecordElem::continueShowHeaders : ShowRecordElem::cancelShowHeaders;
    00598 }
    00599
    00600