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