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 #include <opie2/ocontactaccessbackend_xml.h>
00036 #include <opie2/xmltree.h>
00037 #include <opie2/ocontactaccessbackend.h>
00038 #include <opie2/ocontactaccess.h>
00039 #include <opie2/odebug.h>
00040
00041 #include <qpe/global.h>
00042
00043
00044 #include <qasciidict.h>
00045 #include <qfile.h>
00046 #include <qfileinfo.h>
00047 #include <qregexp.h>
00048 #include <qarray.h>
00049 #include <qmap.h>
00050
00051
00052 #include <stdlib.h>
00053 #include <errno.h>
00054
00055 using namespace Opie::Core;
00056
00057
00058 namespace Opie {
00059 OPimContactAccessBackend_XML::OPimContactAccessBackend_XML ( const QString& appname, const QString& filename ):
00060 m_changed( false )
00061 {
00062
00063
00064 m_contactList.setAutoDelete( true );
00065 m_uidToContact.setAutoDelete( false );
00066
00067 m_appName = appname;
00068
00069
00070 m_journalName = getenv("HOME");
00071 m_journalName +="/.abjournal" + appname;
00072
00073
00074 if ( filename.isEmpty() ){
00075 m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
00076 } else
00077 m_fileName = filename;
00078
00079
00080 load ();
00081 }
00082
00083 bool OPimContactAccessBackend_XML::save()
00084 {
00085
00086 if ( !m_changed )
00087 return true;
00088
00089 QString strNewFile = m_fileName + ".new";
00090 QFile f( strNewFile );
00091 if ( !f.open( IO_WriteOnly|IO_Raw ) )
00092 return false;
00093
00094 int total_written;
00095 int idx_offset = 0;
00096 QString out;
00097
00098
00099 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
00100 " <Groups>\n"
00101 " </Groups>\n"
00102 " <Contacts>\n";
00103 QCString cstr = out.utf8();
00104 f.writeBlock( cstr.data(), cstr.length() );
00105 idx_offset += cstr.length();
00106 out = "";
00107
00108
00109 QListIterator<OPimContact> it( m_contactList );
00110 for ( ; it.current(); ++it ) {
00111 out += "<Contact ";
00112 (*it)->save( out );
00113 out += "/>\n";
00114 cstr = out.utf8();
00115 total_written = f.writeBlock( cstr.data(), cstr.length() );
00116 idx_offset += cstr.length();
00117 if ( total_written != int(cstr.length()) ) {
00118 f.close();
00119 QFile::remove( strNewFile );
00120 return false;
00121 }
00122 out = "";
00123 }
00124 out += " </Contacts>\n</AddressBook>\n";
00125
00126
00127 cstr = out.utf8();
00128 total_written = f.writeBlock( cstr.data(), cstr.length() );
00129 if ( total_written != int( cstr.length() ) ) {
00130 f.close();
00131 QFile::remove( strNewFile );
00132 return false;
00133 }
00134 f.close();
00135
00136
00137
00138 if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
00139
00140 QFile::remove( strNewFile );
00141 }
00142
00143
00144 removeJournal();
00145
00146 m_changed = false;
00147 return true;
00148 }
00149
00150 bool OPimContactAccessBackend_XML::load ()
00151 {
00152 m_contactList.clear();
00153 m_uidToContact.clear();
00154
00155
00156 if ( !load ( m_fileName, false ) )
00157 return false;
00158
00159
00160
00161
00162
00163
00164 load (m_journalName, true);
00165
00166 return true;
00167 }
00168
00169 void OPimContactAccessBackend_XML::clear ()
00170 {
00171 m_contactList.clear();
00172 m_uidToContact.clear();
00173
00174 m_changed = false;
00175 }
00176
00177 bool OPimContactAccessBackend_XML::wasChangedExternally()
00178 {
00179 QFileInfo fi( m_fileName );
00180
00181 QDateTime lastmod = fi.lastModified ();
00182
00183 return (lastmod != m_readtime);
00184 }
00185
00186 UIDArray OPimContactAccessBackend_XML::allRecords() const
00187 {
00188 UIDArray uid_list( m_contactList.count() );
00189
00190 uint counter = 0;
00191 QListIterator<OPimContact> it( m_contactList );
00192 for( ; it.current(); ++it ){
00193 uid_list[counter++] = (*it)->uid();
00194 }
00195
00196 return ( uid_list );
00197 }
00198
00199 OPimContact OPimContactAccessBackend_XML::find ( int uid ) const
00200 {
00201 OPimContact foundContact;
00202
00203 OPimContact* found = m_uidToContact.find( QString().setNum( uid ) );
00204
00205 if ( found ){
00206 foundContact = *found;
00207 }
00208
00209 return ( foundContact );
00210 }
00211
00212
00213 UIDArray OPimContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
00214 {
00215 UIDArray m_currentQuery( m_contactList.count() );
00216 QListIterator<OPimContact> it( m_contactList );
00217 uint arraycounter = 0;
00218
00219 for( ; it.current(); ++it ){
00220 if ( (*it)->match( r ) ){
00221 m_currentQuery[arraycounter++] = (*it)->uid();
00222 }
00223
00224 }
00225
00226 m_currentQuery.resize(arraycounter);
00227
00228 return m_currentQuery;
00229 }
00230
00231
00232
00233 #if 0
00234
00235 UIDArray OPimContactAccessBackend_XML::sorted( bool asc, int , int , int )
00236 {
00237 QMap<QString, int> nameToUid;
00238 QStringList names;
00239 UIDArray m_currentQuery( m_contactList.count() );
00240
00241
00242
00243 QListIterator<OPimContact> it( m_contactList );
00244 for( ; it.current(); ++it ){
00245 names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) );
00246 nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() );
00247 }
00248 names.sort();
00249
00250 int i = 0;
00251 if ( asc ){
00252 for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
00253 m_currentQuery[i++] = nameToUid[ (*it) ];
00254 }else{
00255 for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
00256 m_currentQuery[i++] = nameToUid[ (*it) ];
00257 }
00258
00259 return m_currentQuery;
00260
00261 }
00262 #endif
00263
00264
00265 bool OPimContactAccessBackend_XML::add ( const OPimContact &newcontact )
00266 {
00267 updateJournal (newcontact, ACTION_ADD);
00268 addContact_p( newcontact );
00269
00270 m_changed = true;
00271
00272 return true;
00273 }
00274
00275 bool OPimContactAccessBackend_XML::replace ( const OPimContact &contact )
00276 {
00277 m_changed = true;
00278
00279 OPimContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) );
00280
00281 if ( found ) {
00282 OPimContact* newCont = new OPimContact( contact );
00283
00284 updateJournal ( *newCont, ACTION_REPLACE);
00285 m_contactList.removeRef ( found );
00286 m_contactList.append ( newCont );
00287 m_uidToContact.remove( QString().setNum( contact.uid() ) );
00288 m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont );
00289
00290 return true;
00291 } else
00292 return false;
00293 }
00294
00295 bool OPimContactAccessBackend_XML::remove ( int uid )
00296 {
00297 m_changed = true;
00298
00299 OPimContact* found = m_uidToContact.find ( QString().setNum( uid ) );
00300
00301 if ( found ) {
00302 updateJournal ( *found, ACTION_REMOVE);
00303 m_contactList.removeRef ( found );
00304 m_uidToContact.remove( QString().setNum( uid ) );
00305
00306 return true;
00307 } else
00308 return false;
00309 }
00310
00311 bool OPimContactAccessBackend_XML::reload(){
00312
00313 return ( load() );
00314 }
00315
00316 void OPimContactAccessBackend_XML::addContact_p( const OPimContact &newcontact )
00317 {
00318 OPimContact* contRef = new OPimContact( newcontact );
00319
00320 m_contactList.append ( contRef );
00321 m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef );
00322 }
00323
00324
00325 bool OPimContactAccessBackend_XML::load( const QString filename, bool isJournal )
00326 {
00327
00328
00329
00330
00331 if ( !isJournal ){
00332 QFileInfo fi( filename );
00333 m_readtime = fi.lastModified ();
00334 }
00335
00336 const int JOURNALACTION = Qtopia::Notes + 1;
00337 const int JOURNALROW = JOURNALACTION + 1;
00338
00339 bool foundAction = false;
00340 journal_action action = ACTION_ADD;
00341 int journalKey = 0;
00342 QMap<int, QString> contactMap;
00343 QMap<QString, QString> customMap;
00344 QMap<QString, QString>::Iterator customIt;
00345 QAsciiDict<int> dict( 47 );
00346
00347 dict.setAutoDelete( TRUE );
00348 dict.insert( "Uid", new int(Qtopia::AddressUid) );
00349 dict.insert( "Title", new int(Qtopia::Title) );
00350 dict.insert( "FirstName", new int(Qtopia::FirstName) );
00351 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
00352 dict.insert( "LastName", new int(Qtopia::LastName) );
00353 dict.insert( "Suffix", new int(Qtopia::Suffix) );
00354 dict.insert( "FileAs", new int(Qtopia::FileAs) );
00355 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
00356 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
00357 dict.insert( "Emails", new int(Qtopia::Emails) );
00358 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
00359 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
00360 dict.insert( "HomeState", new int(Qtopia::HomeState) );
00361 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
00362 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
00363 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
00364 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
00365 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
00366 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
00367 dict.insert( "Company", new int(Qtopia::Company) );
00368 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
00369 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
00370 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
00371 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
00372 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
00373 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
00374 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
00375 dict.insert( "Department", new int(Qtopia::Department) );
00376 dict.insert( "Office", new int(Qtopia::Office) );
00377 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
00378 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
00379 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
00380 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
00381 dict.insert( "Profession", new int(Qtopia::Profession) );
00382 dict.insert( "Assistant", new int(Qtopia::Assistant) );
00383 dict.insert( "Manager", new int(Qtopia::Manager) );
00384 dict.insert( "Spouse", new int(Qtopia::Spouse) );
00385 dict.insert( "Children", new int(Qtopia::Children) );
00386 dict.insert( "Gender", new int(Qtopia::Gender) );
00387 dict.insert( "Birthday", new int(Qtopia::Birthday) );
00388 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
00389 dict.insert( "Nickname", new int(Qtopia::Nickname) );
00390 dict.insert( "Notes", new int(Qtopia::Notes) );
00391 dict.insert( "action", new int(JOURNALACTION) );
00392 dict.insert( "actionrow", new int(JOURNALROW) );
00393
00394 XMLElement *root = XMLElement::load( filename );
00395 if(root != 0l ){
00396
00397
00398
00399 XMLElement *element = root->firstChild();
00400 element = element ? element->firstChild() : 0;
00401
00402
00403 while( element && !isJournal ){
00404 if( element->tagName() != QString::fromLatin1("Contacts") ){
00405 element = element->nextChild();
00406 } else {
00407 element = element->firstChild();
00408 break;
00409 }
00410 }
00411
00412 while( element ){
00413 if( element->tagName() != QString::fromLatin1("Contact") ){
00414 element = element->nextChild();
00415 continue;
00416 }
00417
00418
00419
00420 QString dummy;
00421 foundAction = false;
00422
00423 XMLElement::AttributeMap aMap = element->attributes();
00424 XMLElement::AttributeMap::Iterator it;
00425 contactMap.clear();
00426 customMap.clear();
00427 for( it = aMap.begin(); it != aMap.end(); ++it ){
00428 int *find = dict[ it.key() ];
00429
00430 if ( !find ) {
00431
00432 customMap.insert( it.key(), it.data() );
00433 continue;
00434 }
00435
00436
00437
00438
00439 switch( *find ) {
00440
00441
00442
00443
00444
00445
00446
00447
00448 case JOURNALACTION:
00449 action = journal_action(it.data().toInt());
00450 foundAction = true;
00451 owarn << "ODefBack(journal)::ACTION found: " << action << oendl;
00452 break;
00453 case JOURNALROW:
00454 journalKey = it.data().toInt();
00455 break;
00456 default:
00457 contactMap.insert( *find, it.data() );
00458 break;
00459 }
00460 }
00461
00462 OPimContact contact( contactMap );
00463
00464 for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
00465 contact.setCustomField( customIt.key(), customIt.data() );
00466 }
00467
00468 if (foundAction){
00469 foundAction = false;
00470 switch ( action ) {
00471 case ACTION_ADD:
00472 addContact_p (contact);
00473 break;
00474 case ACTION_REMOVE:
00475 if ( !remove (contact.uid()) )
00476 owarn << "ODefBack(journal)::Unable to remove uid: " << contact.uid() << oendl;
00477 break;
00478 case ACTION_REPLACE:
00479 if ( !replace ( contact ) )
00480 owarn << "ODefBack(journal)::Unable to replace uid: " << contact.uid() << oendl;
00481 break;
00482 default:
00483 owarn << "Unknown action: ignored !" << oendl;
00484 break;
00485 }
00486 }else{
00487
00488 addContact_p (contact);
00489 }
00490
00491
00492 element = element->nextChild();
00493 }
00494 }else {
00495 }
00496 delete root;
00497 return true;
00498 }
00499
00500
00501 void OPimContactAccessBackend_XML::updateJournal( const OPimContact& cnt,
00502 journal_action action )
00503 {
00504 QFile f( m_journalName );
00505 bool created = !f.exists();
00506 if ( !f.open(IO_WriteOnly|IO_Append) )
00507 return;
00508
00509 QString buf;
00510 QCString str;
00511
00512
00513
00514
00515 if ( created ){
00516 buf = "<Contacts>";
00517 QCString cstr = buf.utf8();
00518 f.writeBlock( cstr.data(), cstr.length() );
00519 }
00520
00521 buf = "<Contact ";
00522 cnt.save( buf );
00523 buf += " action=\"" + QString::number( (int)action ) + "\" ";
00524 buf += "/>\n";
00525 QCString cstr = buf.utf8();
00526 f.writeBlock( cstr.data(), cstr.length() );
00527 }
00528
00529 void OPimContactAccessBackend_XML::removeJournal()
00530 {
00531 QFile f ( m_journalName );
00532 if ( f.exists() )
00533 f.remove();
00534 }
00535
00536 }