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
36 extern "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
75 static GNOKIIXXPort *this_filter;
76 static KProgressDialog *m_progressDlg;
77 
78 K_EXPORT_KADDRESSBOOK_XXFILTER( libkaddrbk_gnokii_xxport, GNOKIIXXPort )
79 
80 GNOKIIXXPort::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
91 static 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 
115 static const char *manufacturer, *model, *revision, *imei;
116 static struct gn_statemachine *state;
117 
118 static void busterminate(void)
119 {
120  gn_lib_phone_close(state);
121  gn_lib_phoneprofile_free(&state);
122  gn_lib_library_free();
123 }
124 
125 static 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)
162 static 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 
174 static 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
189 static 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 
390 static 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 
521 static 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 
527 TDEABC::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 
576 bool 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 
675 try_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 
748 finish:
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 */
793 static char *lockfile = NULL;
794 static char manufacturer[64], model[GN_MODEL_MAX_LENGTH+1],
795  revision[GN_REVISION_MAX_LENGTH+1], imei[GN_IMEI_MAX_LENGTH+1];
796 static TQString PhoneProductId;
797 
798 static struct gn_statemachine state;
799 static gn_data data;
800 
801 static void busterminate(void)
802 {
803  gn_sm_functions(GN_OP_Terminate, NULL, &state);
804  if (lockfile) gn_device_unlock(lockfile);
805 }
806 
807 static 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)
886 static 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
917 static 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 
928 static 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 
944 static 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 
958 static 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
968 static 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 
1156 static 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 
1304 static 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 
1318 TDEABC::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 
1367 bool 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 
1466 try_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 
1539 finish:
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 
1575 TDEABC::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 
1583 bool 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"