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