summaryrefslogtreecommitdiff
path: root/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
Unidiff
Diffstat (limited to 'libopie2/opiepim/backend/ocontactaccessbackend_xml.h') (more/less context) (show whitespace changes)
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_xml.h550
1 files changed, 550 insertions, 0 deletions
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_xml.h b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
new file mode 100644
index 0000000..2cdb45b
--- a/dev/null
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
@@ -0,0 +1,550 @@
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 2002/09/27 17:11:44 eilers
21 * Added API for accessing the Contact-Database ! It is compiling, but
22 * please do not expect that anything is working !
23 * I will debug that stuff in the next time ..
24 * Please read README_COMPILE for compiling !
25 *
26 *
27 */
28
29#ifndef _OContactAccessBackend_XML_
30#define _OContactAccessBackend_XML_
31
32#include <qasciidict.h>
33#include <qdatetime.h>
34#include <qfile.h>
35#include <qregexp.h>
36#include <qarray.h>
37
38#include <opie/xmltree.h>
39#include "ocontactaccessbackend.h"
40
41using namespace Opie;
42
43/* the default xml implementation */
44class OContactAccessBackend_XML : public OContactAccessBackend {
45 public:
46 OContactAccessBackend_XML ( QString appname, QString filename = 0l )
47 {
48 m_appName = appname;
49
50 /* Set journalfile name ... */
51 m_journalName = getenv("HOME");
52 m_journalName +="/.abjournal" + appname;
53
54 /* Expecting to access the default filename if nothing else is set */
55 if ( filename.isEmpty() ){
56 m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
57 } else
58 m_fileName = filename;
59
60 /* Load Database now */
61 load ();
62 }
63
64 bool save() {
65 QString strNewFile = m_fileName + ".new";
66 QFile f( strNewFile );
67 if ( !f.open( IO_WriteOnly|IO_Raw ) )
68 return false;
69
70 int total_written;
71 QString out;
72 out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n"
73 " <Groups>\n"
74 " </Groups>\n"
75 " <Contacts>\n";
76 //QValueList<Contact>::iterator it;
77 QValueListConstIterator<OContact> it;
78 for ( it = m_contactList.begin(); it != m_contactList.end(); ++it ) {
79 out += "<Contact ";
80 (*it).save( out );
81 out += "/>\n";
82 QCString cstr = out.utf8();
83 total_written = f.writeBlock( cstr.data(), cstr.length() );
84 if ( total_written != int(cstr.length()) ) {
85 f.close();
86 QFile::remove( strNewFile );
87 return false;
88 }
89 out = "";
90 }
91 out += " </Contacts>\n</AddressBook>\n";
92
93 QCString cstr = out.utf8();
94 total_written = f.writeBlock( cstr.data(), cstr.length() );
95 if ( total_written != int( cstr.length() ) ) {
96 f.close();
97 QFile::remove( strNewFile );
98 return false;
99 }
100 f.close();
101
102 // move the file over, I'm just going to use the system call
103 // because, I don't feel like using QDir.
104 if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
105 qWarning( "problem renaming file %s to %s, errno: %d",
106 strNewFile.latin1(), m_journalName.latin1(), errno );
107 // remove the tmp file...
108 QFile::remove( strNewFile );
109 }
110
111 /* The journalfile should be removed now... */
112 removeJournal();
113 return true;
114 }
115
116 bool load () {
117 m_contactList.clear();
118
119 /* Load XML-File and journal if it exists */
120 if ( !load ( m_fileName, false ) )
121 return false;
122 /* The returncode of the journalfile is ignored due to the
123 * fact that it does not exist when this class is instantiated !
124 * But there may such a file exist, if the application crashed.
125 * Therefore we try to load it to get the changes before the #
126 * crash happened...
127 */
128 load (m_journalName, true);
129
130 return true;
131 }
132
133 void clear () {
134 m_contactList.clear();
135
136 }
137
138 bool wasChangedExternally()
139 {
140 QFileInfo fi( m_fileName );
141
142 QDateTime lastmod = fi.lastModified ();
143
144 return (lastmod != m_readtime);
145 }
146
147 QArray<int> allRecords() const {
148 QArray<int> uid_list( m_contactList.count() );
149
150 uint counter = 0;
151 QValueListConstIterator<OContact> it;
152 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
153 uid_list[counter++] = (*it).uid();
154 }
155
156 return ( uid_list );
157 }
158
159 OContact find ( int uid ) const
160 {
161 bool found = false;
162 OContact foundContact; //Create empty contact
163
164 QValueListConstIterator<OContact> it;
165 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
166 if ((*it).uid() == uid){
167 found = true;
168 break;
169 }
170 }
171 if ( found ){
172 foundContact = *it;
173 }
174
175 return ( foundContact );
176 }
177
178 QArray<int> queryByExample ( const OContact &query, int settings ){
179
180 QArray<int> m_currentQuery( m_contactList.count() );
181 QValueListConstIterator<OContact> it;
182 uint arraycounter = 0;
183
184 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
185 /* Search all fields and compare them with query object. Store them into list
186 * if all fields matches.
187 */
188 bool allcorrect = true;
189 for ( int i = 0; i < Qtopia::rid; i++ ) {
190 /* Just compare fields which are not empty in the query object */
191 if ( !query.field(i).isEmpty() ){
192 switch ( settings & ~OContactAccess::query_IgnoreCase ){
193 case OContactAccess::query_RegExp:{
194 QRegExp expr ( query.field(i),
195 !(settings & OContactAccess::query_IgnoreCase),
196 false );
197 if ( expr.find ( (*it).field(i), 0 ) == -1 )
198 allcorrect = false;
199 }
200 break;
201 case OContactAccess::query_WildCards:{
202 QRegExp expr ( query.field(i),
203 !(settings & OContactAccess::query_IgnoreCase),
204 true );
205 if ( expr.find ( (*it).field(i), 0 ) == -1 )
206 allcorrect = false;
207 }
208 break;
209 case OContactAccess::query_ExactMatch:{
210 if (settings & OContactAccess::query_IgnoreCase){
211 if ( query.field(i).upper() !=
212 (*it).field(i).upper() )
213 allcorrect = false;
214 }else{
215 if ( query.field(i) != (*it).field(i) )
216 allcorrect = false;
217 }
218 }
219 break;
220 }
221 }
222 }
223 if ( allcorrect ){
224 m_currentQuery[arraycounter++] = (*it).uid();
225 }
226 }
227
228 // Shrink to fit..
229 m_currentQuery.resize(arraycounter);
230
231 return m_currentQuery;
232 }
233
234 const uint querySettings()
235 {
236 return ( OContactAccess::query_WildCards
237 & OContactAccess::query_IgnoreCase
238 & OContactAccess::query_RegExp
239 & OContactAccess::query_ExactMatch );
240 }
241
242 bool hasQuerySettings (uint querySettings) const
243 {
244 /* OContactAccess::query_IgnoreCase may be added with one
245 * of the other settings, but never used alone.
246 * The other settings are just valid alone...
247 */
248 switch ( querySettings & ~OContactAccess::query_IgnoreCase ){
249 case OContactAccess::query_RegExp:
250 return ( true );
251 case OContactAccess::query_WildCards:
252 return ( true );
253 case OContactAccess::query_ExactMatch:
254 return ( true );
255 default:
256 return ( false );
257 }
258 }
259
260 bool add ( const OContact &newcontact )
261 {
262 //qWarning("odefaultbackend: ACTION::ADD");
263 updateJournal (newcontact, OContact::ACTION_ADD);
264 addContact_p( newcontact );
265 return true;
266 }
267
268 bool replace ( const OContact &contact )
269 {
270 bool found = false;
271
272 QValueListIterator<OContact> it;
273 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
274 if ( (*it).uid() == contact.uid() ){
275 found = true;
276 break;
277 }
278 }
279 if (found) {
280 updateJournal (contact, OContact::ACTION_REPLACE);
281 m_contactList.remove (it);
282 m_contactList.append (contact);
283 return true;
284 } else
285 return false;
286 }
287
288 bool remove ( int uid )
289 {
290 bool found = false;
291 QValueListIterator<OContact> it;
292 for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
293 if ((*it).uid() == uid){
294 found = true;
295 break;
296 }
297 }
298 if (found) {
299 updateJournal ( *it, OContact::ACTION_REMOVE);
300 m_contactList.remove (it);
301 return true;
302 } else
303 return false;
304 }
305
306 bool reload(){
307 /* Reload is the same as load in this implementation */
308 return ( load() );
309 }
310
311 private:
312 void addContact_p( const OContact &newcontact ){
313 m_contactList.append (newcontact);
314 }
315
316 /* This function loads the xml-database and the journalfile */
317 bool load( const QString filename, bool isJournal ) {
318
319 /* We use the time of the last read to check if the file was
320 * changed externally.
321 */
322 if ( !isJournal ){
323 QFileInfo fi( filename );
324 m_readtime = fi.lastModified ();
325 }
326
327 const int JOURNALACTION = Qtopia::Notes + 1;
328 const int JOURNALROW = JOURNALACTION + 1;
329
330 bool foundAction = false;
331 OContact::journal_action action = OContact::ACTION_ADD;
332 int journalKey = 0;
333 QMap<int, QString> contactMap;
334 QMap<QString, QString> customMap;
335 QMap<QString, QString>::Iterator customIt;
336 QAsciiDict<int> dict( 47 );
337
338 dict.setAutoDelete( TRUE );
339 dict.insert( "Uid", new int(Qtopia::AddressUid) );
340 dict.insert( "Title", new int(Qtopia::Title) );
341 dict.insert( "FirstName", new int(Qtopia::FirstName) );
342 dict.insert( "MiddleName", new int(Qtopia::MiddleName) );
343 dict.insert( "LastName", new int(Qtopia::LastName) );
344 dict.insert( "Suffix", new int(Qtopia::Suffix) );
345 dict.insert( "FileAs", new int(Qtopia::FileAs) );
346 dict.insert( "Categories", new int(Qtopia::AddressCategory) );
347 dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) );
348 dict.insert( "Emails", new int(Qtopia::Emails) );
349 dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) );
350 dict.insert( "HomeCity", new int(Qtopia::HomeCity) );
351 dict.insert( "HomeState", new int(Qtopia::HomeState) );
352 dict.insert( "HomeZip", new int(Qtopia::HomeZip) );
353 dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) );
354 dict.insert( "HomePhone", new int(Qtopia::HomePhone) );
355 dict.insert( "HomeFax", new int(Qtopia::HomeFax) );
356 dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) );
357 dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) );
358 dict.insert( "Company", new int(Qtopia::Company) );
359 dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) );
360 dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) );
361 dict.insert( "BusinessState", new int(Qtopia::BusinessState) );
362 dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) );
363 dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) );
364 dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) );
365 dict.insert( "JobTitle", new int(Qtopia::JobTitle) );
366 dict.insert( "Department", new int(Qtopia::Department) );
367 dict.insert( "Office", new int(Qtopia::Office) );
368 dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) );
369 dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) );
370 dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) );
371 dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) );
372 dict.insert( "Profession", new int(Qtopia::Profession) );
373 dict.insert( "Assistant", new int(Qtopia::Assistant) );
374 dict.insert( "Manager", new int(Qtopia::Manager) );
375 dict.insert( "Spouse", new int(Qtopia::Spouse) );
376 dict.insert( "Children", new int(Qtopia::Children) );
377 dict.insert( "Gender", new int(Qtopia::Gender) );
378 dict.insert( "Birthday", new int(Qtopia::Birthday) );
379 dict.insert( "Anniversary", new int(Qtopia::Anniversary) );
380 dict.insert( "Nickname", new int(Qtopia::Nickname) );
381 dict.insert( "Notes", new int(Qtopia::Notes) );
382 dict.insert( "action", new int(JOURNALACTION) );
383 dict.insert( "actionrow", new int(JOURNALROW) );
384
385 //qWarning( "OContactDefaultBackEnd::loading %s", filename.latin1() );
386
387 XMLElement *root = XMLElement::load( filename );
388 if(root != 0l ){ // start parsing
389 /* Parse all XML-Elements and put the data into the
390 * Contact-Class
391 */
392 XMLElement *element = root->firstChild();
393 //qWarning("OContactAccess::load tagName(): %s", root->tagName().latin1() );
394 element = element->firstChild();
395
396 /* Search Tag "Contacts" which is the parent of all Contacts */
397 while( element && !isJournal ){
398 if( element->tagName() != QString::fromLatin1("Contacts") ){
399 //qWarning ("OContactDefBack::Searching for Tag \"Contacts\"! Found: %s",
400 // element->tagName().latin1());
401 element = element->nextChild();
402 } else {
403 element = element->firstChild();
404 break;
405 }
406 }
407 /* Parse all Contacts and ignore unknown tags */
408 while( element ){
409 if( element->tagName() != QString::fromLatin1("Contact") ){
410 //qWarning ("OContactDefBack::Searching for Tag \"Contact\"! Found: %s",
411 // element->tagName().latin1());
412 element = element->nextChild();
413 continue;
414 }
415 /* Found alement with tagname "contact", now parse and store all
416 * attributes contained
417 */
418 //qWarning("OContactDefBack::load element tagName() : %s",
419 // element->tagName().latin1() );
420 QString dummy;
421 foundAction = false;
422
423 XMLElement::AttributeMap aMap = element->attributes();
424 XMLElement::AttributeMap::Iterator it;
425 contactMap.clear();
426 customMap.clear();
427 for( it = aMap.begin(); it != aMap.end(); ++it ){
428 // qWarning ("Read Attribute: %s=%s", it.key().latin1(),it.data().latin1());
429
430 int *find = dict[ it.key() ];
431 /* Unknown attributes will be stored as "Custom" elements */
432 if ( !find ) {
433 qWarning("Attribute %s not known.", it.key().latin1());
434 //contact.setCustomField(it.key(), it.data());
435 customMap.insert( it.key(), it.data() );
436 continue;
437 }
438
439 /* Check if special conversion is needed and add attribute
440 * into Contact class
441 */
442 switch( *find ) {
443 /*
444 case Qtopia::AddressUid:
445 contact.setUid( it.data().toInt() );
446 break;
447 case Qtopia::AddressCategory:
448 contact.setCategories( Qtopia::Record::idsFromString( it.data( )));
449 break;
450 */
451 case JOURNALACTION:
452 action = OContact::journal_action(it.data().toInt());
453 foundAction = true;
454 qWarning ("ODefBack(journal)::ACTION found: %d", action);
455 break;
456 case JOURNALROW:
457 journalKey = it.data().toInt();
458 break;
459 default: // no conversion needed add them to the map
460 contactMap.insert( *find, it.data() );
461 break;
462 }
463 }
464 /* now generate the Contact contact */
465 OContact contact( contactMap );
466
467 for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
468 contact.setCustomField( customIt.key(), customIt.data() );
469 }
470
471 if (foundAction){
472 foundAction = false;
473 switch ( action ) {
474 case OContact::ACTION_ADD:
475 addContact_p (contact);
476 break;
477 case OContact::ACTION_REMOVE:
478 if ( !remove (contact.uid()) )
479 qWarning ("ODefBack(journal)::Unable to remove uid: %d",
480 contact.uid() );
481 break;
482 case OContact::ACTION_REPLACE:
483 if ( !replace ( contact ) )
484 qWarning ("ODefBack(journal)::Unable to replace uid: %d",
485 contact.uid() );
486 break;
487 default:
488 qWarning ("Unknown action: ignored !");
489 break;
490 }
491 }else{
492 /* Add contact to list */
493 addContact_p (contact);
494 }
495
496 /* Move to next element */
497 element = element->nextChild();
498 }
499 }else {
500 qWarning("ODefBack::could not load");
501 }
502 delete root;
503 qWarning("returning from loading" );
504 return true;
505 }
506
507
508 void updateJournal( const OContact& cnt,
509 OContact::journal_action action ) {
510 QFile f( m_journalName );
511 bool created = !f.exists();
512 if ( !f.open(IO_WriteOnly|IO_Append) )
513 return;
514
515 QString buf;
516 QCString str;
517
518 // if the file was created, we have to set the Tag "<CONTACTS>" to
519 // get a XML-File which is readable by our parser.
520 // This is just a cheat, but better than rewrite the parser.
521 if ( created ){
522 buf = "<Contacts>";
523 QCString cstr = buf.utf8();
524 f.writeBlock( cstr.data(), cstr.length() );
525 }
526
527 buf = "<Contact ";
528 cnt.save( buf );
529 buf += " action=\"" + QString::number( (int)action ) + "\" ";
530 buf += "/>\n";
531 QCString cstr = buf.utf8();
532 f.writeBlock( cstr.data(), cstr.length() );
533 }
534
535 void removeJournal()
536 {
537 QFile f ( m_journalName );
538 if ( f.exists() )
539 f.remove();
540 }
541
542 protected:
543 QString m_journalName;
544 QString m_fileName;
545 QString m_appName;
546 QValueList<OContact> m_contactList;
547 QDateTime m_readtime;
548};
549
550#endif