1/*
2 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
3 Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
4 Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21/**
22 @file
23 This file is part of the API for handling TNEF data and provides
24 static Formatter helpers.
25
26 @brief
27 Provides helpers too format @acronym TNEF attachments into different
28 formats like eg. a HTML representation.
29
30 @author Cornelius Schumacher
31 @author Reinhold Kainhofer
32 @author Rafal Rzepecki
33*/
34
35#include "formatter.h"
36#include "ktnefparser.h"
37#include "ktnefmessage.h"
38#include "ktnefdefs.h"
39
40#include <kpimutils/email.h>
41#include <kabc/phonenumber.h>
42#include <kabc/vcardconverter.h>
43
44#ifndef KDEPIM_NO_KCAL
45#include <kcal/incidenceformatter.h>
46#include <kcal/calendar.h>
47#endif
48
49#include <kcalcore/calendar.h>
50#include <kcalcore/icalformat.h>
51#include <kcalutils/incidenceformatter.h>
52
53#include <klocalizedstring.h>
54#include <kdatetime.h>
55
56#include <QtCore/QBuffer>
57
58#include <time.h>
59
60using namespace KCalCore;
61using namespace KTnef;
62
63/*******************************************************************
64 * Helper functions for the msTNEF -> VPart converter
65 *******************************************************************/
66
67//-----------------------------------------------------------------------------
68//@cond IGNORE
69static QString stringProp( KTNEFMessage *tnefMsg, const quint32 &key,
70 const QString &fallback = QString() )
71{
72 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16, fallback );
73}
74
75static QString sNamedProp( KTNEFMessage *tnefMsg, const QString &name,
76 const QString &fallback = QString() )
77{
78 return tnefMsg->findNamedProp( name, fallback );
79}
80
81struct save_tz {
82 char *old_tz;
83 char *tz_env_str;
84};
85
86/* temporarily go to a different timezone */
87static struct save_tz set_tz( const char *_tc )
88{
89 const char *tc = _tc?_tc:"UTC";
90
91 struct save_tz rv;
92
93 rv.old_tz = 0;
94 rv.tz_env_str = 0;
95
96 //kDebug() << "set_tz(), timezone before =" << timezone;
97
98 char *tz_env = 0;
99 if ( !qgetenv( "TZ" ).isEmpty() ) {
100 tz_env = qstrdup( qgetenv( "TZ" ) );
101 rv.old_tz = tz_env;
102 }
103 char *tmp_env = (char*)malloc( strlen( tc ) + 4 );
104 strcpy( tmp_env, "TZ=" );
105 strcpy( tmp_env+3, tc );
106 putenv( tmp_env );
107
108 rv.tz_env_str = tmp_env;
109
110 /* tmp_env is not free'ed -- it is part of the environment */
111
112 tzset();
113 //kDebug() << "set_tz(), timezone after =" << timezone;
114
115 return rv;
116}
117
118/* restore previous timezone */
119static void unset_tz( struct save_tz old_tz )
120{
121 if ( old_tz.old_tz ) {
122 char *tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
123 strcpy( tmp_env, "TZ=" );
124 strcpy( tmp_env+3, old_tz.old_tz );
125 putenv( tmp_env );
126 /* tmp_env is not free'ed -- it is part of the environment */
127 free( old_tz.old_tz );
128 } else {
129 /* clear TZ from env */
130 putenv( strdup( "TZ" ) );
131 }
132 tzset();
133
134 /* is this OK? */
135 if ( old_tz.tz_env_str ) {
136 free( old_tz.tz_env_str );
137 }
138}
139
140static KDateTime utc2Local( const KDateTime &utcdt )
141{
142 struct tm tmL;
143
144 save_tz tmp_tz = set_tz( "UTC" );
145 time_t utc = utcdt.toTime_t();
146 unset_tz( tmp_tz );
147
148 localtime_r( &utc, &tmL );
149 return KDateTime( QDate( tmL.tm_year + 1900, tmL.tm_mon + 1, tmL.tm_mday ),
150 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
151}
152
153static KDateTime pureISOToLocalQDateTime( const QString &dtStr,
154 bool bDateOnly = false )
155{
156 QDate tmpDate;
157 QTime tmpTime;
158 int year, month, day, hour, minute, second;
159
160 if ( bDateOnly ) {
161 year = dtStr.left( 4 ).toInt();
162 month = dtStr.mid( 4, 2 ).toInt();
163 day = dtStr.mid( 6, 2 ).toInt();
164 hour = 0;
165 minute = 0;
166 second = 0;
167 } else {
168 year = dtStr.left( 4 ).toInt();
169 month = dtStr.mid( 4, 2 ).toInt();
170 day = dtStr.mid( 6, 2 ).toInt();
171 hour = dtStr.mid( 9, 2 ).toInt();
172 minute = dtStr.mid( 11, 2 ).toInt();
173 second = dtStr.mid( 13, 2 ).toInt();
174 }
175 tmpDate.setYMD( year, month, day );
176 tmpTime.setHMS( hour, minute, second );
177
178 if ( tmpDate.isValid() && tmpTime.isValid() ) {
179 KDateTime dT = KDateTime( tmpDate, tmpTime );
180
181 if ( !bDateOnly ) {
182 // correct for GMT ( == Zulu time == UTC )
183 if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) {
184 //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() );
185 //localUTCOffset( dT ) );
186 dT = utc2Local( dT );
187 }
188 }
189 return dT;
190 } else {
191 return KDateTime();
192 }
193}
194//@endcond
195
196QString KTnef::msTNEFToVPart( const QByteArray &tnef )
197{
198 bool bOk = false;
199
200 KTNEFParser parser;
201 QByteArray b( tnef );
202 QBuffer buf( &b );
203 MemoryCalendar::Ptr cal( new MemoryCalendar( KDateTime::UTC ) );
204 KABC::Addressee addressee;
205 ICalFormat calFormat;
206 Event::Ptr event( new Event() );
207
208 if ( parser.openDevice( &buf ) ) {
209 KTNEFMessage *tnefMsg = parser.message();
210 //QMap<int,KTNEFProperty*> props = parser.message()->properties();
211
212 // Everything depends from property PR_MESSAGE_CLASS
213 // (this is added by KTNEFParser):
214 QString msgClass = tnefMsg->findProp( 0x001A, QString(), true ).toUpper();
215 if ( !msgClass.isEmpty() ) {
216 // Match the old class names that might be used by Outlook for
217 // compatibility with Microsoft Mail for Windows for Workgroups 3.1.
218 bool bCompatClassAppointment = false;
219 bool bCompatMethodRequest = false;
220 bool bCompatMethodCancled = false;
221 bool bCompatMethodAccepted = false;
222 bool bCompatMethodAcceptedCond = false;
223 bool bCompatMethodDeclined = false;
224 if ( msgClass.startsWith( QLatin1String( "IPM.MICROSOFT SCHEDULE." ) ) ) {
225 bCompatClassAppointment = true;
226 if ( msgClass.endsWith( QLatin1String( ".MTGREQ" ) ) ) {
227 bCompatMethodRequest = true;
228 }
229 if ( msgClass.endsWith( QLatin1String( ".MTGCNCL" ) ) ) {
230 bCompatMethodCancled = true;
231 }
232 if ( msgClass.endsWith( QLatin1String( ".MTGRESPP" ) ) ) {
233 bCompatMethodAccepted = true;
234 }
235 if ( msgClass.endsWith( QLatin1String( ".MTGRESPA" ) ) ) {
236 bCompatMethodAcceptedCond = true;
237 }
238 if ( msgClass.endsWith( QLatin1String( ".MTGRESPN" ) ) ) {
239 bCompatMethodDeclined = true;
240 }
241 }
242 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
243
244 if ( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
245 // Compose a vCal
246 bool bIsReply = false;
247 QString prodID = "-//Microsoft Corporation//Outlook ";
248 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
249 prodID += "MIMEDIR/EN\n";
250 prodID += "VERSION:2.0\n";
251 calFormat.setApplication( "Outlook", prodID );
252
253 iTIPMethod method;
254 if ( bCompatMethodRequest ) {
255 method = iTIPRequest;
256 } else if ( bCompatMethodCancled ) {
257 method = iTIPCancel;
258 } else if ( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
259 bCompatMethodDeclined ) {
260 method = iTIPReply;
261 bIsReply = true;
262 } else {
263 // pending(khz): verify whether "0x0c17" is the right tag ???
264 //
265 // at the moment we think there are REQUESTS and UPDATES
266 //
267 // but WHAT ABOUT REPLIES ???
268 //
269 //
270
271 if ( tnefMsg->findProp(0x0c17) == "1" ) {
272 bIsReply = true;
273 }
274 method = iTIPRequest;
275 }
276
277 /// ### FIXME Need to get this attribute written
278 ScheduleMessage schedMsg( event, method, ScheduleMessage::Unknown );
279
280 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
281
282 if ( !sSenderSearchKeyEmail.isEmpty() ) {
283 int colon = sSenderSearchKeyEmail.indexOf( ':' );
284 // May be e.g. "SMTP:KHZ@KDE.ORG"
285 if ( sSenderSearchKeyEmail.indexOf( ':' ) == -1 ) {
286 sSenderSearchKeyEmail.remove( 0, colon+1 );
287 }
288 }
289
290 QString s( tnefMsg->findProp( 0x8189 ) );
291 const QStringList attendees = s.split( ';' );
292 if ( attendees.count() ) {
293 for ( QStringList::const_iterator it = attendees.begin();
294 it != attendees.end(); ++it ) {
295 // Skip all entries that have no '@' since these are
296 // no mail addresses
297 if ( (*it).indexOf( '@' ) == -1 ) {
298 s = (*it).trimmed();
299
300 Attendee::Ptr attendee( new Attendee( s, s, true ) );
301 if ( bIsReply ) {
302 if ( bCompatMethodAccepted ) {
303 attendee->setStatus( Attendee::Accepted );
304 }
305 if ( bCompatMethodDeclined ) {
306 attendee->setStatus( Attendee::Declined );
307 }
308 if ( bCompatMethodAcceptedCond ) {
309 attendee->setStatus( Attendee::Tentative );
310 }
311 } else {
312 attendee->setStatus( Attendee::NeedsAction );
313 attendee->setRole( Attendee::ReqParticipant );
314 }
315 event->addAttendee( attendee );
316 }
317 }
318 } else {
319 // Oops, no attendees?
320 // This must be old style, let us use the PR_SENDER_SEARCH_KEY.
321 s = sSenderSearchKeyEmail;
322 if ( !s.isEmpty() ) {
323 Attendee::Ptr attendee( new Attendee( QString(), QString(), true ) );
324 if ( bIsReply ) {
325 if ( bCompatMethodAccepted ) {
326 attendee->setStatus( Attendee::Accepted );
327 }
328 if ( bCompatMethodAcceptedCond ) {
329 attendee->setStatus( Attendee::Declined );
330 }
331 if ( bCompatMethodDeclined ) {
332 attendee->setStatus( Attendee::Tentative );
333 }
334 } else {
335 attendee->setStatus( Attendee::NeedsAction );
336 attendee->setRole( Attendee::ReqParticipant );
337 }
338 event->addAttendee( attendee );
339 }
340 }
341 s = tnefMsg->findProp( 0x3ff8 ); // look for organizer property
342 if ( s.isEmpty() && !bIsReply ) {
343 s = sSenderSearchKeyEmail;
344 }
345 // TODO: Use the common name?
346 if ( !s.isEmpty() ) {
347 event->setOrganizer( s );
348 }
349
350 s = tnefMsg->findProp( 0x819b ).remove( QChar( '-' ) ).remove( QChar( ':' ) );
351 event->setDtStart( KDateTime::fromString( s ) ); // ## Format??
352
353 s = tnefMsg->findProp( 0x819c ).remove( QChar( '-' ) ).remove( QChar( ':' ) );
354 event->setDtEnd( KDateTime::fromString( s ) );
355
356 s = tnefMsg->findProp( 0x810d );
357 event->setLocation( s );
358 // is it OK to set this to OPAQUE always ??
359 //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme!
360 //vPart += "SEQUENCE:0\n";
361
362 // is "0x0023" OK - or should we look for "0x0003" ??
363 s = tnefMsg->findProp( 0x0023 );
364 event->setUid( s );
365
366 // PENDING(khz): is this value in local timezone? Must it be
367 // adjusted? Most likely this is a bug in the server or in
368 // Outlook - we ignore it for now.
369 s = tnefMsg->findProp( 0x8202 ).remove( QChar( '-' ) ).remove( QChar( ':' ) );
370 // ### kcal always uses currentDateTime()
371 // event->setDtStamp( QDateTime::fromString( s ) );
372
373 s = tnefMsg->findNamedProp( "Keywords" );
374 event->setCategories( s );
375
376 s = tnefMsg->findProp( 0x1000 );
377 event->setDescription( s );
378
379 s = tnefMsg->findProp( 0x0070 );
380 event->setSummary( s );
381
382 s = tnefMsg->findProp( 0x0026 );
383 event->setPriority( s.toInt() );
384 // is reminder flag set ?
385 if ( !tnefMsg->findProp( 0x8503 ).isEmpty() ) {
386 Alarm::Ptr alarm( new Alarm( event.data() ) ); // TODO: fix when KCalCore::Alarm is fixed
387 KDateTime highNoonTime =
388 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 ).
389 remove( QChar( '-' ) ).remove( QChar( ':' ) ) );
390 KDateTime wakeMeUpTime =
391 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" ).
392 remove( QChar( '-' ) ).remove( QChar( ':' ) ) );
393 alarm->setTime( wakeMeUpTime );
394
395 if ( highNoonTime.isValid() && wakeMeUpTime.isValid() ) {
396 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
397 } else {
398 // default: wake them up 15 minutes before the appointment
399 alarm->setStartOffset( Duration( 15 * 60 ) );
400 }
401 alarm->setDisplayAlarm( i18n( "Reminder" ) );
402
403 // Sorry: the different action types are not known (yet)
404 // so we always set 'DISPLAY' (no sounds, no images...)
405 event->addAlarm( alarm );
406 }
407 //ensure we have a uid for this event
408 if ( event->uid().isEmpty() ) {
409 event->setUid( CalFormat::createUniqueId() );
410 }
411 cal->addEvent( event );
412 bOk = true;
413 // we finished composing a vCal
414 } else if ( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
415 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
416 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
417 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
418 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
419 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
420 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress",
421 sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
422 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName",
423 stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
424 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName",
425 stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
426 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName",
427 stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
428 addressee.insertCustom( "KADDRESSBOOK", "X-Department",
429 stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
430 addressee.insertCustom( "KADDRESSBOOK", "X-Office",
431 stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
432 addressee.insertCustom( "KADDRESSBOOK", "X-Profession",
433 stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
434
435 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY ).
436 remove( QChar( '-' ) ).remove( QChar( ':' ) );
437 if ( !s.isEmpty() ) {
438 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
439 }
440
441 addressee.setUrl( KUrl( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
442
443 // collect parts of Name entry
444 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
445 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
446 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
447 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
448 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
449
450 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
451 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
452 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
453 /*
454 the MAPI property ID of this (multiline) )field is unknown:
455 vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" );
456 */
457
458 KABC::Address adr;
459 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
460 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
461 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
462 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
463 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
464 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
465 adr.setType( KABC::Address::Home );
466 addressee.insertAddress( adr );
467
468 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
469 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
470 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
471 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
472 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
473 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
474 adr.setType( KABC::Address::Work );
475 addressee.insertAddress( adr );
476
477 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
478 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
479 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
480 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
481 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
482 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
483 adr.setType( KABC::Address::Dom );
484 addressee.insertAddress( adr );
485
486 // problem: the 'other' address was stored by KOrganizer in
487 // a line looking like the following one:
488 // vPart += "\nADR;TYPE=dom;TYPE=intl;TYPE=parcel;TYPE=postal;TYPE=work;"
489 // "TYPE=home:other_pobox;;other_str1\nother_str2;other_loc;other_region;"
490 // "other_pocode;other_country"
491
492 QString nr;
493 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
494 addressee.insertPhoneNumber(
495 KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
496 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
497 addressee.insertPhoneNumber(
498 KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
499 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
500 addressee.insertPhoneNumber(
501 KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
502 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
503 addressee.insertPhoneNumber(
504 KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
505 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
506 addressee.insertPhoneNumber(
507 KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
508
509 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY ).
510 remove( QChar( '-' ) ).remove( QChar( ':' ) );
511 if ( !s.isEmpty() ) {
512 addressee.setBirthday( QDateTime::fromString( s ) );
513 }
514
515 bOk = ( !addressee.isEmpty() );
516 } else if ( "IPM.NOTE" == msgClass ) {
517
518 } // else if ... and so on ...
519 }
520 }
521
522 // Compose return string
523 // KDAB_TODO: Interesting, without the explicit QString the toString call is
524 // reported to be ambigious with toString( const Incidence::Ptr & ).
525 const QString iCal = calFormat.toString( cal, QString() );
526 if ( !iCal.isEmpty() ) {
527 // This was an iCal
528 return iCal;
529 }
530
531 // Not an iCal - try a vCard
532 KABC::VCardConverter converter;
533 return QString::fromUtf8( converter.createVCard( addressee ) );
534}
535
536#ifndef KDEPIM_NO_KCAL
537QString KTnef::formatTNEFInvitation( const QByteArray &tnef,
538 KCal::Calendar *cal,
539 KCal::InvitationFormatterHelper *h )
540{
541 QString vPart = msTNEFToVPart( tnef );
542 QString iCal = KCal::IncidenceFormatter::formatICalInvitation( vPart, cal, h );
543 if ( !iCal.isEmpty() ) {
544 return iCal;
545 } else {
546 return vPart;
547 }
548}
549#endif
550
551QString KTnef::formatTNEFInvitation( const QByteArray &tnef,
552 const MemoryCalendar::Ptr &cal,
553 KCalUtils::InvitationFormatterHelper *h )
554{
555 const QString vPart = msTNEFToVPart( tnef );
556 QString iCal = KCalUtils::IncidenceFormatter::formatICalInvitation( vPart, cal, h, true );
557 if ( !iCal.isEmpty() ) {
558 return iCal;
559 } else {
560 return vPart;
561 }
562}
563
564