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