kaddressbook

gnokii_xxport.cpp
1/*
2 This file is part of KAddressbook.
3 Copyright (c) 2003-2006 Helge Deller <deller@kde.org>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 As a special exception, permission is given to link this program
19 with any edition of TQt, and distribute the resulting executable,
20 without including the source code for TQt in the source distribution.
21*/
22/*
23 Description:
24 This filter allows you to import and export the TDE addressbook entries
25 to/from a mobile phone, which is accessible via gnokii.
26 Gnokii homepage: http://www.gnokii.org
27
28 TODO:
29 - create a log file and give user possibility to see it afterwards
30 - handle callergroup value (Friend, VIP, Family, ...) better
31*/
32
33#include "config.h"
34
35#ifdef HAVE_GNOKII_H
36extern "C" {
37#include <gnokii.h>
38}
39#else
40#ifdef __GNUC__
41# warning "Please install gnokii (http://www.gnokii.org) development headers and libraries !"
42# warning "Please use at least version 0.6.13 or later of gnokii."
43#endif
44#endif
45
46#include <tqcursor.h>
47
48#include <kdebug.h>
49#include <tdelocale.h>
50#include <tdemessagebox.h>
51#include <kprogress.h>
52#include <kguiitem.h>
53
54#include "gnokii_xxport.h"
55
56#define APP "GNOKII_XXPORT"
57
58#if 1 // !defined(NDEBUG)
59 #define GNOKII_DEBUG(x) do { kdWarning() << (x); } while (0)
60#else
61 #define GNOKII_DEBUG(x) do { } while (0)
62#endif
63#define GNOKII_CHECK_ERROR(error) \
64 do { \
65 if (error) \
66 kdError() << TQString("ERROR %1: %2\n").arg(error).arg(gn_error_print(error));\
67 } while (0)
68
69// Locale conversion routines:
70// Gnokii uses the local 8 Bit encoding (based on LC_ALL), kaddressbook uses Unicode
71#define GN_FROM(x) TQString::fromLocal8Bit(x)
72#define GN_TO(x) (x).local8Bit()
73
74// static variables for GUI updates
75static GNOKIIXXPort *this_filter;
76static KProgressDialog *m_progressDlg;
77
78K_EXPORT_KADDRESSBOOK_XXFILTER( libkaddrbk_gnokii_xxport, GNOKIIXXPort )
79
80GNOKIIXXPort::GNOKIIXXPort( TDEABC::AddressBook *ab, TQWidget *parent, const char *name )
81 : KAB::XXPort( ab, parent, name )
82{
83 this_filter = this;
84 m_progressDlg = NULL;
85 createImportAction( i18n( "Import From Mobile Phone..." ) );
86 createExportAction( i18n( "Export to Mobile Phone..." ) );
87}
88
89
90#ifdef HAVE_GNOKII_H
91static TQString makeValidPhone( const TQString &number )
92{
93 // allowed chars: 0-9, *, #, p, w, +
94 TQString num = number.simplifyWhiteSpace();
95 TQString allowed("0123456789*+#pw");
96 for (unsigned int i=num.length(); i>=1; i--)
97 if (allowed.find(num[i-1])==-1)
98 num.remove(i-1,1);
99 if (num.isEmpty())
100 num = "0";
101 return num;
102}
103#endif
104
105/******************************************************************************
106 ******************************************************************************
107 ******************************************************************************
108 ******************************************************************************
109 ******************************************************************************/
110
111#if defined(HAVE_GNOKII_H) && defined(LIBGNOKII_VERSION_MAJOR) && (LIBGNOKII_VERSION_MAJOR >= 3)
112
113/* NEW GNOKII LIBRARIES (>= 0.6.13) */
114
115static const char *manufacturer, *model, *revision, *imei;
116static struct gn_statemachine *state;
117
118static void busterminate(void)
119{
120 gn_lib_phone_close(state);
121 gn_lib_phoneprofile_free(&state);
122 gn_lib_library_free();
123}
124
125static TQString businit(void)
126{
127 GNOKII_DEBUG( "Using new gnokii version." );
128
129 GNOKII_DEBUG( TQString("Compiled with libgnokii version 0x%1\n").arg(TQString::number(LIBGNOKII_VERSION,16)) );
130 GNOKII_DEBUG( TQString("Using libgnokii runtime version 0x%1\n").arg(TQString::number(gn_lib_version(),16)) );
131
132 gn_error error = gn_lib_phoneprofile_load(NULL, &state);
133 if (error)
134 return i18n("Failed to initialize the gnokii library.");
135
136 error = gn_lib_phone_open( state );
137 GNOKII_CHECK_ERROR(error);
138 if (error != GN_ERR_NONE) {
139 busterminate();
140 return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
141 "The returned error message was:<br><b>%1</b><br><br>"
142 "You might try to run \"gnokii --identify\" on the command line to "
143 "check any cable/transport issues and to verify if your gnokii "
144 "configuration is correct.</center></qt>")
145 .arg(gn_error_print(error));
146 }
147
148 // identify phone
149 manufacturer = gn_lib_get_phone_manufacturer(state);
150 model = gn_lib_get_phone_model(state);
151 revision = gn_lib_get_phone_revision(state);
152 imei = gn_lib_get_phone_imei(state);
153
154 GNOKII_DEBUG( TQString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
155 .arg(manufacturer, model, revision, imei) );
156
157 return TQString();
158}
159
160
161// get number of entries in this phone memory type (internal/SIM-card)
162static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
163{
164 gn_error error;
165
166 error = gn_lib_addressbook_memstat(state, memtype, &memstat->used, &memstat->free);
167
168 GNOKII_DEBUG( TQString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
169 .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
170 return error;
171}
172
173
174static TQString buildPhoneInfoString( const gn_memory_status &memstat )
175{
176 TQString format = TQString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
177
178 return TQString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
179 .arg(i18n("Mobile Phone information:"))
180 .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
181 .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
182 .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
183 .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
184 .arg(format.arg(i18n("Phonebook status"))
185 .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
186}
187
188// read and evaluate all phone entries
189static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
190 TDEABC::AddresseeList *addrList )
191{
192 gn_error error;
193
194 if (m_progressDlg->wasCancelled())
195 return GN_ERR_NONE;
196
197 KProgress* progress = (KProgress*)m_progressDlg->progressBar();
198
199 progress->setProgress(0);
200 this_filter->processEvents();
201
202 // get number of entries in this phone memory type (internal/SIM-card)
203 gn_memory_status memstat;
204 error = read_phone_memstat(memtype, &memstat);
205
206 TQStringList addrlist;
207 TDEABC::Address *addr;
208 TQString s, country;
209
210 progress->setTotalSteps(memstat.used);
211 m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
212 .arg(memstat.used)
213 .arg(gn_memory_type2str(memtype))
214 .arg(buildPhoneInfoString(memstat)) );
215
216 int num_read = 0;
217
218 for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
219 error = gn_lib_phonebook_read_entry(state, memtype, i);
220 GNOKII_CHECK_ERROR(error);
221
222 progress->setProgress(num_read);
223 this_filter->processEvents();
224
225 if (error == GN_ERR_EMPTYLOCATION)
226 continue;
227 if (error == GN_ERR_INVALIDLOCATION)
228 break;
229 if (error == GN_ERR_INVALIDMEMORYTYPE)
230 break;
231 if (error == GN_ERR_NONE) {
232 const int subentries_count = gn_lib_get_pb_num_subentries(state);
233 const char *name = gn_lib_get_pb_name(state);
234 const char *number = gn_lib_get_pb_number(state);
235
236 GNOKII_DEBUG(TQString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i)
237 .arg(GN_FROM(name)).arg(GN_FROM(number))
238 .arg(gn_lib_get_pb_location(state)).arg(gn_lib_get_pb_caller_group(state))
239 .arg(subentries_count));
240 TDEABC::Addressee *a = new TDEABC::Addressee();
241
242 // try to split Name into FamilyName and GivenName
243 s = GN_FROM(name).simplifyWhiteSpace();
244 a->setFormattedName(s); // set formatted name as in Phone
245 if (s.find(',') == -1) {
246 // assumed format: "givenname [... familyname]"
247 addrlist = TQStringList::split(' ', s);
248 if (addrlist.count() == 1) {
249 // only one string -> put it in the GivenName
250 a->setGivenName(s);
251 } else {
252 // multiple strings -> split them.
253 a->setFamilyName(addrlist.last().simplifyWhiteSpace());
254 addrlist.remove(addrlist.last());
255 a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
256 }
257 } else {
258 // assumed format: "familyname, ... givenname"
259 addrlist = TQStringList::split(',', s);
260 a->setFamilyName(addrlist.first().simplifyWhiteSpace());
261 addrlist.remove(addrlist.first());
262 a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
263 }
264
265 a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(gn_lib_get_pb_caller_group(state)));
266 a->insertCustom(APP, "X_GSM_STORE_AT", TQString("%1%2").arg(memtypestr).arg(gn_lib_get_pb_location(state)));
267
268 // set ProductId
269 a->setProductId(TQString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei));
270
271 // evaluate timestamp (ignore timezone)
272 TQDateTime datetime;
273 gn_timestamp ts = gn_lib_get_pb_date(state);
274 if (ts.year<1998)
275 datetime = TQDateTime::currentDateTime();
276 else
277 datetime = TQDateTime( TQDate(ts.year, ts.month, ts.day),
278 TQTime(ts.hour, ts.minute, ts.second) );
279 GNOKII_DEBUG(TQString(" date=%1\n").arg(datetime.toString()));
280 a->setRevision(datetime);
281
282 if (!subentries_count)
283 a->insertPhoneNumber(TDEABC::PhoneNumber(number,
284 TDEABC::PhoneNumber::Work | TDEABC::PhoneNumber::Pref));
285
286 /* scan sub-entries */
287 if (subentries_count)
288 for (int n=0; n<subentries_count; n++) {
289 gn_phonebook_entry_type entry_type;
290 gn_phonebook_number_type number_type;
291 const char *number;
292
293 error = gn_lib_get_pb_subentry(state, n, &entry_type, &number_type, &number);
294 GNOKII_CHECK_ERROR(error);
295
296 TQString s = GN_FROM(number).simplifyWhiteSpace();
297 GNOKII_DEBUG(TQString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
298 .arg(n).arg(entry_type).arg(number_type).arg(s));
299 if (s.isEmpty())
300 continue;
301 switch(entry_type) {
302 case GN_PHONEBOOK_ENTRY_Name:
303 a->setName(s);
304 break;
305 case GN_PHONEBOOK_ENTRY_Email:
306 a->insertEmail(s);
307 break;
308 case GN_PHONEBOOK_ENTRY_Postal:
309 addrlist = TQStringList::split(';', s, true);
310 addr = new TDEABC::Address(TDEABC::Address::Work);
311 if (addrlist.count() <= 1) {
312 addrlist = TQStringList::split(',', s, true);
313 if (addrlist.count() > 1 ) {
314 // assumed format: "Locality, ZIP, Country"
315 addr->setLocality(addrlist[0]);
316 addr->setPostalCode(addrlist[1]);
317 if (!addrlist[2].isEmpty())
318 addr->setCountry(i18n(GN_TO(addrlist[2])));
319 } else {
320 // no idea about the format, just store it.
321 addr->setLocality(s);
322 }
323 } else {
324 // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
325 addr->setPostOfficeBox(addrlist[0]);
326 addr->setExtended(addrlist[1]);
327 addr->setStreet(addrlist[2]);
328 addr->setLocality(addrlist[3]);
329 addr->setRegion(addrlist[4]);
330 addr->setPostalCode(addrlist[5]);
331 country = addrlist[6];
332 if (!country.isEmpty())
333 addr->setCountry(i18n(GN_TO(country)));
334 }
335 a->insertAddress(*addr);
336 delete addr;
337 break;
338 case GN_PHONEBOOK_ENTRY_Note:
339 if (!a->note().isEmpty())
340 s = "\n" + s;
341 a->setNote(a->note()+s);
342 break;
343 case GN_PHONEBOOK_ENTRY_Number:
344 enum TDEABC::PhoneNumber::Types phonetype;
345 switch (number_type) {
346 case GN_PHONEBOOK_NUMBER_Mobile: phonetype = TDEABC::PhoneNumber::Cell; break;
347 case GN_PHONEBOOK_NUMBER_Fax: phonetype = TDEABC::PhoneNumber::Fax; break;
348 case GN_PHONEBOOK_NUMBER_General:
349 case GN_PHONEBOOK_NUMBER_Work: phonetype = TDEABC::PhoneNumber::Work; break;
350 default:
351 case GN_PHONEBOOK_NUMBER_Home: phonetype = TDEABC::PhoneNumber::Home; break;
352 }
353 //if (s == entry.number)
354 // type = (TDEABC::PhoneNumber::Types) (phonetype | TDEABC::PhoneNumber::Pref);
355 a->insertPhoneNumber(TDEABC::PhoneNumber(s, phonetype));
356 break;
357 case GN_PHONEBOOK_ENTRY_URL:
358 a->setUrl(s);
359 break;
360 case GN_PHONEBOOK_ENTRY_Group:
361 a->insertCategory(s);
362 break;
363 default:
364 GNOKII_DEBUG(TQString(" Not handled id=%1, entry=%2\n")
365 .arg(entry_type).arg(s));
366 break;
367 } // switch()
368 } // if(subentry)
369
370 // add only if entry was valid
371 if (strlen(name) || strlen(number) || subentries_count)
372 addrList->append(*a);
373
374 // did we read all valid phonebook-entries ?
375 num_read++;
376 delete a;
377 if (num_read >= memstat.used)
378 break; // yes, all were read
379 else
380 continue; // no, we are still missing some.
381 }
382 GNOKII_CHECK_ERROR(error);
383 }
384
385 return GN_ERR_NONE;
386}
387
388// export to phone
389
390static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
391 const TDEABC::Addressee *addr)
392{
393 TQString s;
394
395 /* initialize the phonebook entry values to zero */
396 gn_lib_phonebook_prepare_write_entry(state);
397
398 gn_lib_set_pb_location(state, phone_location);
399
400 gn_lib_set_pb_name(state, GN_TO(addr->realName()));
401 s = addr->phoneNumber(TDEABC::PhoneNumber::Pref).number();
402 if (s.isEmpty())
403 s = addr->phoneNumber(TDEABC::PhoneNumber::Work).number();
404 if (s.isEmpty())
405 s = addr->phoneNumber(TDEABC::PhoneNumber::Home).number();
406 if (s.isEmpty())
407 s = addr->phoneNumber(TDEABC::PhoneNumber::Cell).number();
408 if (s.isEmpty() && addr->phoneNumbers().count()>0)
409 s = (*addr->phoneNumbers().at(0)).number();
410 s = makeValidPhone(s);
411 gn_lib_set_pb_number(state, s.ascii());
412 gn_lib_set_pb_memtype(state, memtype);
413 TQString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
414 if (cg.isEmpty())
415 gn_lib_set_pb_caller_group(state, GN_PHONEBOOK_GROUP_None); // default group
416 else
417 gn_lib_set_pb_caller_group(state, (gn_phonebook_group_type) cg.toInt());
418
419 // set date/revision
420 TQDateTime datetime = addr->revision();
421 TQDate date(datetime.date());
422 TQTime time(datetime.time());
423 gn_timestamp ts;
424 gn_timestamp_set( &ts, date.year(), date.month(), date.day(),
425 time.hour(), time.minute(), time.second(), 0 );
426 gn_lib_set_pb_date(state, ts);
427
428 GNOKII_DEBUG(TQString("Write #%1: name=%2, number=%3\n").arg(phone_location)
429 .arg(GN_FROM(gn_lib_get_pb_name(state))).arg(GN_FROM(gn_lib_get_pb_number(state))));
430
431 const TDEABC::Address homeAddr = addr->address(TDEABC::Address::Home);
432 const TDEABC::Address workAddr = addr->address(TDEABC::Address::Work);
433
434 // add all phone numbers
435 const TDEABC::PhoneNumber::List phoneList = addr->phoneNumbers();
436 TDEABC::PhoneNumber::List::ConstIterator it;
437 for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
438 const TDEABC::PhoneNumber *phonenumber = &(*it);
439 s = phonenumber->number();
440 if (s.isEmpty()) continue;
441 gn_phonebook_number_type type;
442 int pn_type = phonenumber->type();
443 if ((pn_type & TDEABC::PhoneNumber::Cell))
444 type = GN_PHONEBOOK_NUMBER_Mobile;
445 else if ((pn_type & TDEABC::PhoneNumber::Fax))
446 type = GN_PHONEBOOK_NUMBER_Fax;
447 else if ((pn_type & TDEABC::PhoneNumber::Home))
448 type = GN_PHONEBOOK_NUMBER_Home;
449 else if ((pn_type & TDEABC::PhoneNumber::Work))
450 type = GN_PHONEBOOK_NUMBER_Work;
451 else type = GN_PHONEBOOK_NUMBER_General;
452 gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
453 GN_PHONEBOOK_ENTRY_Number, type, makeValidPhone(s).ascii());
454 /*subentry->id = phone_location<<8+entry.subentries_count;*/
455 }
456 // add URL
457 s = addr->url().prettyURL();
458 if (!s.isEmpty()) {
459 gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
460 GN_PHONEBOOK_ENTRY_URL, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
461 }
462 // add E-Mails
463 TQStringList emails = addr->emails();
464 for (unsigned int n=0; n<emails.count(); n++) {
465 s = emails[n].simplifyWhiteSpace();
466 if (s.isEmpty()) continue;
467 // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
468 if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
469 GNOKII_DEBUG(TQString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
470 .arg(s));
471 continue;
472 }
473 gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
474 GN_PHONEBOOK_ENTRY_Email, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
475 }
476 // add Adresses
477 const TDEABC::Address::List addresses = addr->addresses();
478 TDEABC::Address::List::ConstIterator it2;
479 for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
480 const TDEABC::Address *Addr = &(*it2);
481 if (Addr->isEmpty()) continue;
482 TQStringList a;
483 TQChar sem(';');
484 TQString sem_repl(TQString::fromLatin1(","));
485 a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
486 a.append( Addr->extended() .replace( sem, sem_repl ) );
487 a.append( Addr->street() .replace( sem, sem_repl ) );
488 a.append( Addr->locality() .replace( sem, sem_repl ) );
489 a.append( Addr->region() .replace( sem, sem_repl ) );
490 a.append( Addr->postalCode() .replace( sem, sem_repl ) );
491 a.append( Addr->country() .replace( sem, sem_repl ) );
492 s = a.join(sem);
493 gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
494 GN_PHONEBOOK_ENTRY_Postal, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
495 }
496 // add Note
497 s = addr->note().simplifyWhiteSpace();
498 if (!s.isEmpty()) {
499 gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
500 GN_PHONEBOOK_ENTRY_Note, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
501 }
502
503 // debug output
504 for (int st=0; st<gn_lib_get_pb_num_subentries(state); st++) {
505 gn_phonebook_entry_type entry_type;
506 gn_phonebook_number_type number_type;
507 const char *number;
508 gn_lib_get_pb_subentry(state, st, &entry_type, &number_type, &number);
509 GNOKII_DEBUG(TQString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
510 .arg(st).arg(entry_type)
511 .arg(number_type).arg(GN_FROM(number)));
512 }
513
514 gn_error error = gn_lib_phonebook_write_entry(state, memtype, phone_location);
515 GNOKII_CHECK_ERROR(error);
516
517 return error;
518}
519
520
521static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
522{
523 return gn_lib_phonebook_entry_delete(state, memtype, phone_location);
524}
525
526
527TDEABC::AddresseeList GNOKIIXXPort::importContacts( const TQString& ) const
528{
529 TDEABC::AddresseeList addrList;
530
531 if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
532 i18n("<qt>Please connect your Mobile Phone to your computer and press "
533 "<b>Continue</b> to start importing the personal contacts.<br><br>"
534 "Please note that if your Mobile Phone is not properly connected "
535 "the following detection phase might take up to two minutes, during which "
536 "KAddressbook will behave unresponsively.</qt>") ))
537 return addrList;
538
539 m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
540 i18n("Mobile Phone Import"),
541 i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
542 "Please wait...</center></qt>") );
543 m_progressDlg->setAllowCancel(true);
544 m_progressDlg->progressBar()->setProgress(0);
545 m_progressDlg->progressBar()->setCenterIndicator(true);
546 m_progressDlg->setModal(true);
547 m_progressDlg->setInitialSize(TQSize(450,350));
548 m_progressDlg->show();
549 processEvents();
550
551 m_progressDlg->setCursor( TQt::BusyCursor );
552 TQString errStr = businit();
553 m_progressDlg->unsetCursor();
554
555 if (!errStr.isEmpty()) {
556 KMessageBox::error(parentWidget(), errStr);
557 delete m_progressDlg;
558 return addrList;
559 }
560
561 GNOKII_DEBUG("GNOKII import filter started.\n");
562 m_progressDlg->setButtonText(i18n("&Stop Import"));
563
564 read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
565 read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
566
567 GNOKII_DEBUG("GNOKII import filter finished.\n");
568
569 busterminate();
570 delete m_progressDlg;
571
572 return addrList;
573}
574
575
576bool GNOKIIXXPort::exportContacts( const TDEABC::AddresseeList &list, const TQString & )
577{
578 if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
579 i18n("<qt>Please connect your Mobile Phone to your computer and press "
580 "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
581 "Please note that if your Mobile Phone is not properly connected "
582 "the following detection phase might take up to two minutes, during which "
583 "KAddressbook will behave unresponsively.</qt>") ))
584 return false;
585
586 m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
587 i18n("Mobile Phone Export"),
588 i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
589 "Please wait...</center></qt>") );
590 m_progressDlg->setAllowCancel(true);
591 m_progressDlg->progressBar()->setProgress(0);
592 m_progressDlg->progressBar()->setCenterIndicator(true);
593 m_progressDlg->setModal(true);
594 m_progressDlg->setInitialSize(TQSize(450,350));
595 m_progressDlg->show();
596 processEvents();
597
598 KProgress* progress = (KProgress*)m_progressDlg->progressBar();
599
600 TDEABC::AddresseeList::ConstIterator it;
601 TQStringList failedList;
602
603 gn_error error;
604 bool deleteLabelInitialized = false;
605
606 m_progressDlg->setCursor( TQt::BusyCursor );
607 TQString errStr = businit();
608 m_progressDlg->unsetCursor();
609
610 if (!errStr.isEmpty()) {
611 KMessageBox::error(parentWidget(), errStr);
612 delete m_progressDlg;
613 return false;
614 }
615
616 GNOKII_DEBUG("GNOKII export filter started.\n");
617
618 gn_memory_type memtype = GN_MT_ME; // internal phone memory
619
620 int phone_count; // num entries in phone
621 bool overwrite_phone_entries = false;
622 int phone_entry_no, entries_written;
623 bool entry_empty;
624
625 // get number of entries in this phone memory
626 gn_memory_status memstat;
627 error = read_phone_memstat(memtype, &memstat);
628 if (error == GN_ERR_NONE) {
629 GNOKII_DEBUG("Writing to internal phone memory.\n");
630 } else {
631 memtype = GN_MT_SM; // try SIM card instead
632 error = read_phone_memstat(memtype, &memstat);
633 if (error != GN_ERR_NONE)
634 goto finish;
635 GNOKII_DEBUG("Writing to SIM card memory.\n");
636 }
637 phone_count = memstat.used;
638
639 if (memstat.free >= (int) list.count()) {
640 if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
641 i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
642 "the current mobile phonebook or should they <b>replace</b> all "
643 "currently existing phonebook entries ?<br><br>"
644 "Please note, that in case you choose to replace the phonebook "
645 "entries, every contact in your phone will be deleted and only "
646 "the newly exported contacts will be available from inside your phone.</qt>"),
647 i18n("Export to Mobile Phone"),
648 KGuiItem(i18n("&Append to Current Phonebook")),
649 KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
650 overwrite_phone_entries = true;
651 }
652
653 progress->setTotalSteps(list.count());
654 entries_written = 0;
655 progress->setProgress(entries_written);
656 m_progressDlg->setButtonText(i18n("&Stop Export"));
657 m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
658 "of the Mobile Phone.<br><br>%3</qt>")
659 .arg(list.count())
660 .arg(gn_memory_type2str(memtype))
661 .arg(buildPhoneInfoString(memstat)) );
662
663 // Now run the loop...
664 phone_entry_no = 1;
665 for ( it = list.begin(); it != list.end(); ++it ) {
666 const TDEABC::Addressee *addr = &(*it);
667 if (addr->isEmpty())
668 continue;
669 // don't write back SIM-card entries !
670 if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
671 continue;
672
673 progress->setProgress(entries_written++);
674
675try_next_phone_entry:
676 this_filter->processEvents();
677 if (m_progressDlg->wasCancelled())
678 break;
679
680 // End of phone memory reached ?
681 if (phone_entry_no > (memstat.used + memstat.free))
682 break;
683
684 GNOKII_DEBUG(TQString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
685 .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
686
687 error = GN_ERR_NONE;
688
689 // is this phone entry empty ?
690 entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
691 if (overwrite_phone_entries) {
692 // overwrite this phonebook entry ...
693 if (!entry_empty)
694 phone_count--;
695 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
696 phone_entry_no++;
697 } else {
698 // add this phonebook entry if possible ...
699 if (entry_empty) {
700 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
701 phone_entry_no++;
702 } else {
703 phone_entry_no++;
704 goto try_next_phone_entry;
705 }
706 }
707
708 if (error != GN_ERR_NONE)
709 failedList.append(addr->realName());
710
711 // break if we got an error on the first entry
712 if (error != GN_ERR_NONE && it==list.begin())
713 break;
714
715 } // for()
716
717 // if we wanted to overwrite all entries, make sure, that we also
718 // delete all remaining entries in the mobile phone.
719 while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
720 if (m_progressDlg->wasCancelled())
721 break;
722 if (!deleteLabelInitialized) {
723 m_progressDlg->setLabel(
724 i18n("<qt><center>"
725 "All selected contacts have been sucessfully copied to "
726 "the Mobile Phone.<br><br>"
727 "Please wait until all remaining orphaned contacts from "
728 "the Mobile Phone have been deleted.</center></qt>") );
729 m_progressDlg->setButtonText(i18n("&Stop Delete"));
730 deleteLabelInitialized = true;
731 progress->setTotalSteps(phone_count);
732 entries_written = 0;
733 progress->setProgress(entries_written);
734 this_filter->processEvents();
735 }
736 if (phone_entry_no > (memstat.used + memstat.free))
737 break;
738 entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
739 if (!entry_empty) {
740 error = xxport_phone_delete_entry(phone_entry_no, memtype);
741 phone_count--;
742 progress->setProgress(++entries_written);
743 this_filter->processEvents();
744 }
745 phone_entry_no++;
746 }
747
748finish:
749 m_progressDlg->setLabel(i18n("Export to phone finished."));
750 this_filter->processEvents();
751
752 GNOKII_DEBUG("GNOKII export filter finished.\n");
753
754 busterminate();
755 delete m_progressDlg;
756
757 if (!failedList.isEmpty()) {
758 GNOKII_DEBUG(TQString("Failed to export: %1\n").arg(failedList.join(", ")));
759 KMessageBox::informationList(parentWidget(),
760 i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
761 "Possible Reasons for this problem could be:<br><ul>"
762 "<li>The contacts contain more information per entry than the phone can store.</li>"
763 "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
764 "<li>other storage size related problems.</li>"
765 "</ul>"
766 "To avoid those kind of problems in the future please reduce the amount of different "
767 "fields in the above contacts.</qt>"),
768 failedList,
769 i18n("Mobile Phone Export") );
770 }
771
772
773 return true;
774}
775
776/******************************************************************************
777 ******************************************************************************
778 ******************************************************************************
779 ******************************************************************************
780 ******************************************************************************/
781
782#elif defined(HAVE_GNOKII_H)
783
784#ifdef __GNUC__
785# warning "Please upgrade your gnokii installation to at least version 0.6.13"
786# warning "Older gnokii versions below 0.6.13 are not binary compatible and"
787# warning "prevents TDE users to upgrade gnokii to newer versions later."
788#endif
789
790/* OLD GNOKII LIBRARIES (< 0.6.13) */
791
792/* import */
793static char *lockfile = NULL;
794static char manufacturer[64], model[GN_MODEL_MAX_LENGTH+1],
795 revision[GN_REVISION_MAX_LENGTH+1], imei[GN_IMEI_MAX_LENGTH+1];
796static TQString PhoneProductId;
797
798static struct gn_statemachine state;
799static gn_data data;
800
801static void busterminate(void)
802{
803 gn_sm_functions(GN_OP_Terminate, NULL, &state);
804 if (lockfile) gn_device_unlock(lockfile);
805}
806
807static TQString businit(void)
808{
809 gn_error error;
810 char *aux;
811
812 GNOKII_DEBUG( "Using old gnokii version." );
813
814#if defined(LIBGNOKII_VERSION)
815 if (gn_cfg_read_default()<0)
816#else
817 static char *BinDir;
818 if (gn_cfg_read(&BinDir)<0)
819#endif
820 return i18n("Failed to initialize the gnokii library.");
821
822 if (!gn_cfg_phone_load("", &state))
823 return i18n("Gnokii is not yet configured.");
824
825 // uncomment to debug all gnokii communication on stderr.
826 // gn_log_debug_mask = GN_LOG_T_STDERR;
827
828 gn_data_clear(&data);
829
830 aux = gn_cfg_get(gn_cfg_info, "global", "use_locking");
831 // Defaults to 'no'
832 if (aux && !strcmp(aux, "yes")) {
833 lockfile = gn_device_lock(state.config.port_device);
834 if (lockfile == NULL) {
835 return i18n("Gnokii reports a 'Lock File Error'.\n "
836 "Please exit all other running instances of gnokii, check if you have "
837 "write permissions in the /var/lock directory and try again.");
838 }
839 }
840
841 // Initialise the code for the GSM interface.
842 int old_dcd = state.config.require_dcd; // work-around for older gnokii versions
843 state.config.require_dcd = false;
844 error = gn_gsm_initialise(&state);
845 GNOKII_CHECK_ERROR(error);
846 state.config.require_dcd = old_dcd;
847 if (error != GN_ERR_NONE) {
848 busterminate();
849 return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
850 "The returned error message was:<br><b>%1</b><br><br>"
851 "You might try to run \"gnokii --identify\" on the command line to "
852 "check any cable/transport issues and to verify if your gnokii "
853 "configuration is correct.</center></qt>")
854 .arg(gn_error_print(error));
855 }
856
857 // identify phone
858 gn_data_clear(&data);
859 data.manufacturer = manufacturer;
860 data.model = model;
861 data.revision = revision;
862 data.imei = imei;
863
864 TQCString unknown(GN_TO(i18n("Unknown")));
865 tqstrncpy(manufacturer, unknown, sizeof(manufacturer)-1);
866 tqstrncpy(model, unknown, sizeof(model)-1);
867 tqstrncpy(revision, unknown, sizeof(revision)-1);
868 tqstrncpy(imei, unknown, sizeof(imei)-1);
869
870 if (m_progressDlg->wasCancelled())
871 return TQString();
872 else
873 error = gn_sm_functions(GN_OP_Identify, &data, &state);
874 GNOKII_CHECK_ERROR(error);
875
876 GNOKII_DEBUG( TQString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
877 .arg(manufacturer, model, revision, imei) );
878
879 PhoneProductId = TQString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei);
880
881 return TQString();
882}
883
884
885// get number of entries in this phone memory type (internal/SIM-card)
886static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
887{
888 gn_error error;
889
890 gn_data_clear(&data);
891 memset(memstat, 0, sizeof(*memstat));
892 memstat->memory_type = memtype;
893 data.memory_status = memstat;
894 error = gn_sm_functions(GN_OP_GetMemoryStatus, &data, &state);
895 GNOKII_CHECK_ERROR(error);
896 if (error != GN_ERR_NONE) {
897 switch (memtype) {
898 case GN_MT_SM:
899 // use at least 100 entries
900 memstat->used = 0;
901 memstat->free = 100;
902 break;
903 default:
904 case GN_MT_ME:
905 // Phone doesn't support ME (5110)
906 memstat->used = memstat->free = 0;
907 break;
908 }
909 }
910 GNOKII_DEBUG( TQString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
911 .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
912 return error;
913}
914
915
916// read phone entry #index from memory #memtype
917static gn_error read_phone_entry( const int index, const gn_memory_type memtype, gn_phonebook_entry *entry )
918{
919 gn_error error;
920 entry->memory_type = memtype;
921 entry->location = index;
922 data.phonebook_entry = entry;
923 error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
924 GNOKII_CHECK_ERROR(error);
925 return error;
926}
927
928static bool phone_entry_empty( const int index, const gn_memory_type memtype )
929{
930 gn_error error;
931 gn_phonebook_entry entry;
932 entry.memory_type = memtype;
933 entry.location = index;
934 data.phonebook_entry = &entry;
935 error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
936 if (error == GN_ERR_EMPTYLOCATION)
937 return true;
938 GNOKII_CHECK_ERROR(error);
939 if (error == GN_ERR_NONE && entry.empty)
940 return true;
941 return false;
942}
943
944static TQString buildPhoneInfoString( const gn_memory_status &memstat )
945{
946 TQString format = TQString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
947
948 return TQString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
949 .arg(i18n("Mobile Phone information:"))
950 .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
951 .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
952 .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
953 .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
954 .arg(format.arg(i18n("Phonebook status"))
955 .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
956}
957
958static TQString buildMemoryTypeString( gn_memory_type memtype )
959{
960 switch (memtype) {
961 case GN_MT_ME: return i18n("internal memory");
962 case GN_MT_SM: return i18n("SIM-card memory");
963 default: return i18n("unknown memory");
964 }
965}
966
967// read and evaluate all phone entries
968static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
969 TDEABC::AddresseeList *addrList )
970{
971 gn_error error;
972
973 if (m_progressDlg->wasCancelled())
974 return GN_ERR_NONE;
975
976 KProgress* progress = (KProgress*)m_progressDlg->progressBar();
977
978 progress->setProgress(0);
979 this_filter->processEvents();
980
981 // get number of entries in this phone memory type (internal/SIM-card)
982 gn_memory_status memstat;
983 error = read_phone_memstat(memtype, &memstat);
984
985 gn_phonebook_entry entry;
986 TQStringList addrlist;
987 TDEABC::Address *addr;
988 TQString s, country;
989
990 progress->setTotalSteps(memstat.used);
991 m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
992 .arg(memstat.used)
993 .arg(buildMemoryTypeString(memtype))
994 .arg(buildPhoneInfoString(memstat)) );
995
996 int num_read = 0;
997
998 for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
999 error = read_phone_entry( i, memtype, &entry );
1000
1001 progress->setProgress(num_read);
1002 this_filter->processEvents();
1003
1004 if (error == GN_ERR_EMPTYLOCATION)
1005 continue;
1006 if (error == GN_ERR_INVALIDLOCATION)
1007 break;
1008 if (error == GN_ERR_INVALIDMEMORYTYPE)
1009 break;
1010 if (error == GN_ERR_NONE) {
1011 GNOKII_DEBUG(TQString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i).arg(GN_FROM(entry.name))
1012 .arg(GN_FROM(entry.number)).arg(entry.location).arg(entry.caller_group).arg(entry.subentries_count));
1013 TDEABC::Addressee *a = new TDEABC::Addressee();
1014
1015 // try to split Name into FamilyName and GivenName
1016 s = GN_FROM(entry.name).simplifyWhiteSpace();
1017 a->setFormattedName(s); // set formatted name as in Phone
1018 if (s.find(',') == -1) {
1019 // assumed format: "givenname [... familyname]"
1020 addrlist = TQStringList::split(' ', s);
1021 if (addrlist.count() == 1) {
1022 // only one string -> put it in the GivenName
1023 a->setGivenName(s);
1024 } else {
1025 // multiple strings -> split them.
1026 a->setFamilyName(addrlist.last().simplifyWhiteSpace());
1027 addrlist.remove(addrlist.last());
1028 a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
1029 }
1030 } else {
1031 // assumed format: "familyname, ... givenname"
1032 addrlist = TQStringList::split(',', s);
1033 a->setFamilyName(addrlist.first().simplifyWhiteSpace());
1034 addrlist.remove(addrlist.first());
1035 a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
1036 }
1037
1038 a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(entry.caller_group));
1039 a->insertCustom(APP, "X_GSM_STORE_AT", TQString("%1%2").arg(memtypestr).arg(entry.location));
1040
1041 // set ProductId
1042 a->setProductId(PhoneProductId);
1043
1044 // evaluate timestamp (ignore timezone)
1045 TQDateTime datetime;
1046 if (entry.date.year<1998)
1047 datetime = TQDateTime::currentDateTime();
1048 else
1049 datetime = TQDateTime( TQDate(entry.date.year, entry.date.month, entry.date.day),
1050 TQTime(entry.date.hour, entry.date.minute, entry.date.second) );
1051 GNOKII_DEBUG(TQString(" date=%1\n").arg(datetime.toString()));
1052 a->setRevision(datetime);
1053
1054 if (!entry.subentries_count)
1055 a->insertPhoneNumber(TDEABC::PhoneNumber(entry.number, TDEABC::PhoneNumber::Work | TDEABC::PhoneNumber::Pref));
1056
1057 /* scan sub-entries */
1058 if (entry.subentries_count)
1059 for (int n=0; n<entry.subentries_count; n++) {
1060 TQString s = GN_FROM(entry.subentries[n].data.number).simplifyWhiteSpace();
1061 GNOKII_DEBUG(TQString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
1062 .arg(n).arg(entry.subentries[n].entry_type)
1063 .arg(entry.subentries[n].number_type).arg(s));
1064 if (s.isEmpty())
1065 continue;
1066 switch(entry.subentries[n].entry_type) {
1067 case GN_PHONEBOOK_ENTRY_Name:
1068 a->setName(s);
1069 break;
1070 case GN_PHONEBOOK_ENTRY_Email:
1071 a->insertEmail(s);
1072 break;
1073 case GN_PHONEBOOK_ENTRY_Postal:
1074 addrlist = TQStringList::split(';', s, true);
1075 addr = new TDEABC::Address(TDEABC::Address::Work);
1076 if (addrlist.count() <= 1) {
1077 addrlist = TQStringList::split(',', s, true);
1078 if (addrlist.count() > 1 ) {
1079 // assumed format: "Locality, ZIP, Country"
1080 addr->setLocality(addrlist[0]);
1081 addr->setPostalCode(addrlist[1]);
1082 if (!addrlist[2].isEmpty())
1083 addr->setCountry(i18n(GN_TO(addrlist[2])));
1084 } else {
1085 // no idea about the format, just store it.
1086 addr->setLocality(s);
1087 }
1088 } else {
1089 // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
1090 addr->setPostOfficeBox(addrlist[0]);
1091 addr->setExtended(addrlist[1]);
1092 addr->setStreet(addrlist[2]);
1093 addr->setLocality(addrlist[3]);
1094 addr->setRegion(addrlist[4]);
1095 addr->setPostalCode(addrlist[5]);
1096 country = addrlist[6];
1097 if (!country.isEmpty())
1098 addr->setCountry(i18n(GN_TO(country)));
1099 }
1100 a->insertAddress(*addr);
1101 delete addr;
1102 break;
1103 case GN_PHONEBOOK_ENTRY_Note:
1104 if (!a->note().isEmpty())
1105 s = "\n" + s;
1106 a->setNote(a->note()+s);
1107 break;
1108 case GN_PHONEBOOK_ENTRY_Number:
1109 enum TDEABC::PhoneNumber::Types phonetype;
1110 switch (entry.subentries[n].number_type) {
1111 case GN_PHONEBOOK_NUMBER_Mobile: phonetype = TDEABC::PhoneNumber::Cell; break;
1112 case GN_PHONEBOOK_NUMBER_Fax: phonetype = TDEABC::PhoneNumber::Fax; break;
1113 case GN_PHONEBOOK_NUMBER_General:
1114 case GN_PHONEBOOK_NUMBER_Work: phonetype = TDEABC::PhoneNumber::Work; break;
1115 default:
1116 case GN_PHONEBOOK_NUMBER_Home: phonetype = TDEABC::PhoneNumber::Home; break;
1117 }
1118 //if (s == entry.number)
1119 // type = (TDEABC::PhoneNumber::Types) (phonetype | TDEABC::PhoneNumber::Pref);
1120 a->insertPhoneNumber(TDEABC::PhoneNumber(s, phonetype));
1121 break;
1122 case GN_PHONEBOOK_ENTRY_URL:
1123 a->setUrl(s);
1124 break;
1125 case GN_PHONEBOOK_ENTRY_Group:
1126 a->insertCategory(s);
1127 break;
1128 default:
1129 GNOKII_DEBUG(TQString(" Not handled id=%1, entry=%2\n")
1130 .arg(entry.subentries[n].entry_type).arg(s));
1131 break;
1132 } // switch()
1133 } // if(subentry)
1134
1135 // add only if entry was valid
1136 if (strlen(entry.name) || strlen(entry.number) || entry.subentries_count)
1137 addrList->append(*a);
1138
1139 // did we read all valid phonebook-entries ?
1140 num_read++;
1141 delete a;
1142 if (num_read >= memstat.used)
1143 break; // yes, all were read
1144 else
1145 continue; // no, we are still missing some.
1146 }
1147 GNOKII_CHECK_ERROR(error);
1148 }
1149
1150 return GN_ERR_NONE;
1151}
1152
1153
1154// export to phone
1155
1156static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
1157 const TDEABC::Addressee *addr)
1158{
1159 gn_phonebook_entry entry;
1160 TQString s;
1161
1162 memset(&entry, 0, sizeof(entry));
1163 strncpy(entry.name, GN_TO(addr->realName()), sizeof(entry.name)-1);
1164 s = addr->phoneNumber(TDEABC::PhoneNumber::Pref).number();
1165 if (s.isEmpty())
1166 s = addr->phoneNumber(TDEABC::PhoneNumber::Work).number();
1167 if (s.isEmpty())
1168 s = addr->phoneNumber(TDEABC::PhoneNumber::Home).number();
1169 if (s.isEmpty())
1170 s = addr->phoneNumber(TDEABC::PhoneNumber::Cell).number();
1171 if (s.isEmpty() && addr->phoneNumbers().count()>0)
1172 s = (*addr->phoneNumbers().at(0)).number();
1173 s = makeValidPhone(s);
1174 strncpy(entry.number, s.ascii(), sizeof(entry.number)-1);
1175 entry.memory_type = memtype;
1176 TQString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
1177 if (cg.isEmpty())
1178 entry.caller_group = 5; // default group
1179 else
1180 entry.caller_group = cg.toInt();
1181 entry.location = phone_location;
1182
1183 // set date/revision
1184 TQDateTime datetime = addr->revision();
1185 TQDate date(datetime.date());
1186 TQTime time(datetime.time());
1187 entry.date.year = date.year();
1188 entry.date.month = date.month();
1189 entry.date.day = date.day();
1190 entry.date.hour = time.hour();
1191 entry.date.minute = time.minute();
1192 entry.date.second = time.second();
1193
1194 GNOKII_DEBUG(TQString("Write #%1: name=%2, number=%3\n").arg(phone_location)
1195 .arg(GN_FROM(entry.name)).arg(GN_FROM(entry.number)));
1196
1197 const TDEABC::Address homeAddr = addr->address(TDEABC::Address::Home);
1198 const TDEABC::Address workAddr = addr->address(TDEABC::Address::Work);
1199
1200 entry.subentries_count = 0;
1201 gn_phonebook_subentry *subentry = &entry.subentries[0];
1202 // add all phone numbers
1203 const TDEABC::PhoneNumber::List phoneList = addr->phoneNumbers();
1204 TDEABC::PhoneNumber::List::ConstIterator it;
1205 for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
1206 const TDEABC::PhoneNumber *phonenumber = &(*it);
1207 s = phonenumber->number();
1208 if (s.isEmpty()) continue;
1209 subentry->entry_type = GN_PHONEBOOK_ENTRY_Number;
1210 gn_phonebook_number_type type;
1211 int pn_type = phonenumber->type();
1212 if ((pn_type & TDEABC::PhoneNumber::Cell))
1213 type = GN_PHONEBOOK_NUMBER_Mobile;
1214 else if ((pn_type & TDEABC::PhoneNumber::Fax))
1215 type = GN_PHONEBOOK_NUMBER_Fax;
1216 else if ((pn_type & TDEABC::PhoneNumber::Home))
1217 type = GN_PHONEBOOK_NUMBER_Home;
1218 else if ((pn_type & TDEABC::PhoneNumber::Work))
1219 type = GN_PHONEBOOK_NUMBER_Work;
1220 else type = GN_PHONEBOOK_NUMBER_General;
1221 subentry->number_type = type;
1222 strncpy(subentry->data.number, makeValidPhone(s).ascii(), sizeof(subentry->data.number)-1);
1223 subentry->id = phone_location<<8+entry.subentries_count;
1224 entry.subentries_count++;
1225 subentry++;
1226 if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
1227 break; // Phonebook full
1228 }
1229 // add URL
1230 s = addr->url().prettyURL();
1231 if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
1232 subentry->entry_type = GN_PHONEBOOK_ENTRY_URL;
1233 strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
1234 entry.subentries_count++;
1235 subentry++;
1236 }
1237 // add E-Mails
1238 TQStringList emails = addr->emails();
1239 for (unsigned int n=0; n<emails.count(); n++) {
1240 if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
1241 break; // Phonebook full
1242 s = emails[n].simplifyWhiteSpace();
1243 if (s.isEmpty()) continue;
1244 // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
1245 if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
1246 GNOKII_DEBUG(TQString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
1247 .arg(s));
1248 continue;
1249 }
1250 subentry->entry_type = GN_PHONEBOOK_ENTRY_Email;
1251 strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
1252 entry.subentries_count++;
1253 subentry++;
1254 }
1255 // add Adresses
1256 const TDEABC::Address::List addresses = addr->addresses();
1257 TDEABC::Address::List::ConstIterator it2;
1258 for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
1259 if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
1260 break; // Phonebook full
1261 const TDEABC::Address *Addr = &(*it2);
1262 if (Addr->isEmpty()) continue;
1263 subentry->entry_type = GN_PHONEBOOK_ENTRY_Postal;
1264 TQStringList a;
1265 TQChar sem(';');
1266 TQString sem_repl(TQString::fromLatin1(","));
1267 a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
1268 a.append( Addr->extended() .replace( sem, sem_repl ) );
1269 a.append( Addr->street() .replace( sem, sem_repl ) );
1270 a.append( Addr->locality() .replace( sem, sem_repl ) );
1271 a.append( Addr->region() .replace( sem, sem_repl ) );
1272 a.append( Addr->postalCode() .replace( sem, sem_repl ) );
1273 a.append( Addr->country() .replace( sem, sem_repl ) );
1274 s = a.join(sem);
1275 strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
1276 entry.subentries_count++;
1277 subentry++;
1278 }
1279 // add Note
1280 s = addr->note().simplifyWhiteSpace();
1281 if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
1282 subentry->entry_type = GN_PHONEBOOK_ENTRY_Note;
1283 strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
1284 entry.subentries_count++;
1285 subentry++;
1286 }
1287
1288 // debug output
1289 for (int st=0; st<entry.subentries_count; st++) {
1290 gn_phonebook_subentry *subentry = &entry.subentries[st];
1291 GNOKII_DEBUG(TQString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
1292 .arg(st).arg(subentry->entry_type)
1293 .arg(subentry->number_type).arg(GN_FROM(subentry->data.number)));
1294 }
1295
1296 data.phonebook_entry = &entry;
1297 gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
1298 GNOKII_CHECK_ERROR(error);
1299
1300 return error;
1301}
1302
1303
1304static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
1305{
1306 gn_phonebook_entry entry;
1307 memset(&entry, 0, sizeof(entry));
1308 entry.empty = 1;
1309 entry.memory_type = memtype;
1310 entry.location = phone_location;
1311 data.phonebook_entry = &entry;
1312 GNOKII_DEBUG(TQString("Deleting entry %1\n").arg(phone_location));
1313 gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
1314 GNOKII_CHECK_ERROR(error);
1315 return error;
1316}
1317
1318TDEABC::AddresseeList GNOKIIXXPort::importContacts( const TQString& ) const
1319{
1320 TDEABC::AddresseeList addrList;
1321
1322 if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
1323 i18n("<qt>Please connect your Mobile Phone to your computer and press "
1324 "<b>Continue</b> to start importing the personal contacts.<br><br>"
1325 "Please note that if your Mobile Phone is not properly connected "
1326 "the following detection phase might take up to two minutes, during which "
1327 "KAddressbook will behave unresponsively.</qt>") ))
1328 return addrList;
1329
1330 m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
1331 i18n("Mobile Phone Import"),
1332 i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
1333 "Please wait...</center></qt>") );
1334 m_progressDlg->setAllowCancel(true);
1335 m_progressDlg->progressBar()->setProgress(0);
1336 m_progressDlg->progressBar()->setCenterIndicator(true);
1337 m_progressDlg->setModal(true);
1338 m_progressDlg->setInitialSize(TQSize(450,350));
1339 m_progressDlg->show();
1340 processEvents();
1341
1342 m_progressDlg->setCursor( TQt::BusyCursor );
1343 TQString errStr = businit();
1344 m_progressDlg->unsetCursor();
1345
1346 if (!errStr.isEmpty()) {
1347 KMessageBox::error(parentWidget(), errStr);
1348 delete m_progressDlg;
1349 return addrList;
1350 }
1351
1352 GNOKII_DEBUG("GNOKII import filter started.\n");
1353 m_progressDlg->setButtonText(i18n("&Stop Import"));
1354
1355 read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
1356 read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
1357
1358 GNOKII_DEBUG("GNOKII import filter finished.\n");
1359
1360 busterminate();
1361 delete m_progressDlg;
1362
1363 return addrList;
1364}
1365
1366
1367bool GNOKIIXXPort::exportContacts( const TDEABC::AddresseeList &list, const TQString & )
1368{
1369 if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
1370 i18n("<qt>Please connect your Mobile Phone to your computer and press "
1371 "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
1372 "Please note that if your Mobile Phone is not properly connected "
1373 "the following detection phase might take up to two minutes, during which "
1374 "KAddressbook will behave unresponsively.</qt>") ))
1375 return false;
1376
1377 m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
1378 i18n("Mobile Phone Export"),
1379 i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
1380 "Please wait...</center></qt>") );
1381 m_progressDlg->setAllowCancel(true);
1382 m_progressDlg->progressBar()->setProgress(0);
1383 m_progressDlg->progressBar()->setCenterIndicator(true);
1384 m_progressDlg->setModal(true);
1385 m_progressDlg->setInitialSize(TQSize(450,350));
1386 m_progressDlg->show();
1387 processEvents();
1388
1389 KProgress* progress = (KProgress*)m_progressDlg->progressBar();
1390
1391 TDEABC::AddresseeList::ConstIterator it;
1392 TQStringList failedList;
1393
1394 gn_error error;
1395 bool deleteLabelInitialized = false;
1396
1397 m_progressDlg->setCursor( TQt::BusyCursor );
1398 TQString errStr = businit();
1399 m_progressDlg->unsetCursor();
1400
1401 if (!errStr.isEmpty()) {
1402 KMessageBox::error(parentWidget(), errStr);
1403 delete m_progressDlg;
1404 return false;
1405 }
1406
1407 GNOKII_DEBUG("GNOKII export filter started.\n");
1408
1409 gn_memory_type memtype = GN_MT_ME; // internal phone memory
1410
1411 int phone_count; // num entries in phone
1412 bool overwrite_phone_entries = false;
1413 int phone_entry_no, entries_written;
1414 bool entry_empty;
1415
1416 // get number of entries in this phone memory
1417 gn_memory_status memstat;
1418 error = read_phone_memstat(memtype, &memstat);
1419 if (error == GN_ERR_NONE) {
1420 GNOKII_DEBUG("Writing to internal phone memory.\n");
1421 } else {
1422 memtype = GN_MT_SM; // try SIM card instead
1423 error = read_phone_memstat(memtype, &memstat);
1424 if (error != GN_ERR_NONE)
1425 goto finish;
1426 GNOKII_DEBUG("Writing to SIM card memory.\n");
1427 }
1428 phone_count = memstat.used;
1429
1430 if (memstat.free >= (int) list.count()) {
1431 if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
1432 i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
1433 "the current mobile phonebook or should they <b>replace</b> all "
1434 "currently existing phonebook entries ?<br><br>"
1435 "Please note, that in case you choose to replace the phonebook "
1436 "entries, every contact in your phone will be deleted and only "
1437 "the newly exported contacts will be available from inside your phone.</qt>"),
1438 i18n("Export to Mobile Phone"),
1439 KGuiItem(i18n("&Append to Current Phonebook")),
1440 KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
1441 overwrite_phone_entries = true;
1442 }
1443
1444 progress->setTotalSteps(list.count());
1445 entries_written = 0;
1446 progress->setProgress(entries_written);
1447 m_progressDlg->setButtonText(i18n("&Stop Export"));
1448 m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
1449 "of the Mobile Phone.<br><br>%3</qt>")
1450 .arg(list.count())
1451 .arg(buildMemoryTypeString(memtype))
1452 .arg(buildPhoneInfoString(memstat)) );
1453
1454 // Now run the loop...
1455 phone_entry_no = 1;
1456 for ( it = list.begin(); it != list.end(); ++it ) {
1457 const TDEABC::Addressee *addr = &(*it);
1458 if (addr->isEmpty())
1459 continue;
1460 // don't write back SIM-card entries !
1461 if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
1462 continue;
1463
1464 progress->setProgress(entries_written++);
1465
1466try_next_phone_entry:
1467 this_filter->processEvents();
1468 if (m_progressDlg->wasCancelled())
1469 break;
1470
1471 // End of phone memory reached ?
1472 if (phone_entry_no > (memstat.used + memstat.free))
1473 break;
1474
1475 GNOKII_DEBUG(TQString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
1476 .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
1477
1478 error = GN_ERR_NONE;
1479
1480 // is this phone entry empty ?
1481 entry_empty = phone_entry_empty(phone_entry_no, memtype);
1482 if (overwrite_phone_entries) {
1483 // overwrite this phonebook entry ...
1484 if (!entry_empty)
1485 phone_count--;
1486 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
1487 phone_entry_no++;
1488 } else {
1489 // add this phonebook entry if possible ...
1490 if (entry_empty) {
1491 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
1492 phone_entry_no++;
1493 } else {
1494 phone_entry_no++;
1495 goto try_next_phone_entry;
1496 }
1497 }
1498
1499 if (error != GN_ERR_NONE)
1500 failedList.append(addr->realName());
1501
1502 // break if we got an error on the first entry
1503 if (error != GN_ERR_NONE && it==list.begin())
1504 break;
1505
1506 } // for()
1507
1508 // if we wanted to overwrite all entries, make sure, that we also
1509 // delete all remaining entries in the mobile phone.
1510 while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
1511 if (m_progressDlg->wasCancelled())
1512 break;
1513 if (!deleteLabelInitialized) {
1514 m_progressDlg->setLabel(
1515 i18n("<qt><center>"
1516 "All selected contacts have been sucessfully copied to "
1517 "the Mobile Phone.<br><br>"
1518 "Please wait until all remaining orphaned contacts from "
1519 "the Mobile Phone have been deleted.</center></qt>") );
1520 m_progressDlg->setButtonText(i18n("&Stop Delete"));
1521 deleteLabelInitialized = true;
1522 progress->setTotalSteps(phone_count);
1523 entries_written = 0;
1524 progress->setProgress(entries_written);
1525 this_filter->processEvents();
1526 }
1527 if (phone_entry_no > (memstat.used + memstat.free))
1528 break;
1529 entry_empty = phone_entry_empty(phone_entry_no, memtype);
1530 if (!entry_empty) {
1531 error = xxport_phone_delete_entry(phone_entry_no, memtype);
1532 phone_count--;
1533 progress->setProgress(++entries_written);
1534 this_filter->processEvents();
1535 }
1536 phone_entry_no++;
1537 }
1538
1539finish:
1540 m_progressDlg->setLabel(i18n("Export to phone finished."));
1541 this_filter->processEvents();
1542
1543 GNOKII_DEBUG("GNOKII export filter finished.\n");
1544
1545 busterminate();
1546 delete m_progressDlg;
1547
1548 if (!failedList.isEmpty()) {
1549 GNOKII_DEBUG(TQString("Failed to export: %1\n").arg(failedList.join(", ")));
1550 KMessageBox::informationList(parentWidget(),
1551 i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
1552 "Possible Reasons for this problem could be:<br><ul>"
1553 "<li>The contacts contain more information per entry than the phone can store.</li>"
1554 "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
1555 "<li>other storage size related problems.</li>"
1556 "</ul>"
1557 "To avoid those kind of problems in the future please reduce the amount of different "
1558 "fields in the above contacts.</qt>"),
1559 failedList,
1560 i18n("Mobile Phone Export") );
1561 }
1562
1563 return true;
1564}
1565
1566
1567/******************************************************************************
1568 ******************************************************************************
1569 ******************************************************************************
1570 ******************************************************************************
1571 ******************************************************************************/
1572
1573#else /* no gnokii installed */
1574
1575TDEABC::AddresseeList GNOKIIXXPort::importContacts( const TQString& ) const
1576{
1577 TDEABC::AddresseeList addrList;
1578 KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
1579 "Please ask your distributor to add gnokii at compile time."));
1580 return addrList;
1581}
1582
1583bool GNOKIIXXPort::exportContacts( const TDEABC::AddresseeList &list, const TQString & )
1584{
1585 Q_UNUSED(list);
1586 KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
1587 "Please ask your distributor to add gnokii at compile time."));
1588 return true;
1589}
1590
1591#endif /* END OF GNOKII LIB SWITCH */
1592
1593/******************************************************************************
1594 ******************************************************************************
1595 ******************************************************************************
1596 ******************************************************************************
1597 ******************************************************************************/
1598
1599#include "gnokii_xxport.moc"