00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 #include "ocontactaccessbackend_xml.h"
00111
00112 #include <qasciidict.h>
00113 #include <qfile.h>
00114 #include <qfileinfo.h>
00115 #include <qregexp.h>
00116 #include <qarray.h>
00117 #include <qmap.h>
00118
00119 #include <qpe/global.h>
00120
00121 #include <opie/xmltree.h>
00122 #include "ocontactaccessbackend.h"
00123 #include "ocontactaccess.h"
00124
00125 #include <stdlib.h>
00126 #include <errno.h>
00127
00128 using namespace Opie;
00129
00130
00131 OContactAccessBackend_XML::OContactAccessBackend_XML ( const QString& appname, const QString& filename ):
00132 m_changed( false )
00133 {
00134
00135
00136 m_contactList.setAutoDelete( true );
00137 m_uidToContact.setAutoDelete( false );
00138
00139 m_appName = appname;
00140
00141
00142 m_journalName = getenv("HOME");
00143 m_journalName +="/.abjournal" + appname;
00144
00145
00146 if ( filename.isEmpty() ){
00147 m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
00148 } else
00149 m_fileName = filename;
00150
00151
00152 load ();
00153 }
00154
00155 bool OContactAccessBackend_XML::save()
00156 {
00157
00158 if ( !m_changed )
00159 return true;
00160
00161 QString strNewFile = m_fileName + ".new";
00162 QFile f( strNewFile );
00163 if ( !f.open( IO_WriteOnly|IO_Raw ) )
00164 return false;
00165
00166 int total_written;
00167 int idx_offset = 0;
00168 QString out;
00169
00170
00171 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
00172 " <Groups>\n"
00173 " </Groups>\n"
00174 " <Contacts>\n";
00175 QCString cstr = out.utf8();
00176 f.writeBlock( cstr.data(), cstr.length() );
00177 idx_offset += cstr.length();
00178 out = "";
00179
00180
00181 QListIterator<OContact> it( m_contactList );
00182 for ( ; it.current(); ++it ) {
00183
00184 out += "<Contact ";
00185 (*it)->save( out );
00186 out += "/>\n";
00187 cstr = out.utf8();
00188 total_written = f.writeBlock( cstr.data(), cstr.length() );
00189 idx_offset += cstr.length();
00190 if ( total_written != int(cstr.length()) ) {
00191 f.close();
00192 QFile::remove( strNewFile );
00193 return false;
00194 }
00195 out = "";
00196 }
00197 out += " </Contacts>\n</AddressBook>\n";
00198
00199
00200 cstr = out.utf8();
00201 total_written = f.writeBlock( cstr.data(), cstr.length() );
00202 if ( total_written != int( cstr.length() ) ) {
00203 f.close();
00204 QFile::remove( strNewFile );
00205 return false;
00206 }
00207 f.close();
00208
00209
00210
00211 if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
00212 qWarning( "problem renaming file %s to %s, errno: %d",
00213 strNewFile.latin1(), m_journalName.latin1(), errno );
00214
00215 QFile::remove( strNewFile );
00216 }
00217
00218
00219 removeJournal();
00220
00221 m_changed = false;
00222 return true;
00223 }
00224
00225 bool OContactAccessBackend_XML::load ()
00226 {
00227 m_contactList.clear();
00228 m_uidToContact.clear();
00229
00230
00231 if ( !load ( m_fileName, false ) )
00232 return false;
00233
00234
00235
00236
00237
00238
00239 load (m_journalName, true);
00240
00241 return true;
00242 }
00243
00244 void OContactAccessBackend_XML::clear ()
00245 {
00246 m_contactList.clear();
00247 m_uidToContact.clear();
00248
00249 m_changed = false;
00250 }
00251
00252 bool OContactAccessBackend_XML::wasChangedExternally()
00253 {
00254 QFileInfo fi( m_fileName );
00255
00256 QDateTime lastmod = fi.lastModified ();
00257
00258 return (lastmod != m_readtime);
00259 }
00260
00261 QArray<int> OContactAccessBackend_XML::allRecords() const
00262 {
00263 QArray<int> uid_list( m_contactList.count() );
00264
00265 uint counter = 0;
00266 QListIterator<OContact> it( m_contactList );
00267 for( ; it.current(); ++it ){
00268 uid_list[counter++] = (*it)->uid();
00269 }
00270
00271 return ( uid_list );
00272 }
00273
00274 OContact OContactAccessBackend_XML::find ( int uid ) const
00275 {
00276 OContact foundContact;
00277
00278 OContact* found = m_uidToContact.find( QString().setNum( uid ) );
00279
00280 if ( found ){
00281 foundContact = *found;
00282 }
00283
00284 return ( foundContact );
00285 }
00286
00287 QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings,
00288 const QDateTime& d )
00289 {
00290
00291 QArray<int> m_currentQuery( m_contactList.count() );
00292 QListIterator<OContact> it( m_contactList );
00293 uint arraycounter = 0;
00294
00295 for( ; it.current(); ++it ){
00296
00297
00298
00299 QDate* queryDate = 0l;
00300 QDate* checkDate = 0l;
00301 bool allcorrect = true;
00302 for ( int i = 0; i < Qtopia::Groups; i++ ) {
00303
00304
00305 switch ( i ){
00306 case Qtopia::Birthday:
00307 queryDate = new QDate( query.birthday() );
00308 checkDate = new QDate( (*it)->birthday() );
00309 case Qtopia::Anniversary:
00310 if ( queryDate == 0l ){
00311 queryDate = new QDate( query.anniversary() );
00312 checkDate = new QDate( (*it)->anniversary() );
00313 }
00314
00315 if ( queryDate->isValid() ){
00316 if( checkDate->isValid() ){
00317 if ( settings & OContactAccess::DateYear ){
00318 if ( queryDate->year() != checkDate->year() )
00319 allcorrect = false;
00320 }
00321 if ( settings & OContactAccess::DateMonth ){
00322 if ( queryDate->month() != checkDate->month() )
00323 allcorrect = false;
00324 }
00325 if ( settings & OContactAccess::DateDay ){
00326 if ( queryDate->day() != checkDate->day() )
00327 allcorrect = false;
00328 }
00329 if ( settings & OContactAccess::DateDiff ) {
00330 QDate current;
00331
00332
00333
00334 if ( !d.date().isValid() )
00335 current = QDate::currentDate();
00336 else
00337 current = d.date();
00338
00339
00340
00341 checkDate->setYMD( current.year(),
00342 checkDate->month(),
00343 checkDate->day() );
00344 if ( *checkDate < current )
00345 checkDate->setYMD( current.year()+1,
00346 checkDate->month(),
00347 checkDate->day() );
00348
00349
00350
00351
00352 qWarning("Checking if %s is between %s and %s ! ",
00353 checkDate->toString().latin1(),
00354 current.toString().latin1(),
00355 queryDate->toString().latin1() );
00356 if ( current.daysTo( *queryDate ) >= 0 ){
00357 if ( !( ( *checkDate >= current ) &&
00358 ( *checkDate <= *queryDate ) ) ){
00359 allcorrect = false;
00360 qWarning (" Nope!..");
00361 }
00362 }
00363 }
00364 } else{
00365
00366 allcorrect = false;
00367 }
00368 }
00369
00370 delete queryDate;
00371 queryDate = 0l;
00372 delete checkDate;
00373 checkDate = 0l;
00374 break;
00375 default:
00376
00377 if ( !query.field(i).isEmpty() ){
00378 switch ( settings & ~( OContactAccess::IgnoreCase
00379 | OContactAccess::DateDiff
00380 | OContactAccess::DateYear
00381 | OContactAccess::DateMonth
00382 | OContactAccess::DateDay
00383 | OContactAccess::MatchOne
00384 ) ){
00385
00386 case OContactAccess::RegExp:{
00387 QRegExp expr ( query.field(i),
00388 !(settings & OContactAccess::IgnoreCase),
00389 false );
00390 if ( expr.find ( (*it)->field(i), 0 ) == -1 )
00391 allcorrect = false;
00392 }
00393 break;
00394 case OContactAccess::WildCards:{
00395 QRegExp expr ( query.field(i),
00396 !(settings & OContactAccess::IgnoreCase),
00397 true );
00398 if ( expr.find ( (*it)->field(i), 0 ) == -1 )
00399 allcorrect = false;
00400 }
00401 break;
00402 case OContactAccess::ExactMatch:{
00403 if (settings & OContactAccess::IgnoreCase){
00404 if ( query.field(i).upper() !=
00405 (*it)->field(i).upper() )
00406 allcorrect = false;
00407 }else{
00408 if ( query.field(i) != (*it)->field(i) )
00409 allcorrect = false;
00410 }
00411 }
00412 break;
00413 }
00414 }
00415 }
00416 }
00417 if ( allcorrect ){
00418 m_currentQuery[arraycounter++] = (*it)->uid();
00419 }
00420 }
00421
00422
00423 m_currentQuery.resize(arraycounter);
00424
00425 return m_currentQuery;
00426 }
00427
00428 QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
00429 {
00430 QArray<int> m_currentQuery( m_contactList.count() );
00431 QListIterator<OContact> it( m_contactList );
00432 uint arraycounter = 0;
00433
00434 for( ; it.current(); ++it ){
00435 if ( (*it)->match( r ) ){
00436 m_currentQuery[arraycounter++] = (*it)->uid();
00437 }
00438
00439 }
00440
00441 m_currentQuery.resize(arraycounter);
00442
00443 return m_currentQuery;
00444 }
00445
00446 const uint OContactAccessBackend_XML::querySettings()
00447 {
00448 return ( OContactAccess::WildCards
00449 | OContactAccess::IgnoreCase
00450 | OContactAccess::RegExp
00451 | OContactAccess::ExactMatch
00452 | OContactAccess::DateDiff
00453 | OContactAccess::DateYear
00454 | OContactAccess::DateMonth
00455 | OContactAccess::DateDay
00456 );
00457 }
00458
00459 bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const
00460 {
00461
00462
00463
00464
00465
00466
00467 if ( ( querySettings & (
00468 OContactAccess::IgnoreCase
00469 | OContactAccess::WildCards
00470 | OContactAccess::DateDiff
00471 | OContactAccess::DateYear
00472 | OContactAccess::DateMonth
00473 | OContactAccess::DateDay
00474 | OContactAccess::RegExp
00475 | OContactAccess::ExactMatch
00476 ) ) != querySettings )
00477 return false;
00478
00479
00480
00481
00482 if ( querySettings == OContactAccess::IgnoreCase )
00483 return false;
00484
00485
00486 switch ( querySettings & ~( OContactAccess::IgnoreCase
00487 | OContactAccess::DateDiff
00488 | OContactAccess::DateYear
00489 | OContactAccess::DateMonth
00490 | OContactAccess::DateDay
00491 )
00492 ){
00493 case OContactAccess::RegExp:
00494 return ( true );
00495 case OContactAccess::WildCards:
00496 return ( true );
00497 case OContactAccess::ExactMatch:
00498 return ( true );
00499 case 0:
00500 return ( true );
00501 default:
00502 return ( false );
00503 }
00504 }
00505
00506
00507 QArray<int> OContactAccessBackend_XML::sorted( bool asc, int , int , int )
00508 {
00509 QMap<QString, int> nameToUid;
00510 QStringList names;
00511 QArray<int> m_currentQuery( m_contactList.count() );
00512
00513
00514
00515 QListIterator<OContact> it( m_contactList );
00516 for( ; it.current(); ++it ){
00517 names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) );
00518 nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() );
00519 }
00520 names.sort();
00521
00522 int i = 0;
00523 if ( asc ){
00524 for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
00525 m_currentQuery[i++] = nameToUid[ (*it) ];
00526 }else{
00527 for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
00528 m_currentQuery[i++] = nameToUid[ (*it) ];
00529 }
00530
00531 return m_currentQuery;
00532
00533 }
00534
00535 bool OContactAccessBackend_XML::add ( const OContact &newcontact )
00536 {
00537
00538 updateJournal (newcontact, ACTION_ADD);
00539 addContact_p( newcontact );
00540
00541 m_changed = true;
00542
00543 return true;
00544 }
00545
00546 bool OContactAccessBackend_XML::replace ( const OContact &contact )
00547 {
00548 m_changed = true;
00549
00550 OContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) );
00551
00552 if ( found ) {
00553 OContact* newCont = new OContact( contact );
00554
00555 updateJournal ( *newCont, ACTION_REPLACE);
00556 m_contactList.removeRef ( found );
00557 m_contactList.append ( newCont );
00558 m_uidToContact.remove( QString().setNum( contact.uid() ) );
00559 m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont );
00560
00561 qWarning("Nur zur Sicherheit: %d == %d ?",contact.uid(), newCont->uid());
00562
00563 return true;
00564 } else
00565 return false;
00566 }
00567
00568 bool OContactAccessBackend_XML::remove ( int uid )
00569 {
00570 m_changed = true;
00571
00572 OContact* found = m_uidToContact.find ( QString().setNum( uid ) );
00573
00574 if ( found ) {
00575 updateJournal ( *found, ACTION_REMOVE);
00576 m_contactList.removeRef ( found );
00577 m_uidToContact.remove( QString().setNum( uid ) );
00578
00579 return true;
00580 } else
00581 return false;
00582 }
00583
00584 bool OContactAccessBackend_XML::reload(){
00585
00586 return ( load() );
00587 }
00588
00589 void OContactAccessBackend_XML::addContact_p( const OContact &newcontact )
00590 {
00591 OContact* contRef = new OContact( newcontact );
00592
00593 m_contactList.append ( contRef );
00594 m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef );
00595 }
00596
00597
00598 bool OContactAccessBackend_XML::load( const QString filename, bool isJournal )
00599 {
00600
00601
00602
00603
00604 if ( !isJournal ){
00605 QFileInfo fi( filename );
00606 m_readtime = fi.lastModified ();
00607 }
00608
00609 const int JOURNALACTION = Qtopia::Notes + 1;
00610 const int JOURNALROW = JOURNALACTION + 1;
00611
00612 bool foundAction = false;
00613 journal_action action = ACTION_ADD;
00614 int journalKey = 0;
00615 QMap<int, QString> contactMap;
00616 QMap<QString, QString> customMap;
00617 QMap<QString, QString>::Iterator customIt;
00618 QAsciiDict<int> dict( 47 );
00619
00620 dict.setAutoDelete( TRUE );
00621 dict.insert( "Uid", new int(Qtopia::AddressUid) );
00622 dict.insert( "Title", new int(Qtopia::Title) );
00623 dict.insert( "FirstName", new int(Qtopia::FirstName) );
00624 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
00625 dict.insert( "LastName", new int(Qtopia::LastName) );
00626 dict.insert( "Suffix", new int(Qtopia::Suffix) );
00627 dict.insert( "FileAs", new int(Qtopia::FileAs) );
00628 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
00629 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
00630 dict.insert( "Emails", new int(Qtopia::Emails) );
00631 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
00632 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
00633 dict.insert( "HomeState", new int(Qtopia::HomeState) );
00634 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
00635 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
00636 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
00637 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
00638 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
00639 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
00640 dict.insert( "Company", new int(Qtopia::Company) );
00641 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
00642 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
00643 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
00644 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
00645 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
00646 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
00647 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
00648 dict.insert( "Department", new int(Qtopia::Department) );
00649 dict.insert( "Office", new int(Qtopia::Office) );
00650 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
00651 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
00652 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
00653 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
00654 dict.insert( "Profession", new int(Qtopia::Profession) );
00655 dict.insert( "Assistant", new int(Qtopia::Assistant) );
00656 dict.insert( "Manager", new int(Qtopia::Manager) );
00657 dict.insert( "Spouse", new int(Qtopia::Spouse) );
00658 dict.insert( "Children", new int(Qtopia::Children) );
00659 dict.insert( "Gender", new int(Qtopia::Gender) );
00660 dict.insert( "Birthday", new int(Qtopia::Birthday) );
00661 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
00662 dict.insert( "Nickname", new int(Qtopia::Nickname) );
00663 dict.insert( "Notes", new int(Qtopia::Notes) );
00664 dict.insert( "action", new int(JOURNALACTION) );
00665 dict.insert( "actionrow", new int(JOURNALROW) );
00666
00667
00668
00669 XMLElement *root = XMLElement::load( filename );
00670 if(root != 0l ){
00671
00672
00673
00674 XMLElement *element = root->firstChild();
00675
00676 element = element->firstChild();
00677
00678
00679 while( element && !isJournal ){
00680 if( element->tagName() != QString::fromLatin1("Contacts") ){
00681
00682
00683 element = element->nextChild();
00684 } else {
00685 element = element->firstChild();
00686 break;
00687 }
00688 }
00689
00690 while( element ){
00691 if( element->tagName() != QString::fromLatin1("Contact") ){
00692
00693
00694 element = element->nextChild();
00695 continue;
00696 }
00697
00698
00699
00700
00701
00702 QString dummy;
00703 foundAction = false;
00704
00705 XMLElement::AttributeMap aMap = element->attributes();
00706 XMLElement::AttributeMap::Iterator it;
00707 contactMap.clear();
00708 customMap.clear();
00709 for( it = aMap.begin(); it != aMap.end(); ++it ){
00710
00711
00712 int *find = dict[ it.key() ];
00713
00714 if ( !find ) {
00715
00716
00717 customMap.insert( it.key(), it.data() );
00718 continue;
00719 }
00720
00721
00722
00723
00724 switch( *find ) {
00725
00726
00727
00728
00729
00730
00731
00732
00733 case JOURNALACTION:
00734 action = journal_action(it.data().toInt());
00735 foundAction = true;
00736 qWarning ("ODefBack(journal)::ACTION found: %d", action);
00737 break;
00738 case JOURNALROW:
00739 journalKey = it.data().toInt();
00740 break;
00741 default:
00742 contactMap.insert( *find, it.data() );
00743 break;
00744 }
00745 }
00746
00747 OContact contact( contactMap );
00748
00749 for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
00750 contact.setCustomField( customIt.key(), customIt.data() );
00751 }
00752
00753 if (foundAction){
00754 foundAction = false;
00755 switch ( action ) {
00756 case ACTION_ADD:
00757 addContact_p (contact);
00758 break;
00759 case ACTION_REMOVE:
00760 if ( !remove (contact.uid()) )
00761 qWarning ("ODefBack(journal)::Unable to remove uid: %d",
00762 contact.uid() );
00763 break;
00764 case ACTION_REPLACE:
00765 if ( !replace ( contact ) )
00766 qWarning ("ODefBack(journal)::Unable to replace uid: %d",
00767 contact.uid() );
00768 break;
00769 default:
00770 qWarning ("Unknown action: ignored !");
00771 break;
00772 }
00773 }else{
00774
00775 addContact_p (contact);
00776 }
00777
00778
00779 element = element->nextChild();
00780 }
00781 }else {
00782 qWarning("ODefBack::could not load");
00783 }
00784 delete root;
00785 qWarning("returning from loading" );
00786 return true;
00787 }
00788
00789
00790 void OContactAccessBackend_XML::updateJournal( const OContact& cnt,
00791 journal_action action )
00792 {
00793 QFile f( m_journalName );
00794 bool created = !f.exists();
00795 if ( !f.open(IO_WriteOnly|IO_Append) )
00796 return;
00797
00798 QString buf;
00799 QCString str;
00800
00801
00802
00803
00804 if ( created ){
00805 buf = "<Contacts>";
00806 QCString cstr = buf.utf8();
00807 f.writeBlock( cstr.data(), cstr.length() );
00808 }
00809
00810 buf = "<Contact ";
00811 cnt.save( buf );
00812 buf += " action=\"" + QString::number( (int)action ) + "\" ";
00813 buf += "/>\n";
00814 QCString cstr = buf.utf8();
00815 f.writeBlock( cstr.data(), cstr.length() );
00816 }
00817
00818 void OContactAccessBackend_XML::removeJournal()
00819 {
00820 QFile f ( m_journalName );
00821 if ( f.exists() )
00822 f.remove();
00823 }
00824