summaryrefslogtreecommitdiff
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--libopie/pim/ocontactaccessbackend_xml.cpp739
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp739
2 files changed, 1478 insertions, 0 deletions
diff --git a/libopie/pim/ocontactaccessbackend_xml.cpp b/libopie/pim/ocontactaccessbackend_xml.cpp
new file mode 100644
index 0000000..2df6757
--- a/dev/null
+++ b/libopie/pim/ocontactaccessbackend_xml.cpp
@@ -0,0 +1,739 @@
1/*
2 * XML Backend for the OPIE-Contact Database.
3 *
4 * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de)
5 *
6 * =====================================================================
7 *This program is free software; you can redistribute it and/or
8 *modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 * =====================================================================
12 * ToDo: XML-Backend: Automatic reload if something was changed...
13 *
14 *
15 * =====================================================================
16 * Version: $Id$
17 * =====================================================================
18 * History:
19 * $Log$
20 * Revision 1.1 2003/02/09 15:05:01 eilers
21 * Nothing happened.. Just some cleanup before I will start..
22 *
23 * Revision 1.12 2003/01/03 16:58:03 eilers
24 * Reenable debug output
25 *
26 * Revision 1.11 2003/01/03 12:31:28 eilers
27 * Bugfix for calculating data diffs..
28 *
29 * Revision 1.10 2003/01/02 14:27:12 eilers
30 * Improved query by example: Search by date is possible.. First step
31 * for a today plugin for birthdays..
32 *
33 * Revision 1.9 2002/12/08 12:48:57 eilers
34 * Moved journal-enum from ocontact into i the xml-backend..
35 *
36 * Revision 1.8 2002/11/14 17:04:24 eilers
37 * Sorting will now work if fullname is identical on some entries
38 *
39 * Revision 1.7 2002/11/13 15:02:46 eilers
40 * Small Bug in sorted fixed
41 *
42 * Revision 1.6 2002/11/13 14:14:51 eilers
43 * Added sorted for Contacts..
44 *
45 * Revision 1.5 2002/11/01 15:10:42 eilers
46 * Added regExp-search in database for all fields in a contact.
47 *
48 * Revision 1.4 2002/10/16 10:52:40 eilers
49 * Added some docu to the interface and now using the cache infrastucture by zecke.. :)
50 *
51 * Revision 1.3 2002/10/14 16:21:54 eilers
52 * Some minor interface updates
53 *
54 * Revision 1.2 2002/10/07 17:34:24 eilers
55 * added OBackendFactory for advanced backend access
56 *
57 * Revision 1.1 2002/09/27 17:11:44 eilers
58 * Added API for accessing the Contact-Database ! It is compiling, but
59 * please do not expect that anything is working !
60 * I will debug that stuff in the next time ..
61 * Please read README_COMPILE for compiling !
62 *
63 *
64 */
65
66#include "ocontactaccessbackend_xml.h"
67
68#include <qasciidict.h>
69#include <qdatetime.h>
70#include <qfile.h>
71#include <qfileinfo.h>
72#include <qregexp.h>
73#include <qarray.h>
74#include <qmap.h>
75#include <qdatetime.h>
76
77#include <qpe/global.h>
78
79#include <opie/xmltree.h>
80#include "ocontactaccessbackend.h"
81#include "ocontactaccess.h"
82
83#include <stdlib.h>
84#include <errno.h>
85
86using namespace Opie;
87
88
89OContactAccessBackend_XML::OContactAccessBackend_XML ( QString appname, QString filename = 0l ):
90 m_changed( false )
91{
92 m_appName = appname;
93
94 /* Set journalfile name ... */
95 m_journalName = getenv("HOME");
96 m_journalName +="/.abjournal" + appname;
97
98 /* Expecting to access the default filename if nothing else is set */
99 if ( filename.isEmpty() ){
100 m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
101 } else
102 m_fileName = filename;
103
104 /* Load Database now */
105 load ();
106}
107
108bool OContactAccessBackend_XML::save()
109{
110
111 if ( !m_changed )
112 return true;
113
114 QString strNewFile = m_fileName + ".new";
115 QFile f( strNewFile );
116 if ( !f.open( IO_WriteOnly|IO_Raw ) )
117 return false;
118
119 int total_written;
120 QString out;
121 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
122 " <Groups>\n"
123 " </Groups>\n"
124 " <Contacts>\n";
125 //QValueList<Contact>::iterator it;
126 QValueListConstIterator<OContact> it;
127 for ( it = m_contactList.begin(); it != m_contactList.end(); ++it ) {
128 out += "<Contact ";
129 (*it).save( out );
130 out += "/>\n";
131 QCString cstr = out.utf8();
132 total_written = f.writeBlock( cstr.data(), cstr.length() );
133 if ( total_written != int(cstr.length()) ) {
134 f.close();
135 QFile::remove( strNewFile );
136 return false;
137 }
138 out = "";
139 }
140 out += " </Contacts>\n</AddressBook>\n";
141
142 QCString cstr = out.utf8();
143 total_written = f.writeBlock( cstr.data(), cstr.length() );
144 if ( total_written != int( cstr.length() ) ) {
145 f.close();
146 QFile::remove( strNewFile );
147 return false;
148 }
149 f.close();
150
151 // move the file over, I'm just going to use the system call
152 // because, I don't feel like using QDir.
153 if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
154 qWarning( "problem renaming file %s to %s, errno: %d",
155 strNewFile.latin1(), m_journalName.latin1(), errno );
156 // remove the tmp file...
157 QFile::remove( strNewFile );
158 }
159
160 /* The journalfile should be removed now... */
161 removeJournal();
162
163 m_changed = false;
164 return true;
165}
166
167bool OContactAccessBackend_XML::load ()
168{
169 m_contactList.clear();
170
171 /* Load XML-File and journal if it exists */
172 if ( !load ( m_fileName, false ) )
173 return false;
174 /* The returncode of the journalfile is ignored due to the
175 * fact that it does not exist when this class is instantiated !
176 * But there may such a file exist, if the application crashed.
177 * Therefore we try to load it to get the changes before the #
178 * crash happened...
179 */
180 load (m_journalName, true);
181
182 return true;
183}
184
185void OContactAccessBackend_XML::clear ()
186{
187 m_contactList.clear();
188 m_changed = false;
189
190}
191
192bool OContactAccessBackend_XML::wasChangedExternally()
193{
194 QFileInfo fi( m_fileName );
195
196 QDateTime lastmod = fi.lastModified ();
197
198 return (lastmod != m_readtime);
199}
200
201QArray<int> OContactAccessBackend_XML::allRecords() const
202{
203 QArray<int> uid_list( m_contactList.count() );
204
205 uint counter = 0;
206 QValueListConstIterator<OContact> it;
207 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
208 uid_list[counter++] = (*it).uid();
209 }
210
211 return ( uid_list );
212}
213
214OContact OContactAccessBackend_XML::find ( int uid ) const
215{
216 bool found = false;
217 OContact foundContact; //Create empty contact
218
219 QValueListConstIterator<OContact> it;
220 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
221 if ((*it).uid() == uid){
222 found = true;
223 break;
224 }
225 }
226 if ( found ){
227 foundContact = *it;
228 }
229
230 return ( foundContact );
231}
232
233QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings )
234{
235
236 QArray<int> m_currentQuery( m_contactList.count() );
237 QValueListConstIterator<OContact> it;
238 uint arraycounter = 0;
239
240 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
241 /* Search all fields and compare them with query object. Store them into list
242 * if all fields matches.
243 */
244 QDate* queryDate = 0l;
245 QDate* checkDate = 0l;
246 bool allcorrect = true;
247 for ( int i = 0; i < Qtopia::Groups; i++ ) {
248 // Birthday and anniversary are special nonstring fields and should
249 // be handled specially
250 switch ( i ){
251 case Qtopia::Birthday:
252 queryDate = new QDate( query.birthday() );
253 checkDate = new QDate( (*it).birthday() );
254 case Qtopia::Anniversary:
255 if ( queryDate == 0l ){
256 queryDate = new QDate( query.anniversary() );
257 checkDate = new QDate( (*it).anniversary() );
258 }
259
260 if ( queryDate->isValid() ){
261 if( checkDate->isValid() ){
262 if ( settings & OContactAccess::DateYear ){
263 if ( queryDate->year() != checkDate->year() )
264 allcorrect = false;
265 }
266 if ( settings & OContactAccess::DateMonth ){
267 if ( queryDate->month() != checkDate->month() )
268 allcorrect = false;
269 }
270 if ( settings & OContactAccess::DateDay ){
271 if ( queryDate->day() != checkDate->day() )
272 allcorrect = false;
273 }
274 if ( settings & OContactAccess::DateDiff ) {
275 QDate current = QDate::currentDate();
276 // We have to equalize the year, otherwise
277 // the search will fail..
278 checkDate->setYMD( current.year(),
279 checkDate->month(),
280 checkDate->day() );
281 if ( *checkDate < current )
282 checkDate->setYMD( current.year()+1,
283 checkDate->month(),
284 checkDate->day() );
285 qWarning("Checking if %s is between %s and %s ! ",
286 checkDate->toString().latin1(),
287 current.toString().latin1(),
288 queryDate->toString().latin1() );
289 if ( current.daysTo( *queryDate ) > 0 ){
290 if ( !( ( *checkDate >= current ) &&
291 ( *checkDate <= *queryDate ) ) ){
292 allcorrect = false;
293 qWarning (" Nope!..");
294 }
295 }
296 }
297 } else{
298 // checkDate is invalid. Therfore this entry is always rejected
299 allcorrect = false;
300 }
301 }
302
303 delete queryDate;
304 queryDate = 0l;
305 delete checkDate;
306 checkDate = 0l;
307 break;
308 default:
309 /* Just compare fields which are not empty in the query object */
310 if ( !query.field(i).isEmpty() ){
311 switch ( settings & ~( OContactAccess::IgnoreCase
312 | OContactAccess::DateDiff
313 | OContactAccess::DateYear
314 | OContactAccess::DateMonth
315 | OContactAccess::DateDay
316 | OContactAccess::MatchOne
317 ) ){
318
319 case OContactAccess::RegExp:{
320 QRegExp expr ( query.field(i),
321 !(settings & OContactAccess::IgnoreCase),
322 false );
323 if ( expr.find ( (*it).field(i), 0 ) == -1 )
324 allcorrect = false;
325 }
326 break;
327 case OContactAccess::WildCards:{
328 QRegExp expr ( query.field(i),
329 !(settings & OContactAccess::IgnoreCase),
330 true );
331 if ( expr.find ( (*it).field(i), 0 ) == -1 )
332 allcorrect = false;
333 }
334 break;
335 case OContactAccess::ExactMatch:{
336 if (settings & OContactAccess::IgnoreCase){
337 if ( query.field(i).upper() !=
338 (*it).field(i).upper() )
339 allcorrect = false;
340 }else{
341 if ( query.field(i) != (*it).field(i) )
342 allcorrect = false;
343 }
344 }
345 break;
346 }
347 }
348 }
349 }
350 if ( allcorrect ){
351 m_currentQuery[arraycounter++] = (*it).uid();
352 }
353 }
354
355 // Shrink to fit..
356 m_currentQuery.resize(arraycounter);
357
358 return m_currentQuery;
359}
360
361QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
362{
363 QArray<int> m_currentQuery( m_contactList.count() );
364 QValueListConstIterator<OContact> it;
365 uint arraycounter = 0;
366
367 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
368 if ( (*it).match( r ) ){
369 m_currentQuery[arraycounter++] = (*it).uid();
370 }
371
372 }
373 // Shrink to fit..
374 m_currentQuery.resize(arraycounter);
375
376 return m_currentQuery;
377}
378
379const uint OContactAccessBackend_XML::querySettings()
380{
381 return ( OContactAccess::WildCards
382 | OContactAccess::IgnoreCase
383 | OContactAccess::RegExp
384 | OContactAccess::ExactMatch
385 | OContactAccess::DateDiff
386 | OContactAccess::DateYear
387 | OContactAccess::DateMonth
388 | OContactAccess::DateDay
389 );
390}
391
392bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const
393{
394 /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
395 * may be added with any of the other settings. IgnoreCase should never used alone.
396 * Wildcards, RegExp, ExactMatch should never used at the same time...
397 */
398
399 if ( querySettings == OContactAccess::IgnoreCase )
400 return false;
401
402 switch ( querySettings & ~( OContactAccess::IgnoreCase
403 | OContactAccess::DateDiff
404 | OContactAccess::DateYear
405 | OContactAccess::DateMonth
406 | OContactAccess::DateDay
407 )
408 ){
409 case OContactAccess::RegExp:
410 return ( true );
411 case OContactAccess::WildCards:
412 return ( true );
413 case OContactAccess::ExactMatch:
414 return ( true );
415 default:
416 return ( false );
417 }
418}
419
420// Currently only asc implemented..
421QArray<int> OContactAccessBackend_XML::sorted( bool asc, int , int , int )
422{
423 QMap<QString, int> nameToUid;
424 QStringList names;
425 QArray<int> m_currentQuery( m_contactList.count() );
426
427 // First fill map and StringList with all Names
428 // Afterwards sort namelist and use map to fill array to return..
429 QValueListConstIterator<OContact> it;
430 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
431 names.append( (*it).fileAs() + QString::number( (*it).uid() ) );
432 nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
433 }
434 names.sort();
435
436 int i = 0;
437 if ( asc ){
438 for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
439 m_currentQuery[i++] = nameToUid[ (*it) ];
440 }else{
441 for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
442 m_currentQuery[i++] = nameToUid[ (*it) ];
443 }
444
445 return m_currentQuery;
446
447}
448
449bool OContactAccessBackend_XML::add ( const OContact &newcontact )
450{
451 //qWarning("odefaultbackend: ACTION::ADD");
452 updateJournal (newcontact, ACTION_ADD);
453 addContact_p( newcontact );
454
455 m_changed = true;
456
457 return true;
458}
459
460bool OContactAccessBackend_XML::replace ( const OContact &contact )
461{
462 m_changed = true;
463
464 bool found = false;
465
466 QValueListIterator<OContact> it;
467 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
468 if ( (*it).uid() == contact.uid() ){
469 found = true;
470 break;
471 }
472 }
473 if (found) {
474 updateJournal (contact, ACTION_REPLACE);
475 m_contactList.remove (it);
476 m_contactList.append (contact);
477 return true;
478 } else
479 return false;
480}
481
482bool OContactAccessBackend_XML::remove ( int uid )
483{
484 m_changed = true;
485
486 bool found = false;
487 QValueListIterator<OContact> it;
488 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
489 if ((*it).uid() == uid){
490 found = true;
491 break;
492 }
493 }
494 if (found) {
495 updateJournal ( *it, ACTION_REMOVE);
496 m_contactList.remove (it);
497 return true;
498 } else
499 return false;
500}
501
502bool OContactAccessBackend_XML::reload(){
503 /* Reload is the same as load in this implementation */
504 return ( load() );
505}
506
507void OContactAccessBackend_XML::addContact_p( const OContact &newcontact )
508{
509 m_contactList.append (newcontact);
510}
511
512/* This function loads the xml-database and the journalfile */
513bool OContactAccessBackend_XML::load( const QString filename, bool isJournal )
514{
515
516 /* We use the time of the last read to check if the file was
517 * changed externally.
518 */
519 if ( !isJournal ){
520 QFileInfo fi( filename );
521 m_readtime = fi.lastModified ();
522 }
523
524 const int JOURNALACTION = Qtopia::Notes + 1;
525 const int JOURNALROW = JOURNALACTION + 1;
526
527 bool foundAction = false;
528 journal_action action = ACTION_ADD;
529 int journalKey = 0;
530 QMap<int, QString> contactMap;
531 QMap<QString, QString> customMap;
532 QMap<QString, QString>::Iterator customIt;
533 QAsciiDict<int> dict( 47 );
534
535 dict.setAutoDelete( TRUE );
536 dict.insert( "Uid", new int(Qtopia::AddressUid) );
537 dict.insert( "Title", new int(Qtopia::Title) );
538 dict.insert( "FirstName", new int(Qtopia::FirstName) );
539 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
540 dict.insert( "LastName", new int(Qtopia::LastName) );
541 dict.insert( "Suffix", new int(Qtopia::Suffix) );
542 dict.insert( "FileAs", new int(Qtopia::FileAs) );
543 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
544 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
545 dict.insert( "Emails", new int(Qtopia::Emails) );
546 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
547 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
548 dict.insert( "HomeState", new int(Qtopia::HomeState) );
549 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
550 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
551 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
552 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
553 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
554 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
555 dict.insert( "Company", new int(Qtopia::Company) );
556 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
557 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
558 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
559 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
560 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
561 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
562 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
563 dict.insert( "Department", new int(Qtopia::Department) );
564 dict.insert( "Office", new int(Qtopia::Office) );
565 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
566 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
567 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
568 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
569 dict.insert( "Profession", new int(Qtopia::Profession) );
570 dict.insert( "Assistant", new int(Qtopia::Assistant) );
571 dict.insert( "Manager", new int(Qtopia::Manager) );
572 dict.insert( "Spouse", new int(Qtopia::Spouse) );
573 dict.insert( "Children", new int(Qtopia::Children) );
574 dict.insert( "Gender", new int(Qtopia::Gender) );
575 dict.insert( "Birthday", new int(Qtopia::Birthday) );
576 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
577 dict.insert( "Nickname", new int(Qtopia::Nickname) );
578 dict.insert( "Notes", new int(Qtopia::Notes) );
579 dict.insert( "action", new int(JOURNALACTION) );
580 dict.insert( "actionrow", new int(JOURNALROW) );
581
582 //qWarning( "OContactDefaultBackEnd::loading %s", filename.latin1() );
583
584 XMLElement *root = XMLElement::load( filename );
585 if(root != 0l ){ // start parsing
586 /* Parse all XML-Elements and put the data into the
587 * Contact-Class
588 */
589 XMLElement *element = root->firstChild();
590 //qWarning("OContactAccess::load tagName(): %s", root->tagName().latin1() );
591 element = element->firstChild();
592
593 /* Search Tag "Contacts" which is the parent of all Contacts */
594 while( element && !isJournal ){
595 if( element->tagName() != QString::fromLatin1("Contacts") ){
596 //qWarning ("OContactDefBack::Searching for Tag \"Contacts\"! Found: %s",
597 // element->tagName().latin1());
598 element = element->nextChild();
599 } else {
600 element = element->firstChild();
601 break;
602 }
603 }
604 /* Parse all Contacts and ignore unknown tags */
605 while( element ){
606 if( element->tagName() != QString::fromLatin1("Contact") ){
607 //qWarning ("OContactDefBack::Searching for Tag \"Contact\"! Found: %s",
608 // element->tagName().latin1());
609 element = element->nextChild();
610 continue;
611 }
612 /* Found alement with tagname "contact", now parse and store all
613 * attributes contained
614 */
615 //qWarning("OContactDefBack::load element tagName() : %s",
616 // element->tagName().latin1() );
617 QString dummy;
618 foundAction = false;
619
620 XMLElement::AttributeMap aMap = element->attributes();
621 XMLElement::AttributeMap::Iterator it;
622 contactMap.clear();
623 customMap.clear();
624 for( it = aMap.begin(); it != aMap.end(); ++it ){
625 // qWarning ("Read Attribute: %s=%s", it.key().latin1(),it.data().latin1());
626
627 int *find = dict[ it.key() ];
628 /* Unknown attributes will be stored as "Custom" elements */
629 if ( !find ) {
630 qWarning("Attribute %s not known.", it.key().latin1());
631 //contact.setCustomField(it.key(), it.data());
632 customMap.insert( it.key(), it.data() );
633 continue;
634 }
635
636 /* Check if special conversion is needed and add attribute
637 * into Contact class
638 */
639 switch( *find ) {
640 /*
641 case Qtopia::AddressUid:
642 contact.setUid( it.data().toInt() );
643 break;
644 case Qtopia::AddressCategory:
645 contact.setCategories( Qtopia::Record::idsFromString( it.data( )));
646 break;
647 */
648 case JOURNALACTION:
649 action = journal_action(it.data().toInt());
650 foundAction = true;
651 qWarning ("ODefBack(journal)::ACTION found: %d", action);
652 break;
653 case JOURNALROW:
654 journalKey = it.data().toInt();
655 break;
656 default: // no conversion needed add them to the map
657 contactMap.insert( *find, it.data() );
658 break;
659 }
660 }
661 /* now generate the Contact contact */
662 OContact contact( contactMap );
663
664 for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
665 contact.setCustomField( customIt.key(), customIt.data() );
666 }
667
668 if (foundAction){
669 foundAction = false;
670 switch ( action ) {
671 case ACTION_ADD:
672 addContact_p (contact);
673 break;
674 case ACTION_REMOVE:
675 if ( !remove (contact.uid()) )
676 qWarning ("ODefBack(journal)::Unable to remove uid: %d",
677 contact.uid() );
678 break;
679 case ACTION_REPLACE:
680 if ( !replace ( contact ) )
681 qWarning ("ODefBack(journal)::Unable to replace uid: %d",
682 contact.uid() );
683 break;
684 default:
685 qWarning ("Unknown action: ignored !");
686 break;
687 }
688 }else{
689 /* Add contact to list */
690 addContact_p (contact);
691 }
692
693 /* Move to next element */
694 element = element->nextChild();
695 }
696 }else {
697 qWarning("ODefBack::could not load");
698 }
699 delete root;
700 qWarning("returning from loading" );
701 return true;
702}
703
704
705void OContactAccessBackend_XML::updateJournal( const OContact& cnt,
706 journal_action action )
707{
708 QFile f( m_journalName );
709 bool created = !f.exists();
710 if ( !f.open(IO_WriteOnly|IO_Append) )
711 return;
712
713 QString buf;
714 QCString str;
715
716 // if the file was created, we have to set the Tag "<CONTACTS>" to
717 // get a XML-File which is readable by our parser.
718 // This is just a cheat, but better than rewrite the parser.
719 if ( created ){
720 buf = "<Contacts>";
721 QCString cstr = buf.utf8();
722 f.writeBlock( cstr.data(), cstr.length() );
723 }
724
725 buf = "<Contact ";
726 cnt.save( buf );
727 buf += " action=\"" + QString::number( (int)action ) + "\" ";
728 buf += "/>\n";
729 QCString cstr = buf.utf8();
730 f.writeBlock( cstr.data(), cstr.length() );
731}
732
733void OContactAccessBackend_XML::removeJournal()
734{
735 QFile f ( m_journalName );
736 if ( f.exists() )
737 f.remove();
738}
739
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp
new file mode 100644
index 0000000..2df6757
--- a/dev/null
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp
@@ -0,0 +1,739 @@
1/*
2 * XML Backend for the OPIE-Contact Database.
3 *
4 * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de)
5 *
6 * =====================================================================
7 *This program is free software; you can redistribute it and/or
8 *modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 * =====================================================================
12 * ToDo: XML-Backend: Automatic reload if something was changed...
13 *
14 *
15 * =====================================================================
16 * Version: $Id$
17 * =====================================================================
18 * History:
19 * $Log$
20 * Revision 1.1 2003/02/09 15:05:01 eilers
21 * Nothing happened.. Just some cleanup before I will start..
22 *
23 * Revision 1.12 2003/01/03 16:58:03 eilers
24 * Reenable debug output
25 *
26 * Revision 1.11 2003/01/03 12:31:28 eilers
27 * Bugfix for calculating data diffs..
28 *
29 * Revision 1.10 2003/01/02 14:27:12 eilers
30 * Improved query by example: Search by date is possible.. First step
31 * for a today plugin for birthdays..
32 *
33 * Revision 1.9 2002/12/08 12:48:57 eilers
34 * Moved journal-enum from ocontact into i the xml-backend..
35 *
36 * Revision 1.8 2002/11/14 17:04:24 eilers
37 * Sorting will now work if fullname is identical on some entries
38 *
39 * Revision 1.7 2002/11/13 15:02:46 eilers
40 * Small Bug in sorted fixed
41 *
42 * Revision 1.6 2002/11/13 14:14:51 eilers
43 * Added sorted for Contacts..
44 *
45 * Revision 1.5 2002/11/01 15:10:42 eilers
46 * Added regExp-search in database for all fields in a contact.
47 *
48 * Revision 1.4 2002/10/16 10:52:40 eilers
49 * Added some docu to the interface and now using the cache infrastucture by zecke.. :)
50 *
51 * Revision 1.3 2002/10/14 16:21:54 eilers
52 * Some minor interface updates
53 *
54 * Revision 1.2 2002/10/07 17:34:24 eilers
55 * added OBackendFactory for advanced backend access
56 *
57 * Revision 1.1 2002/09/27 17:11:44 eilers
58 * Added API for accessing the Contact-Database ! It is compiling, but
59 * please do not expect that anything is working !
60 * I will debug that stuff in the next time ..
61 * Please read README_COMPILE for compiling !
62 *
63 *
64 */
65
66#include "ocontactaccessbackend_xml.h"
67
68#include <qasciidict.h>
69#include <qdatetime.h>
70#include <qfile.h>
71#include <qfileinfo.h>
72#include <qregexp.h>
73#include <qarray.h>
74#include <qmap.h>
75#include <qdatetime.h>
76
77#include <qpe/global.h>
78
79#include <opie/xmltree.h>
80#include "ocontactaccessbackend.h"
81#include "ocontactaccess.h"
82
83#include <stdlib.h>
84#include <errno.h>
85
86using namespace Opie;
87
88
89OContactAccessBackend_XML::OContactAccessBackend_XML ( QString appname, QString filename = 0l ):
90 m_changed( false )
91{
92 m_appName = appname;
93
94 /* Set journalfile name ... */
95 m_journalName = getenv("HOME");
96 m_journalName +="/.abjournal" + appname;
97
98 /* Expecting to access the default filename if nothing else is set */
99 if ( filename.isEmpty() ){
100 m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
101 } else
102 m_fileName = filename;
103
104 /* Load Database now */
105 load ();
106}
107
108bool OContactAccessBackend_XML::save()
109{
110
111 if ( !m_changed )
112 return true;
113
114 QString strNewFile = m_fileName + ".new";
115 QFile f( strNewFile );
116 if ( !f.open( IO_WriteOnly|IO_Raw ) )
117 return false;
118
119 int total_written;
120 QString out;
121 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
122 " <Groups>\n"
123 " </Groups>\n"
124 " <Contacts>\n";
125 //QValueList<Contact>::iterator it;
126 QValueListConstIterator<OContact> it;
127 for ( it = m_contactList.begin(); it != m_contactList.end(); ++it ) {
128 out += "<Contact ";
129 (*it).save( out );
130 out += "/>\n";
131 QCString cstr = out.utf8();
132 total_written = f.writeBlock( cstr.data(), cstr.length() );
133 if ( total_written != int(cstr.length()) ) {
134 f.close();
135 QFile::remove( strNewFile );
136 return false;
137 }
138 out = "";
139 }
140 out += " </Contacts>\n</AddressBook>\n";
141
142 QCString cstr = out.utf8();
143 total_written = f.writeBlock( cstr.data(), cstr.length() );
144 if ( total_written != int( cstr.length() ) ) {
145 f.close();
146 QFile::remove( strNewFile );
147 return false;
148 }
149 f.close();
150
151 // move the file over, I'm just going to use the system call
152 // because, I don't feel like using QDir.
153 if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
154 qWarning( "problem renaming file %s to %s, errno: %d",
155 strNewFile.latin1(), m_journalName.latin1(), errno );
156 // remove the tmp file...
157 QFile::remove( strNewFile );
158 }
159
160 /* The journalfile should be removed now... */
161 removeJournal();
162
163 m_changed = false;
164 return true;
165}
166
167bool OContactAccessBackend_XML::load ()
168{
169 m_contactList.clear();
170
171 /* Load XML-File and journal if it exists */
172 if ( !load ( m_fileName, false ) )
173 return false;
174 /* The returncode of the journalfile is ignored due to the
175 * fact that it does not exist when this class is instantiated !
176 * But there may such a file exist, if the application crashed.
177 * Therefore we try to load it to get the changes before the #
178 * crash happened...
179 */
180 load (m_journalName, true);
181
182 return true;
183}
184
185void OContactAccessBackend_XML::clear ()
186{
187 m_contactList.clear();
188 m_changed = false;
189
190}
191
192bool OContactAccessBackend_XML::wasChangedExternally()
193{
194 QFileInfo fi( m_fileName );
195
196 QDateTime lastmod = fi.lastModified ();
197
198 return (lastmod != m_readtime);
199}
200
201QArray<int> OContactAccessBackend_XML::allRecords() const
202{
203 QArray<int> uid_list( m_contactList.count() );
204
205 uint counter = 0;
206 QValueListConstIterator<OContact> it;
207 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
208 uid_list[counter++] = (*it).uid();
209 }
210
211 return ( uid_list );
212}
213
214OContact OContactAccessBackend_XML::find ( int uid ) const
215{
216 bool found = false;
217 OContact foundContact; //Create empty contact
218
219 QValueListConstIterator<OContact> it;
220 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
221 if ((*it).uid() == uid){
222 found = true;
223 break;
224 }
225 }
226 if ( found ){
227 foundContact = *it;
228 }
229
230 return ( foundContact );
231}
232
233QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings )
234{
235
236 QArray<int> m_currentQuery( m_contactList.count() );
237 QValueListConstIterator<OContact> it;
238 uint arraycounter = 0;
239
240 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
241 /* Search all fields and compare them with query object. Store them into list
242 * if all fields matches.
243 */
244 QDate* queryDate = 0l;
245 QDate* checkDate = 0l;
246 bool allcorrect = true;
247 for ( int i = 0; i < Qtopia::Groups; i++ ) {
248 // Birthday and anniversary are special nonstring fields and should
249 // be handled specially
250 switch ( i ){
251 case Qtopia::Birthday:
252 queryDate = new QDate( query.birthday() );
253 checkDate = new QDate( (*it).birthday() );
254 case Qtopia::Anniversary:
255 if ( queryDate == 0l ){
256 queryDate = new QDate( query.anniversary() );
257 checkDate = new QDate( (*it).anniversary() );
258 }
259
260 if ( queryDate->isValid() ){
261 if( checkDate->isValid() ){
262 if ( settings & OContactAccess::DateYear ){
263 if ( queryDate->year() != checkDate->year() )
264 allcorrect = false;
265 }
266 if ( settings & OContactAccess::DateMonth ){
267 if ( queryDate->month() != checkDate->month() )
268 allcorrect = false;
269 }
270 if ( settings & OContactAccess::DateDay ){
271 if ( queryDate->day() != checkDate->day() )
272 allcorrect = false;
273 }
274 if ( settings & OContactAccess::DateDiff ) {
275 QDate current = QDate::currentDate();
276 // We have to equalize the year, otherwise
277 // the search will fail..
278 checkDate->setYMD( current.year(),
279 checkDate->month(),
280 checkDate->day() );
281 if ( *checkDate < current )
282 checkDate->setYMD( current.year()+1,
283 checkDate->month(),
284 checkDate->day() );
285 qWarning("Checking if %s is between %s and %s ! ",
286 checkDate->toString().latin1(),
287 current.toString().latin1(),
288 queryDate->toString().latin1() );
289 if ( current.daysTo( *queryDate ) > 0 ){
290 if ( !( ( *checkDate >= current ) &&
291 ( *checkDate <= *queryDate ) ) ){
292 allcorrect = false;
293 qWarning (" Nope!..");
294 }
295 }
296 }
297 } else{
298 // checkDate is invalid. Therfore this entry is always rejected
299 allcorrect = false;
300 }
301 }
302
303 delete queryDate;
304 queryDate = 0l;
305 delete checkDate;
306 checkDate = 0l;
307 break;
308 default:
309 /* Just compare fields which are not empty in the query object */
310 if ( !query.field(i).isEmpty() ){
311 switch ( settings & ~( OContactAccess::IgnoreCase
312 | OContactAccess::DateDiff
313 | OContactAccess::DateYear
314 | OContactAccess::DateMonth
315 | OContactAccess::DateDay
316 | OContactAccess::MatchOne
317 ) ){
318
319 case OContactAccess::RegExp:{
320 QRegExp expr ( query.field(i),
321 !(settings & OContactAccess::IgnoreCase),
322 false );
323 if ( expr.find ( (*it).field(i), 0 ) == -1 )
324 allcorrect = false;
325 }
326 break;
327 case OContactAccess::WildCards:{
328 QRegExp expr ( query.field(i),
329 !(settings & OContactAccess::IgnoreCase),
330 true );
331 if ( expr.find ( (*it).field(i), 0 ) == -1 )
332 allcorrect = false;
333 }
334 break;
335 case OContactAccess::ExactMatch:{
336 if (settings & OContactAccess::IgnoreCase){
337 if ( query.field(i).upper() !=
338 (*it).field(i).upper() )
339 allcorrect = false;
340 }else{
341 if ( query.field(i) != (*it).field(i) )
342 allcorrect = false;
343 }
344 }
345 break;
346 }
347 }
348 }
349 }
350 if ( allcorrect ){
351 m_currentQuery[arraycounter++] = (*it).uid();
352 }
353 }
354
355 // Shrink to fit..
356 m_currentQuery.resize(arraycounter);
357
358 return m_currentQuery;
359}
360
361QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
362{
363 QArray<int> m_currentQuery( m_contactList.count() );
364 QValueListConstIterator<OContact> it;
365 uint arraycounter = 0;
366
367 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
368 if ( (*it).match( r ) ){
369 m_currentQuery[arraycounter++] = (*it).uid();
370 }
371
372 }
373 // Shrink to fit..
374 m_currentQuery.resize(arraycounter);
375
376 return m_currentQuery;
377}
378
379const uint OContactAccessBackend_XML::querySettings()
380{
381 return ( OContactAccess::WildCards
382 | OContactAccess::IgnoreCase
383 | OContactAccess::RegExp
384 | OContactAccess::ExactMatch
385 | OContactAccess::DateDiff
386 | OContactAccess::DateYear
387 | OContactAccess::DateMonth
388 | OContactAccess::DateDay
389 );
390}
391
392bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const
393{
394 /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
395 * may be added with any of the other settings. IgnoreCase should never used alone.
396 * Wildcards, RegExp, ExactMatch should never used at the same time...
397 */
398
399 if ( querySettings == OContactAccess::IgnoreCase )
400 return false;
401
402 switch ( querySettings & ~( OContactAccess::IgnoreCase
403 | OContactAccess::DateDiff
404 | OContactAccess::DateYear
405 | OContactAccess::DateMonth
406 | OContactAccess::DateDay
407 )
408 ){
409 case OContactAccess::RegExp:
410 return ( true );
411 case OContactAccess::WildCards:
412 return ( true );
413 case OContactAccess::ExactMatch:
414 return ( true );
415 default:
416 return ( false );
417 }
418}
419
420// Currently only asc implemented..
421QArray<int> OContactAccessBackend_XML::sorted( bool asc, int , int , int )
422{
423 QMap<QString, int> nameToUid;
424 QStringList names;
425 QArray<int> m_currentQuery( m_contactList.count() );
426
427 // First fill map and StringList with all Names
428 // Afterwards sort namelist and use map to fill array to return..
429 QValueListConstIterator<OContact> it;
430 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
431 names.append( (*it).fileAs() + QString::number( (*it).uid() ) );
432 nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
433 }
434 names.sort();
435
436 int i = 0;
437 if ( asc ){
438 for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
439 m_currentQuery[i++] = nameToUid[ (*it) ];
440 }else{
441 for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
442 m_currentQuery[i++] = nameToUid[ (*it) ];
443 }
444
445 return m_currentQuery;
446
447}
448
449bool OContactAccessBackend_XML::add ( const OContact &newcontact )
450{
451 //qWarning("odefaultbackend: ACTION::ADD");
452 updateJournal (newcontact, ACTION_ADD);
453 addContact_p( newcontact );
454
455 m_changed = true;
456
457 return true;
458}
459
460bool OContactAccessBackend_XML::replace ( const OContact &contact )
461{
462 m_changed = true;
463
464 bool found = false;
465
466 QValueListIterator<OContact> it;
467 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
468 if ( (*it).uid() == contact.uid() ){
469 found = true;
470 break;
471 }
472 }
473 if (found) {
474 updateJournal (contact, ACTION_REPLACE);
475 m_contactList.remove (it);
476 m_contactList.append (contact);
477 return true;
478 } else
479 return false;
480}
481
482bool OContactAccessBackend_XML::remove ( int uid )
483{
484 m_changed = true;
485
486 bool found = false;
487 QValueListIterator<OContact> it;
488 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
489 if ((*it).uid() == uid){
490 found = true;
491 break;
492 }
493 }
494 if (found) {
495 updateJournal ( *it, ACTION_REMOVE);
496 m_contactList.remove (it);
497 return true;
498 } else
499 return false;
500}
501
502bool OContactAccessBackend_XML::reload(){
503 /* Reload is the same as load in this implementation */
504 return ( load() );
505}
506
507void OContactAccessBackend_XML::addContact_p( const OContact &newcontact )
508{
509 m_contactList.append (newcontact);
510}
511
512/* This function loads the xml-database and the journalfile */
513bool OContactAccessBackend_XML::load( const QString filename, bool isJournal )
514{
515
516 /* We use the time of the last read to check if the file was
517 * changed externally.
518 */
519 if ( !isJournal ){
520 QFileInfo fi( filename );
521 m_readtime = fi.lastModified ();
522 }
523
524 const int JOURNALACTION = Qtopia::Notes + 1;
525 const int JOURNALROW = JOURNALACTION + 1;
526
527 bool foundAction = false;
528 journal_action action = ACTION_ADD;
529 int journalKey = 0;
530 QMap<int, QString> contactMap;
531 QMap<QString, QString> customMap;
532 QMap<QString, QString>::Iterator customIt;
533 QAsciiDict<int> dict( 47 );
534
535 dict.setAutoDelete( TRUE );
536 dict.insert( "Uid", new int(Qtopia::AddressUid) );
537 dict.insert( "Title", new int(Qtopia::Title) );
538 dict.insert( "FirstName", new int(Qtopia::FirstName) );
539 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
540 dict.insert( "LastName", new int(Qtopia::LastName) );
541 dict.insert( "Suffix", new int(Qtopia::Suffix) );
542 dict.insert( "FileAs", new int(Qtopia::FileAs) );
543 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
544 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
545 dict.insert( "Emails", new int(Qtopia::Emails) );
546 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
547 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
548 dict.insert( "HomeState", new int(Qtopia::HomeState) );
549 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
550 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
551 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
552 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
553 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
554 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
555 dict.insert( "Company", new int(Qtopia::Company) );
556 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
557 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
558 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
559 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
560 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
561 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
562 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
563 dict.insert( "Department", new int(Qtopia::Department) );
564 dict.insert( "Office", new int(Qtopia::Office) );
565 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
566 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
567 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
568 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
569 dict.insert( "Profession", new int(Qtopia::Profession) );
570 dict.insert( "Assistant", new int(Qtopia::Assistant) );
571 dict.insert( "Manager", new int(Qtopia::Manager) );
572 dict.insert( "Spouse", new int(Qtopia::Spouse) );
573 dict.insert( "Children", new int(Qtopia::Children) );
574 dict.insert( "Gender", new int(Qtopia::Gender) );
575 dict.insert( "Birthday", new int(Qtopia::Birthday) );
576 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
577 dict.insert( "Nickname", new int(Qtopia::Nickname) );
578 dict.insert( "Notes", new int(Qtopia::Notes) );
579 dict.insert( "action", new int(JOURNALACTION) );
580 dict.insert( "actionrow", new int(JOURNALROW) );
581
582 //qWarning( "OContactDefaultBackEnd::loading %s", filename.latin1() );
583
584 XMLElement *root = XMLElement::load( filename );
585 if(root != 0l ){ // start parsing
586 /* Parse all XML-Elements and put the data into the
587 * Contact-Class
588 */
589 XMLElement *element = root->firstChild();
590 //qWarning("OContactAccess::load tagName(): %s", root->tagName().latin1() );
591 element = element->firstChild();
592
593 /* Search Tag "Contacts" which is the parent of all Contacts */
594 while( element && !isJournal ){
595 if( element->tagName() != QString::fromLatin1("Contacts") ){
596 //qWarning ("OContactDefBack::Searching for Tag \"Contacts\"! Found: %s",
597 // element->tagName().latin1());
598 element = element->nextChild();
599 } else {
600 element = element->firstChild();
601 break;
602 }
603 }
604 /* Parse all Contacts and ignore unknown tags */
605 while( element ){
606 if( element->tagName() != QString::fromLatin1("Contact") ){
607 //qWarning ("OContactDefBack::Searching for Tag \"Contact\"! Found: %s",
608 // element->tagName().latin1());
609 element = element->nextChild();
610 continue;
611 }
612 /* Found alement with tagname "contact", now parse and store all
613 * attributes contained
614 */
615 //qWarning("OContactDefBack::load element tagName() : %s",
616 // element->tagName().latin1() );
617 QString dummy;
618 foundAction = false;
619
620 XMLElement::AttributeMap aMap = element->attributes();
621 XMLElement::AttributeMap::Iterator it;
622 contactMap.clear();
623 customMap.clear();
624 for( it = aMap.begin(); it != aMap.end(); ++it ){
625 // qWarning ("Read Attribute: %s=%s", it.key().latin1(),it.data().latin1());
626
627 int *find = dict[ it.key() ];
628 /* Unknown attributes will be stored as "Custom" elements */
629 if ( !find ) {
630 qWarning("Attribute %s not known.", it.key().latin1());
631 //contact.setCustomField(it.key(), it.data());
632 customMap.insert( it.key(), it.data() );
633 continue;
634 }
635
636 /* Check if special conversion is needed and add attribute
637 * into Contact class
638 */
639 switch( *find ) {
640 /*
641 case Qtopia::AddressUid:
642 contact.setUid( it.data().toInt() );
643 break;
644 case Qtopia::AddressCategory:
645 contact.setCategories( Qtopia::Record::idsFromString( it.data( )));
646 break;
647 */
648 case JOURNALACTION:
649 action = journal_action(it.data().toInt());
650 foundAction = true;
651 qWarning ("ODefBack(journal)::ACTION found: %d", action);
652 break;
653 case JOURNALROW:
654 journalKey = it.data().toInt();
655 break;
656 default: // no conversion needed add them to the map
657 contactMap.insert( *find, it.data() );
658 break;
659 }
660 }
661 /* now generate the Contact contact */
662 OContact contact( contactMap );
663
664 for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
665 contact.setCustomField( customIt.key(), customIt.data() );
666 }
667
668 if (foundAction){
669 foundAction = false;
670 switch ( action ) {
671 case ACTION_ADD:
672 addContact_p (contact);
673 break;
674 case ACTION_REMOVE:
675 if ( !remove (contact.uid()) )
676 qWarning ("ODefBack(journal)::Unable to remove uid: %d",
677 contact.uid() );
678 break;
679 case ACTION_REPLACE:
680 if ( !replace ( contact ) )
681 qWarning ("ODefBack(journal)::Unable to replace uid: %d",
682 contact.uid() );
683 break;
684 default:
685 qWarning ("Unknown action: ignored !");
686 break;
687 }
688 }else{
689 /* Add contact to list */
690 addContact_p (contact);
691 }
692
693 /* Move to next element */
694 element = element->nextChild();
695 }
696 }else {
697 qWarning("ODefBack::could not load");
698 }
699 delete root;
700 qWarning("returning from loading" );
701 return true;
702}
703
704
705void OContactAccessBackend_XML::updateJournal( const OContact& cnt,
706 journal_action action )
707{
708 QFile f( m_journalName );
709 bool created = !f.exists();
710 if ( !f.open(IO_WriteOnly|IO_Append) )
711 return;
712
713 QString buf;
714 QCString str;
715
716 // if the file was created, we have to set the Tag "<CONTACTS>" to
717 // get a XML-File which is readable by our parser.
718 // This is just a cheat, but better than rewrite the parser.
719 if ( created ){
720 buf = "<Contacts>";
721 QCString cstr = buf.utf8();
722 f.writeBlock( cstr.data(), cstr.length() );
723 }
724
725 buf = "<Contact ";
726 cnt.save( buf );
727 buf += " action=\"" + QString::number( (int)action ) + "\" ";
728 buf += "/>\n";
729 QCString cstr = buf.utf8();
730 f.writeBlock( cstr.data(), cstr.length() );
731}
732
733void OContactAccessBackend_XML::removeJournal()
734{
735 QFile f ( m_journalName );
736 if ( f.exists() )
737 f.remove();
738}
739