korganizer Library API Documentation

kogroupware.cpp

00001 /* 00002 This file is part of the Groupware/KOrganizer integration. 00003 00004 Requires the Qt and KDE widget libraries, available at no cost at 00005 http://www.trolltech.com and http://www.kde.org respectively 00006 00007 Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB 00008 <info@klaralvdalens-datakonsult.se> 00009 00010 This program is free software; you can redistribute it and/or modify 00011 it under the terms of the GNU General Public License as published by 00012 the Free Software Foundation; either version 2 of the License, or 00013 (at your option) any later version. 00014 00015 This program is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU General Public License for more details. 00019 00020 You should have received a copy of the GNU General Public License 00021 along with this program; if not, write to the Free Software 00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, 00023 MA 02111-1307, USA. 00024 00025 In addition, as a special exception, the copyright holders give 00026 permission to link the code of this program with any edition of 00027 the Qt library by Trolltech AS, Norway (or with modified versions 00028 of Qt that use the same license as Qt), and distribute linked 00029 combinations including the two. You must obey the GNU General 00030 Public License in all respects for all of the code used other than 00031 Qt. If you modify this file, you may extend this exception to 00032 your version of the file, but you are not obligated to do so. If 00033 you do not wish to do so, delete this exception statement from 00034 your version. 00035 */ 00036 00037 #include "kogroupware.h" 00038 00039 #include "freebusymanager.h" 00040 #include "calendarview.h" 00041 #include "mailscheduler.h" 00042 #include "kogroupwareincomingdialog.h" 00043 #include "koviewmanager.h" 00044 #include "kocore.h" 00045 00046 #include <libkdepim/email.h> 00047 #include <ktnef/ktnefparser.h> 00048 #include <ktnef/ktnefmessage.h> 00049 #include <ktnef/ktnefdefs.h> 00050 00051 #include <libkcal/incidencebase.h> 00052 #include <libkcal/attendee.h> 00053 #include <libkcal/freebusy.h> 00054 #include <libkcal/journal.h> 00055 #include <libkcal/calendarlocal.h> 00056 #include <libkcal/icalformat.h> 00057 00058 #include <kabc/phonenumber.h> 00059 #include <kabc/vcardconverter.h> 00060 00061 #include <kdebug.h> 00062 #include <kmessagebox.h> 00063 #include <ktempfile.h> 00064 #include <kio/netaccess.h> 00065 #include <kapplication.h> 00066 #include <kconfig.h> 00067 #include <dcopclient.h> 00068 #include <dcopref.h> 00069 #include <kstandarddirs.h> 00070 #include <kdirwatch.h> 00071 00072 #include <qfile.h> 00073 #include <qbuffer.h> 00074 #include <qregexp.h> 00075 00076 #include <mimelib/enum.h> 00077 00078 #include <stdlib.h> 00079 #include <time.h> 00080 #include <qdir.h> 00081 #include "koprefs.h" 00082 00083 FreeBusyManager *KOGroupware::mFreeBusyManager = 0; 00084 00085 KOGroupware *KOGroupware::mInstance = 0; 00086 00087 KOGroupware *KOGroupware::create( CalendarView *view, 00088 KCal::Calendar *calendar ) 00089 { 00090 if( !mInstance ) 00091 mInstance = new KOGroupware( view, calendar ); 00092 return mInstance; 00093 } 00094 00095 KOGroupware *KOGroupware::instance() 00096 { 00097 // Doesn't create, that is the task of create() 00098 Q_ASSERT( mInstance ); 00099 return mInstance; 00100 } 00101 00102 00103 KOGroupware::KOGroupware( CalendarView* view, KCal::Calendar* calendar ) 00104 : QObject( 0, "kmgroupware_instance" ) 00105 { 00106 mView = view; 00107 mCalendar = calendar; 00108 00109 // Set up the dir watch of the three incoming dirs 00110 KDirWatch* watcher = KDirWatch::self(); 00111 watcher->addDir( locateLocal( "data", "korganizer/income.accepted/" ) ); 00112 watcher->addDir( locateLocal( "data", "korganizer/income.cancel/" ) ); 00113 watcher->addDir( locateLocal( "data", "korganizer/income.reply/" ) ); 00114 connect( watcher, SIGNAL( dirty( const QString& ) ), 00115 this, SLOT( incomingDirChanged( const QString& ) ) ); 00116 // Now set the ball rolling 00117 incomingDirChanged( locateLocal( "data", "korganizer/income.accepted/" ) ); 00118 incomingDirChanged( locateLocal( "data", "korganizer/income.cancel/" ) ); 00119 incomingDirChanged( locateLocal( "data", "korganizer/income.reply/" ) ); 00120 } 00121 00122 FreeBusyManager *KOGroupware::freeBusyManager() 00123 { 00124 if ( !mFreeBusyManager ) { 00125 mFreeBusyManager = new FreeBusyManager( this, "freebusymanager" ); 00126 mFreeBusyManager->setCalendar( mCalendar ); 00127 connect( mCalendar, SIGNAL( calendarChanged() ), 00128 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) ); 00129 } 00130 00131 return mFreeBusyManager; 00132 } 00133 00134 void KOGroupware::incomingDirChanged( const QString& path ) 00135 { 00136 const QString incomingDirName = locateLocal( "data","korganizer/" ) 00137 + "income."; 00138 if ( !path.startsWith( incomingDirName ) ) { 00139 kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl; 00140 return; 00141 } 00142 QString action = path.mid( incomingDirName.length() ); 00143 while ( action.length() > 0 && action[ action.length()-1 ] == '/' ) 00144 // Strip slashes at the end 00145 action.truncate( action.length()-1 ); 00146 00147 // Handle accepted invitations 00148 QDir dir( path ); 00149 QStringList files = dir.entryList( QDir::Files ); 00150 if ( files.count() == 0 ) 00151 // No more files here 00152 return; 00153 00154 // Read the file and remove it 00155 QFile f( path + "/" + files[0] ); 00156 if (!f.open(IO_ReadOnly)) { 00157 kdError(5850) << "Can't open file '" << files[0] << "'" << endl; 00158 return; 00159 } 00160 QTextStream t(&f); 00161 t.setEncoding( QTextStream::UnicodeUTF8 ); 00162 QString receiver = KPIM::getEmailAddr( t.readLine() ); 00163 QString iCal = t.read(); 00164 00165 ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal ); 00166 if ( !message ) { 00167 QString errorMessage; 00168 if (mFormat.exception()) 00169 errorMessage = "\nError message: " + mFormat.exception()->message(); 00170 kdDebug(5850) << "MailScheduler::retrieveTransactions() Error parsing" 00171 << errorMessage << endl; 00172 f.close(); 00173 return; 00174 } else 00175 f.remove(); 00176 00177 KCal::Scheduler::Method method = 00178 static_cast<KCal::Scheduler::Method>( message->method() ); 00179 KCal::ScheduleMessage::Status status = message->status(); 00180 KCal::Incidence* incidence = 00181 dynamic_cast<KCal::Incidence*>( message->event() ); 00182 KCal::MailScheduler scheduler( mCalendar ); 00183 if ( action.startsWith( "accepted" ) ) { 00184 // Find myself and set to answered and accepted 00185 KCal::Attendee::List attendees = incidence->attendees(); 00186 KCal::Attendee::List::ConstIterator it; 00187 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 00188 if( (*it)->email() == receiver ) { 00189 (*it)->setStatus( KCal::Attendee::Accepted ); 00190 (*it)->setRSVP(false); 00191 break; 00192 } 00193 } 00194 scheduler.acceptTransaction( incidence, method, status ); 00195 } else if ( action.startsWith( "cancel" ) ) 00196 // TODO: Could this be done like the others? 00197 mCalendar->deleteIncidence( incidence ); 00198 else if ( action.startsWith( "reply" ) ) 00199 scheduler.acceptTransaction( incidence, method, status ); 00200 else 00201 kdError(5850) << "Unknown incoming action " << action << endl; 00202 mView->updateView(); 00203 } 00204 00205 static void vPartMicroParser( const QString& str, QString& s ) 00206 { 00207 QString line; 00208 uint len = str.length(); 00209 00210 for( uint i=0; i<len; ++i) { 00211 if( str[i] == '\r' || str[i] == '\n' ) { 00212 if( str[i] == '\r' ) 00213 ++i; 00214 if( i+1 < len && str[i+1] == ' ' ) { 00215 // Found a continuation line, skip it's leading blanc 00216 ++i; 00217 } else { 00218 // Found a logical line end, process the line 00219 if( line.startsWith( s ) ) { 00220 s = line.mid( s.length() + 1 ); 00221 return; 00222 } 00223 line = ""; 00224 } 00225 } else { 00226 line += str[i]; 00227 } 00228 } 00229 s.truncate(0); 00230 } 00231 00232 static void string2HTML( QString& str ) { 00233 str.replace( QChar( '&' ), "&amp;" ); 00234 str.replace( QChar( '<' ), "&lt;" ); 00235 str.replace( QChar( '>' ), "&gt;" ); 00236 str.replace( QChar( '\"' ), "&quot;" ); 00237 str.replace( "\\n", "<br>" ); 00238 str.replace( "\\,", "," ); 00239 } 00240 00241 static QString meetingDetails( Incidence* incidence, Event* event ) 00242 { 00243 // Meeting details are formatted into an HTML table 00244 00245 QString html; 00246 00247 QString sSummary = i18n( "Summary unspecified" ); 00248 if ( incidence ) { 00249 if ( ! incidence->summary().isEmpty() ) { 00250 sSummary = incidence->summary(); 00251 string2HTML( sSummary ); 00252 } 00253 } 00254 00255 QString sLocation = i18n( "Location unspecified" ); 00256 if ( incidence ) { 00257 if ( ! incidence->location().isEmpty() ) { 00258 sLocation = incidence->location(); 00259 string2HTML( sLocation ); 00260 } 00261 } 00262 00263 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" ); 00264 html = QString("<div dir=\"%1\">\n").arg(dir); 00265 00266 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n"; 00267 00268 // Meeting Summary Row 00269 html += "<tr>"; 00270 html += "<td>" + i18n( "What:" ) + "</td>"; 00271 html += "<td>" + sSummary + "</td>"; 00272 html += "</tr>\n"; 00273 00274 // Meeting Location Row 00275 html += "<tr>"; 00276 html += "<td>" + i18n( "Where:" ) + "</td>"; 00277 html += "<td>" + sLocation + "</td>"; 00278 html += "</tr>\n"; 00279 00280 // Meeting Start Time Row 00281 html += "<tr>"; 00282 html += "<td>" + i18n( "Start Time:" ) + "</td>"; 00283 html += "<td>"; 00284 if ( ! event->doesFloat() ) { 00285 html += i18n("%1: Start Date, %2: Start Time", "%1 %2") 00286 .arg( event->dtStartDateStr(), event->dtStartTimeStr() ); 00287 } else { 00288 html += i18n("%1: Start Date", "%1 (time unspecified)") 00289 .arg( event->dtStartDateStr() ); 00290 } 00291 html += "</td>"; 00292 html += "</tr>\n"; 00293 00294 // Meeting End Time Row 00295 html += "<tr>"; 00296 html += "<td>" + i18n( "End Time:" ) + "</td>"; 00297 html += "<td>"; 00298 if ( event->hasEndDate() ) { 00299 if ( ! event->doesFloat() ) { 00300 html += i18n("%1: End Date, %2: End Time", "%1 %2") 00301 .arg( event->dtEndDateStr(), event->dtEndTimeStr() ); 00302 } else { 00303 html += i18n("%1: End Date", "%1 (time unspecified)") 00304 .arg( event->dtEndDateStr() ); 00305 } 00306 } else { 00307 html += i18n( "Unspecified" ); 00308 } 00309 html += "</td>"; 00310 html += "</tr>\n"; 00311 00312 // Meeting Duration Row 00313 if ( !event->doesFloat() && event->hasEndDate() ) { 00314 html += "<tr>"; 00315 QTime sDuration, t; 00316 int secs = event->dtStart().secsTo( event->dtEnd() ); 00317 t = sDuration.addSecs( secs ); 00318 html += "<td>" + i18n( "Duration:" ) + "</td>"; 00319 html += "<td>"; 00320 if ( t.hour() > 0 ) { 00321 html += i18n( "1 hour ", "%n hours ", t.hour() ); 00322 } 00323 if ( t.minute() > 0 ) { 00324 html += i18n( "1 minute ", "%n minutes ", t.minute() ); 00325 } 00326 html += "</td>"; 00327 html += "</tr>\n"; 00328 } 00329 00330 html += "</table>\n"; 00331 html += "</div>\n"; 00332 00333 return html; 00334 } 00335 00336 static QString taskDetails( Incidence* incidence ) 00337 { 00338 // Task details are formatted into an HTML table 00339 00340 QString html; 00341 00342 QString sSummary = i18n( "Summary unspecified" ); 00343 QString sDescr = i18n( "Description unspecified" ); 00344 if ( incidence ) { 00345 if ( ! incidence->summary().isEmpty() ) { 00346 sSummary = incidence->summary(); 00347 } 00348 if ( ! incidence->description().isEmpty() ) { 00349 sDescr = incidence->description(); 00350 } 00351 } 00352 html = "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n"; 00353 00354 // Task Summary Row 00355 html += "<tr>"; 00356 html += "<td>" + i18n( "Summary:" ) + "</td>"; 00357 html += "<td>" + sSummary + "</td>"; 00358 html += "</tr>\n"; 00359 00360 // Task Description Row 00361 html += "<tr>"; 00362 html += "<td>" + i18n( "Description:" ) + "</td>"; 00363 html += "<td>" + sDescr + "</td>"; 00364 html += "</tr>\n"; 00365 00366 html += "</table>\n"; 00367 00368 return html; 00369 } 00370 00371 QString KOGroupware::formatICal( const QString& iCal ) 00372 { 00373 KCal::CalendarLocal cl( mCalendar->timeZoneId() ); 00374 ICalFormat format; 00375 format.fromString( &cl, iCal ); 00376 00377 // Make a shallow copy of the event and task lists 00378 if( cl.events().count() == 0 && cl.todos().count() == 0 ) { 00379 kdDebug(5850) << "No iCal in this one\n"; 00380 return QString(); 00381 } 00382 00383 // Parse the first event out of the vcal 00384 // TODO: Is it legal to have several events/todos per mail part? 00385 Incidence* incidence = 0; 00386 Event* event = 0; 00387 Todo* todo = 0; 00388 if( cl.events().count() > 0 ) 00389 incidence = event = cl.events().first(); 00390 else 00391 incidence = todo = cl.todos().first(); 00392 00393 // TODO: Actually the scheduler needs to do this: 00394 QString sMethod; // = incidence->method(); 00395 // TODO: This is a temporary workaround to get the method 00396 sMethod = "METHOD"; 00397 vPartMicroParser( iCal, sMethod ); 00398 sMethod = sMethod.lower(); 00399 00400 // First make the text of the message 00401 QString html; 00402 if( sMethod == "request" ) { 00403 if( event ) { 00404 html = i18n( "<h2>You have been invited to this meeting</h2>" ); 00405 html += meetingDetails( incidence, event ); 00406 } else { 00407 html = i18n( "<h2>You have been assigned this task</h2>" ); 00408 html += taskDetails( incidence ); 00409 } 00410 } else if( sMethod == "reply" ) { 00411 Attendee::List attendees = incidence->attendees(); 00412 if( attendees.count() == 0 ) { 00413 kdDebug(5850) << "No attendees in the iCal reply!\n"; 00414 return QString(); 00415 } 00416 if( attendees.count() != 1 ) 00417 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " 00418 << "but is " << attendees.count() << endl; 00419 Attendee* attendee = *attendees.begin(); 00420 00421 switch( attendee->status() ) { 00422 case Attendee::Accepted: 00423 if( event ) { 00424 html = i18n( "<h2>Sender accepts this meeting invitation</h2>" ); 00425 html += meetingDetails( incidence, event ); 00426 } else { 00427 html = i18n( "<h2>Sender accepts this task</h2>" ); 00428 html += taskDetails( incidence ); 00429 } 00430 break; 00431 00432 case Attendee::Tentative: 00433 if( event ) { 00434 html = i18n( "<h2>Sender tentatively accepts this " 00435 "meeting invitation</h2>" ); 00436 html += meetingDetails( incidence, event ); 00437 } else { 00438 html = i18n( "<h2>Sender tentatively accepts this task</h2>" ); 00439 html += taskDetails( incidence ); 00440 } 00441 break; 00442 00443 case Attendee::Declined: 00444 if( event ) { 00445 html = i18n( "<h2>Sender declines this meeting invitation</h2>" ); 00446 html += meetingDetails( incidence, event ); 00447 } else { 00448 html = i18n( "<h2>Sender declines this task</h2>" ); 00449 html += taskDetails( incidence ); 00450 } 00451 break; 00452 00453 default: 00454 if( event ) { 00455 html = i18n( "<h2>Unknown response to this meeting invitation</h2>" ); 00456 html += meetingDetails( incidence, event ); 00457 } else { 00458 html = i18n( "<h2>Unknown response to this task</h2>" ); 00459 html += taskDetails( incidence ); 00460 } 00461 } 00462 } else if( sMethod == "cancel" ) { 00463 if( event ) { 00464 html = i18n( "<h2>This meeting has been canceled</h2>" ); 00465 html += meetingDetails( incidence, event ); 00466 } else { 00467 html = i18n( "<h2>This task was canceled</h2>" ); 00468 html += taskDetails( incidence ); 00469 } 00470 } 00471 00472 // Add the groupware URLs 00473 html += "<br>&nbsp;<br>&nbsp;<br>"; 00474 html += "<table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td><td>"; 00475 if( sMethod == "request" || sMethod == "update" ) { 00476 // Accept 00477 html += "<a href=\"kmail:groupware_request_accept\"><b>"; 00478 html += i18n( "[Accept]" ); 00479 html += "</b></a></td><td> &nbsp; </td><td>"; 00480 // Accept conditionally 00481 html += "<a href=\"kmail:groupware_request_accept conditionally\"><b>"; 00482 html += i18n( "Accept conditionally", "[Accept cond.]" ); 00483 html += "</b></a></td><td> &nbsp; </td><td>"; 00484 // Decline 00485 html += "<a href=\"kmail:groupware_request_decline\"><b>"; 00486 html += i18n( "[Decline]" ); 00487 if( event ) { 00488 // Check my calendar... 00489 html += "</b></a></td><td> &nbsp; </td><td>"; 00490 html += "<a href=\"kmail:groupware_request_check\"><b>"; 00491 html += i18n("[Check my calendar...]" ); 00492 } 00493 } else if( sMethod == "reply" ) { 00494 // Enter this into my calendar 00495 html += "<a href=\"kmail:groupware_reply#%1\"><b>"; 00496 if( event ) 00497 html += i18n( "[Enter this into my calendar]" ); 00498 else 00499 html += i18n( "[Enter this into my task list]" ); 00500 } else if( sMethod == "cancel" ) { 00501 // Cancel event from my calendar 00502 html += "<a href=\"kmail:groupware_cancel\"><b>"; 00503 html += i18n( "[Remove this from my calendar]" ); 00504 } 00505 html += "</b></a></td></tr></table>"; 00506 00507 QString sDescr = incidence->description(); 00508 if( ( sMethod == "request" || sMethod == "cancel" ) && !sDescr.isEmpty() ) { 00509 string2HTML( sDescr ); 00510 html += "<br>&nbsp;<br>&nbsp;<br><u>" + i18n("Description:") 00511 + "</u><br><table border=\"0\"><tr><td>&nbsp;</td><td>"; 00512 html += sDescr + "</td></tr></table>"; 00513 } 00514 html += "&nbsp;<br>&nbsp;<br><u>" + i18n( "Original message:" ) + "</u>"; 00515 00516 return html; 00517 } 00518 00519 QString KOGroupware::formatTNEF( const QByteArray& tnef ) 00520 { 00521 QString vPart = msTNEFToVPart( tnef ); 00522 QString iCal = formatICal( vPart ); 00523 if( !iCal.isEmpty() ) 00524 return iCal; 00525 return vPart; 00526 } 00527 00528 bool KOGroupware::incomingEventRequest( const QString& request, 00529 const QString& receiver, 00530 const QString& vCalIn ) 00531 { 00532 // This was the code to accept a choice from KMail. Needs porting 00533 EventState state; 00534 if( request == "accept" ) 00535 state = Accepted; 00536 else if( request == "accept conditionally" ) 00537 state = ConditionallyAccepted; 00538 else if( request == "decline" ) 00539 state = Declined; 00540 else if( request == "check" ) 00541 state = Request; 00542 else 00543 return false; 00544 00545 // Parse the event request into a ScheduleMessage; this needs to 00546 // be done in any case. 00547 KCal::ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, 00548 vCalIn ); 00549 if( message ) { 00550 kdDebug(5850) << "KOGroupware::incomingEventRequest: got message '" 00551 << vCalIn << "'" << endl; 00552 } else { 00553 QString errorMessage; 00554 if( mFormat.exception() ) { 00555 errorMessage = mFormat.exception()->message(); 00556 } 00557 kdDebug(5850) << "KOGroupware::incomingEventRequest() Error parsing " 00558 << "message: " << errorMessage << endl; 00559 // If the message was broken, there's nothing we can do. 00560 return false; 00561 } 00562 00563 KCal::Incidence* event = dynamic_cast<KCal::Incidence*>( message->event() ); 00564 Q_ASSERT( event ); 00565 if( !event ) { // something bad happened, just to be safe 00566 kdDebug(5850) << "KOGroupware::incomingEventRequest(): Not an event???\n"; 00567 return false; 00568 } 00569 00570 // Now check if the event needs to be accepted or if this is 00571 // already done. 00572 if( state == Request ) { 00573 // Need to accept, present it to the user 00574 KOGroupwareIncomingDialog dlg( event ); 00575 int ret = dlg.exec(); 00576 if( ret == QDialog::Rejected ) { 00577 // User declined to make a choice, we can't send a vCal back 00578 kdDebug(5850) << "KOGroupware::incomingEventRequest(): User canceled\n"; 00579 return false; 00580 } 00581 00582 if( dlg.isDeclined() ) 00583 state = Declined; 00584 else if( dlg.isConditionallyAccepted() ) 00585 state = ConditionallyAccepted; 00586 else if( dlg.isAccepted() ) 00587 state = Accepted; 00588 else 00589 kdDebug(5850) << "KOGroupware::incomingEventRequest(): unknown " 00590 << "event request state" << endl; 00591 } 00592 00593 // If the event has an alarm, make sure it doesn't have a negative time. 00594 // This is yet another OL workaround 00595 #if 0 00596 // PENDING(bo): Disabled for now, until I figure out how the old offset 00597 // matches the two new offsets 00598 Alarm::List alarms = event->alarms(); 00599 Alarm::List::ConstIterator it; 00600 for ( it = alarms.begin(); it != alarms.end(); ++it) { 00601 if ( (*it)->hasTime() ) { 00602 QDateTime t = (*it)->time(); 00603 int offset = event->dtStart().secsTo( t ); 00604 if( offset > 0 ) 00605 // PENDING(Bo): Not implemented yet 00606 kdDebug(5850) << "Warning: Alarm fires after the event\n"; 00607 } else { 00608 int offset = (*it)->offset().asSeconds(); 00609 if( offset > 0 ) { 00610 // This number should be negative so the alarm fires before the event 00611 Duration d( -offset ); 00612 (*it)->setOffset( d ); 00613 } 00614 } 00615 } 00616 #endif 00617 00618 // Enter the event into the calendar. We just create a 00619 // Scheduler, because all the code we need is already there. We 00620 // take an MailScheduler, because we need a concrete one, but we 00621 // really only want code from Scheduler. 00622 // PENDING(kalle) Handle tentative acceptance differently. 00623 KCal::MailScheduler scheduler( mCalendar ); 00624 if( state == Accepted || state == ConditionallyAccepted ) { 00625 scheduler.acceptTransaction( event, 00626 (KCal::Scheduler::Method)message->method(), 00627 message->status() ); 00628 mView->updateView(); 00629 } 00630 00631 KCal::Attendee::List attendees = event->attendees(); 00632 KCal::Attendee::List::ConstIterator it; 00633 KCal::Attendee* myself = 0; 00634 // Find myself, there will always be all attendees listed, even if 00635 // only I need to answer it. 00636 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 00637 if( (*it)->email() == receiver ) { 00638 // We are the current one, and even the receiver, note 00639 // this and quit searching. 00640 myself = (*it); 00641 break; 00642 } 00643 00644 if ( KOPrefs::instance()->thatIsMe( (*it)->email() ) ) { 00645 // If we are the current one, note that. Still continue to 00646 // search in case we find the receiver himself. 00647 myself = (*it); 00648 } 00649 } 00650 00651 Q_ASSERT( myself ); 00652 00653 KCal::Attendee* newMyself = 0; 00654 if( myself ) { 00655 switch( state ) { 00656 case Accepted: 00657 myself->setStatus( KCal::Attendee::Accepted ); 00658 break; 00659 case ConditionallyAccepted: 00660 myself->setStatus( KCal::Attendee::Tentative ); 00661 break; 00662 case Declined: 00663 myself->setStatus( KCal::Attendee::Declined ); 00664 break; 00665 default: 00666 ; 00667 }; 00668 00669 // No more request response 00670 myself->setRSVP(false); 00671 00672 event->updated(); 00673 00674 newMyself = new KCal::Attendee( myself->name(), 00675 receiver.isEmpty() ? 00676 myself->email() : 00677 receiver, 00678 myself->RSVP(), 00679 myself->status(), 00680 myself->role(), 00681 myself->uid() ); 00682 } 00683 00684 event->updated(); 00685 00686 // Send back the answer; construct it on the base of state. We 00687 // make a clone of the event since we need to manipulate it here. 00688 // NOTE: This contains a workaround around a libkcal bug: REPLY 00689 // vCals may not have more than one ATTENDEE (as libical correctly 00690 // specifies), but libkcal always writes out all the ATTENDEEs, 00691 // thus producing invalid vCals. We make a clone of the vEvent 00692 // here and remove all attendees except ourselves. 00693 Incidence* newIncidence = event->clone(); 00694 Event* newEvent = static_cast<KCal::Event*>( newIncidence ); 00695 00696 #if 0 00697 // OL compatibility thing. To be ported. 00698 // The problem is that OL is braindead when it comes to receiving 00699 // events that mention alarms. So strip them before sending to OL 00700 // people 00701 bool stripAlarms = false; 00702 emit getStripAlarmsForSending( stripAlarms ); 00703 if( stripAlarms ) 00704 // Strip alarms from the send 00705 newEvent->clearAlarms(); 00706 #endif 00707 00708 newEvent->clearAttendees(); 00709 if( newMyself ) 00710 newEvent->addAttendee( newMyself ); 00711 00712 // Create the outgoing vCal 00713 QString messageText = 00714 mFormat.createScheduleMessage( newEvent, KCal::Scheduler::Reply ); 00715 scheduler.performTransaction( newEvent, KCal::Scheduler::Reply ); 00716 00717 // Fix broken OL appointments 00718 if( vCalIn.contains( "PRODID:-//Microsoft" ) ) { 00719 // OL doesn't send the organizer as an attendee as it should 00720 Attendee* organizer = new KCal::Attendee( i18n("Organizer"), 00721 event->organizer(), false, 00722 KCal::Attendee::Accepted ); 00723 event->addAttendee( organizer ); 00724 } 00725 00726 kdDebug(5850) << "Done" << endl; 00727 return true; 00728 } 00729 00734 void KOGroupware::incomingResourceRequest( const QValueList<QPair<QDateTime, 00735 QDateTime> >& busy, 00736 const QCString& resource, 00737 const QString& vCalIn, 00738 bool& vCalInOK, 00739 QString& vCalOut, 00740 bool& vCalOutOK, 00741 bool& isFree, 00742 QDateTime& start, 00743 QDateTime& end ) 00744 { 00745 // Parse the event request into a ScheduleMessage; this needs to 00746 // be done in any case. 00747 KCal::ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, 00748 vCalIn ); 00749 if( message ) 00750 vCalInOK = true; 00751 else { 00752 QString errorMessage; 00753 if( mFormat.exception() ) { 00754 errorMessage = mFormat.exception()->message(); 00755 } 00756 kdDebug(5850) << "KOGroupware::incomingResourceRequest() Error parsing " 00757 "message: " << errorMessage << endl; 00758 vCalInOK = false; 00759 // If the message was broken, there's nothing we can do. 00760 return; 00761 } 00762 00763 KCal::Event* event = dynamic_cast<KCal::Event*>( message->event() ); 00764 Q_ASSERT( event ); 00765 if( !event ) { 00766 // Something has gone badly wrong 00767 vCalInOK = false; 00768 return; 00769 } 00770 00771 // Now find out whether the resource is free at the requested 00772 // time, take the opportunity to assign the reference parameters. 00773 start = event->dtStart(); 00774 end = event->dtEnd(); 00775 isFree = true; 00776 QValueList<QPair<QDateTime, QDateTime> >::ConstIterator it; 00777 for( it = busy.begin(); it != busy.end(); ++it ) { 00778 if( (*it).second <= start || (*it).first >= end ) 00779 // Busy period ends before try period or starts after try period 00780 continue; 00781 else { 00782 isFree = false; 00783 break; // no need to search further 00784 } 00785 } 00786 00787 // Send back the answer; construct it on the base of state 00788 KCal::Attendee::List attendees = event->attendees(); 00789 KCal::Attendee* resourceAtt = 0; 00790 00791 // Find the resource addresse, there will always be all attendees 00792 // listed, even if only one needs to answer it. 00793 KCal::Attendee::List::ConstIterator it2; 00794 for( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { 00795 if( (*it2)->email().utf8() == resource ) { 00796 resourceAtt = *it2; 00797 break; 00798 } 00799 } 00800 Q_ASSERT( resourceAtt ); 00801 if( resourceAtt ) { 00802 if( isFree ) 00803 resourceAtt->setStatus( KCal::Attendee::Accepted ); 00804 else 00805 resourceAtt->setStatus( KCal::Attendee::Declined ); 00806 } else { 00807 vCalOutOK = false; 00808 return; 00809 } 00810 00811 // Create the outgoing vCal 00812 QString messageText = 00813 mFormat.createScheduleMessage( event, KCal::Scheduler::Reply ); 00814 // kdDebug(5850) << "Sending vCal back to KMail: " << messageText << endl; 00815 vCalOut = messageText; 00816 vCalOutOK = true; 00817 return; 00818 } 00819 00820 00825 bool KOGroupware::incidenceAnswer( const QString& vCal ) 00826 { 00827 // Parse the event reply 00828 KCal::ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, 00829 vCal ); 00830 if( !message ) { 00831 // a parse error of some sort 00832 KMessageBox:: 00833 error( mView, 00834 i18n( "<b>There was a problem parsing the iCal data:</b><br>%1" ) 00835 .arg( mFormat.exception()->message() ) ); 00836 return false; 00837 } 00838 00839 KCal::IncidenceBase* incidence = message->event(); 00840 00841 // Enter the answer into the calendar. 00842 QString uid = incidence->uid(); 00843 KCal::MailScheduler scheduler( mCalendar ); 00844 if( !scheduler.acceptTransaction( incidence, 00845 (KCal::Scheduler::Method)message->method(), 00846 message->status() ) ) { 00847 KMessageBox::error( mView, i18n("Scheduling failed") ); 00848 return false; 00849 } 00850 00851 mView->updateView(); 00852 return true; 00853 } 00854 00855 bool KOGroupware::cancelIncidence( const QString& iCal ) 00856 { 00857 // Parse the event reply 00858 KCal::ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, 00859 iCal ); 00860 if( !message ) { 00861 // a parse error of some sort 00862 KMessageBox:: 00863 error( mView, 00864 i18n( "<b>There was a problem parsing the iCal data:</b><br>%1" ) 00865 .arg( mFormat.exception()->message() ) ); 00866 return false; 00867 } 00868 00869 KCal::IncidenceBase* incidence = message->event(); 00870 00871 // Enter the answer into the calendar. 00872 Event* event = mCalendar->event( incidence->uid() ); 00873 if( !event ) 00874 return false; 00875 00876 mCalendar->deleteEvent( event ); 00877 return true; 00878 } 00879 00880 /* This function sends mails if necessary, and makes sure the user really 00881 * want to change his calendar. 00882 * 00883 * Return true means accept the changes 00884 * Return false means revert the changes 00885 */ 00886 bool KOGroupware::sendICalMessage( QWidget* parent, 00887 KCal::Scheduler::Method method, 00888 Incidence* incidence, bool isDeleting ) 00889 { 00890 bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer() ); 00891 00892 int rc = 0; 00893 if( isOrganizer ) { 00894 // Figure out if there are other people involved in this incidence 00895 bool otherPeople = false; 00896 Attendee::List attendees = incidence->attendees(); 00897 Attendee::List::ConstIterator it; 00898 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 00899 // Don't send email to ourselves 00900 if ( !KOPrefs::instance()->thatIsMe( (*it)->email() ) ) { 00901 otherPeople = true; 00902 break; 00903 } 00904 } 00905 if( !otherPeople ) 00906 // You never send something out if no others are involved 00907 return true; 00908 00909 QString type; 00910 if( incidence->type() == "Event") type = i18n("event"); 00911 else if( incidence->type() == "Todo" ) type = i18n("task"); 00912 else if( incidence->type() == "Journal" ) type = i18n("journal entry"); 00913 else type = incidence->type(); 00914 QString txt = i18n( "This %1 includes other people. " 00915 "Should email be sent out to the attendees?" ) 00916 .arg( type ); 00917 rc = KMessageBox::questionYesNoCancel( parent, txt, 00918 i18n("Group scheduling email") ); 00919 } else if( incidence->type() == "Todo" ) { 00920 if( method == Scheduler::Request ) 00921 // This is an update to be sent to the organizer 00922 method = Scheduler::Reply; 00923 00924 // Ask if the user wants to tell the organizer about the current status 00925 QString txt = i18n( "Do you want to send a status update to the " 00926 "organizer of this task?"); 00927 rc = KMessageBox::questionYesNo( parent, txt ); 00928 } else if( incidence->type() == "Event" ) { 00929 // When you're not the organizer of an event, an update mail can 00930 // never be sent out 00931 // Pending(Bo): So how will an attendee cancel his participation? 00932 QString txt; 00933 if( isDeleting ) 00934 txt = i18n( "You are not the organizer of this event. " 00935 "Deleting it will bring your calendar out of sync " 00936 "with the organizers calendar. Do you really want " 00937 "to delete it?" ); 00938 else 00939 txt = i18n( "You are not the organizer of this event. " 00940 "Editing it will bring your calendar out of sync " 00941 "with the organizers calendar. Do you really want " 00942 "to edit it?" ); 00943 rc = KMessageBox::questionYesNo( parent, txt ); 00944 return ( rc == KMessageBox::Yes ); 00945 } else { 00946 qFatal( "Some unimplemented thing happened" ); 00947 } 00948 00949 if( rc == KMessageBox::Yes ) { 00950 // We will be sending out a message here. Now make sure there is 00951 // some summary 00952 if( incidence->summary().isEmpty() ) 00953 incidence->setSummary( i18n("<No summary given>") ); 00954 00955 // Send the mail 00956 KCal::MailScheduler scheduler( mCalendar ); 00957 scheduler.performTransaction( incidence, method ); 00958 00959 return true; 00960 } else if( rc == KMessageBox::No ) 00961 return true; 00962 else 00963 return false; 00964 } 00965 00966 00967 //----------------------------------------------------------------------------- 00968 00969 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key, 00970 const QString& fallback = QString::null) 00971 { 00972 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16, 00973 fallback ); 00974 } 00975 00976 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name, 00977 const QString& fallback = QString::null ) 00978 { 00979 return tnefMsg->findNamedProp( name, fallback ); 00980 } 00981 00982 struct save_tz { char* old_tz; char* tz_env_str; }; 00983 00984 /* temporarily go to a different timezone */ 00985 static struct save_tz set_tz( const char* _tc ) 00986 { 00987 const char *tc = _tc?_tc:"UTC"; 00988 00989 struct save_tz rv; 00990 00991 rv.old_tz = 0; 00992 rv.tz_env_str = 0; 00993 00994 //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl; 00995 00996 char* tz_env = 0; 00997 if( getenv( "TZ" ) ) { 00998 tz_env = strdup( getenv( "TZ" ) ); 00999 rv.old_tz = tz_env; 01000 } 01001 char* tmp_env = (char*)malloc( strlen( tc ) + 4 ); 01002 strcpy( tmp_env, "TZ=" ); 01003 strcpy( tmp_env+3, tc ); 01004 putenv( tmp_env ); 01005 01006 rv.tz_env_str = tmp_env; 01007 01008 /* tmp_env is not free'ed -- it is part of the environment */ 01009 01010 tzset(); 01011 //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl; 01012 01013 return rv; 01014 } 01015 01016 /* restore previous timezone */ 01017 static void unset_tz( struct save_tz old_tz ) 01018 { 01019 if( old_tz.old_tz ) { 01020 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 ); 01021 strcpy( tmp_env, "TZ=" ); 01022 strcpy( tmp_env+3, old_tz.old_tz ); 01023 putenv( tmp_env ); 01024 /* tmp_env is not free'ed -- it is part of the environment */ 01025 free( old_tz.old_tz ); 01026 } else { 01027 /* clear TZ from env */ 01028 putenv( strdup("TZ") ); 01029 } 01030 tzset(); 01031 01032 /* is this OK? */ 01033 if( old_tz.tz_env_str ) free( old_tz.tz_env_str ); 01034 } 01035 01036 static QDateTime utc2Local( const QDateTime& utcdt ) 01037 { 01038 struct tm tmL; 01039 01040 save_tz tmp_tz = set_tz("UTC"); 01041 time_t utc = utcdt.toTime_t(); 01042 unset_tz( tmp_tz ); 01043 01044 localtime_r( &utc, &tmL ); 01045 return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ), 01046 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) ); 01047 } 01048 01049 static QDateTime pureISOToLocalQDateTime( const QString& dtStr, 01050 bool bDateOnly = false ) 01051 { 01052 QDate tmpDate; 01053 QTime tmpTime; 01054 int year, month, day, hour, minute, second; 01055 01056 if( bDateOnly ) { 01057 year = dtStr.left( 4 ).toInt(); 01058 month = dtStr.mid( 4, 2 ).toInt(); 01059 day = dtStr.mid( 6, 2 ).toInt(); 01060 hour = 0; 01061 minute = 0; 01062 second = 0; 01063 } else { 01064 year = dtStr.left( 4 ).toInt(); 01065 month = dtStr.mid( 4, 2 ).toInt(); 01066 day = dtStr.mid( 6, 2 ).toInt(); 01067 hour = dtStr.mid( 9, 2 ).toInt(); 01068 minute = dtStr.mid( 11, 2 ).toInt(); 01069 second = dtStr.mid( 13, 2 ).toInt(); 01070 } 01071 tmpDate.setYMD( year, month, day ); 01072 tmpTime.setHMS( hour, minute, second ); 01073 01074 if( tmpDate.isValid() && tmpTime.isValid() ) { 01075 QDateTime dT = QDateTime( tmpDate, tmpTime ); 01076 01077 if( !bDateOnly ) { 01078 // correct for GMT ( == Zulu time == UTC ) 01079 if (dtStr.at(dtStr.length()-1) == 'Z') { 01080 //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() ); 01081 //localUTCOffset( dT ) ); 01082 dT = utc2Local( dT ); 01083 } 01084 } 01085 return dT; 01086 } else 01087 return QDateTime(); 01088 } 01089 01090 QString KOGroupware::msTNEFToVPart( const QByteArray& tnef ) 01091 { 01092 bool bOk = false; 01093 01094 KTNEFParser parser; 01095 QBuffer buf( tnef ); 01096 CalendarLocal cal; 01097 KABC::Addressee addressee; 01098 KABC::VCardConverter cardConv; 01099 ICalFormat calFormat; 01100 Event* event = new Event(); 01101 01102 if( parser.openDevice( &buf ) ) { 01103 KTNEFMessage* tnefMsg = parser.message(); 01104 //QMap<int,KTNEFProperty*> props = parser.message()->properties(); 01105 01106 // Everything depends from property PR_MESSAGE_CLASS 01107 // (this is added by KTNEFParser): 01108 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true ) 01109 .upper(); 01110 if( !msgClass.isEmpty() ) { 01111 // Match the old class names that might be used by Outlook for 01112 // compatibility with Microsoft Mail for Windows for Workgroups 3.1. 01113 bool bCompatClassAppointment = false; 01114 bool bCompatMethodRequest = false; 01115 bool bCompatMethodCancled = false; 01116 bool bCompatMethodAccepted = false; 01117 bool bCompatMethodAcceptedCond = false; 01118 bool bCompatMethodDeclined = false; 01119 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) { 01120 bCompatClassAppointment = true; 01121 if( msgClass.endsWith( ".MTGREQ" ) ) 01122 bCompatMethodRequest = true; 01123 if( msgClass.endsWith( ".MTGCNCL" ) ) 01124 bCompatMethodCancled = true; 01125 if( msgClass.endsWith( ".MTGRESPP" ) ) 01126 bCompatMethodAccepted = true; 01127 if( msgClass.endsWith( ".MTGRESPA" ) ) 01128 bCompatMethodAcceptedCond = true; 01129 if( msgClass.endsWith( ".MTGRESPN" ) ) 01130 bCompatMethodDeclined = true; 01131 } 01132 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" ); 01133 01134 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) { 01135 // Compose a vCal 01136 bool bIsReply = false; 01137 QString prodID = "-//Microsoft Corporation//Outlook "; 01138 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" ); 01139 prodID += "MIMEDIR/EN\n"; 01140 prodID += "VERSION:2.0\n"; 01141 calFormat.setApplication( "Outlook", prodID ); 01142 01143 Scheduler::Method method; 01144 if( bCompatMethodRequest ) 01145 method = Scheduler::Request; 01146 else if( bCompatMethodCancled ) 01147 method = Scheduler::Cancel; 01148 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond || 01149 bCompatMethodDeclined ) { 01150 method = Scheduler::Reply; 01151 bIsReply = true; 01152 } else { 01153 // pending(khz): verify whether "0x0c17" is the right tag ??? 01154 // 01155 // at the moment we think there are REQUESTS and UPDATES 01156 // 01157 // but WHAT ABOUT REPLIES ??? 01158 // 01159 // 01160 01161 if( tnefMsg->findProp(0x0c17) == "1" ) 01162 bIsReply = true; 01163 method = Scheduler::Request; 01164 } 01165 01167 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown ); 01168 01169 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) ); 01170 01171 if( !sSenderSearchKeyEmail.isEmpty() ) { 01172 int colon = sSenderSearchKeyEmail.find( ':' ); 01173 // May be e.g. "SMTP:KHZ@KDE.ORG" 01174 if( sSenderSearchKeyEmail.find( ':' ) == -1 ) 01175 sSenderSearchKeyEmail.remove( 0, colon+1 ); 01176 } 01177 01178 QString s( tnefMsg->findProp( 0x0e04 ) ); 01179 QStringList attendees = QStringList::split( ';', s ); 01180 if( attendees.count() ) { 01181 for( QStringList::Iterator it = attendees.begin(); 01182 it != attendees.end(); ++it ) { 01183 // Skip all entries that have no '@' since these are 01184 // no mail addresses 01185 if( (*it).find('@') == -1 ) { 01186 s = (*it).stripWhiteSpace(); 01187 01188 Attendee *attendee = new Attendee( s, s, true ); 01189 if( bIsReply ) { 01190 if( bCompatMethodAccepted ) 01191 attendee->setStatus( Attendee::Accepted ); 01192 if( bCompatMethodDeclined ) 01193 attendee->setStatus( Attendee::Declined ); 01194 if( bCompatMethodAcceptedCond ) 01195 attendee->setStatus(Attendee::Tentative); 01196 } else { 01197 attendee->setStatus( Attendee::NeedsAction ); 01198 attendee->setRole( Attendee::ReqParticipant ); 01199 } 01200 event->addAttendee(attendee); 01201 } 01202 } 01203 } else { 01204 // Oops, no attendees? 01205 // This must be old style, let us use the PR_SENDER_SEARCH_KEY. 01206 s = sSenderSearchKeyEmail; 01207 if( !s.isEmpty() ) { 01208 Attendee *attendee = new Attendee( QString::null, QString::null, 01209 true ); 01210 if( bIsReply ) { 01211 if( bCompatMethodAccepted ) 01212 attendee->setStatus( Attendee::Accepted ); 01213 if( bCompatMethodAcceptedCond ) 01214 attendee->setStatus( Attendee::Declined ); 01215 if( bCompatMethodDeclined ) 01216 attendee->setStatus( Attendee::Tentative ); 01217 } else { 01218 attendee->setStatus(Attendee::NeedsAction); 01219 attendee->setRole(Attendee::ReqParticipant); 01220 } 01221 event->addAttendee(attendee); 01222 } 01223 } 01224 s = tnefMsg->findProp( 0x0c1f ); // look for organizer property 01225 if( s.isEmpty() && !bIsReply ) 01226 s = sSenderSearchKeyEmail; 01227 if( !s.isEmpty() ) 01228 event->setOrganizer( s ); 01229 01230 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null ) 01231 .replace( QChar( ':' ), QString::null ); 01232 event->setDtStart( QDateTime::fromString( s ) ); // ## Format?? 01233 01234 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null ) 01235 .replace( QChar( ':' ), QString::null ); 01236 event->setDtEnd( QDateTime::fromString( s ) ); 01237 01238 s = tnefMsg->findProp( 0x8208 ); 01239 event->setLocation( s ); 01240 01241 // is it OK to set this to OPAQUE always ?? 01242 //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme! 01243 //vPart += "SEQUENCE:0\n"; 01244 01245 // is "0x0023" OK - or should we look for "0x0003" ?? 01246 s = tnefMsg->findProp( 0x0023 ); 01247 event->setUid( s ); 01248 01249 // PENDING(khz): is this value in local timezone? Must it be 01250 // adjusted? Most likely this is a bug in the server or in 01251 // Outlook - we ignore it for now. 01252 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null ) 01253 .replace( QChar( ':' ), QString::null ); 01254 // ### libkcal always uses currentDateTime() 01255 // event->setDtStamp(QDateTime::fromString(s)); 01256 01257 s = tnefMsg->findNamedProp( "Keywords" ); 01258 event->setCategories( s ); 01259 01260 s = tnefMsg->findProp( 0x1000 ); 01261 event->setDescription( s ); 01262 01263 s = tnefMsg->findProp( 0x0070 ); 01264 event->setSummary( s ); 01265 01266 s = tnefMsg->findProp( 0x0026 ); 01267 event->setPriority( s.toInt() ); 01268 01269 // is reminder flag set ? 01270 if(!tnefMsg->findProp(0x8503).isEmpty()) { 01271 Alarm *alarm = new Alarm(event); 01272 QDateTime highNoonTime = 01273 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 ) 01274 .replace( QChar( '-' ), "" ) 01275 .replace( QChar( ':' ), "" ) ); 01276 QDateTime wakeMeUpTime = 01277 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" ) 01278 .replace( QChar( '-' ), "" ) 01279 .replace( QChar( ':' ), "" ) ); 01280 alarm->setTime(wakeMeUpTime); 01281 01282 if( highNoonTime.isValid() && wakeMeUpTime.isValid() ) 01283 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) ); 01284 else 01285 // default: wake them up 15 minutes before the appointment 01286 alarm->setStartOffset( Duration( 15*60 ) ); 01287 alarm->setDisplayAlarm( i18n( "Reminder" ) ); 01288 01289 // Sorry: the different action types are not known (yet) 01290 // so we always set 'DISPLAY' (no sounds, no images...) 01291 event->addAlarm( alarm ); 01292 } 01293 cal.addEvent( event ); 01294 bOk = true; 01295 // we finished composing a vCal 01296 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) { 01297 addressee.setUid( stringProp( tnefMsg, attMSGID ) ); 01298 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) ); 01299 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true ); 01300 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false ); 01301 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false ); 01302 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) ); 01303 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) ); 01304 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) ); 01305 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) ); 01306 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) ); 01307 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) ); 01308 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) ); 01309 01310 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY ) 01311 .replace( QChar( '-' ), QString::null ) 01312 .replace( QChar( ':' ), QString::null ); 01313 if( !s.isEmpty() ) 01314 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s ); 01315 01316 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) ); 01317 01318 // collect parts of Name entry 01319 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) ); 01320 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) ); 01321 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) ); 01322 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) ); 01323 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) ); 01324 01325 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) ); 01326 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) ); 01327 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) ); 01328 /* 01329 the MAPI property ID of this (multiline) )field is unknown: 01330 vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" ); 01331 */ 01332 01333 KABC::Address adr; 01334 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) ); 01335 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) ); 01336 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) ); 01337 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) ); 01338 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) ); 01339 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) ); 01340 adr.setType(KABC::Address::Home); 01341 addressee.insertAddress(adr); 01342 01343 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) ); 01344 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) ); 01345 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) ); 01346 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) ); 01347 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) ); 01348 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) ); 01349 adr.setType( KABC::Address::Work ); 01350 addressee.insertAddress( adr ); 01351 01352 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) ); 01353 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) ); 01354 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) ); 01355 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) ); 01356 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) ); 01357 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) ); 01358 adr.setType( KABC::Address::Dom ); 01359 addressee.insertAddress(adr); 01360 01361 // problem: the 'other' address was stored by KOrganizer in 01362 // a line looking like the following one: 01363 // vPart += "\nADR;TYPE=dom;TYPE=intl;TYPE=parcel;TYPE=postal;TYPE=work;TYPE=home:other_pobox;;other_str1\nother_str2;other_loc;other_region;other_pocode;other_country 01364 01365 QString nr; 01366 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER ); 01367 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) ); 01368 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER ); 01369 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) ); 01370 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER ); 01371 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) ); 01372 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER ); 01373 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) ); 01374 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER ); 01375 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) ); 01376 01377 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY ) 01378 .replace( QChar( '-' ), QString::null ) 01379 .replace( QChar( ':' ), QString::null ); 01380 if( !s.isEmpty() ) 01381 addressee.setBirthday( QDateTime::fromString( s ) ); 01382 01383 bOk = ( !addressee.isEmpty() ); 01384 } else if( "IPM.NOTE" == msgClass ) { 01385 01386 } // else if ... and so on ... 01387 } 01388 } 01389 01390 // Compose return string 01391 QString iCal = calFormat.toString( &cal ); 01392 if( !iCal.isEmpty() ) 01393 // This was an iCal 01394 return iCal; 01395 01396 // Not an iCal - try a vCard 01397 KABC::VCardConverter converter; 01398 return converter.createVCard( addressee ); 01399 } 01400 01401 01402 #include "kogroupware.moc"
KDE Logo
This file is part of the documentation for korganizer Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 27 12:53:24 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003