summaryrefslogtreecommitdiffstats
path: root/kaddressbook/xxport/gnokii_xxport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kaddressbook/xxport/gnokii_xxport.cpp')
-rw-r--r--kaddressbook/xxport/gnokii_xxport.cpp1609
1 files changed, 1609 insertions, 0 deletions
diff --git a/kaddressbook/xxport/gnokii_xxport.cpp b/kaddressbook/xxport/gnokii_xxport.cpp
new file mode 100644
index 00000000..60675384
--- /dev/null
+++ b/kaddressbook/xxport/gnokii_xxport.cpp
@@ -0,0 +1,1609 @@
+/*
+ This file is part of KAddressbook.
+ Copyright (c) 2003-2006 Helge Deller <deller@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+/*
+ Description:
+ This filter allows you to import and export the KDE addressbook entries
+ to/from a mobile phone, which is accessible via gnokii.
+ Gnokii homepage: http://www.gnokii.org
+
+ TODO:
+ - create a log file and give user possibility to see it afterwards
+ - handle callergroup value (Friend, VIP, Family, ...) better
+*/
+
+#include "config.h"
+
+#ifdef HAVE_GNOKII_H
+extern "C" {
+#include <gnokii.h>
+}
+#else
+#ifdef __GNUC__
+# warning "Please install gnokii (http://www.gnokii.org) development headers and libraries !"
+# warning "Please use at least version 0.6.13 or later of gnokii."
+#endif
+#endif
+
+#include <qcursor.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <kguiitem.h>
+
+#include "gnokii_xxport.h"
+
+#define APP "GNOKII_XXPORT"
+
+#if 1 // !defined(NDEBUG)
+ #define GNOKII_DEBUG(x) do { kdWarning() << (x); } while (0)
+#else
+ #define GNOKII_DEBUG(x) do { } while (0)
+#endif
+#define GNOKII_CHECK_ERROR(error) \
+ do { \
+ if (error) \
+ kdError() << QString("ERROR %1: %2\n").arg(error).arg(gn_error_print(error));\
+ } while (0)
+
+// Locale conversion routines:
+// Gnokii uses the local 8 Bit encoding (based on LC_ALL), kaddressbook uses Unicode
+#define GN_FROM(x) QString::fromLocal8Bit(x)
+#define GN_TO(x) (x).local8Bit()
+
+// static variables for GUI updates
+static GNOKIIXXPort *this_filter;
+static KProgressDialog *m_progressDlg;
+
+K_EXPORT_KADDRESSBOOK_XXFILTER( libkaddrbk_gnokii_xxport, GNOKIIXXPort )
+
+GNOKIIXXPort::GNOKIIXXPort( KABC::AddressBook *ab, QWidget *parent, const char *name )
+ : KAB::XXPort( ab, parent, name )
+{
+ this_filter = this;
+ m_progressDlg = NULL;
+ createImportAction( i18n( "Import From Mobile Phone..." ) );
+ createExportAction( i18n( "Export to Mobile Phone..." ) );
+}
+
+
+#ifdef HAVE_GNOKII_H
+static QString makeValidPhone( const QString &number )
+{
+ // allowed chars: 0-9, *, #, p, w, +
+ QString num = number.simplifyWhiteSpace();
+ QString allowed("0123456789*+#pw");
+ for (unsigned int i=num.length(); i>=1; i--)
+ if (allowed.find(num[i-1])==-1)
+ num.remove(i-1,1);
+ if (num.isEmpty())
+ num = "0";
+ return num;
+}
+#endif
+
+/******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************/
+
+#if defined(HAVE_GNOKII_H) && defined(LIBGNOKII_VERSION_MAJOR) && (LIBGNOKII_VERSION_MAJOR >= 3)
+
+/* NEW GNOKII LIBRARIES (>= 0.6.13) */
+
+static const char *manufacturer, *model, *revision, *imei;
+static struct gn_statemachine *state;
+
+static void busterminate(void)
+{
+ gn_lib_phone_close(state);
+ gn_lib_phoneprofile_free(&state);
+ gn_lib_library_free();
+}
+
+static QString businit(void)
+{
+ GNOKII_DEBUG( "Using new gnokii version." );
+
+ GNOKII_DEBUG( QString("Compiled with libgnokii version 0x%1\n").arg(QString::number(LIBGNOKII_VERSION,16)) );
+ GNOKII_DEBUG( QString("Using libgnokii runtime version 0x%1\n").arg(QString::number(gn_lib_version(),16)) );
+
+ gn_error error = gn_lib_phoneprofile_load(NULL, &state);
+ if (error)
+ return i18n("Failed to initialize the gnokii library.");
+
+ error = gn_lib_phone_open( state );
+ GNOKII_CHECK_ERROR(error);
+ if (error != GN_ERR_NONE) {
+ busterminate();
+ return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
+ "The returned error message was:<br><b>%1</b><br><br>"
+ "You might try to run \"gnokii --identify\" on the command line to "
+ "check any cable/transport issues and to verify if your gnokii "
+ "configuration is correct.</center></qt>")
+ .arg(gn_error_print(error));
+ }
+
+ // identify phone
+ manufacturer = gn_lib_get_phone_manufacturer(state);
+ model = gn_lib_get_phone_model(state);
+ revision = gn_lib_get_phone_revision(state);
+ imei = gn_lib_get_phone_imei(state);
+
+ GNOKII_DEBUG( QString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
+ .arg(manufacturer, model, revision, imei) );
+
+ return QString::null;
+}
+
+
+// get number of entries in this phone memory type (internal/SIM-card)
+static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
+{
+ gn_error error;
+
+ error = gn_lib_addressbook_memstat(state, memtype, &memstat->used, &memstat->free);
+
+ GNOKII_DEBUG( QString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
+ .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
+ return error;
+}
+
+
+static QString buildPhoneInfoString( const gn_memory_status &memstat )
+{
+ QString format = QString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
+
+ return QString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
+ .arg(i18n("Mobile Phone information:"))
+ .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
+ .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
+ .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
+ .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
+ .arg(format.arg(i18n("Phonebook status"))
+ .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
+}
+
+// read and evaluate all phone entries
+static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
+ KABC::AddresseeList *addrList )
+{
+ gn_error error;
+
+ if (m_progressDlg->wasCancelled())
+ return GN_ERR_NONE;
+
+ KProgress* progress = (KProgress*)m_progressDlg->progressBar();
+
+ progress->setProgress(0);
+ this_filter->processEvents();
+
+ // get number of entries in this phone memory type (internal/SIM-card)
+ gn_memory_status memstat;
+ error = read_phone_memstat(memtype, &memstat);
+
+ QStringList addrlist;
+ KABC::Address *addr;
+ QString s, country;
+
+ progress->setTotalSteps(memstat.used);
+ m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
+ .arg(memstat.used)
+ .arg(gn_memory_type2str(memtype))
+ .arg(buildPhoneInfoString(memstat)) );
+
+ int num_read = 0;
+
+ for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
+ error = gn_lib_phonebook_read_entry(state, memtype, i);
+ GNOKII_CHECK_ERROR(error);
+
+ progress->setProgress(num_read);
+ this_filter->processEvents();
+
+ if (error == GN_ERR_EMPTYLOCATION)
+ continue;
+ if (error == GN_ERR_INVALIDLOCATION)
+ break;
+ if (error == GN_ERR_INVALIDMEMORYTYPE)
+ break;
+ if (error == GN_ERR_NONE) {
+ const int subentries_count = gn_lib_get_pb_num_subentries(state);
+ const char *name = gn_lib_get_pb_name(state);
+ const char *number = gn_lib_get_pb_number(state);
+
+ GNOKII_DEBUG(QString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i)
+ .arg(GN_FROM(name)).arg(GN_FROM(number))
+ .arg(gn_lib_get_pb_location(state)).arg(gn_lib_get_pb_caller_group(state))
+ .arg(subentries_count));
+ KABC::Addressee *a = new KABC::Addressee();
+
+ // try to split Name into FamilyName and GivenName
+ s = GN_FROM(name).simplifyWhiteSpace();
+ a->setFormattedName(s); // set formatted name as in Phone
+ if (s.find(',') == -1) {
+ // assumed format: "givenname [... familyname]"
+ addrlist = QStringList::split(' ', s);
+ if (addrlist.count() == 1) {
+ // only one string -> put it in the GivenName
+ a->setGivenName(s);
+ } else {
+ // multiple strings -> split them.
+ a->setFamilyName(addrlist.last().simplifyWhiteSpace());
+ addrlist.remove(addrlist.last());
+ a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
+ }
+ } else {
+ // assumed format: "familyname, ... givenname"
+ addrlist = QStringList::split(',', s);
+ a->setFamilyName(addrlist.first().simplifyWhiteSpace());
+ addrlist.remove(addrlist.first());
+ a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
+ }
+
+ a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(gn_lib_get_pb_caller_group(state)));
+ a->insertCustom(APP, "X_GSM_STORE_AT", QString("%1%2").arg(memtypestr).arg(gn_lib_get_pb_location(state)));
+
+ // set ProductId
+ a->setProductId(QString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei));
+
+ // evaluate timestamp (ignore timezone)
+ QDateTime datetime;
+ gn_timestamp ts = gn_lib_get_pb_date(state);
+ if (ts.year<1998)
+ datetime = QDateTime::currentDateTime();
+ else
+ datetime = QDateTime( QDate(ts.year, ts.month, ts.day),
+ QTime(ts.hour, ts.minute, ts.second) );
+ GNOKII_DEBUG(QString(" date=%1\n").arg(datetime.toString()));
+ a->setRevision(datetime);
+
+ if (!subentries_count)
+ a->insertPhoneNumber(KABC::PhoneNumber(number,
+ KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref));
+
+ /* scan sub-entries */
+ if (subentries_count)
+ for (int n=0; n<subentries_count; n++) {
+ gn_phonebook_entry_type entry_type;
+ gn_phonebook_number_type number_type;
+ const char *number;
+
+ error = gn_lib_get_pb_subentry(state, n, &entry_type, &number_type, &number);
+ GNOKII_CHECK_ERROR(error);
+
+ QString s = GN_FROM(number).simplifyWhiteSpace();
+ GNOKII_DEBUG(QString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
+ .arg(n).arg(entry_type).arg(number_type).arg(s));
+ if (s.isEmpty())
+ continue;
+ switch(entry_type) {
+ case GN_PHONEBOOK_ENTRY_Name:
+ a->setName(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Email:
+ a->insertEmail(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Postal:
+ addrlist = QStringList::split(';', s, true);
+ addr = new KABC::Address(KABC::Address::Work);
+ if (addrlist.count() <= 1) {
+ addrlist = QStringList::split(',', s, true);
+ if (addrlist.count() > 1 ) {
+ // assumed format: "Locality, ZIP, Country"
+ addr->setLocality(addrlist[0]);
+ addr->setPostalCode(addrlist[1]);
+ if (!addrlist[2].isEmpty())
+ addr->setCountry(i18n(GN_TO(addrlist[2])));
+ } else {
+ // no idea about the format, just store it.
+ addr->setLocality(s);
+ }
+ } else {
+ // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
+ addr->setPostOfficeBox(addrlist[0]);
+ addr->setExtended(addrlist[1]);
+ addr->setStreet(addrlist[2]);
+ addr->setLocality(addrlist[3]);
+ addr->setRegion(addrlist[4]);
+ addr->setPostalCode(addrlist[5]);
+ country = addrlist[6];
+ if (!country.isEmpty())
+ addr->setCountry(i18n(GN_TO(country)));
+ }
+ a->insertAddress(*addr);
+ delete addr;
+ break;
+ case GN_PHONEBOOK_ENTRY_Note:
+ if (!a->note().isEmpty())
+ s = "\n" + s;
+ a->setNote(a->note()+s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Number:
+ enum KABC::PhoneNumber::Types phonetype;
+ switch (number_type) {
+ case GN_PHONEBOOK_NUMBER_Mobile: phonetype = KABC::PhoneNumber::Cell; break;
+ case GN_PHONEBOOK_NUMBER_Fax: phonetype = KABC::PhoneNumber::Fax; break;
+ case GN_PHONEBOOK_NUMBER_General:
+ case GN_PHONEBOOK_NUMBER_Work: phonetype = KABC::PhoneNumber::Work; break;
+ default:
+ case GN_PHONEBOOK_NUMBER_Home: phonetype = KABC::PhoneNumber::Home; break;
+ }
+ //if (s == entry.number)
+ // type = (KABC::PhoneNumber::Types) (phonetype | KABC::PhoneNumber::Pref);
+ a->insertPhoneNumber(KABC::PhoneNumber(s, phonetype));
+ break;
+ case GN_PHONEBOOK_ENTRY_URL:
+ a->setUrl(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Group:
+ a->insertCategory(s);
+ break;
+ default:
+ GNOKII_DEBUG(QString(" Not handled id=%1, entry=%2\n")
+ .arg(entry_type).arg(s));
+ break;
+ } // switch()
+ } // if(subentry)
+
+ // add only if entry was valid
+ if (strlen(name) || strlen(number) || subentries_count)
+ addrList->append(*a);
+
+ // did we read all valid phonebook-entries ?
+ num_read++;
+ delete a;
+ if (num_read >= memstat.used)
+ break; // yes, all were read
+ else
+ continue; // no, we are still missing some.
+ }
+ GNOKII_CHECK_ERROR(error);
+ }
+
+ return GN_ERR_NONE;
+}
+
+// export to phone
+
+static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
+ const KABC::Addressee *addr)
+{
+ QString s;
+
+ /* initialize the phonebook entry values to zero */
+ gn_lib_phonebook_prepare_write_entry(state);
+
+ gn_lib_set_pb_location(state, phone_location);
+
+ gn_lib_set_pb_name(state, GN_TO(addr->realName()));
+ s = addr->phoneNumber(KABC::PhoneNumber::Pref).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Work).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Home).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Cell).number();
+ if (s.isEmpty() && addr->phoneNumbers().count()>0)
+ s = (*addr->phoneNumbers().at(0)).number();
+ s = makeValidPhone(s);
+ gn_lib_set_pb_number(state, s.ascii());
+ gn_lib_set_pb_memtype(state, memtype);
+ QString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
+ if (cg.isEmpty())
+ gn_lib_set_pb_caller_group(state, GN_PHONEBOOK_GROUP_None); // default group
+ else
+ gn_lib_set_pb_caller_group(state, (gn_phonebook_group_type) cg.toInt());
+
+ // set date/revision
+ QDateTime datetime = addr->revision();
+ QDate date(datetime.date());
+ QTime time(datetime.time());
+ gn_timestamp ts;
+ gn_timestamp_set( &ts, date.year(), date.month(), date.day(),
+ time.hour(), time.minute(), time.second(), 0 );
+ gn_lib_set_pb_date(state, ts);
+
+ GNOKII_DEBUG(QString("Write #%1: name=%2, number=%3\n").arg(phone_location)
+ .arg(GN_FROM(gn_lib_get_pb_name(state))).arg(GN_FROM(gn_lib_get_pb_number(state))));
+
+ const KABC::Address homeAddr = addr->address(KABC::Address::Home);
+ const KABC::Address workAddr = addr->address(KABC::Address::Work);
+
+ // add all phone numbers
+ const KABC::PhoneNumber::List phoneList = addr->phoneNumbers();
+ KABC::PhoneNumber::List::ConstIterator it;
+ for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
+ const KABC::PhoneNumber *phonenumber = &(*it);
+ s = phonenumber->number();
+ if (s.isEmpty()) continue;
+ gn_phonebook_number_type type;
+ int pn_type = phonenumber->type();
+ if ((pn_type & KABC::PhoneNumber::Cell))
+ type = GN_PHONEBOOK_NUMBER_Mobile;
+ else if ((pn_type & KABC::PhoneNumber::Fax))
+ type = GN_PHONEBOOK_NUMBER_Fax;
+ else if ((pn_type & KABC::PhoneNumber::Home))
+ type = GN_PHONEBOOK_NUMBER_Home;
+ else if ((pn_type & KABC::PhoneNumber::Work))
+ type = GN_PHONEBOOK_NUMBER_Work;
+ else type = GN_PHONEBOOK_NUMBER_General;
+ gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
+ GN_PHONEBOOK_ENTRY_Number, type, makeValidPhone(s).ascii());
+ /*subentry->id = phone_location<<8+entry.subentries_count;*/
+ }
+ // add URL
+ s = addr->url().prettyURL();
+ if (!s.isEmpty()) {
+ gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
+ GN_PHONEBOOK_ENTRY_URL, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
+ }
+ // add E-Mails
+ QStringList emails = addr->emails();
+ for (unsigned int n=0; n<emails.count(); n++) {
+ s = emails[n].simplifyWhiteSpace();
+ if (s.isEmpty()) continue;
+ // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
+ if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
+ GNOKII_DEBUG(QString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
+ .arg(s));
+ continue;
+ }
+ gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
+ GN_PHONEBOOK_ENTRY_Email, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
+ }
+ // add Adresses
+ const KABC::Address::List addresses = addr->addresses();
+ KABC::Address::List::ConstIterator it2;
+ for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
+ const KABC::Address *Addr = &(*it2);
+ if (Addr->isEmpty()) continue;
+ QStringList a;
+ QChar sem(';');
+ QString sem_repl(QString::fromLatin1(","));
+ a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
+ a.append( Addr->extended() .replace( sem, sem_repl ) );
+ a.append( Addr->street() .replace( sem, sem_repl ) );
+ a.append( Addr->locality() .replace( sem, sem_repl ) );
+ a.append( Addr->region() .replace( sem, sem_repl ) );
+ a.append( Addr->postalCode() .replace( sem, sem_repl ) );
+ a.append( Addr->country() .replace( sem, sem_repl ) );
+ s = a.join(sem);
+ gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
+ GN_PHONEBOOK_ENTRY_Postal, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
+ }
+ // add Note
+ s = addr->note().simplifyWhiteSpace();
+ if (!s.isEmpty()) {
+ gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
+ GN_PHONEBOOK_ENTRY_Note, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
+ }
+
+ // debug output
+ for (int st=0; st<gn_lib_get_pb_num_subentries(state); st++) {
+ gn_phonebook_entry_type entry_type;
+ gn_phonebook_number_type number_type;
+ const char *number;
+ gn_lib_get_pb_subentry(state, st, &entry_type, &number_type, &number);
+ GNOKII_DEBUG(QString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
+ .arg(st).arg(entry_type)
+ .arg(number_type).arg(GN_FROM(number)));
+ }
+
+ gn_error error = gn_lib_phonebook_write_entry(state, memtype, phone_location);
+ GNOKII_CHECK_ERROR(error);
+
+ return error;
+}
+
+
+static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
+{
+ return gn_lib_phonebook_entry_delete(state, memtype, phone_location);
+}
+
+
+KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
+{
+ KABC::AddresseeList addrList;
+
+ if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
+ i18n("<qt>Please connect your Mobile Phone to your computer and press "
+ "<b>Continue</b> to start importing the personal contacts.<br><br>"
+ "Please note that if your Mobile Phone is not properly connected "
+ "the following detection phase might take up to two minutes, during which "
+ "KAddressbook will behave unresponsively.</qt>") ))
+ return addrList;
+
+ m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
+ i18n("Mobile Phone Import"),
+ i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
+ "Please wait...</center></qt>") );
+ m_progressDlg->setAllowCancel(true);
+ m_progressDlg->progressBar()->setProgress(0);
+ m_progressDlg->progressBar()->setCenterIndicator(true);
+ m_progressDlg->setModal(true);
+ m_progressDlg->setInitialSize(QSize(450,350));
+ m_progressDlg->show();
+ processEvents();
+
+#if (QT_VERSION >= 0x030300)
+ m_progressDlg->setCursor( Qt::BusyCursor );
+#endif
+ QString errStr = businit();
+ m_progressDlg->unsetCursor();
+
+ if (!errStr.isEmpty()) {
+ KMessageBox::error(parentWidget(), errStr);
+ delete m_progressDlg;
+ return addrList;
+ }
+
+ GNOKII_DEBUG("GNOKII import filter started.\n");
+ m_progressDlg->setButtonText(i18n("&Stop Import"));
+
+ read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
+ read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
+
+ GNOKII_DEBUG("GNOKII import filter finished.\n");
+
+ busterminate();
+ delete m_progressDlg;
+
+ return addrList;
+}
+
+
+bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
+{
+ if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
+ i18n("<qt>Please connect your Mobile Phone to your computer and press "
+ "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
+ "Please note that if your Mobile Phone is not properly connected "
+ "the following detection phase might take up to two minutes, during which "
+ "KAddressbook will behave unresponsively.</qt>") ))
+ return false;
+
+ m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
+ i18n("Mobile Phone Export"),
+ i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
+ "Please wait...</center></qt>") );
+ m_progressDlg->setAllowCancel(true);
+ m_progressDlg->progressBar()->setProgress(0);
+ m_progressDlg->progressBar()->setCenterIndicator(true);
+ m_progressDlg->setModal(true);
+ m_progressDlg->setInitialSize(QSize(450,350));
+ m_progressDlg->show();
+ processEvents();
+
+ KProgress* progress = (KProgress*)m_progressDlg->progressBar();
+
+ KABC::AddresseeList::ConstIterator it;
+ QStringList failedList;
+
+ gn_error error;
+ bool deleteLabelInitialized = false;
+
+#if (QT_VERSION >= 0x030300)
+ m_progressDlg->setCursor( Qt::BusyCursor );
+#endif
+ QString errStr = businit();
+ m_progressDlg->unsetCursor();
+
+ if (!errStr.isEmpty()) {
+ KMessageBox::error(parentWidget(), errStr);
+ delete m_progressDlg;
+ return false;
+ }
+
+ GNOKII_DEBUG("GNOKII export filter started.\n");
+
+ gn_memory_type memtype = GN_MT_ME; // internal phone memory
+
+ int phone_count; // num entries in phone
+ bool overwrite_phone_entries = false;
+ int phone_entry_no, entries_written;
+ bool entry_empty;
+
+ // get number of entries in this phone memory
+ gn_memory_status memstat;
+ error = read_phone_memstat(memtype, &memstat);
+ if (error == GN_ERR_NONE) {
+ GNOKII_DEBUG("Writing to internal phone memory.\n");
+ } else {
+ memtype = GN_MT_SM; // try SIM card instead
+ error = read_phone_memstat(memtype, &memstat);
+ if (error != GN_ERR_NONE)
+ goto finish;
+ GNOKII_DEBUG("Writing to SIM card memory.\n");
+ }
+ phone_count = memstat.used;
+
+ if (memstat.free >= (int) list.count()) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
+ i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
+ "the current mobile phonebook or should they <b>replace</b> all "
+ "currently existing phonebook entries ?<br><br>"
+ "Please note, that in case you choose to replace the phonebook "
+ "entries, every contact in your phone will be deleted and only "
+ "the newly exported contacts will be available from inside your phone.</qt>"),
+ i18n("Export to Mobile Phone"),
+ KGuiItem(i18n("&Append to Current Phonebook")),
+ KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
+ overwrite_phone_entries = true;
+ }
+
+ progress->setTotalSteps(list.count());
+ entries_written = 0;
+ progress->setProgress(entries_written);
+ m_progressDlg->setButtonText(i18n("&Stop Export"));
+ m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
+ "of the Mobile Phone.<br><br>%3</qt>")
+ .arg(list.count())
+ .arg(gn_memory_type2str(memtype))
+ .arg(buildPhoneInfoString(memstat)) );
+
+ // Now run the loop...
+ phone_entry_no = 1;
+ for ( it = list.begin(); it != list.end(); ++it ) {
+ const KABC::Addressee *addr = &(*it);
+ if (addr->isEmpty())
+ continue;
+ // don't write back SIM-card entries !
+ if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
+ continue;
+
+ progress->setProgress(entries_written++);
+
+try_next_phone_entry:
+ this_filter->processEvents();
+ if (m_progressDlg->wasCancelled())
+ break;
+
+ // End of phone memory reached ?
+ if (phone_entry_no > (memstat.used + memstat.free))
+ break;
+
+ GNOKII_DEBUG(QString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
+ .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
+
+ error = GN_ERR_NONE;
+
+ // is this phone entry empty ?
+ entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
+ if (overwrite_phone_entries) {
+ // overwrite this phonebook entry ...
+ if (!entry_empty)
+ phone_count--;
+ error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
+ phone_entry_no++;
+ } else {
+ // add this phonebook entry if possible ...
+ if (entry_empty) {
+ error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
+ phone_entry_no++;
+ } else {
+ phone_entry_no++;
+ goto try_next_phone_entry;
+ }
+ }
+
+ if (error != GN_ERR_NONE)
+ failedList.append(addr->realName());
+
+ // break if we got an error on the first entry
+ if (error != GN_ERR_NONE && it==list.begin())
+ break;
+
+ } // for()
+
+ // if we wanted to overwrite all entries, make sure, that we also
+ // delete all remaining entries in the mobile phone.
+ while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
+ if (m_progressDlg->wasCancelled())
+ break;
+ if (!deleteLabelInitialized) {
+ m_progressDlg->setLabel(
+ i18n("<qt><center>"
+ "All selected contacts have been sucessfully copied to "
+ "the Mobile Phone.<br><br>"
+ "Please wait until all remaining orphaned contacts from "
+ "the Mobile Phone have been deleted.</center></qt>") );
+ m_progressDlg->setButtonText(i18n("&Stop Delete"));
+ deleteLabelInitialized = true;
+ progress->setTotalSteps(phone_count);
+ entries_written = 0;
+ progress->setProgress(entries_written);
+ this_filter->processEvents();
+ }
+ if (phone_entry_no > (memstat.used + memstat.free))
+ break;
+ entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
+ if (!entry_empty) {
+ error = xxport_phone_delete_entry(phone_entry_no, memtype);
+ phone_count--;
+ progress->setProgress(++entries_written);
+ this_filter->processEvents();
+ }
+ phone_entry_no++;
+ }
+
+finish:
+ m_progressDlg->setLabel(i18n("Export to phone finished."));
+ this_filter->processEvents();
+
+ GNOKII_DEBUG("GNOKII export filter finished.\n");
+
+ busterminate();
+ delete m_progressDlg;
+
+ if (!failedList.isEmpty()) {
+ GNOKII_DEBUG(QString("Failed to export: %1\n").arg(failedList.join(", ")));
+ KMessageBox::informationList(parentWidget(),
+ i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
+ "Possible Reasons for this problem could be:<br><ul>"
+ "<li>The contacts contain more information per entry than the phone can store.</li>"
+ "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
+ "<li>other storage size related problems.</li>"
+ "</ul>"
+ "To avoid those kind of problems in the future please reduce the amount of different "
+ "fields in the above contacts.</qt>"),
+ failedList,
+ i18n("Mobile Phone Export") );
+ }
+
+
+ return true;
+}
+
+/******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************/
+
+#elif defined(HAVE_GNOKII_H)
+
+#ifdef __GNUC__
+# warning "Please upgrade your gnokii installation to at least version 0.6.13"
+# warning "Older gnokii versions below 0.6.13 are not binary compatible and"
+# warning "prevents KDE users to upgrade gnokii to newer versions later."
+#endif
+
+/* OLD GNOKII LIBRARIES (< 0.6.13) */
+
+/* import */
+static char *lockfile = NULL;
+static char manufacturer[64], model[GN_MODEL_MAX_LENGTH+1],
+ revision[GN_REVISION_MAX_LENGTH+1], imei[GN_IMEI_MAX_LENGTH+1];
+static QString PhoneProductId;
+
+static struct gn_statemachine state;
+static gn_data data;
+
+static void busterminate(void)
+{
+ gn_sm_functions(GN_OP_Terminate, NULL, &state);
+ if (lockfile) gn_device_unlock(lockfile);
+}
+
+static QString businit(void)
+{
+ gn_error error;
+ char *aux;
+
+ GNOKII_DEBUG( "Using old gnokii version." );
+
+#if defined(LIBGNOKII_VERSION)
+ if (gn_cfg_read_default()<0)
+#else
+ static char *BinDir;
+ if (gn_cfg_read(&BinDir)<0)
+#endif
+ return i18n("Failed to initialize the gnokii library.");
+
+ if (!gn_cfg_phone_load("", &state))
+ return i18n("Gnokii is not yet configured.");
+
+ // uncomment to debug all gnokii communication on stderr.
+ // gn_log_debug_mask = GN_LOG_T_STDERR;
+
+ gn_data_clear(&data);
+
+ aux = gn_cfg_get(gn_cfg_info, "global", "use_locking");
+ // Defaults to 'no'
+ if (aux && !strcmp(aux, "yes")) {
+ lockfile = gn_device_lock(state.config.port_device);
+ if (lockfile == NULL) {
+ return i18n("Gnokii reports a 'Lock File Error'.\n "
+ "Please exit all other running instances of gnokii, check if you have "
+ "write permissions in the /var/lock directory and try again.");
+ }
+ }
+
+ // Initialise the code for the GSM interface.
+ int old_dcd = state.config.require_dcd; // work-around for older gnokii versions
+ state.config.require_dcd = false;
+ error = gn_gsm_initialise(&state);
+ GNOKII_CHECK_ERROR(error);
+ state.config.require_dcd = old_dcd;
+ if (error != GN_ERR_NONE) {
+ busterminate();
+ return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
+ "The returned error message was:<br><b>%1</b><br><br>"
+ "You might try to run \"gnokii --identify\" on the command line to "
+ "check any cable/transport issues and to verify if your gnokii "
+ "configuration is correct.</center></qt>")
+ .arg(gn_error_print(error));
+ }
+
+ // identify phone
+ gn_data_clear(&data);
+ data.manufacturer = manufacturer;
+ data.model = model;
+ data.revision = revision;
+ data.imei = imei;
+
+ QCString unknown(GN_TO(i18n("Unknown")));
+ qstrncpy(manufacturer, unknown, sizeof(manufacturer)-1);
+ qstrncpy(model, unknown, sizeof(model)-1);
+ qstrncpy(revision, unknown, sizeof(revision)-1);
+ qstrncpy(imei, unknown, sizeof(imei)-1);
+
+ if (m_progressDlg->wasCancelled())
+ return QString::null;
+ else
+ error = gn_sm_functions(GN_OP_Identify, &data, &state);
+ GNOKII_CHECK_ERROR(error);
+
+ GNOKII_DEBUG( QString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
+ .arg(manufacturer, model, revision, imei) );
+
+ PhoneProductId = QString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei);
+
+ return QString::null;
+}
+
+
+// get number of entries in this phone memory type (internal/SIM-card)
+static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
+{
+ gn_error error;
+
+ gn_data_clear(&data);
+ memset(memstat, 0, sizeof(*memstat));
+ memstat->memory_type = memtype;
+ data.memory_status = memstat;
+ error = gn_sm_functions(GN_OP_GetMemoryStatus, &data, &state);
+ GNOKII_CHECK_ERROR(error);
+ if (error != GN_ERR_NONE) {
+ switch (memtype) {
+ case GN_MT_SM:
+ // use at least 100 entries
+ memstat->used = 0;
+ memstat->free = 100;
+ break;
+ default:
+ case GN_MT_ME:
+ // Phone doesn't support ME (5110)
+ memstat->used = memstat->free = 0;
+ break;
+ }
+ }
+ GNOKII_DEBUG( QString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
+ .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
+ return error;
+}
+
+
+// read phone entry #index from memory #memtype
+static gn_error read_phone_entry( const int index, const gn_memory_type memtype, gn_phonebook_entry *entry )
+{
+ gn_error error;
+ entry->memory_type = memtype;
+ entry->location = index;
+ data.phonebook_entry = entry;
+ error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
+ GNOKII_CHECK_ERROR(error);
+ return error;
+}
+
+static bool phone_entry_empty( const int index, const gn_memory_type memtype )
+{
+ gn_error error;
+ gn_phonebook_entry entry;
+ entry.memory_type = memtype;
+ entry.location = index;
+ data.phonebook_entry = &entry;
+ error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
+ if (error == GN_ERR_EMPTYLOCATION)
+ return true;
+ GNOKII_CHECK_ERROR(error);
+ if (error == GN_ERR_NONE && entry.empty)
+ return true;
+ return false;
+}
+
+static QString buildPhoneInfoString( const gn_memory_status &memstat )
+{
+ QString format = QString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
+
+ return QString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
+ .arg(i18n("Mobile Phone information:"))
+ .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
+ .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
+ .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
+ .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
+ .arg(format.arg(i18n("Phonebook status"))
+ .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
+}
+
+static QString buildMemoryTypeString( gn_memory_type memtype )
+{
+ switch (memtype) {
+ case GN_MT_ME: return i18n("internal memory");
+ case GN_MT_SM: return i18n("SIM-card memory");
+ default: return i18n("unknown memory");
+ }
+}
+
+// read and evaluate all phone entries
+static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
+ KABC::AddresseeList *addrList )
+{
+ gn_error error;
+
+ if (m_progressDlg->wasCancelled())
+ return GN_ERR_NONE;
+
+ KProgress* progress = (KProgress*)m_progressDlg->progressBar();
+
+ progress->setProgress(0);
+ this_filter->processEvents();
+
+ // get number of entries in this phone memory type (internal/SIM-card)
+ gn_memory_status memstat;
+ error = read_phone_memstat(memtype, &memstat);
+
+ gn_phonebook_entry entry;
+ QStringList addrlist;
+ KABC::Address *addr;
+ QString s, country;
+
+ progress->setTotalSteps(memstat.used);
+ m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
+ .arg(memstat.used)
+ .arg(buildMemoryTypeString(memtype))
+ .arg(buildPhoneInfoString(memstat)) );
+
+ int num_read = 0;
+
+ for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
+ error = read_phone_entry( i, memtype, &entry );
+
+ progress->setProgress(num_read);
+ this_filter->processEvents();
+
+ if (error == GN_ERR_EMPTYLOCATION)
+ continue;
+ if (error == GN_ERR_INVALIDLOCATION)
+ break;
+ if (error == GN_ERR_INVALIDMEMORYTYPE)
+ break;
+ if (error == GN_ERR_NONE) {
+ GNOKII_DEBUG(QString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i).arg(GN_FROM(entry.name))
+ .arg(GN_FROM(entry.number)).arg(entry.location).arg(entry.caller_group).arg(entry.subentries_count));
+ KABC::Addressee *a = new KABC::Addressee();
+
+ // try to split Name into FamilyName and GivenName
+ s = GN_FROM(entry.name).simplifyWhiteSpace();
+ a->setFormattedName(s); // set formatted name as in Phone
+ if (s.find(',') == -1) {
+ // assumed format: "givenname [... familyname]"
+ addrlist = QStringList::split(' ', s);
+ if (addrlist.count() == 1) {
+ // only one string -> put it in the GivenName
+ a->setGivenName(s);
+ } else {
+ // multiple strings -> split them.
+ a->setFamilyName(addrlist.last().simplifyWhiteSpace());
+ addrlist.remove(addrlist.last());
+ a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
+ }
+ } else {
+ // assumed format: "familyname, ... givenname"
+ addrlist = QStringList::split(',', s);
+ a->setFamilyName(addrlist.first().simplifyWhiteSpace());
+ addrlist.remove(addrlist.first());
+ a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
+ }
+
+ a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(entry.caller_group));
+ a->insertCustom(APP, "X_GSM_STORE_AT", QString("%1%2").arg(memtypestr).arg(entry.location));
+
+ // set ProductId
+ a->setProductId(PhoneProductId);
+
+ // evaluate timestamp (ignore timezone)
+ QDateTime datetime;
+ if (entry.date.year<1998)
+ datetime = QDateTime::currentDateTime();
+ else
+ datetime = QDateTime( QDate(entry.date.year, entry.date.month, entry.date.day),
+ QTime(entry.date.hour, entry.date.minute, entry.date.second) );
+ GNOKII_DEBUG(QString(" date=%1\n").arg(datetime.toString()));
+ a->setRevision(datetime);
+
+ if (!entry.subentries_count)
+ a->insertPhoneNumber(KABC::PhoneNumber(entry.number, KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref));
+
+ /* scan sub-entries */
+ if (entry.subentries_count)
+ for (int n=0; n<entry.subentries_count; n++) {
+ QString s = GN_FROM(entry.subentries[n].data.number).simplifyWhiteSpace();
+ GNOKII_DEBUG(QString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
+ .arg(n).arg(entry.subentries[n].entry_type)
+ .arg(entry.subentries[n].number_type).arg(s));
+ if (s.isEmpty())
+ continue;
+ switch(entry.subentries[n].entry_type) {
+ case GN_PHONEBOOK_ENTRY_Name:
+ a->setName(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Email:
+ a->insertEmail(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Postal:
+ addrlist = QStringList::split(';', s, true);
+ addr = new KABC::Address(KABC::Address::Work);
+ if (addrlist.count() <= 1) {
+ addrlist = QStringList::split(',', s, true);
+ if (addrlist.count() > 1 ) {
+ // assumed format: "Locality, ZIP, Country"
+ addr->setLocality(addrlist[0]);
+ addr->setPostalCode(addrlist[1]);
+ if (!addrlist[2].isEmpty())
+ addr->setCountry(i18n(GN_TO(addrlist[2])));
+ } else {
+ // no idea about the format, just store it.
+ addr->setLocality(s);
+ }
+ } else {
+ // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
+ addr->setPostOfficeBox(addrlist[0]);
+ addr->setExtended(addrlist[1]);
+ addr->setStreet(addrlist[2]);
+ addr->setLocality(addrlist[3]);
+ addr->setRegion(addrlist[4]);
+ addr->setPostalCode(addrlist[5]);
+ country = addrlist[6];
+ if (!country.isEmpty())
+ addr->setCountry(i18n(GN_TO(country)));
+ }
+ a->insertAddress(*addr);
+ delete addr;
+ break;
+ case GN_PHONEBOOK_ENTRY_Note:
+ if (!a->note().isEmpty())
+ s = "\n" + s;
+ a->setNote(a->note()+s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Number:
+ enum KABC::PhoneNumber::Types phonetype;
+ switch (entry.subentries[n].number_type) {
+ case GN_PHONEBOOK_NUMBER_Mobile: phonetype = KABC::PhoneNumber::Cell; break;
+ case GN_PHONEBOOK_NUMBER_Fax: phonetype = KABC::PhoneNumber::Fax; break;
+ case GN_PHONEBOOK_NUMBER_General:
+ case GN_PHONEBOOK_NUMBER_Work: phonetype = KABC::PhoneNumber::Work; break;
+ default:
+ case GN_PHONEBOOK_NUMBER_Home: phonetype = KABC::PhoneNumber::Home; break;
+ }
+ //if (s == entry.number)
+ // type = (KABC::PhoneNumber::Types) (phonetype | KABC::PhoneNumber::Pref);
+ a->insertPhoneNumber(KABC::PhoneNumber(s, phonetype));
+ break;
+ case GN_PHONEBOOK_ENTRY_URL:
+ a->setUrl(s);
+ break;
+ case GN_PHONEBOOK_ENTRY_Group:
+ a->insertCategory(s);
+ break;
+ default:
+ GNOKII_DEBUG(QString(" Not handled id=%1, entry=%2\n")
+ .arg(entry.subentries[n].entry_type).arg(s));
+ break;
+ } // switch()
+ } // if(subentry)
+
+ // add only if entry was valid
+ if (strlen(entry.name) || strlen(entry.number) || entry.subentries_count)
+ addrList->append(*a);
+
+ // did we read all valid phonebook-entries ?
+ num_read++;
+ delete a;
+ if (num_read >= memstat.used)
+ break; // yes, all were read
+ else
+ continue; // no, we are still missing some.
+ }
+ GNOKII_CHECK_ERROR(error);
+ }
+
+ return GN_ERR_NONE;
+}
+
+
+// export to phone
+
+static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
+ const KABC::Addressee *addr)
+{
+ gn_phonebook_entry entry;
+ QString s;
+
+ memset(&entry, 0, sizeof(entry));
+ strncpy(entry.name, GN_TO(addr->realName()), sizeof(entry.name)-1);
+ s = addr->phoneNumber(KABC::PhoneNumber::Pref).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Work).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Home).number();
+ if (s.isEmpty())
+ s = addr->phoneNumber(KABC::PhoneNumber::Cell).number();
+ if (s.isEmpty() && addr->phoneNumbers().count()>0)
+ s = (*addr->phoneNumbers().at(0)).number();
+ s = makeValidPhone(s);
+ strncpy(entry.number, s.ascii(), sizeof(entry.number)-1);
+ entry.memory_type = memtype;
+ QString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
+ if (cg.isEmpty())
+ entry.caller_group = 5; // default group
+ else
+ entry.caller_group = cg.toInt();
+ entry.location = phone_location;
+
+ // set date/revision
+ QDateTime datetime = addr->revision();
+ QDate date(datetime.date());
+ QTime time(datetime.time());
+ entry.date.year = date.year();
+ entry.date.month = date.month();
+ entry.date.day = date.day();
+ entry.date.hour = time.hour();
+ entry.date.minute = time.minute();
+ entry.date.second = time.second();
+
+ GNOKII_DEBUG(QString("Write #%1: name=%2, number=%3\n").arg(phone_location)
+ .arg(GN_FROM(entry.name)).arg(GN_FROM(entry.number)));
+
+ const KABC::Address homeAddr = addr->address(KABC::Address::Home);
+ const KABC::Address workAddr = addr->address(KABC::Address::Work);
+
+ entry.subentries_count = 0;
+ gn_phonebook_subentry *subentry = &entry.subentries[0];
+ // add all phone numbers
+ const KABC::PhoneNumber::List phoneList = addr->phoneNumbers();
+ KABC::PhoneNumber::List::ConstIterator it;
+ for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
+ const KABC::PhoneNumber *phonenumber = &(*it);
+ s = phonenumber->number();
+ if (s.isEmpty()) continue;
+ subentry->entry_type = GN_PHONEBOOK_ENTRY_Number;
+ gn_phonebook_number_type type;
+ int pn_type = phonenumber->type();
+ if ((pn_type & KABC::PhoneNumber::Cell))
+ type = GN_PHONEBOOK_NUMBER_Mobile;
+ else if ((pn_type & KABC::PhoneNumber::Fax))
+ type = GN_PHONEBOOK_NUMBER_Fax;
+ else if ((pn_type & KABC::PhoneNumber::Home))
+ type = GN_PHONEBOOK_NUMBER_Home;
+ else if ((pn_type & KABC::PhoneNumber::Work))
+ type = GN_PHONEBOOK_NUMBER_Work;
+ else type = GN_PHONEBOOK_NUMBER_General;
+ subentry->number_type = type;
+ strncpy(subentry->data.number, makeValidPhone(s).ascii(), sizeof(subentry->data.number)-1);
+ subentry->id = phone_location<<8+entry.subentries_count;
+ entry.subentries_count++;
+ subentry++;
+ if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
+ break; // Phonebook full
+ }
+ // add URL
+ s = addr->url().prettyURL();
+ if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
+ subentry->entry_type = GN_PHONEBOOK_ENTRY_URL;
+ strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
+ entry.subentries_count++;
+ subentry++;
+ }
+ // add E-Mails
+ QStringList emails = addr->emails();
+ for (unsigned int n=0; n<emails.count(); n++) {
+ if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
+ break; // Phonebook full
+ s = emails[n].simplifyWhiteSpace();
+ if (s.isEmpty()) continue;
+ // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
+ if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
+ GNOKII_DEBUG(QString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
+ .arg(s));
+ continue;
+ }
+ subentry->entry_type = GN_PHONEBOOK_ENTRY_Email;
+ strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
+ entry.subentries_count++;
+ subentry++;
+ }
+ // add Adresses
+ const KABC::Address::List addresses = addr->addresses();
+ KABC::Address::List::ConstIterator it2;
+ for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
+ if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
+ break; // Phonebook full
+ const KABC::Address *Addr = &(*it2);
+ if (Addr->isEmpty()) continue;
+ subentry->entry_type = GN_PHONEBOOK_ENTRY_Postal;
+ QStringList a;
+ QChar sem(';');
+ QString sem_repl(QString::fromLatin1(","));
+ a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
+ a.append( Addr->extended() .replace( sem, sem_repl ) );
+ a.append( Addr->street() .replace( sem, sem_repl ) );
+ a.append( Addr->locality() .replace( sem, sem_repl ) );
+ a.append( Addr->region() .replace( sem, sem_repl ) );
+ a.append( Addr->postalCode() .replace( sem, sem_repl ) );
+ a.append( Addr->country() .replace( sem, sem_repl ) );
+ s = a.join(sem);
+ strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
+ entry.subentries_count++;
+ subentry++;
+ }
+ // add Note
+ s = addr->note().simplifyWhiteSpace();
+ if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
+ subentry->entry_type = GN_PHONEBOOK_ENTRY_Note;
+ strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
+ entry.subentries_count++;
+ subentry++;
+ }
+
+ // debug output
+ for (int st=0; st<entry.subentries_count; st++) {
+ gn_phonebook_subentry *subentry = &entry.subentries[st];
+ GNOKII_DEBUG(QString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
+ .arg(st).arg(subentry->entry_type)
+ .arg(subentry->number_type).arg(GN_FROM(subentry->data.number)));
+ }
+
+ data.phonebook_entry = &entry;
+ gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
+ GNOKII_CHECK_ERROR(error);
+
+ return error;
+}
+
+
+static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
+{
+ gn_phonebook_entry entry;
+ memset(&entry, 0, sizeof(entry));
+ entry.empty = 1;
+ entry.memory_type = memtype;
+ entry.location = phone_location;
+ data.phonebook_entry = &entry;
+ GNOKII_DEBUG(QString("Deleting entry %1\n").arg(phone_location));
+ gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
+ GNOKII_CHECK_ERROR(error);
+ return error;
+}
+
+KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
+{
+ KABC::AddresseeList addrList;
+
+ if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
+ i18n("<qt>Please connect your Mobile Phone to your computer and press "
+ "<b>Continue</b> to start importing the personal contacts.<br><br>"
+ "Please note that if your Mobile Phone is not properly connected "
+ "the following detection phase might take up to two minutes, during which "
+ "KAddressbook will behave unresponsively.</qt>") ))
+ return addrList;
+
+ m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
+ i18n("Mobile Phone Import"),
+ i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
+ "Please wait...</center></qt>") );
+ m_progressDlg->setAllowCancel(true);
+ m_progressDlg->progressBar()->setProgress(0);
+ m_progressDlg->progressBar()->setCenterIndicator(true);
+ m_progressDlg->setModal(true);
+ m_progressDlg->setInitialSize(QSize(450,350));
+ m_progressDlg->show();
+ processEvents();
+
+#if (QT_VERSION >= 0x030300)
+ m_progressDlg->setCursor( Qt::BusyCursor );
+#endif
+ QString errStr = businit();
+ m_progressDlg->unsetCursor();
+
+ if (!errStr.isEmpty()) {
+ KMessageBox::error(parentWidget(), errStr);
+ delete m_progressDlg;
+ return addrList;
+ }
+
+ GNOKII_DEBUG("GNOKII import filter started.\n");
+ m_progressDlg->setButtonText(i18n("&Stop Import"));
+
+ read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
+ read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
+
+ GNOKII_DEBUG("GNOKII import filter finished.\n");
+
+ busterminate();
+ delete m_progressDlg;
+
+ return addrList;
+}
+
+
+bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
+{
+ if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
+ i18n("<qt>Please connect your Mobile Phone to your computer and press "
+ "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
+ "Please note that if your Mobile Phone is not properly connected "
+ "the following detection phase might take up to two minutes, during which "
+ "KAddressbook will behave unresponsively.</qt>") ))
+ return false;
+
+ m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
+ i18n("Mobile Phone Export"),
+ i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
+ "Please wait...</center></qt>") );
+ m_progressDlg->setAllowCancel(true);
+ m_progressDlg->progressBar()->setProgress(0);
+ m_progressDlg->progressBar()->setCenterIndicator(true);
+ m_progressDlg->setModal(true);
+ m_progressDlg->setInitialSize(QSize(450,350));
+ m_progressDlg->show();
+ processEvents();
+
+ KProgress* progress = (KProgress*)m_progressDlg->progressBar();
+
+ KABC::AddresseeList::ConstIterator it;
+ QStringList failedList;
+
+ gn_error error;
+ bool deleteLabelInitialized = false;
+
+#if (QT_VERSION >= 0x030300)
+ m_progressDlg->setCursor( Qt::BusyCursor );
+#endif
+ QString errStr = businit();
+ m_progressDlg->unsetCursor();
+
+ if (!errStr.isEmpty()) {
+ KMessageBox::error(parentWidget(), errStr);
+ delete m_progressDlg;
+ return false;
+ }
+
+ GNOKII_DEBUG("GNOKII export filter started.\n");
+
+ gn_memory_type memtype = GN_MT_ME; // internal phone memory
+
+ int phone_count; // num entries in phone
+ bool overwrite_phone_entries = false;
+ int phone_entry_no, entries_written;
+ bool entry_empty;
+
+ // get number of entries in this phone memory
+ gn_memory_status memstat;
+ error = read_phone_memstat(memtype, &memstat);
+ if (error == GN_ERR_NONE) {
+ GNOKII_DEBUG("Writing to internal phone memory.\n");
+ } else {
+ memtype = GN_MT_SM; // try SIM card instead
+ error = read_phone_memstat(memtype, &memstat);
+ if (error != GN_ERR_NONE)
+ goto finish;
+ GNOKII_DEBUG("Writing to SIM card memory.\n");
+ }
+ phone_count = memstat.used;
+
+ if (memstat.free >= (int) list.count()) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
+ i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
+ "the current mobile phonebook or should they <b>replace</b> all "
+ "currently existing phonebook entries ?<br><br>"
+ "Please note, that in case you choose to replace the phonebook "
+ "entries, every contact in your phone will be deleted and only "
+ "the newly exported contacts will be available from inside your phone.</qt>"),
+ i18n("Export to Mobile Phone"),
+ KGuiItem(i18n("&Append to Current Phonebook")),
+ KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
+ overwrite_phone_entries = true;
+ }
+
+ progress->setTotalSteps(list.count());
+ entries_written = 0;
+ progress->setProgress(entries_written);
+ m_progressDlg->setButtonText(i18n("&Stop Export"));
+ m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
+ "of the Mobile Phone.<br><br>%3</qt>")
+ .arg(list.count())
+ .arg(buildMemoryTypeString(memtype))
+ .arg(buildPhoneInfoString(memstat)) );
+
+ // Now run the loop...
+ phone_entry_no = 1;
+ for ( it = list.begin(); it != list.end(); ++it ) {
+ const KABC::Addressee *addr = &(*it);
+ if (addr->isEmpty())
+ continue;
+ // don't write back SIM-card entries !
+ if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
+ continue;
+
+ progress->setProgress(entries_written++);
+
+try_next_phone_entry:
+ this_filter->processEvents();
+ if (m_progressDlg->wasCancelled())
+ break;
+
+ // End of phone memory reached ?
+ if (phone_entry_no > (memstat.used + memstat.free))
+ break;
+
+ GNOKII_DEBUG(QString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
+ .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
+
+ error = GN_ERR_NONE;
+
+ // is this phone entry empty ?
+ entry_empty = phone_entry_empty(phone_entry_no, memtype);
+ if (overwrite_phone_entries) {
+ // overwrite this phonebook entry ...
+ if (!entry_empty)
+ phone_count--;
+ error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
+ phone_entry_no++;
+ } else {
+ // add this phonebook entry if possible ...
+ if (entry_empty) {
+ error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
+ phone_entry_no++;
+ } else {
+ phone_entry_no++;
+ goto try_next_phone_entry;
+ }
+ }
+
+ if (error != GN_ERR_NONE)
+ failedList.append(addr->realName());
+
+ // break if we got an error on the first entry
+ if (error != GN_ERR_NONE && it==list.begin())
+ break;
+
+ } // for()
+
+ // if we wanted to overwrite all entries, make sure, that we also
+ // delete all remaining entries in the mobile phone.
+ while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
+ if (m_progressDlg->wasCancelled())
+ break;
+ if (!deleteLabelInitialized) {
+ m_progressDlg->setLabel(
+ i18n("<qt><center>"
+ "All selected contacts have been sucessfully copied to "
+ "the Mobile Phone.<br><br>"
+ "Please wait until all remaining orphaned contacts from "
+ "the Mobile Phone have been deleted.</center></qt>") );
+ m_progressDlg->setButtonText(i18n("&Stop Delete"));
+ deleteLabelInitialized = true;
+ progress->setTotalSteps(phone_count);
+ entries_written = 0;
+ progress->setProgress(entries_written);
+ this_filter->processEvents();
+ }
+ if (phone_entry_no > (memstat.used + memstat.free))
+ break;
+ entry_empty = phone_entry_empty(phone_entry_no, memtype);
+ if (!entry_empty) {
+ error = xxport_phone_delete_entry(phone_entry_no, memtype);
+ phone_count--;
+ progress->setProgress(++entries_written);
+ this_filter->processEvents();
+ }
+ phone_entry_no++;
+ }
+
+finish:
+ m_progressDlg->setLabel(i18n("Export to phone finished."));
+ this_filter->processEvents();
+
+ GNOKII_DEBUG("GNOKII export filter finished.\n");
+
+ busterminate();
+ delete m_progressDlg;
+
+ if (!failedList.isEmpty()) {
+ GNOKII_DEBUG(QString("Failed to export: %1\n").arg(failedList.join(", ")));
+ KMessageBox::informationList(parentWidget(),
+ i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
+ "Possible Reasons for this problem could be:<br><ul>"
+ "<li>The contacts contain more information per entry than the phone can store.</li>"
+ "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
+ "<li>other storage size related problems.</li>"
+ "</ul>"
+ "To avoid those kind of problems in the future please reduce the amount of different "
+ "fields in the above contacts.</qt>"),
+ failedList,
+ i18n("Mobile Phone Export") );
+ }
+
+ return true;
+}
+
+
+/******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************/
+
+#else /* no gnokii installed */
+
+KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
+{
+ KABC::AddresseeList addrList;
+ KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
+ "Please ask your distributor to add gnokii at compile time."));
+ return addrList;
+}
+
+bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
+{
+ Q_UNUSED(list);
+ KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
+ "Please ask your distributor to add gnokii at compile time."));
+ return true;
+}
+
+#endif /* END OF GNOKII LIB SWITCH */
+
+/******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************
+ ******************************************************************************/
+
+#include "gnokii_xxport.moc"
+
+/* vim: set sts=4 ts=4 sw=4: */