00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00035 #include "formatter.h"
00036 #include "ktnefparser.h"
00037 #include "ktnefmessage.h"
00038 #include "ktnefdefs.h"
00039
00040 #include <kpimutils/email.h>
00041 #include <kabc/phonenumber.h>
00042 #include <kabc/vcardconverter.h>
00043 #include <kabc/stdaddressbook.h>
00044 #include <kcal/incidenceformatter.h>
00045 #include <kcal/calendar.h>
00046 #include <kcal/calendarlocal.h>
00047 #include <kcal/icalformat.h>
00048
00049 #include <klocale.h>
00050 #include <kdatetime.h>
00051
00052 #include <QtCore/QBuffer>
00053
00054 #include <time.h>
00055
00056 using namespace KCal;
00057 using namespace KTnef;
00058
00059
00060
00061
00062
00063
00064
00065 static QString stringProp( KTNEFMessage *tnefMsg, const quint32 &key,
00066 const QString &fallback = QString())
00067 {
00068 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
00069 fallback );
00070 }
00071
00072 static QString sNamedProp( KTNEFMessage *tnefMsg, const QString &name,
00073 const QString &fallback = QString() )
00074 {
00075 return tnefMsg->findNamedProp( name, fallback );
00076 }
00077
00078 struct save_tz {
00079 char *old_tz;
00080 char *tz_env_str;
00081 };
00082
00083
00084 static struct save_tz set_tz( const char *_tc )
00085 {
00086 const char *tc = _tc?_tc:"UTC";
00087
00088 struct save_tz rv;
00089
00090 rv.old_tz = 0;
00091 rv.tz_env_str = 0;
00092
00093
00094
00095 char *tz_env = 0;
00096 if ( getenv( "TZ" ) ) {
00097 tz_env = strdup( getenv( "TZ" ) );
00098 rv.old_tz = tz_env;
00099 }
00100 char *tmp_env = (char*)malloc( strlen( tc ) + 4 );
00101 strcpy( tmp_env, "TZ=" );
00102 strcpy( tmp_env+3, tc );
00103 putenv( tmp_env );
00104
00105 rv.tz_env_str = tmp_env;
00106
00107
00108
00109 tzset();
00110
00111
00112 return rv;
00113 }
00114
00115
00116 static void unset_tz( struct save_tz old_tz )
00117 {
00118 if ( old_tz.old_tz ) {
00119 char *tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
00120 strcpy( tmp_env, "TZ=" );
00121 strcpy( tmp_env+3, old_tz.old_tz );
00122 putenv( tmp_env );
00123
00124 free( old_tz.old_tz );
00125 } else {
00126
00127 putenv( strdup("TZ") );
00128 }
00129 tzset();
00130
00131
00132 if ( old_tz.tz_env_str ) {
00133 free( old_tz.tz_env_str );
00134 }
00135 }
00136
00137 static KDateTime utc2Local( const KDateTime &utcdt )
00138 {
00139 struct tm tmL;
00140
00141 save_tz tmp_tz = set_tz("UTC");
00142 time_t utc = utcdt.toTime_t();
00143 unset_tz( tmp_tz );
00144
00145 localtime_r( &utc, &tmL );
00146 return KDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
00147 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
00148 }
00149
00150 static KDateTime pureISOToLocalQDateTime( const QString &dtStr,
00151 bool bDateOnly = false )
00152 {
00153 QDate tmpDate;
00154 QTime tmpTime;
00155 int year, month, day, hour, minute, second;
00156
00157 if ( bDateOnly ) {
00158 year = dtStr.left( 4 ).toInt();
00159 month = dtStr.mid( 4, 2 ).toInt();
00160 day = dtStr.mid( 6, 2 ).toInt();
00161 hour = 0;
00162 minute = 0;
00163 second = 0;
00164 } else {
00165 year = dtStr.left( 4 ).toInt();
00166 month = dtStr.mid( 4, 2 ).toInt();
00167 day = dtStr.mid( 6, 2 ).toInt();
00168 hour = dtStr.mid( 9, 2 ).toInt();
00169 minute = dtStr.mid( 11, 2 ).toInt();
00170 second = dtStr.mid( 13, 2 ).toInt();
00171 }
00172 tmpDate.setYMD( year, month, day );
00173 tmpTime.setHMS( hour, minute, second );
00174
00175 if ( tmpDate.isValid() && tmpTime.isValid() ) {
00176 KDateTime dT = KDateTime( tmpDate, tmpTime );
00177
00178 if ( !bDateOnly ) {
00179
00180 if (dtStr.at(dtStr.length()-1) == 'Z') {
00181
00182
00183 dT = utc2Local( dT );
00184 }
00185 }
00186 return dT;
00187 } else {
00188 return KDateTime();
00189 }
00190 }
00191
00192
00193 QString KTnef::msTNEFToVPart( const QByteArray &tnef )
00194 {
00195 bool bOk = false;
00196
00197 KTNEFParser parser;
00198 QByteArray b( tnef );
00199 QBuffer buf( &b );
00200 CalendarLocal cal ( KDateTime::UTC );
00201 KABC::Addressee addressee;
00202 ICalFormat calFormat;
00203 Event *event = new Event();
00204
00205 if ( parser.openDevice( &buf ) ) {
00206 KTNEFMessage *tnefMsg = parser.message();
00207
00208
00209
00210
00211 QString msgClass = tnefMsg->findProp( 0x001A, QString(), true )
00212 .toUpper();
00213 if ( !msgClass.isEmpty() ) {
00214
00215
00216 bool bCompatClassAppointment = false;
00217 bool bCompatMethodRequest = false;
00218 bool bCompatMethodCancled = false;
00219 bool bCompatMethodAccepted = false;
00220 bool bCompatMethodAcceptedCond = false;
00221 bool bCompatMethodDeclined = false;
00222 if ( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
00223 bCompatClassAppointment = true;
00224 if ( msgClass.endsWith( ".MTGREQ" ) )
00225 bCompatMethodRequest = true;
00226 if ( msgClass.endsWith( ".MTGCNCL" ) )
00227 bCompatMethodCancled = true;
00228 if ( msgClass.endsWith( ".MTGRESPP" ) )
00229 bCompatMethodAccepted = true;
00230 if ( msgClass.endsWith( ".MTGRESPA" ) )
00231 bCompatMethodAcceptedCond = true;
00232 if ( msgClass.endsWith( ".MTGRESPN" ) )
00233 bCompatMethodDeclined = true;
00234 }
00235 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
00236
00237 if ( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
00238
00239 bool bIsReply = false;
00240 QString prodID = "-//Microsoft Corporation//Outlook ";
00241 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
00242 prodID += "MIMEDIR/EN\n";
00243 prodID += "VERSION:2.0\n";
00244 calFormat.setApplication( "Outlook", prodID );
00245
00246 iTIPMethod method;
00247 if ( bCompatMethodRequest )
00248 method = iTIPRequest;
00249 else if ( bCompatMethodCancled )
00250 method = iTIPCancel;
00251 else if ( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
00252 bCompatMethodDeclined ) {
00253 method = iTIPReply;
00254 bIsReply = true;
00255 } else {
00256
00257
00258
00259
00260
00261
00262
00263
00264 if ( tnefMsg->findProp(0x0c17) == "1" ) {
00265 bIsReply = true;
00266 }
00267 method = iTIPRequest;
00268 }
00269
00271 ScheduleMessage schedMsg( event, method, ScheduleMessage::Unknown );
00272
00273 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
00274
00275 if ( !sSenderSearchKeyEmail.isEmpty() ) {
00276 int colon = sSenderSearchKeyEmail.indexOf( ':' );
00277
00278 if ( sSenderSearchKeyEmail.indexOf( ':' ) == -1 ) {
00279 sSenderSearchKeyEmail.remove( 0, colon+1 );
00280 }
00281 }
00282
00283 QString s( tnefMsg->findProp( 0x0e04 ) );
00284 QStringList attendees = s.split( ';' );
00285 if ( attendees.count() ) {
00286 for ( QStringList::Iterator it = attendees.begin();
00287 it != attendees.end(); ++it ) {
00288
00289
00290 if ( (*it).indexOf( '@' ) == -1 ) {
00291 s = (*it).trimmed();
00292
00293 Attendee *attendee = new Attendee( s, s, true );
00294 if ( bIsReply ) {
00295 if ( bCompatMethodAccepted )
00296 attendee->setStatus( Attendee::Accepted );
00297 if ( bCompatMethodDeclined )
00298 attendee->setStatus( Attendee::Declined );
00299 if ( bCompatMethodAcceptedCond )
00300 attendee->setStatus( Attendee::Tentative );
00301 } else {
00302 attendee->setStatus( Attendee::NeedsAction );
00303 attendee->setRole( Attendee::ReqParticipant );
00304 }
00305 event->addAttendee( attendee );
00306 }
00307 }
00308 } else {
00309
00310
00311 s = sSenderSearchKeyEmail;
00312 if ( !s.isEmpty() ) {
00313 Attendee *attendee = new Attendee( QString(), QString(), true );
00314 if ( bIsReply ) {
00315 if ( bCompatMethodAccepted )
00316 attendee->setStatus( Attendee::Accepted );
00317 if ( bCompatMethodAcceptedCond )
00318 attendee->setStatus( Attendee::Declined );
00319 if ( bCompatMethodDeclined )
00320 attendee->setStatus( Attendee::Tentative );
00321 } else {
00322 attendee->setStatus( Attendee::NeedsAction );
00323 attendee->setRole( Attendee::ReqParticipant );
00324 }
00325 event->addAttendee( attendee );
00326 }
00327 }
00328 s = tnefMsg->findProp( 0x0c1f );
00329 if ( s.isEmpty() && !bIsReply ) {
00330 s = sSenderSearchKeyEmail;
00331 }
00332
00333 if ( !s.isEmpty() ) {
00334 event->setOrganizer( s );
00335 }
00336
00337 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString() )
00338 .replace( QChar( ':' ), QString() );
00339 event->setDtStart( KDateTime::fromString( s ) );
00340
00341 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString() )
00342 .replace( QChar( ':' ), QString() );
00343 event->setDtEnd( KDateTime::fromString( s ) );
00344
00345 s = tnefMsg->findProp( 0x8208 );
00346 event->setLocation( s );
00347
00348
00349
00350
00351
00352
00353 s = tnefMsg->findProp( 0x0023 );
00354 event->setUid( s );
00355
00356
00357
00358
00359 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString() )
00360 .replace( QChar( ':' ), QString() );
00361
00362
00363
00364 s = tnefMsg->findNamedProp( "Keywords" );
00365 event->setCategories( s );
00366
00367 s = tnefMsg->findProp( 0x1000 );
00368 event->setDescription( s );
00369
00370 s = tnefMsg->findProp( 0x0070 );
00371 event->setSummary( s );
00372
00373 s = tnefMsg->findProp( 0x0026 );
00374 event->setPriority( s.toInt() );
00375
00376
00377 if (!tnefMsg->findProp(0x8503).isEmpty()) {
00378 Alarm *alarm = new Alarm( event );
00379 KDateTime highNoonTime =
00380 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
00381 .replace( QChar( '-' ), "" )
00382 .replace( QChar( ':' ), "" ) );
00383 KDateTime wakeMeUpTime =
00384 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
00385 .replace( QChar( '-' ), "" )
00386 .replace( QChar( ':' ), "" ) );
00387 alarm->setTime( wakeMeUpTime );
00388
00389 if ( highNoonTime.isValid() && wakeMeUpTime.isValid() ) {
00390 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
00391 } else {
00392
00393 alarm->setStartOffset( Duration( 15*60 ) );
00394 }
00395 alarm->setDisplayAlarm( i18n( "Reminder" ) );
00396
00397
00398
00399 event->addAlarm( alarm );
00400 }
00401 cal.addEvent( event );
00402 bOk = true;
00403
00404 } else if ( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
00405 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
00406 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
00407 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
00408 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
00409 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
00410 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress",
00411 sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
00412 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName",
00413 stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
00414 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName",
00415 stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
00416 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName",
00417 stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
00418 addressee.insertCustom( "KADDRESSBOOK", "X-Department",
00419 stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
00420 addressee.insertCustom( "KADDRESSBOOK", "X-Office",
00421 stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
00422 addressee.insertCustom( "KADDRESSBOOK", "X-Profession",
00423 stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
00424
00425 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
00426 .replace( QChar( '-' ), QString() )
00427 .replace( QChar( ':' ), QString() );
00428 if ( !s.isEmpty() ) {
00429 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
00430 }
00431
00432 addressee.setUrl( KUrl( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
00433
00434
00435 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
00436 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
00437 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
00438 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
00439 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
00440
00441 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
00442 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
00443 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
00444
00445
00446
00447
00448
00449 KABC::Address adr;
00450 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
00451 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
00452 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
00453 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
00454 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
00455 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
00456 adr.setType( KABC::Address::Home );
00457 addressee.insertAddress( adr );
00458
00459 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
00460 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
00461 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
00462 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
00463 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
00464 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
00465 adr.setType( KABC::Address::Work );
00466 addressee.insertAddress( adr );
00467
00468 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
00469 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
00470 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
00471 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
00472 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
00473 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
00474 adr.setType( KABC::Address::Dom );
00475 addressee.insertAddress( adr );
00476
00477
00478
00479
00480
00481 QString nr;
00482 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
00483 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
00484 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
00485 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
00486 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
00487 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
00488 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
00489 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
00490 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
00491 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
00492
00493 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
00494 .replace( QChar( '-' ), QString() )
00495 .replace( QChar( ':' ), QString() );
00496 if ( !s.isEmpty() ) {
00497 addressee.setBirthday( QDateTime::fromString( s ) );
00498 }
00499
00500 bOk = ( !addressee.isEmpty() );
00501 } else if ( "IPM.NOTE" == msgClass ) {
00502
00503 }
00504 }
00505 }
00506
00507
00508 QString iCal = calFormat.toString( &cal );
00509 if ( !iCal.isEmpty() ) {
00510
00511 return iCal;
00512 }
00513
00514
00515 KABC::VCardConverter converter;
00516 return QString::fromUtf8( converter.createVCard( addressee ) );
00517 }
00518
00519 QString KTnef::formatTNEFInvitation( const QByteArray &tnef,
00520 KCal::Calendar *cal,
00521 KCal::InvitationFormatterHelper *h )
00522 {
00523 QString vPart = msTNEFToVPart( tnef );
00524 QString iCal = IncidenceFormatter::formatICalInvitation( vPart, cal, h );
00525 if ( !iCal.isEmpty() ) {
00526 return iCal;
00527 } else {
00528 return vPart;
00529 }
00530 }
00531