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