kaddressbook

gnokii_xxport.cpp

00001 /*
00002     This file is part of KAddressbook.
00003     Copyright (c) 2003-2006 Helge Deller <deller@kde.org>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License version 2 as
00007     published by the Free Software Foundation.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 
00018     As a special exception, permission is given to link this program
00019     with any edition of Qt, and distribute the resulting executable,
00020     without including the source code for Qt in the source distribution.
00021 */
00022 /*
00023     Description:
00024     This filter allows you to import and export the KDE addressbook entries
00025     to/from a mobile phone, which is accessible via gnokii.
00026     Gnokii homepage: http://www.gnokii.org
00027 
00028     TODO:
00029     - create a log file and give user possibility to see it afterwards
00030     - handle callergroup value (Friend, VIP, Family, ...) better
00031 */
00032 
00033 #include "config.h"
00034 
00035 #ifdef HAVE_GNOKII_H
00036 extern "C" {
00037 #include <gnokii.h>
00038 }
00039 #else
00040 #ifdef __GNUC__
00041 # warning "Please install gnokii (http://www.gnokii.org) development headers and libraries !"
00042 # warning "Please use at least version 0.6.13 or later of gnokii."
00043 #endif
00044 #endif
00045 
00046 #include <qcursor.h>
00047 
00048 #include <kdebug.h>
00049 #include <klocale.h>
00050 #include <kmessagebox.h>
00051 #include <kprogress.h>
00052 #include <kguiitem.h>
00053 
00054 #include "gnokii_xxport.h"
00055 
00056 #define APP "GNOKII_XXPORT"
00057 
00058 #if 1 // !defined(NDEBUG)
00059  #define GNOKII_DEBUG(x)    do { kdWarning() << (x); } while (0)
00060 #else
00061  #define GNOKII_DEBUG(x)    do { } while (0)
00062 #endif
00063 #define GNOKII_CHECK_ERROR(error) \
00064     do { \
00065         if (error) \
00066             kdError() << QString("ERROR %1: %2\n").arg(error).arg(gn_error_print(error));\
00067     } while (0)
00068 
00069 // Locale conversion routines:
00070 // Gnokii uses the local 8 Bit encoding (based on LC_ALL), kaddressbook uses Unicode
00071 #define GN_FROM(x)  QString::fromLocal8Bit(x)
00072 #define GN_TO(x)    (x).local8Bit()
00073 
00074 // static variables for GUI updates
00075 static GNOKIIXXPort *this_filter;
00076 static KProgressDialog *m_progressDlg;
00077 
00078 K_EXPORT_KADDRESSBOOK_XXFILTER( libkaddrbk_gnokii_xxport, GNOKIIXXPort )
00079 
00080 GNOKIIXXPort::GNOKIIXXPort( KABC::AddressBook *ab, QWidget *parent, const char *name )
00081   : KAB::XXPort( ab, parent, name )
00082 {
00083     this_filter = this;
00084     m_progressDlg = NULL;
00085     createImportAction( i18n( "Import From Mobile Phone..." ) );
00086     createExportAction( i18n( "Export to Mobile Phone..." ) );
00087 }
00088 
00089 
00090 #ifdef HAVE_GNOKII_H
00091 static QString makeValidPhone( const QString &number )
00092 {
00093     // allowed chars: 0-9, *, #, p, w, +
00094     QString num = number.simplifyWhiteSpace();
00095     QString allowed("0123456789*+#pw");
00096     for (unsigned int i=num.length(); i>=1; i--)
00097         if (allowed.find(num[i-1])==-1)
00098             num.remove(i-1,1);
00099     if (num.isEmpty())
00100         num = "0";
00101     return num;
00102 }
00103 #endif
00104 
00105 /******************************************************************************
00106  ******************************************************************************
00107  ******************************************************************************
00108  ******************************************************************************
00109  ******************************************************************************/
00110 
00111 #if defined(HAVE_GNOKII_H) && defined(LIBGNOKII_VERSION_MAJOR) && (LIBGNOKII_VERSION_MAJOR >= 3)
00112 
00113 /* NEW GNOKII LIBRARIES (>= 0.6.13) */
00114 
00115 static const char *manufacturer, *model, *revision, *imei;
00116 static struct gn_statemachine *state;
00117 
00118 static void busterminate(void)
00119 {
00120     gn_lib_phone_close(state);
00121     gn_lib_phoneprofile_free(&state);
00122     gn_lib_library_free();
00123 }
00124 
00125 static QString businit(void)
00126 {
00127     GNOKII_DEBUG( "Using new gnokii version." );
00128 
00129     GNOKII_DEBUG( QString("Compiled with libgnokii version 0x%1\n").arg(QString::number(LIBGNOKII_VERSION,16)) );
00130     GNOKII_DEBUG( QString("Using libgnokii runtime version 0x%1\n").arg(QString::number(gn_lib_version(),16)) );
00131 
00132     gn_error error = gn_lib_phoneprofile_load(NULL, &state);
00133     if (error) 
00134         return i18n("Failed to initialize the gnokii library.");
00135 
00136     error = gn_lib_phone_open( state );
00137     GNOKII_CHECK_ERROR(error);
00138     if (error != GN_ERR_NONE) {
00139         busterminate();
00140         return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
00141             "The returned error message was:<br><b>%1</b><br><br>"
00142             "You might try to run \"gnokii --identify\" on the command line to "
00143             "check any cable/transport issues and to verify if your gnokii "
00144             "configuration is correct.</center></qt>")
00145             .arg(gn_error_print(error));
00146     }
00147 
00148     // identify phone
00149     manufacturer = gn_lib_get_phone_manufacturer(state);
00150     model        = gn_lib_get_phone_model(state);
00151     revision     = gn_lib_get_phone_revision(state);
00152     imei         = gn_lib_get_phone_imei(state);
00153 
00154     GNOKII_DEBUG( QString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
00155                 .arg(manufacturer, model, revision, imei) );
00156 
00157     return QString::null;
00158 }
00159 
00160 
00161 // get number of entries in this phone memory type (internal/SIM-card)
00162 static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
00163 {
00164     gn_error error;
00165 
00166     error = gn_lib_addressbook_memstat(state, memtype, &memstat->used, &memstat->free);
00167 
00168     GNOKII_DEBUG( QString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
00169                     .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
00170     return error;
00171 }
00172 
00173 
00174 static QString buildPhoneInfoString( const gn_memory_status &memstat )
00175 {
00176     QString format = QString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
00177 
00178     return QString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
00179         .arg(i18n("Mobile Phone information:"))
00180         .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
00181         .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
00182         .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
00183         .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
00184         .arg(format.arg(i18n("Phonebook status"))
00185                .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
00186 }
00187 
00188 // read and evaluate all phone entries
00189 static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
00190             KABC::AddresseeList *addrList )
00191 {
00192   gn_error error;
00193 
00194   if (m_progressDlg->wasCancelled())
00195     return GN_ERR_NONE;
00196 
00197   KProgress* progress = (KProgress*)m_progressDlg->progressBar();
00198 
00199   progress->setProgress(0);
00200   this_filter->processEvents();
00201 
00202   // get number of entries in this phone memory type (internal/SIM-card)
00203   gn_memory_status memstat;
00204   error = read_phone_memstat(memtype, &memstat);
00205 
00206   QStringList addrlist;
00207   KABC::Address *addr;
00208   QString s, country;
00209 
00210   progress->setTotalSteps(memstat.used);
00211   m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
00212         .arg(memstat.used)
00213         .arg(gn_memory_type2str(memtype))
00214         .arg(buildPhoneInfoString(memstat)) );
00215 
00216   int num_read = 0;
00217 
00218   for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
00219     error = gn_lib_phonebook_read_entry(state, memtype, i);
00220     GNOKII_CHECK_ERROR(error);
00221 
00222     progress->setProgress(num_read);
00223     this_filter->processEvents();
00224 
00225     if (error == GN_ERR_EMPTYLOCATION)
00226         continue;
00227     if (error == GN_ERR_INVALIDLOCATION)
00228         break;
00229     if (error == GN_ERR_INVALIDMEMORYTYPE)
00230         break;
00231     if (error == GN_ERR_NONE) {
00232         const int subentries_count = gn_lib_get_pb_num_subentries(state);
00233         const char *name = gn_lib_get_pb_name(state);
00234         const char *number = gn_lib_get_pb_number(state);
00235 
00236         GNOKII_DEBUG(QString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i)
00237             .arg(GN_FROM(name)).arg(GN_FROM(number))
00238             .arg(gn_lib_get_pb_location(state)).arg(gn_lib_get_pb_caller_group(state))
00239             .arg(subentries_count));
00240         KABC::Addressee *a = new KABC::Addressee();
00241 
00242         // try to split Name into FamilyName and GivenName
00243         s = GN_FROM(name).simplifyWhiteSpace();
00244         a->setFormattedName(s); // set formatted name as in Phone
00245         if (s.find(',') == -1) {
00246           // assumed format: "givenname [... familyname]"
00247           addrlist = QStringList::split(' ', s);
00248           if (addrlist.count() == 1) {
00249             // only one string -> put it in the GivenName
00250             a->setGivenName(s);
00251           } else {
00252             // multiple strings -> split them.
00253             a->setFamilyName(addrlist.last().simplifyWhiteSpace());
00254             addrlist.remove(addrlist.last());
00255             a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
00256           }
00257         } else {
00258           // assumed format: "familyname, ... givenname"
00259           addrlist = QStringList::split(',', s);
00260           a->setFamilyName(addrlist.first().simplifyWhiteSpace());
00261           addrlist.remove(addrlist.first());
00262           a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
00263         }
00264 
00265         a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(gn_lib_get_pb_caller_group(state)));
00266         a->insertCustom(APP, "X_GSM_STORE_AT", QString("%1%2").arg(memtypestr).arg(gn_lib_get_pb_location(state)));
00267 
00268         // set ProductId
00269         a->setProductId(QString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei));
00270 
00271         // evaluate timestamp (ignore timezone)
00272         QDateTime datetime;
00273         gn_timestamp ts = gn_lib_get_pb_date(state);
00274         if (ts.year<1998)
00275             datetime = QDateTime::currentDateTime();
00276         else
00277             datetime = QDateTime( QDate(ts.year, ts.month, ts.day),
00278                               QTime(ts.hour, ts.minute, ts.second) );
00279         GNOKII_DEBUG(QString(" date=%1\n").arg(datetime.toString()));
00280         a->setRevision(datetime);
00281 
00282         if (!subentries_count)
00283           a->insertPhoneNumber(KABC::PhoneNumber(number,
00284                 KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref));
00285 
00286         /* scan sub-entries */
00287         if (subentries_count)
00288          for (int n=0; n<subentries_count; n++) {
00289           gn_phonebook_entry_type entry_type;
00290           gn_phonebook_number_type number_type;
00291           const char *number;
00292 
00293           error = gn_lib_get_pb_subentry(state, n, &entry_type, &number_type, &number);
00294           GNOKII_CHECK_ERROR(error);
00295 
00296           QString s = GN_FROM(number).simplifyWhiteSpace();
00297           GNOKII_DEBUG(QString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
00298                 .arg(n).arg(entry_type).arg(number_type).arg(s));
00299           if (s.isEmpty())
00300             continue;
00301           switch(entry_type) {
00302            case GN_PHONEBOOK_ENTRY_Name:
00303             a->setName(s);
00304             break;
00305            case GN_PHONEBOOK_ENTRY_Email:
00306             a->insertEmail(s);
00307             break;
00308            case GN_PHONEBOOK_ENTRY_Postal:
00309             addrlist = QStringList::split(';', s, true);
00310             addr = new KABC::Address(KABC::Address::Work);
00311             if (addrlist.count() <= 1) {
00312                 addrlist = QStringList::split(',', s, true);
00313                 if (addrlist.count() > 1 ) {
00314                     // assumed format: "Locality, ZIP, Country"
00315                     addr->setLocality(addrlist[0]);
00316                     addr->setPostalCode(addrlist[1]);
00317                     if (!addrlist[2].isEmpty())
00318                         addr->setCountry(i18n(GN_TO(addrlist[2])));
00319                 } else {
00320                     // no idea about the format, just store it.
00321                     addr->setLocality(s);
00322                 }
00323             } else {
00324                 // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
00325                 addr->setPostOfficeBox(addrlist[0]);
00326                 addr->setExtended(addrlist[1]);
00327                 addr->setStreet(addrlist[2]);
00328                 addr->setLocality(addrlist[3]);
00329                 addr->setRegion(addrlist[4]);
00330                 addr->setPostalCode(addrlist[5]);
00331                 country = addrlist[6];
00332                 if (!country.isEmpty())
00333                     addr->setCountry(i18n(GN_TO(country)));
00334             }
00335             a->insertAddress(*addr);
00336             delete addr;
00337             break;
00338            case GN_PHONEBOOK_ENTRY_Note:
00339             if (!a->note().isEmpty())
00340                 s = "\n" + s;
00341             a->setNote(a->note()+s);
00342             break;
00343            case GN_PHONEBOOK_ENTRY_Number:
00344             enum KABC::PhoneNumber::Types phonetype;
00345             switch (number_type) {
00346              case GN_PHONEBOOK_NUMBER_Mobile: phonetype = KABC::PhoneNumber::Cell; break;
00347              case GN_PHONEBOOK_NUMBER_Fax:    phonetype = KABC::PhoneNumber::Fax;  break;
00348              case GN_PHONEBOOK_NUMBER_General:
00349              case GN_PHONEBOOK_NUMBER_Work:   phonetype = KABC::PhoneNumber::Work; break;
00350              default:
00351              case GN_PHONEBOOK_NUMBER_Home:   phonetype = KABC::PhoneNumber::Home; break;
00352             }
00353             //if (s == entry.number)
00354             //  type = (KABC::PhoneNumber::Types) (phonetype | KABC::PhoneNumber::Pref);
00355             a->insertPhoneNumber(KABC::PhoneNumber(s, phonetype));
00356             break;
00357            case GN_PHONEBOOK_ENTRY_URL:
00358             a->setUrl(s);
00359             break;
00360            case GN_PHONEBOOK_ENTRY_Group:
00361             a->insertCategory(s);
00362             break;
00363            default:
00364             GNOKII_DEBUG(QString(" Not handled id=%1, entry=%2\n")
00365                 .arg(entry_type).arg(s));
00366             break;
00367           } // switch()
00368         } // if(subentry)
00369 
00370         // add only if entry was valid
00371         if (strlen(name) || strlen(number) || subentries_count)
00372             addrList->append(*a);
00373 
00374         // did we read all valid phonebook-entries ?
00375         num_read++;
00376         delete a;
00377         if (num_read >= memstat.used)
00378             break;  // yes, all were read
00379         else
00380             continue; // no, we are still missing some.
00381     }
00382     GNOKII_CHECK_ERROR(error);
00383   }
00384 
00385   return GN_ERR_NONE;
00386 }
00387 
00388 // export to phone
00389 
00390 static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
00391             const KABC::Addressee *addr)
00392 {
00393     QString s;
00394 
00395     /* initialize the phonebook entry values to zero */
00396     gn_lib_phonebook_prepare_write_entry(state);
00397 
00398     gn_lib_set_pb_location(state, phone_location);
00399 
00400     gn_lib_set_pb_name(state, GN_TO(addr->realName()));
00401     s = addr->phoneNumber(KABC::PhoneNumber::Pref).number();
00402     if (s.isEmpty())
00403         s = addr->phoneNumber(KABC::PhoneNumber::Work).number();
00404     if (s.isEmpty())
00405         s = addr->phoneNumber(KABC::PhoneNumber::Home).number();
00406     if (s.isEmpty())
00407         s = addr->phoneNumber(KABC::PhoneNumber::Cell).number();
00408     if (s.isEmpty() && addr->phoneNumbers().count()>0)
00409         s = (*addr->phoneNumbers().at(0)).number();
00410     s = makeValidPhone(s);
00411     gn_lib_set_pb_number(state, s.ascii());
00412     gn_lib_set_pb_memtype(state, memtype);
00413     QString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
00414     if (cg.isEmpty())
00415         gn_lib_set_pb_caller_group(state, GN_PHONEBOOK_GROUP_None); // default group
00416     else
00417         gn_lib_set_pb_caller_group(state, (gn_phonebook_group_type) cg.toInt());
00418 
00419     // set date/revision
00420     QDateTime datetime = addr->revision();
00421     QDate date(datetime.date());
00422     QTime time(datetime.time());
00423     gn_timestamp ts;
00424     gn_timestamp_set( &ts, date.year(), date.month(), date.day(),
00425                    time.hour(), time.minute(), time.second(), 0 );
00426     gn_lib_set_pb_date(state, ts);
00427 
00428     GNOKII_DEBUG(QString("Write #%1: name=%2, number=%3\n").arg(phone_location)
00429         .arg(GN_FROM(gn_lib_get_pb_name(state))).arg(GN_FROM(gn_lib_get_pb_number(state))));
00430 
00431     const KABC::Address homeAddr = addr->address(KABC::Address::Home);
00432     const KABC::Address workAddr = addr->address(KABC::Address::Work);
00433 
00434     // add all phone numbers
00435     const KABC::PhoneNumber::List phoneList = addr->phoneNumbers();
00436     KABC::PhoneNumber::List::ConstIterator it;
00437     for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
00438         const KABC::PhoneNumber *phonenumber = &(*it);
00439         s = phonenumber->number();
00440         if (s.isEmpty()) continue;
00441         gn_phonebook_number_type type;
00442         int pn_type = phonenumber->type();
00443         if ((pn_type & KABC::PhoneNumber::Cell))
00444             type = GN_PHONEBOOK_NUMBER_Mobile;
00445         else if ((pn_type & KABC::PhoneNumber::Fax))
00446             type = GN_PHONEBOOK_NUMBER_Fax;
00447         else if ((pn_type & KABC::PhoneNumber::Home))
00448             type = GN_PHONEBOOK_NUMBER_Home;
00449         else if ((pn_type & KABC::PhoneNumber::Work))
00450             type = GN_PHONEBOOK_NUMBER_Work;
00451         else type = GN_PHONEBOOK_NUMBER_General;
00452         gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
00453             GN_PHONEBOOK_ENTRY_Number, type, makeValidPhone(s).ascii());
00454         /*subentry->id = phone_location<<8+entry.subentries_count;*/
00455     }
00456     // add URL
00457     s = addr->url().prettyURL();
00458     if (!s.isEmpty()) {
00459         gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
00460             GN_PHONEBOOK_ENTRY_URL, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
00461     }
00462     // add E-Mails
00463     QStringList emails = addr->emails();
00464     for (unsigned int n=0; n<emails.count(); n++) {
00465         s = emails[n].simplifyWhiteSpace();
00466         if (s.isEmpty()) continue;
00467         // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
00468         if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
00469             GNOKII_DEBUG(QString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
00470                     .arg(s));
00471             continue;
00472         }
00473         gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
00474             GN_PHONEBOOK_ENTRY_Email, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
00475     }
00476     // add Adresses
00477     const KABC::Address::List addresses = addr->addresses();
00478     KABC::Address::List::ConstIterator it2;
00479     for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
00480         const KABC::Address *Addr = &(*it2);
00481         if (Addr->isEmpty()) continue;
00482         QStringList a;
00483         QChar sem(';');
00484         QString sem_repl(QString::fromLatin1(","));
00485             a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
00486         a.append( Addr->extended()     .replace( sem, sem_repl ) );
00487         a.append( Addr->street()       .replace( sem, sem_repl ) );
00488         a.append( Addr->locality()     .replace( sem, sem_repl ) );
00489         a.append( Addr->region()       .replace( sem, sem_repl ) );
00490         a.append( Addr->postalCode()   .replace( sem, sem_repl ) );
00491         a.append( Addr->country()      .replace( sem, sem_repl ) );
00492         s = a.join(sem);
00493         gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
00494             GN_PHONEBOOK_ENTRY_Postal, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
00495     }
00496     // add Note
00497     s = addr->note().simplifyWhiteSpace();
00498     if (!s.isEmpty()) {
00499         gn_lib_set_pb_subentry(state, -1 /* index to append entry */,
00500             GN_PHONEBOOK_ENTRY_Note, GN_PHONEBOOK_NUMBER_General, GN_TO(s));
00501     }
00502 
00503     // debug output
00504     for (int st=0; st<gn_lib_get_pb_num_subentries(state); st++) {
00505         gn_phonebook_entry_type entry_type;
00506         gn_phonebook_number_type number_type;
00507         const char *number;
00508         gn_lib_get_pb_subentry(state, st, &entry_type, &number_type, &number);
00509         GNOKII_DEBUG(QString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
00510                         .arg(st).arg(entry_type)
00511                         .arg(number_type).arg(GN_FROM(number)));
00512     }
00513 
00514     gn_error error = gn_lib_phonebook_write_entry(state, memtype, phone_location);
00515     GNOKII_CHECK_ERROR(error);
00516 
00517     return error;
00518 }
00519 
00520 
00521 static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
00522 {
00523     return gn_lib_phonebook_entry_delete(state, memtype, phone_location);
00524 }
00525 
00526 
00527 KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
00528 {
00529     KABC::AddresseeList addrList;
00530 
00531     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
00532         i18n("<qt>Please connect your Mobile Phone to your computer and press "
00533              "<b>Continue</b> to start importing the personal contacts.<br><br>"
00534              "Please note that if your Mobile Phone is not properly connected "
00535              "the following detection phase might take up to two minutes, during which "
00536                      "KAddressbook will behave unresponsively.</qt>") ))
00537       return addrList;
00538 
00539     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
00540         i18n("Mobile Phone Import"),
00541         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
00542              "Please wait...</center></qt>") );
00543     m_progressDlg->setAllowCancel(true);
00544     m_progressDlg->progressBar()->setProgress(0);
00545     m_progressDlg->progressBar()->setCenterIndicator(true);
00546     m_progressDlg->setModal(true);
00547     m_progressDlg->setInitialSize(QSize(450,350));
00548     m_progressDlg->show();
00549     processEvents();
00550 
00551 #if (QT_VERSION >= 0x030300)
00552     m_progressDlg->setCursor( Qt::BusyCursor );
00553 #endif
00554     QString errStr = businit();
00555     m_progressDlg->unsetCursor();
00556 
00557     if (!errStr.isEmpty()) {
00558         KMessageBox::error(parentWidget(), errStr);
00559         delete m_progressDlg;
00560         return addrList;
00561     }
00562 
00563     GNOKII_DEBUG("GNOKII import filter started.\n");
00564     m_progressDlg->setButtonText(i18n("&Stop Import"));
00565 
00566     read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
00567     read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
00568 
00569     GNOKII_DEBUG("GNOKII import filter finished.\n");
00570 
00571     busterminate();
00572     delete m_progressDlg;
00573 
00574     return addrList;
00575 }
00576 
00577 
00578 bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
00579 {
00580     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
00581         i18n("<qt>Please connect your Mobile Phone to your computer and press "
00582              "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
00583              "Please note that if your Mobile Phone is not properly connected "
00584              "the following detection phase might take up to two minutes, during which "
00585              "KAddressbook will behave unresponsively.</qt>") ))
00586       return false;
00587 
00588     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
00589         i18n("Mobile Phone Export"),
00590         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
00591              "Please wait...</center></qt>") );
00592     m_progressDlg->setAllowCancel(true);
00593     m_progressDlg->progressBar()->setProgress(0);
00594     m_progressDlg->progressBar()->setCenterIndicator(true);
00595     m_progressDlg->setModal(true);
00596     m_progressDlg->setInitialSize(QSize(450,350));
00597     m_progressDlg->show();
00598     processEvents();
00599 
00600     KProgress* progress = (KProgress*)m_progressDlg->progressBar();
00601 
00602     KABC::AddresseeList::ConstIterator it;
00603     QStringList failedList;
00604 
00605     gn_error error;
00606     bool deleteLabelInitialized = false;
00607 
00608 #if (QT_VERSION >= 0x030300)
00609     m_progressDlg->setCursor( Qt::BusyCursor );
00610 #endif
00611     QString errStr = businit();
00612     m_progressDlg->unsetCursor();
00613 
00614     if (!errStr.isEmpty()) {
00615         KMessageBox::error(parentWidget(), errStr);
00616         delete m_progressDlg;
00617         return false;
00618     }
00619 
00620     GNOKII_DEBUG("GNOKII export filter started.\n");
00621 
00622     gn_memory_type memtype = GN_MT_ME;  // internal phone memory
00623 
00624     int phone_count;    // num entries in phone
00625     bool overwrite_phone_entries = false;
00626     int phone_entry_no, entries_written;
00627     bool entry_empty;
00628 
00629     // get number of entries in this phone memory
00630     gn_memory_status memstat;
00631     error = read_phone_memstat(memtype, &memstat);
00632     if (error == GN_ERR_NONE) {
00633         GNOKII_DEBUG("Writing to internal phone memory.\n");
00634     } else {
00635         memtype = GN_MT_SM; // try SIM card instead
00636         error = read_phone_memstat(memtype, &memstat);
00637         if (error != GN_ERR_NONE)
00638             goto finish;
00639         GNOKII_DEBUG("Writing to SIM card memory.\n");
00640     }
00641     phone_count = memstat.used;
00642 
00643     if (memstat.free >= (int) list.count()) {
00644         if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
00645             i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
00646                  "the current mobile phonebook or should they <b>replace</b> all "
00647                  "currently existing phonebook entries ?<br><br>"
00648                  "Please note, that in case you choose to replace the phonebook "
00649                  "entries, every contact in your phone will be deleted and only "
00650                  "the newly exported contacts will be available from inside your phone.</qt>"),
00651             i18n("Export to Mobile Phone"),
00652             KGuiItem(i18n("&Append to Current Phonebook")),
00653             KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
00654                 overwrite_phone_entries = true;
00655     }
00656 
00657     progress->setTotalSteps(list.count());
00658     entries_written = 0;
00659     progress->setProgress(entries_written);
00660     m_progressDlg->setButtonText(i18n("&Stop Export"));
00661     m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
00662             "of the Mobile Phone.<br><br>%3</qt>")
00663         .arg(list.count())
00664         .arg(gn_memory_type2str(memtype))
00665         .arg(buildPhoneInfoString(memstat)) );
00666 
00667     // Now run the loop...
00668     phone_entry_no = 1;
00669     for ( it = list.begin(); it != list.end(); ++it ) {
00670         const KABC::Addressee *addr = &(*it);
00671         if (addr->isEmpty())
00672             continue;
00673         // don't write back SIM-card entries !
00674         if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
00675             continue;
00676 
00677         progress->setProgress(entries_written++);
00678 
00679 try_next_phone_entry:
00680         this_filter->processEvents();
00681         if (m_progressDlg->wasCancelled())
00682             break;
00683 
00684         // End of phone memory reached ?
00685         if (phone_entry_no > (memstat.used + memstat.free))
00686             break;
00687 
00688         GNOKII_DEBUG(QString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
00689                 .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
00690 
00691         error = GN_ERR_NONE;
00692 
00693         // is this phone entry empty ?
00694         entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
00695         if (overwrite_phone_entries) {
00696             // overwrite this phonebook entry ...
00697             if (!entry_empty)
00698                 phone_count--;
00699             error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
00700             phone_entry_no++;
00701         } else {
00702             // add this phonebook entry if possible ...
00703             if (entry_empty) {
00704                 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
00705                 phone_entry_no++;
00706             } else {
00707                 phone_entry_no++;
00708                 goto try_next_phone_entry;
00709             }
00710         }
00711 
00712         if (error != GN_ERR_NONE)
00713             failedList.append(addr->realName());
00714 
00715         // break if we got an error on the first entry
00716         if (error != GN_ERR_NONE && it==list.begin())
00717             break;
00718 
00719     } // for()
00720 
00721     // if we wanted to overwrite all entries, make sure, that we also
00722     // delete all remaining entries in the mobile phone.
00723     while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
00724         if (m_progressDlg->wasCancelled())
00725             break;
00726         if (!deleteLabelInitialized) {
00727             m_progressDlg->setLabel(
00728                 i18n("<qt><center>"
00729                      "All selected contacts have been sucessfully copied to "
00730                      "the Mobile Phone.<br><br>"
00731                      "Please wait until all remaining orphaned contacts from "
00732                      "the Mobile Phone have been deleted.</center></qt>") );
00733             m_progressDlg->setButtonText(i18n("&Stop Delete"));
00734             deleteLabelInitialized = true;
00735             progress->setTotalSteps(phone_count);
00736             entries_written = 0;
00737             progress->setProgress(entries_written);
00738             this_filter->processEvents();
00739         }
00740         if (phone_entry_no > (memstat.used + memstat.free))
00741             break;
00742         entry_empty = gn_lib_phonebook_entry_isempty(state, memtype, phone_entry_no);
00743         if (!entry_empty) {
00744             error = xxport_phone_delete_entry(phone_entry_no, memtype);
00745             phone_count--;
00746             progress->setProgress(++entries_written);
00747             this_filter->processEvents();
00748         }
00749         phone_entry_no++;
00750     }
00751 
00752 finish:
00753     m_progressDlg->setLabel(i18n("Export to phone finished."));
00754     this_filter->processEvents();
00755 
00756     GNOKII_DEBUG("GNOKII export filter finished.\n");
00757 
00758     busterminate();
00759     delete m_progressDlg;
00760 
00761     if (!failedList.isEmpty()) {
00762         GNOKII_DEBUG(QString("Failed to export: %1\n").arg(failedList.join(", ")));
00763         KMessageBox::informationList(parentWidget(),
00764                         i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
00765                  "Possible Reasons for this problem could be:<br><ul>"
00766                  "<li>The contacts contain more information per entry than the phone can store.</li>"
00767                  "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
00768                  "<li>other storage size related problems.</li>"
00769                  "</ul>"
00770                  "To avoid those kind of problems in the future please reduce the amount of different "
00771                  "fields in the above contacts.</qt>"),
00772             failedList,
00773             i18n("Mobile Phone Export") );
00774     }
00775 
00776 
00777     return true;
00778 }
00779 
00780 /******************************************************************************
00781  ******************************************************************************
00782  ******************************************************************************
00783  ******************************************************************************
00784  ******************************************************************************/
00785 
00786 #elif defined(HAVE_GNOKII_H)
00787 
00788 #ifdef __GNUC__
00789 # warning "Please upgrade your gnokii installation to at least version 0.6.13"
00790 # warning "Older gnokii versions below 0.6.13 are not binary compatible and"
00791 # warning "prevents KDE users to upgrade gnokii to newer versions later."
00792 #endif
00793 
00794 /* OLD GNOKII LIBRARIES (< 0.6.13) */
00795 
00796 /* import */
00797 static char *lockfile = NULL;
00798 static char manufacturer[64], model[GN_MODEL_MAX_LENGTH+1],
00799             revision[GN_REVISION_MAX_LENGTH+1], imei[GN_IMEI_MAX_LENGTH+1];
00800 static QString PhoneProductId;
00801 
00802 static struct gn_statemachine state;
00803 static gn_data data;
00804 
00805 static void busterminate(void)
00806 {
00807     gn_sm_functions(GN_OP_Terminate, NULL, &state);
00808     if (lockfile) gn_device_unlock(lockfile);
00809 }
00810 
00811 static QString businit(void)
00812 {
00813     gn_error error;
00814     char *aux;
00815 
00816     GNOKII_DEBUG( "Using old gnokii version." );
00817 
00818 #if defined(LIBGNOKII_VERSION)
00819     if (gn_cfg_read_default()<0)
00820 #else
00821     static char *BinDir;
00822     if (gn_cfg_read(&BinDir)<0)
00823 #endif
00824         return i18n("Failed to initialize the gnokii library.");
00825 
00826     if (!gn_cfg_phone_load("", &state))
00827         return i18n("Gnokii is not yet configured.");
00828 
00829     // uncomment to debug all gnokii communication on stderr.
00830     // gn_log_debug_mask = GN_LOG_T_STDERR;
00831 
00832     gn_data_clear(&data);
00833 
00834     aux = gn_cfg_get(gn_cfg_info, "global", "use_locking");
00835     // Defaults to 'no'
00836     if (aux && !strcmp(aux, "yes")) {
00837         lockfile = gn_device_lock(state.config.port_device);
00838         if (lockfile == NULL) {
00839             return i18n("Gnokii reports a 'Lock File Error'.\n "
00840             "Please exit all other running instances of gnokii, check if you have "
00841             "write permissions in the /var/lock directory and try again.");
00842         }
00843     }
00844 
00845     // Initialise the code for the GSM interface.
00846     int old_dcd = state.config.require_dcd; // work-around for older gnokii versions
00847     state.config.require_dcd = false;
00848     error = gn_gsm_initialise(&state);
00849     GNOKII_CHECK_ERROR(error);
00850     state.config.require_dcd = old_dcd;
00851     if (error != GN_ERR_NONE) {
00852         busterminate();
00853         return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
00854             "The returned error message was:<br><b>%1</b><br><br>"
00855             "You might try to run \"gnokii --identify\" on the command line to "
00856             "check any cable/transport issues and to verify if your gnokii "
00857             "configuration is correct.</center></qt>")
00858             .arg(gn_error_print(error));
00859     }
00860 
00861     // identify phone
00862     gn_data_clear(&data);
00863     data.manufacturer = manufacturer;
00864     data.model = model;
00865     data.revision = revision;
00866     data.imei = imei;
00867 
00868     QCString unknown(GN_TO(i18n("Unknown")));
00869     qstrncpy(manufacturer, unknown, sizeof(manufacturer)-1);
00870     qstrncpy(model, unknown, sizeof(model)-1);
00871     qstrncpy(revision, unknown, sizeof(revision)-1);
00872     qstrncpy(imei, unknown, sizeof(imei)-1);
00873 
00874     if (m_progressDlg->wasCancelled())
00875         return QString::null;
00876     else
00877         error = gn_sm_functions(GN_OP_Identify, &data, &state);
00878     GNOKII_CHECK_ERROR(error);
00879 
00880     GNOKII_DEBUG( QString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
00881                 .arg(manufacturer, model, revision, imei) );
00882 
00883     PhoneProductId = QString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei);
00884 
00885     return QString::null;
00886 }
00887 
00888 
00889 // get number of entries in this phone memory type (internal/SIM-card)
00890 static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
00891 {
00892     gn_error error;
00893 
00894     gn_data_clear(&data);
00895     memset(memstat, 0, sizeof(*memstat));
00896     memstat->memory_type = memtype;
00897     data.memory_status = memstat;
00898     error = gn_sm_functions(GN_OP_GetMemoryStatus, &data, &state);
00899     GNOKII_CHECK_ERROR(error);
00900     if (error != GN_ERR_NONE) {
00901         switch (memtype) {
00902           case GN_MT_SM:
00903             // use at least 100 entries
00904             memstat->used = 0;
00905             memstat->free = 100;
00906             break;
00907           default:
00908           case GN_MT_ME:
00909             // Phone doesn't support ME (5110)
00910             memstat->used = memstat->free = 0;
00911             break;
00912         }
00913     }
00914     GNOKII_DEBUG( QString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
00915                     .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
00916     return error;
00917 }
00918 
00919 
00920 // read phone entry #index from memory #memtype
00921 static gn_error read_phone_entry( const int index, const gn_memory_type memtype, gn_phonebook_entry *entry )
00922 {
00923     gn_error error;
00924     entry->memory_type = memtype;
00925     entry->location = index;
00926     data.phonebook_entry = entry;
00927     error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
00928     GNOKII_CHECK_ERROR(error);
00929     return error;
00930 }
00931 
00932 static bool phone_entry_empty( const int index, const gn_memory_type memtype )
00933 {
00934     gn_error error;
00935     gn_phonebook_entry entry;
00936     entry.memory_type = memtype;
00937     entry.location = index;
00938     data.phonebook_entry = &entry;
00939     error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
00940     if (error == GN_ERR_EMPTYLOCATION)
00941         return true;
00942     GNOKII_CHECK_ERROR(error);
00943     if (error == GN_ERR_NONE && entry.empty)
00944         return true;
00945     return false;
00946 }
00947 
00948 static QString buildPhoneInfoString( const gn_memory_status &memstat )
00949 {
00950     QString format = QString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
00951 
00952     return QString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
00953         .arg(i18n("Mobile Phone information:"))
00954         .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
00955         .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
00956         .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
00957         .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
00958         .arg(format.arg(i18n("Phonebook status"))
00959                .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
00960 }
00961 
00962 static QString buildMemoryTypeString( gn_memory_type memtype )
00963 {
00964     switch (memtype) {
00965     case GN_MT_ME:  return i18n("internal memory");
00966     case GN_MT_SM:  return i18n("SIM-card memory");
00967     default:    return i18n("unknown memory");
00968     }
00969 }
00970 
00971 // read and evaluate all phone entries
00972 static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
00973             KABC::AddresseeList *addrList )
00974 {
00975   gn_error error;
00976 
00977   if (m_progressDlg->wasCancelled())
00978     return GN_ERR_NONE;
00979 
00980   KProgress* progress = (KProgress*)m_progressDlg->progressBar();
00981 
00982   progress->setProgress(0);
00983   this_filter->processEvents();
00984 
00985   // get number of entries in this phone memory type (internal/SIM-card)
00986   gn_memory_status memstat;
00987   error = read_phone_memstat(memtype, &memstat);
00988 
00989   gn_phonebook_entry entry;
00990   QStringList addrlist;
00991   KABC::Address *addr;
00992   QString s, country;
00993 
00994   progress->setTotalSteps(memstat.used);
00995   m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
00996         .arg(memstat.used)
00997         .arg(buildMemoryTypeString(memtype))
00998         .arg(buildPhoneInfoString(memstat)) );
00999 
01000   int num_read = 0;
01001 
01002   for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
01003     error = read_phone_entry( i, memtype, &entry );
01004 
01005     progress->setProgress(num_read);
01006     this_filter->processEvents();
01007 
01008     if (error == GN_ERR_EMPTYLOCATION)
01009         continue;
01010     if (error == GN_ERR_INVALIDLOCATION)
01011         break;
01012     if (error == GN_ERR_INVALIDMEMORYTYPE)
01013         break;
01014     if (error == GN_ERR_NONE) {
01015         GNOKII_DEBUG(QString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i).arg(GN_FROM(entry.name))
01016             .arg(GN_FROM(entry.number)).arg(entry.location).arg(entry.caller_group).arg(entry.subentries_count));
01017         KABC::Addressee *a = new KABC::Addressee();
01018 
01019         // try to split Name into FamilyName and GivenName
01020         s = GN_FROM(entry.name).simplifyWhiteSpace();
01021         a->setFormattedName(s); // set formatted name as in Phone
01022         if (s.find(',') == -1) {
01023           // assumed format: "givenname [... familyname]"
01024           addrlist = QStringList::split(' ', s);
01025           if (addrlist.count() == 1) {
01026             // only one string -> put it in the GivenName
01027             a->setGivenName(s);
01028           } else {
01029             // multiple strings -> split them.
01030             a->setFamilyName(addrlist.last().simplifyWhiteSpace());
01031             addrlist.remove(addrlist.last());
01032             a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
01033           }
01034         } else {
01035           // assumed format: "familyname, ... givenname"
01036           addrlist = QStringList::split(',', s);
01037           a->setFamilyName(addrlist.first().simplifyWhiteSpace());
01038           addrlist.remove(addrlist.first());
01039           a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
01040         }
01041 
01042         a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(entry.caller_group));
01043         a->insertCustom(APP, "X_GSM_STORE_AT", QString("%1%2").arg(memtypestr).arg(entry.location));
01044 
01045         // set ProductId
01046         a->setProductId(PhoneProductId);
01047 
01048         // evaluate timestamp (ignore timezone)
01049         QDateTime datetime;
01050         if (entry.date.year<1998)
01051             datetime = QDateTime::currentDateTime();
01052         else
01053             datetime = QDateTime( QDate(entry.date.year, entry.date.month, entry.date.day),
01054                               QTime(entry.date.hour, entry.date.minute, entry.date.second) );
01055         GNOKII_DEBUG(QString(" date=%1\n").arg(datetime.toString()));
01056         a->setRevision(datetime);
01057 
01058         if (!entry.subentries_count)
01059           a->insertPhoneNumber(KABC::PhoneNumber(entry.number, KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref));
01060 
01061         /* scan sub-entries */
01062         if (entry.subentries_count)
01063          for (int n=0; n<entry.subentries_count; n++) {
01064           QString s = GN_FROM(entry.subentries[n].data.number).simplifyWhiteSpace();
01065           GNOKII_DEBUG(QString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
01066                 .arg(n).arg(entry.subentries[n].entry_type)
01067                 .arg(entry.subentries[n].number_type).arg(s));
01068           if (s.isEmpty())
01069             continue;
01070           switch(entry.subentries[n].entry_type) {
01071            case GN_PHONEBOOK_ENTRY_Name:
01072             a->setName(s);
01073             break;
01074            case GN_PHONEBOOK_ENTRY_Email:
01075             a->insertEmail(s);
01076             break;
01077            case GN_PHONEBOOK_ENTRY_Postal:
01078             addrlist = QStringList::split(';', s, true);
01079             addr = new KABC::Address(KABC::Address::Work);
01080             if (addrlist.count() <= 1) {
01081                 addrlist = QStringList::split(',', s, true);
01082                 if (addrlist.count() > 1 ) {
01083                     // assumed format: "Locality, ZIP, Country"
01084                     addr->setLocality(addrlist[0]);
01085                     addr->setPostalCode(addrlist[1]);
01086                     if (!addrlist[2].isEmpty())
01087                         addr->setCountry(i18n(GN_TO(addrlist[2])));
01088                 } else {
01089                     // no idea about the format, just store it.
01090                     addr->setLocality(s);
01091                 }
01092             } else {
01093                 // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
01094                 addr->setPostOfficeBox(addrlist[0]);
01095                 addr->setExtended(addrlist[1]);
01096                 addr->setStreet(addrlist[2]);
01097                 addr->setLocality(addrlist[3]);
01098                 addr->setRegion(addrlist[4]);
01099                 addr->setPostalCode(addrlist[5]);
01100                 country = addrlist[6];
01101                 if (!country.isEmpty())
01102                     addr->setCountry(i18n(GN_TO(country)));
01103             }
01104             a->insertAddress(*addr);
01105             delete addr;
01106             break;
01107            case GN_PHONEBOOK_ENTRY_Note:
01108             if (!a->note().isEmpty())
01109                 s = "\n" + s;
01110             a->setNote(a->note()+s);
01111             break;
01112            case GN_PHONEBOOK_ENTRY_Number:
01113             enum KABC::PhoneNumber::Types phonetype;
01114             switch (entry.subentries[n].number_type) {
01115              case GN_PHONEBOOK_NUMBER_Mobile: phonetype = KABC::PhoneNumber::Cell; break;
01116              case GN_PHONEBOOK_NUMBER_Fax:    phonetype = KABC::PhoneNumber::Fax;  break;
01117              case GN_PHONEBOOK_NUMBER_General:
01118              case GN_PHONEBOOK_NUMBER_Work:   phonetype = KABC::PhoneNumber::Work; break;
01119              default:
01120              case GN_PHONEBOOK_NUMBER_Home:   phonetype = KABC::PhoneNumber::Home; break;
01121             }
01122             //if (s == entry.number)
01123             //  type = (KABC::PhoneNumber::Types) (phonetype | KABC::PhoneNumber::Pref);
01124             a->insertPhoneNumber(KABC::PhoneNumber(s, phonetype));
01125             break;
01126            case GN_PHONEBOOK_ENTRY_URL:
01127             a->setUrl(s);
01128             break;
01129            case GN_PHONEBOOK_ENTRY_Group:
01130             a->insertCategory(s);
01131             break;
01132            default:
01133             GNOKII_DEBUG(QString(" Not handled id=%1, entry=%2\n")
01134                 .arg(entry.subentries[n].entry_type).arg(s));
01135             break;
01136           } // switch()
01137         } // if(subentry)
01138 
01139         // add only if entry was valid
01140         if (strlen(entry.name) || strlen(entry.number) || entry.subentries_count)
01141             addrList->append(*a);
01142 
01143         // did we read all valid phonebook-entries ?
01144         num_read++;
01145         delete a;
01146         if (num_read >= memstat.used)
01147             break;  // yes, all were read
01148         else
01149             continue; // no, we are still missing some.
01150     }
01151     GNOKII_CHECK_ERROR(error);
01152   }
01153 
01154   return GN_ERR_NONE;
01155 }
01156 
01157 
01158 // export to phone
01159 
01160 static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
01161             const KABC::Addressee *addr)
01162 {
01163     gn_phonebook_entry entry;
01164     QString s;
01165 
01166     memset(&entry, 0, sizeof(entry));
01167     strncpy(entry.name, GN_TO(addr->realName()), sizeof(entry.name)-1);
01168     s = addr->phoneNumber(KABC::PhoneNumber::Pref).number();
01169     if (s.isEmpty())
01170         s = addr->phoneNumber(KABC::PhoneNumber::Work).number();
01171     if (s.isEmpty())
01172         s = addr->phoneNumber(KABC::PhoneNumber::Home).number();
01173     if (s.isEmpty())
01174         s = addr->phoneNumber(KABC::PhoneNumber::Cell).number();
01175     if (s.isEmpty() && addr->phoneNumbers().count()>0)
01176         s = (*addr->phoneNumbers().at(0)).number();
01177     s = makeValidPhone(s);
01178     strncpy(entry.number, s.ascii(), sizeof(entry.number)-1);
01179     entry.memory_type = memtype;
01180     QString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
01181     if (cg.isEmpty())
01182         entry.caller_group = 5;     // default group
01183     else
01184         entry.caller_group = cg.toInt();
01185     entry.location = phone_location;
01186 
01187     // set date/revision
01188     QDateTime datetime = addr->revision();
01189     QDate date(datetime.date());
01190     QTime time(datetime.time());
01191     entry.date.year = date.year();
01192     entry.date.month = date.month();
01193     entry.date.day = date.day();
01194     entry.date.hour = time.hour();
01195     entry.date.minute = time.minute();
01196     entry.date.second = time.second();
01197 
01198     GNOKII_DEBUG(QString("Write #%1: name=%2, number=%3\n").arg(phone_location)
01199                     .arg(GN_FROM(entry.name)).arg(GN_FROM(entry.number)));
01200 
01201     const KABC::Address homeAddr = addr->address(KABC::Address::Home);
01202     const KABC::Address workAddr = addr->address(KABC::Address::Work);
01203 
01204     entry.subentries_count = 0;
01205     gn_phonebook_subentry *subentry = &entry.subentries[0];
01206     // add all phone numbers
01207     const KABC::PhoneNumber::List phoneList = addr->phoneNumbers();
01208     KABC::PhoneNumber::List::ConstIterator it;
01209     for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
01210         const KABC::PhoneNumber *phonenumber = &(*it);
01211         s = phonenumber->number();
01212         if (s.isEmpty()) continue;
01213         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Number;
01214         gn_phonebook_number_type type;
01215         int pn_type = phonenumber->type();
01216         if ((pn_type & KABC::PhoneNumber::Cell))
01217             type = GN_PHONEBOOK_NUMBER_Mobile;
01218         else if ((pn_type & KABC::PhoneNumber::Fax))
01219             type = GN_PHONEBOOK_NUMBER_Fax;
01220         else if ((pn_type & KABC::PhoneNumber::Home))
01221             type = GN_PHONEBOOK_NUMBER_Home;
01222         else if ((pn_type & KABC::PhoneNumber::Work))
01223             type = GN_PHONEBOOK_NUMBER_Work;
01224         else type = GN_PHONEBOOK_NUMBER_General;
01225         subentry->number_type = type;
01226         strncpy(subentry->data.number, makeValidPhone(s).ascii(), sizeof(subentry->data.number)-1);
01227         subentry->id = phone_location<<8+entry.subentries_count;
01228         entry.subentries_count++;
01229         subentry++;
01230         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
01231             break; // Phonebook full
01232     }
01233     // add URL
01234     s = addr->url().prettyURL();
01235     if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
01236         subentry->entry_type = GN_PHONEBOOK_ENTRY_URL;
01237         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
01238         entry.subentries_count++;
01239         subentry++;
01240     }
01241     // add E-Mails
01242     QStringList emails = addr->emails();
01243     for (unsigned int n=0; n<emails.count(); n++) {
01244         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
01245             break; // Phonebook full
01246         s = emails[n].simplifyWhiteSpace();
01247         if (s.isEmpty()) continue;
01248         // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
01249         if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
01250             GNOKII_DEBUG(QString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
01251                     .arg(s));
01252             continue;
01253         }
01254         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Email;
01255         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
01256         entry.subentries_count++;
01257         subentry++;
01258     }
01259     // add Adresses
01260     const KABC::Address::List addresses = addr->addresses();
01261     KABC::Address::List::ConstIterator it2;
01262     for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
01263         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
01264             break; // Phonebook full
01265         const KABC::Address *Addr = &(*it2);
01266         if (Addr->isEmpty()) continue;
01267         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Postal;
01268         QStringList a;
01269         QChar sem(';');
01270         QString sem_repl(QString::fromLatin1(","));
01271             a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
01272         a.append( Addr->extended()     .replace( sem, sem_repl ) );
01273         a.append( Addr->street()       .replace( sem, sem_repl ) );
01274         a.append( Addr->locality()     .replace( sem, sem_repl ) );
01275         a.append( Addr->region()       .replace( sem, sem_repl ) );
01276         a.append( Addr->postalCode()   .replace( sem, sem_repl ) );
01277         a.append( Addr->country()      .replace( sem, sem_repl ) );
01278         s = a.join(sem);
01279         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
01280         entry.subentries_count++;
01281         subentry++;
01282     }
01283     // add Note
01284     s = addr->note().simplifyWhiteSpace();
01285     if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
01286         subentry->entry_type = GN_PHONEBOOK_ENTRY_Note;
01287         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
01288         entry.subentries_count++;
01289         subentry++;
01290     }
01291 
01292     // debug output
01293     for (int st=0; st<entry.subentries_count; st++) {
01294         gn_phonebook_subentry *subentry = &entry.subentries[st];
01295         GNOKII_DEBUG(QString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
01296                         .arg(st).arg(subentry->entry_type)
01297                         .arg(subentry->number_type).arg(GN_FROM(subentry->data.number)));
01298     }
01299 
01300     data.phonebook_entry = &entry;
01301     gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
01302     GNOKII_CHECK_ERROR(error);
01303 
01304     return error;
01305 }
01306 
01307 
01308 static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
01309 {
01310     gn_phonebook_entry entry;
01311     memset(&entry, 0, sizeof(entry));
01312     entry.empty = 1;
01313     entry.memory_type = memtype;
01314     entry.location = phone_location;
01315     data.phonebook_entry = &entry;
01316     GNOKII_DEBUG(QString("Deleting entry %1\n").arg(phone_location));
01317     gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
01318     GNOKII_CHECK_ERROR(error);
01319     return error;
01320 }
01321 
01322 KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
01323 {
01324     KABC::AddresseeList addrList;
01325 
01326     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
01327         i18n("<qt>Please connect your Mobile Phone to your computer and press "
01328              "<b>Continue</b> to start importing the personal contacts.<br><br>"
01329              "Please note that if your Mobile Phone is not properly connected "
01330              "the following detection phase might take up to two minutes, during which "
01331                      "KAddressbook will behave unresponsively.</qt>") ))
01332       return addrList;
01333 
01334     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
01335         i18n("Mobile Phone Import"),
01336         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
01337              "Please wait...</center></qt>") );
01338     m_progressDlg->setAllowCancel(true);
01339     m_progressDlg->progressBar()->setProgress(0);
01340     m_progressDlg->progressBar()->setCenterIndicator(true);
01341     m_progressDlg->setModal(true);
01342     m_progressDlg->setInitialSize(QSize(450,350));
01343     m_progressDlg->show();
01344     processEvents();
01345 
01346 #if (QT_VERSION >= 0x030300)
01347     m_progressDlg->setCursor( Qt::BusyCursor );
01348 #endif
01349     QString errStr = businit();
01350     m_progressDlg->unsetCursor();
01351 
01352     if (!errStr.isEmpty()) {
01353         KMessageBox::error(parentWidget(), errStr);
01354         delete m_progressDlg;
01355         return addrList;
01356     }
01357 
01358     GNOKII_DEBUG("GNOKII import filter started.\n");
01359     m_progressDlg->setButtonText(i18n("&Stop Import"));
01360 
01361     read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
01362     read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
01363 
01364     GNOKII_DEBUG("GNOKII import filter finished.\n");
01365 
01366     busterminate();
01367     delete m_progressDlg;
01368 
01369     return addrList;
01370 }
01371 
01372 
01373 bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
01374 {
01375     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
01376         i18n("<qt>Please connect your Mobile Phone to your computer and press "
01377              "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
01378              "Please note that if your Mobile Phone is not properly connected "
01379              "the following detection phase might take up to two minutes, during which "
01380              "KAddressbook will behave unresponsively.</qt>") ))
01381       return false;
01382 
01383     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
01384         i18n("Mobile Phone Export"),
01385         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
01386              "Please wait...</center></qt>") );
01387     m_progressDlg->setAllowCancel(true);
01388     m_progressDlg->progressBar()->setProgress(0);
01389     m_progressDlg->progressBar()->setCenterIndicator(true);
01390     m_progressDlg->setModal(true);
01391     m_progressDlg->setInitialSize(QSize(450,350));
01392     m_progressDlg->show();
01393     processEvents();
01394 
01395     KProgress* progress = (KProgress*)m_progressDlg->progressBar();
01396 
01397     KABC::AddresseeList::ConstIterator it;
01398     QStringList failedList;
01399 
01400     gn_error error;
01401     bool deleteLabelInitialized = false;
01402 
01403 #if (QT_VERSION >= 0x030300)
01404     m_progressDlg->setCursor( Qt::BusyCursor );
01405 #endif
01406     QString errStr = businit();
01407     m_progressDlg->unsetCursor();
01408 
01409     if (!errStr.isEmpty()) {
01410         KMessageBox::error(parentWidget(), errStr);
01411         delete m_progressDlg;
01412         return false;
01413     }
01414 
01415     GNOKII_DEBUG("GNOKII export filter started.\n");
01416 
01417     gn_memory_type memtype = GN_MT_ME;  // internal phone memory
01418 
01419     int phone_count;    // num entries in phone
01420     bool overwrite_phone_entries = false;
01421     int phone_entry_no, entries_written;
01422     bool entry_empty;
01423 
01424     // get number of entries in this phone memory
01425     gn_memory_status memstat;
01426     error = read_phone_memstat(memtype, &memstat);
01427     if (error == GN_ERR_NONE) {
01428         GNOKII_DEBUG("Writing to internal phone memory.\n");
01429     } else {
01430         memtype = GN_MT_SM; // try SIM card instead
01431         error = read_phone_memstat(memtype, &memstat);
01432         if (error != GN_ERR_NONE)
01433             goto finish;
01434         GNOKII_DEBUG("Writing to SIM card memory.\n");
01435     }
01436     phone_count = memstat.used;
01437 
01438     if (memstat.free >= (int) list.count()) {
01439         if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
01440             i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
01441                  "the current mobile phonebook or should they <b>replace</b> all "
01442                  "currently existing phonebook entries ?<br><br>"
01443                  "Please note, that in case you choose to replace the phonebook "
01444                  "entries, every contact in your phone will be deleted and only "
01445                  "the newly exported contacts will be available from inside your phone.</qt>"),
01446             i18n("Export to Mobile Phone"),
01447             KGuiItem(i18n("&Append to Current Phonebook")),
01448             KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
01449                 overwrite_phone_entries = true;
01450     }
01451 
01452     progress->setTotalSteps(list.count());
01453     entries_written = 0;
01454     progress->setProgress(entries_written);
01455     m_progressDlg->setButtonText(i18n("&Stop Export"));
01456     m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
01457             "of the Mobile Phone.<br><br>%3</qt>")
01458         .arg(list.count())
01459         .arg(buildMemoryTypeString(memtype))
01460         .arg(buildPhoneInfoString(memstat)) );
01461 
01462     // Now run the loop...
01463     phone_entry_no = 1;
01464     for ( it = list.begin(); it != list.end(); ++it ) {
01465         const KABC::Addressee *addr = &(*it);
01466         if (addr->isEmpty())
01467             continue;
01468         // don't write back SIM-card entries !
01469         if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
01470             continue;
01471 
01472         progress->setProgress(entries_written++);
01473 
01474 try_next_phone_entry:
01475         this_filter->processEvents();
01476         if (m_progressDlg->wasCancelled())
01477             break;
01478 
01479         // End of phone memory reached ?
01480         if (phone_entry_no > (memstat.used + memstat.free))
01481             break;
01482 
01483         GNOKII_DEBUG(QString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
01484                 .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
01485 
01486         error = GN_ERR_NONE;
01487 
01488         // is this phone entry empty ?
01489         entry_empty = phone_entry_empty(phone_entry_no, memtype);
01490         if (overwrite_phone_entries) {
01491             // overwrite this phonebook entry ...
01492             if (!entry_empty)
01493                 phone_count--;
01494             error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
01495             phone_entry_no++;
01496         } else {
01497             // add this phonebook entry if possible ...
01498             if (entry_empty) {
01499                 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
01500                 phone_entry_no++;
01501             } else {
01502                 phone_entry_no++;
01503                 goto try_next_phone_entry;
01504             }
01505         }
01506 
01507         if (error != GN_ERR_NONE)
01508             failedList.append(addr->realName());
01509 
01510         // break if we got an error on the first entry
01511         if (error != GN_ERR_NONE && it==list.begin())
01512             break;
01513 
01514     } // for()
01515 
01516     // if we wanted to overwrite all entries, make sure, that we also
01517     // delete all remaining entries in the mobile phone.
01518     while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
01519         if (m_progressDlg->wasCancelled())
01520             break;
01521         if (!deleteLabelInitialized) {
01522             m_progressDlg->setLabel(
01523                 i18n("<qt><center>"
01524                      "All selected contacts have been sucessfully copied to "
01525                      "the Mobile Phone.<br><br>"
01526                      "Please wait until all remaining orphaned contacts from "
01527                      "the Mobile Phone have been deleted.</center></qt>") );
01528             m_progressDlg->setButtonText(i18n("&Stop Delete"));
01529             deleteLabelInitialized = true;
01530             progress->setTotalSteps(phone_count);
01531             entries_written = 0;
01532             progress->setProgress(entries_written);
01533             this_filter->processEvents();
01534         }
01535         if (phone_entry_no > (memstat.used + memstat.free))
01536             break;
01537         entry_empty = phone_entry_empty(phone_entry_no, memtype);
01538         if (!entry_empty) {
01539             error = xxport_phone_delete_entry(phone_entry_no, memtype);
01540             phone_count--;
01541             progress->setProgress(++entries_written);
01542             this_filter->processEvents();
01543         }
01544         phone_entry_no++;
01545     }
01546 
01547 finish:
01548     m_progressDlg->setLabel(i18n("Export to phone finished."));
01549     this_filter->processEvents();
01550 
01551     GNOKII_DEBUG("GNOKII export filter finished.\n");
01552 
01553     busterminate();
01554     delete m_progressDlg;
01555 
01556     if (!failedList.isEmpty()) {
01557         GNOKII_DEBUG(QString("Failed to export: %1\n").arg(failedList.join(", ")));
01558         KMessageBox::informationList(parentWidget(),
01559                         i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
01560                  "Possible Reasons for this problem could be:<br><ul>"
01561                  "<li>The contacts contain more information per entry than the phone can store.</li>"
01562                  "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
01563                  "<li>other storage size related problems.</li>"
01564                  "</ul>"
01565                  "To avoid those kind of problems in the future please reduce the amount of different "
01566                  "fields in the above contacts.</qt>"),
01567             failedList,
01568             i18n("Mobile Phone Export") );
01569     }
01570 
01571     return true;
01572 }
01573 
01574 
01575 /******************************************************************************
01576  ******************************************************************************
01577  ******************************************************************************
01578  ******************************************************************************
01579  ******************************************************************************/
01580 
01581 #else  /* no gnokii installed */
01582 
01583 KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
01584 {
01585     KABC::AddresseeList addrList;
01586     KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
01587         "Please ask your distributor to add gnokii at compile time."));
01588     return addrList;
01589 }
01590 
01591 bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
01592 {
01593     Q_UNUSED(list);
01594     KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
01595         "Please ask your distributor to add gnokii at compile time."));
01596     return true;
01597 }
01598 
01599 #endif /* END OF GNOKII LIB SWITCH */
01600 
01601 /******************************************************************************
01602  ******************************************************************************
01603  ******************************************************************************
01604  ******************************************************************************
01605  ******************************************************************************/
01606 
01607 #include "gnokii_xxport.moc"
01608 
01609 /* vim: set sts=4 ts=4 sw=4: */
KDE Home | KDE Accessibility Home | Description of Access Keys