kaddressbook Library API Documentation

gnokii_xxport.cpp

00001 /*
00002     This file is part of KAddressbook.
00003     Copyright (c) 2003-2004 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 /*
00024     Description:
00025     This filter allows you to import and export the KDE addressbook entries
00026     to/from a mobile phone, which is accessible via gnokii.
00027     Gnokii homepage: http://www.gnokii.org
00028 
00029     TODO:
00030     - create a log file and give user possibility to see it afterwards
00031     - handle callergroup value (Friend, VIP, Family, ...) better
00032 */
00033 
00034 #include "config.h"
00035 
00036 #include <qcursor.h>
00037 
00038 #include <kdebug.h>
00039 #include <klocale.h>
00040 #include <kmessagebox.h>
00041 #include <kprogress.h>
00042 #include <kguiitem.h>
00043 
00044 #ifdef HAVE_GNOKII_H
00045 extern "C" {
00046 #include <gnokii.h>
00047 }
00048 #else
00049 #ifdef __GNUC__
00050 # warning "Please install the gnokii development headers and libraries !"
00051 #endif
00052 #endif
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 class GNOKIIXXPortFactory : public KAB::XXPortFactory
00079 {
00080   public:
00081     KAB::XXPort *xxportObject( KABC::AddressBook *ab, QWidget *parent, const char *name )
00082     {
00083       return new GNOKIIXXPort( ab, parent, name );
00084     }
00085 };
00086 
00087 extern "C"
00088 {
00089   void *init_libkaddrbk_gnokii_xxport()
00090   {
00091     return ( new GNOKIIXXPortFactory() );
00092   }
00093 }
00094 
00095 
00096 GNOKIIXXPort::GNOKIIXXPort( KABC::AddressBook *ab, QWidget *parent, const char *name )
00097   : KAB::XXPort( ab, parent, name )
00098 {
00099     this_filter = this;
00100     m_progressDlg = NULL;
00101     createImportAction( i18n( "Import From Nokia Mobile Phone..." ) );
00102     createExportAction( i18n( "Export to Nokia Mobile Phone..." ) );
00103 }
00104 
00105 /* import */
00106 
00107 #ifdef HAVE_GNOKII_H
00108 static char *lockfile = NULL;
00109 static char manufacturer[64], model[GN_MODEL_MAX_LENGTH+1],
00110             revision[GN_REVISION_MAX_LENGTH+1], imei[GN_IMEI_MAX_LENGTH+1];
00111 static QString PhoneProductId;
00112 
00113 static struct gn_statemachine state;
00114 static gn_data data;
00115 
00116 static void busterminate(void)
00117 {
00118     gn_sm_functions(GN_OP_Terminate, NULL, &state);
00119     if (lockfile) gn_device_unlock(lockfile);
00120 }
00121 
00122 static QString businit(void)
00123 {
00124     gn_error error;
00125     char *aux;
00126 
00127 #if defined(LIBGNOKII_VERSION)
00128     if (gn_cfg_read_default()<0)
00129 #else
00130     static char *BinDir;
00131     if (gn_cfg_read(&BinDir)<0)
00132 #endif
00133         return i18n("Failed to initialize the gnokii library.");
00134 
00135     if (!gn_cfg_phone_load("", &state))
00136         return i18n("Gnokii is not yet configured.");
00137 
00138     // uncomment to debug all gnokii communication on stderr.
00139     // gn_log_debug_mask = GN_LOG_T_STDERR;
00140 
00141     gn_data_clear(&data);
00142 
00143     aux = gn_cfg_get(gn_cfg_info, "global", "use_locking");
00144     // Defaults to 'no'
00145     if (aux && !strcmp(aux, "yes")) {
00146         lockfile = gn_device_lock(state.config.port_device);
00147         if (lockfile == NULL) {
00148             return i18n("Gnokii reports a 'Lock File Error'.\n "
00149             "Please exit all other running instances of gnokii, check if you have "
00150             "write permissions in the /var/lock directory and try again.");
00151         }
00152     }
00153 
00154     // Initialise the code for the GSM interface.
00155     int old_dcd = state.config.require_dcd; // work-around for older gnokii versions
00156     state.config.require_dcd = false;
00157     error = gn_gsm_initialise(&state);
00158     GNOKII_CHECK_ERROR(error);
00159     state.config.require_dcd = old_dcd;
00160     if (error != GN_ERR_NONE) {
00161         busterminate();
00162         return i18n("<qt><center>Mobile Phone interface initialization failed.<br><br>"
00163             "The returned error message was:<br><b>%1</b><br><br>"
00164             "You might try to run \"gnokii --identify\" on the command line to "
00165             "check any cable/transport issues and to verify if your gnokii "
00166             "configuration is correct.</center></qt>")
00167             .arg(gn_error_print(error));
00168     }
00169 
00170     // identify phone
00171     gn_data_clear(&data);
00172     data.manufacturer = manufacturer;
00173     data.model = model;
00174     data.revision = revision;
00175     data.imei = imei;
00176 
00177     QCString unknown(GN_TO(i18n("Unknown")));
00178     qstrncpy(manufacturer, unknown, sizeof(manufacturer)-1);
00179     qstrncpy(model, unknown, sizeof(model)-1);
00180     qstrncpy(revision, unknown, sizeof(revision)-1);
00181     qstrncpy(imei, unknown, sizeof(imei)-1);
00182 
00183     if (m_progressDlg->wasCancelled())
00184         return QString::null;
00185     else
00186         error = gn_sm_functions(GN_OP_Identify, &data, &state);
00187     GNOKII_CHECK_ERROR(error);
00188 
00189     GNOKII_DEBUG( QString("Found mobile phone: %1 %2, Revision: %3, IMEI: %4\n")
00190                 .arg(manufacturer, model, revision, imei) );
00191 
00192     PhoneProductId = QString("%1-%2-%3-%4").arg(APP).arg(model).arg(revision).arg(imei);
00193 
00194     return QString::null;
00195 }
00196 
00197 
00198 // get number of entries in this phone memory type (internal/SIM-card)
00199 static gn_error read_phone_memstat( const gn_memory_type memtype, gn_memory_status *memstat )
00200 {
00201     gn_error error;
00202 
00203     gn_data_clear(&data);
00204     memset(memstat, 0, sizeof(*memstat));
00205     memstat->memory_type = memtype;
00206     data.memory_status = memstat;
00207     error = gn_sm_functions(GN_OP_GetMemoryStatus, &data, &state);
00208     GNOKII_CHECK_ERROR(error);
00209     if (error != GN_ERR_NONE) {
00210         switch (memtype) {
00211           case GN_MT_SM:
00212             // use at least 100 entries
00213             memstat->used = 0;
00214             memstat->free = 100;
00215             break;
00216           default:
00217           case GN_MT_ME:
00218             // Phone doesn't support ME (5110)
00219             memstat->used = memstat->free = 0;
00220             break;
00221         }
00222     }
00223     GNOKII_DEBUG( QString("\n\nMobile phone memory status: Type: %1, used=%2, free=%3, total=%4\n\n")
00224                     .arg(memtype).arg(memstat->used).arg(memstat->free).arg(memstat->used+memstat->free) );
00225     return error;
00226 }
00227 
00228 
00229 // read phone entry #index from memory #memtype
00230 static gn_error read_phone_entry( const int index, const gn_memory_type memtype, gn_phonebook_entry *entry )
00231 {
00232     gn_error error;
00233     entry->memory_type = memtype;
00234     entry->location = index;
00235     data.phonebook_entry = entry;
00236     error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
00237     GNOKII_CHECK_ERROR(error);
00238     return error;
00239 }
00240 
00241 static bool phone_entry_empty( const int index, const gn_memory_type memtype )
00242 {
00243     gn_error error;
00244     gn_phonebook_entry entry;
00245     entry.memory_type = memtype;
00246     entry.location = index;
00247     data.phonebook_entry = &entry;
00248     error = gn_sm_functions(GN_OP_ReadPhonebook, &data, &state);
00249     if (error == GN_ERR_EMPTYLOCATION)
00250         return true;
00251     GNOKII_CHECK_ERROR(error);
00252     if (error == GN_ERR_NONE && entry.empty)
00253         return true;
00254     return false;
00255 }
00256 
00257 static QString buildPhoneInfoString( const gn_memory_status &memstat )
00258 {
00259     QString format = QString::fromLatin1("<tr><td><b>%1</b></td><td>%2</td></tr>");
00260 
00261     return QString::fromLatin1("<b>%1</b><br><table>%2%3%4%5%6</table><br>")
00262         .arg(i18n("Mobile Phone information:"))
00263         .arg(format.arg(i18n("Manufacturer")).arg(GN_FROM(manufacturer)))
00264         .arg(format.arg(i18n("Phone model")).arg(GN_FROM(model)))
00265         .arg(format.arg(i18n("Revision")).arg(GN_FROM(revision)))
00266         .arg(format.arg(i18n("IMEI")).arg(GN_FROM(imei)))
00267         .arg(format.arg(i18n("Phonebook status"))
00268                .arg(i18n("%1 out of %2 contacts used").arg(memstat.used).arg(memstat.used+memstat.free)));
00269 }
00270 
00271 static QString buildMemoryTypeString( gn_memory_type memtype )
00272 {
00273     switch (memtype) {
00274     case GN_MT_ME:  return i18n("internal memory");
00275     case GN_MT_SM:  return i18n("SIM-card memory");
00276     default:    return i18n("unknown memory");
00277     }
00278 }
00279 
00280 // read and evaluate all phone entries
00281 static gn_error read_phone_entries( const char *memtypestr, gn_memory_type memtype,
00282             KABC::AddresseeList *addrList )
00283 {
00284   gn_error error;
00285 
00286   if (m_progressDlg->wasCancelled())
00287     return GN_ERR_NONE;
00288 
00289   KProgress* progress = (KProgress*)m_progressDlg->progressBar();
00290 
00291   progress->setProgress(0);
00292   this_filter->processEvents();
00293 
00294   // get number of entries in this phone memory type (internal/SIM-card)
00295   gn_memory_status memstat;
00296   error = read_phone_memstat(memtype, &memstat);
00297 
00298   gn_phonebook_entry entry;
00299   QStringList addrlist;
00300   KABC::Address *addr;
00301   QString s, country;
00302 
00303   progress->setTotalSteps(memstat.used);
00304   m_progressDlg->setLabel(i18n("<qt>Importing <b>%1</b> contacts from <b>%2</b> of the Mobile Phone.<br><br>%3</qt>")
00305         .arg(memstat.used)
00306         .arg(buildMemoryTypeString(memtype))
00307         .arg(buildPhoneInfoString(memstat)) );
00308 
00309   int num_read = 0;
00310 
00311   for (int i = 1; !m_progressDlg->wasCancelled() && i <= memstat.used + memstat.free; i++) {
00312     error = read_phone_entry( i, memtype, &entry );
00313 
00314     progress->setProgress(num_read);
00315     this_filter->processEvents();
00316 
00317     if (error == GN_ERR_EMPTYLOCATION)
00318         continue;
00319     if (error == GN_ERR_INVALIDLOCATION)
00320         break;
00321     if (error == GN_ERR_INVALIDMEMORYTYPE)
00322         break;
00323     if (error == GN_ERR_NONE) {
00324         GNOKII_DEBUG(QString("%1: %2, num=%3, location=%4, group=%5, count=%6\n").arg(i).arg(GN_FROM(entry.name))
00325             .arg(GN_FROM(entry.number)).arg(entry.location).arg(entry.caller_group).arg(entry.subentries_count));
00326         KABC::Addressee *a = new KABC::Addressee();
00327 
00328         // try to split Name into FamilyName and GivenName
00329         s = GN_FROM(entry.name).simplifyWhiteSpace();
00330         a->setFormattedName(s); // set formatted name as in Phone
00331         if (s.find(',') == -1) {
00332           // assumed format: "givenname [... familyname]"
00333           addrlist = QStringList::split(' ', s);
00334           if (addrlist.count() == 1) {
00335             // only one string -> put it in the GivenName
00336             a->setGivenName(s);
00337           } else {
00338             // multiple strings -> split them.
00339             a->setFamilyName(addrlist.last().simplifyWhiteSpace());
00340             addrlist.remove(addrlist.last());
00341             a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
00342           }
00343         } else {
00344           // assumed format: "familyname, ... givenname"
00345           addrlist = QStringList::split(',', s);
00346           a->setFamilyName(addrlist.first().simplifyWhiteSpace());
00347           addrlist.remove(addrlist.first());
00348           a->setGivenName(addrlist.join(" ").simplifyWhiteSpace());
00349         }
00350 
00351         a->insertCustom(APP, "X_GSM_CALLERGROUP", s.setNum(entry.caller_group));
00352         a->insertCustom(APP, "X_GSM_STORE_AT", QString("%1%2").arg(memtypestr).arg(entry.location));
00353 
00354         // set ProductId
00355         a->setProductId(PhoneProductId);
00356 
00357         // evaluate timestamp (ignore timezone)
00358         QDateTime datetime;
00359         if (entry.date.year<1998)
00360             datetime = QDateTime::currentDateTime();
00361         else
00362             datetime = QDateTime( QDate(entry.date.year, entry.date.month, entry.date.day),
00363                               QTime(entry.date.hour, entry.date.minute, entry.date.second) );
00364         GNOKII_DEBUG(QString(" date=%1\n").arg(datetime.toString()));
00365         a->setRevision(datetime);
00366 
00367         if (!entry.subentries_count)
00368           a->insertPhoneNumber(KABC::PhoneNumber(entry.number, KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref));
00369 
00370         /* scan sub-entries */
00371         if (entry.subentries_count)
00372          for (int n=0; n<entry.subentries_count; n++) {
00373           QString s = GN_FROM(entry.subentries[n].data.number).simplifyWhiteSpace();
00374           GNOKII_DEBUG(QString(" Subentry#%1, entry_type=%2, number_type=%3, number=%4\n")
00375                 .arg(n).arg(entry.subentries[n].entry_type)
00376                 .arg(entry.subentries[n].number_type).arg(s));
00377           if (s.isEmpty())
00378             continue;
00379           switch(entry.subentries[n].entry_type) {
00380            case GN_PHONEBOOK_ENTRY_Name:
00381             a->setName(s);
00382             break;
00383            case GN_PHONEBOOK_ENTRY_Email:
00384             a->insertEmail(s);
00385             break;
00386            case GN_PHONEBOOK_ENTRY_Postal:
00387             addrlist = QStringList::split(';', s, true);
00388             addr = new KABC::Address(KABC::Address::Work);
00389             if (addrlist.count() <= 1) {
00390                 addrlist = QStringList::split(',', s, true);
00391                 if (addrlist.count() > 1 ) {
00392                     // assumed format: "Locality, ZIP, Country"
00393                     addr->setLocality(addrlist[0]);
00394                     addr->setPostalCode(addrlist[1]);
00395                     if (!addrlist[2].isEmpty())
00396                         addr->setCountry(i18n(GN_TO(addrlist[2])));
00397                 } else {
00398                     // no idea about the format, just store it.
00399                     addr->setLocality(s);
00400                 }
00401             } else {
00402                 // assumed format: "POBox; Extended; Street; Locality; Region; ZIP [;Country]
00403                 addr->setPostOfficeBox(addrlist[0]);
00404                 addr->setExtended(addrlist[1]);
00405                 addr->setStreet(addrlist[2]);
00406                 addr->setLocality(addrlist[3]);
00407                 addr->setRegion(addrlist[4]);
00408                 addr->setPostalCode(addrlist[5]);
00409                 country = addrlist[6];
00410                 if (!country.isEmpty())
00411                     addr->setCountry(i18n(GN_TO(country)));
00412             }
00413             a->insertAddress(*addr);
00414             delete addr;
00415             break;
00416            case GN_PHONEBOOK_ENTRY_Note:
00417             if (!a->note().isEmpty())
00418                 s = "\n" + s;
00419             a->setNote(a->note()+s);
00420             break;
00421            case GN_PHONEBOOK_ENTRY_Number:
00422             enum KABC::PhoneNumber::Types phonetype;
00423             switch (entry.subentries[n].number_type) {
00424              case GN_PHONEBOOK_NUMBER_Mobile: phonetype = KABC::PhoneNumber::Cell; break;
00425              case GN_PHONEBOOK_NUMBER_Fax:    phonetype = KABC::PhoneNumber::Fax;  break;
00426              case GN_PHONEBOOK_NUMBER_General:
00427              case GN_PHONEBOOK_NUMBER_Work:   phonetype = KABC::PhoneNumber::Work; break;
00428              default:
00429              case GN_PHONEBOOK_NUMBER_Home:   phonetype = KABC::PhoneNumber::Home; break;
00430             }
00431             //if (s == entry.number)
00432             //  type = (KABC::PhoneNumber::Types) (phonetype | KABC::PhoneNumber::Pref);
00433             a->insertPhoneNumber(KABC::PhoneNumber(s, phonetype));
00434             break;
00435            case GN_PHONEBOOK_ENTRY_URL:
00436             a->setUrl(s);
00437             break;
00438            case GN_PHONEBOOK_ENTRY_Group:
00439             a->insertCategory(s);
00440             break;
00441            default:
00442             GNOKII_DEBUG(QString(" Not handled id=%1, entry=%2\n")
00443                 .arg(entry.subentries[n].entry_type).arg(s));
00444             break;
00445           } // switch()
00446         } // if(subentry)
00447 
00448         // add only if entry was valid
00449         if (strlen(entry.name) || strlen(entry.number) || entry.subentries_count)
00450             addrList->append(*a);
00451 
00452         // did we read all valid phonebook-entries ?
00453         num_read++;
00454         delete a;
00455         if (num_read >= memstat.used)
00456             break;  // yes, all were read
00457         else
00458             continue; // no, we are still missing some.
00459     }
00460     GNOKII_CHECK_ERROR(error);
00461   }
00462 
00463   return GN_ERR_NONE;
00464 }
00465 #endif
00466 
00467 
00468 
00469 KABC::AddresseeList GNOKIIXXPort::importContacts( const QString& ) const
00470 {
00471     KABC::AddresseeList addrList;
00472 
00473 #ifndef HAVE_GNOKII_H
00474 
00475     KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
00476         "Please ask your distributor to add gnokii at compile time."));
00477 
00478 #else
00479 
00480     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
00481         i18n("<qt>Please connect your Mobile Phone to your computer and press "
00482              "<b>Continue</b> to start importing the personal contacts.<br><br>"
00483              "Please note that if your mobile phone is not properly connected "
00484              "the following detection phase might take up to two minutes, during which "
00485                      "KAddressbook will behave unresponsively.</qt>") ))
00486       return addrList;
00487 
00488     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
00489         i18n("Mobile Phone Import"),
00490         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
00491              "Please wait...</center></qt>") );
00492     m_progressDlg->setAllowCancel(true);
00493     m_progressDlg->progressBar()->setProgress(0);
00494     m_progressDlg->progressBar()->setCenterIndicator(true);
00495     m_progressDlg->setModal(true);
00496     m_progressDlg->setMinimumSize(450,350); // not honored yet - seems like bug in KProgressDialog
00497     m_progressDlg->show();
00498     processEvents();
00499 
00500 #if (QT_VERSION >= 0x030300)
00501     m_progressDlg->setCursor( Qt::BusyCursor );
00502 #endif
00503     QString errStr = businit();
00504     m_progressDlg->unsetCursor();
00505 
00506     if (!errStr.isEmpty()) {
00507         KMessageBox::error(parentWidget(), errStr);
00508         delete m_progressDlg;
00509         return addrList;
00510     }
00511 
00512     GNOKII_DEBUG("GNOKII import filter started.\n");
00513     m_progressDlg->setButtonText(i18n("&Stop Import"));
00514 
00515     read_phone_entries("ME", GN_MT_ME, &addrList); // internal phone memory
00516     read_phone_entries("SM", GN_MT_SM, &addrList); // SIM card
00517 
00518     GNOKII_DEBUG("GNOKII import filter finished.\n");
00519 
00520     busterminate();
00521     delete m_progressDlg;
00522 
00523 #endif
00524 
00525     return addrList;
00526 }
00527 
00528 
00529 // export to phone
00530 
00531 #ifdef HAVE_GNOKII_H
00532 
00533 static QString makeValidPhone( const QString &number )
00534 {
00535     // allowed chars: 0-9, *, #, p, w, +
00536     QString num = number.simplifyWhiteSpace();
00537     QString allowed("0123456789*+#pw");
00538     for (unsigned int i=num.length(); i>=1; i--)
00539         if (allowed.find(num[i-1])==-1)
00540             num.remove(i-1,1);
00541     if (num.isEmpty())
00542         num = "0";
00543     return num;
00544 }
00545 
00546 static gn_error xxport_phone_write_entry( int phone_location, gn_memory_type memtype,
00547             const KABC::Addressee *addr)
00548 {
00549     gn_phonebook_entry entry;
00550     QString s;
00551 
00552     memset(&entry, 0, sizeof(entry));
00553     strncpy(entry.name, GN_TO(addr->realName()), sizeof(entry.name)-1);
00554     s = addr->phoneNumber(KABC::PhoneNumber::Pref).number();
00555     if (s.isEmpty())
00556         s = addr->phoneNumber(KABC::PhoneNumber::Work).number();
00557     if (s.isEmpty())
00558         s = addr->phoneNumber(KABC::PhoneNumber::Home).number();
00559     if (s.isEmpty())
00560         s = addr->phoneNumber(KABC::PhoneNumber::Cell).number();
00561     if (s.isEmpty() && addr->phoneNumbers().count()>0)
00562         s = (*addr->phoneNumbers().at(0)).number();
00563     s = makeValidPhone(s);
00564     strncpy(entry.number, s.ascii(), sizeof(entry.number)-1);
00565     entry.memory_type = memtype;
00566     QString cg = addr->custom(APP, "X_GSM_CALLERGROUP");
00567     if (cg.isEmpty())
00568         entry.caller_group = 5;     // default group
00569     else
00570         entry.caller_group = cg.toInt();
00571     entry.location = phone_location;
00572 
00573     // set date/revision
00574     QDateTime datetime = addr->revision();
00575     QDate date(datetime.date());
00576     QTime time(datetime.time());
00577     entry.date.year = date.year();
00578     entry.date.month = date.month();
00579     entry.date.day = date.day();
00580     entry.date.hour = time.hour();
00581     entry.date.minute = time.minute();
00582     entry.date.second = time.second();
00583 
00584     GNOKII_DEBUG(QString("Write #%1: name=%2, number=%3\n").arg(phone_location)
00585                     .arg(GN_FROM(entry.name)).arg(GN_FROM(entry.number)));
00586 
00587     const KABC::Address homeAddr = addr->address(KABC::Address::Home);
00588     const KABC::Address workAddr = addr->address(KABC::Address::Work);
00589 
00590     entry.subentries_count = 0;
00591     gn_phonebook_subentry *subentry = &entry.subentries[0];
00592     // add all phone numbers
00593     const KABC::PhoneNumber::List phoneList = addr->phoneNumbers();
00594     KABC::PhoneNumber::List::ConstIterator it;
00595     for ( it = phoneList.begin(); it != phoneList.end(); ++it ) {
00596         const KABC::PhoneNumber *phonenumber = &(*it);
00597         s = phonenumber->number();
00598         if (s.isEmpty()) continue;
00599         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Number;
00600         gn_phonebook_number_type type;
00601         switch (phonenumber->type() & ~KABC::PhoneNumber::Pref) {
00602             case KABC::PhoneNumber::Home:   type = GN_PHONEBOOK_NUMBER_Home;    break;
00603             case KABC::PhoneNumber::Voice:
00604             case KABC::PhoneNumber::Work:   type = GN_PHONEBOOK_NUMBER_Work;    break;
00605             case KABC::PhoneNumber::Pager:
00606             case KABC::PhoneNumber::Cell:   type = GN_PHONEBOOK_NUMBER_Mobile;  break;
00607             case KABC::PhoneNumber::Fax:    type = GN_PHONEBOOK_NUMBER_Fax;     break;
00608             default:            type = GN_PHONEBOOK_NUMBER_General; break;
00609         }
00610         subentry->number_type = type;
00611         strncpy(subentry->data.number, makeValidPhone(s).ascii(), sizeof(subentry->data.number)-1);
00612         subentry->id = phone_location<<8+entry.subentries_count;
00613         entry.subentries_count++;
00614         subentry++;
00615         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
00616             break; // Phonebook full
00617     }
00618     // add URL
00619     s = addr->url().prettyURL();
00620     if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
00621         subentry->entry_type = GN_PHONEBOOK_ENTRY_URL;
00622         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
00623         entry.subentries_count++;
00624         subentry++;
00625     }
00626     // add E-Mails
00627     QStringList emails = addr->emails();
00628     for (unsigned int n=0; n<emails.count(); n++) {
00629         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
00630             break; // Phonebook full
00631         s = emails[n].simplifyWhiteSpace();
00632         if (s.isEmpty()) continue;
00633         // only one email allowed if we have URLS, notes, addresses (to avoid phone limitations)
00634         if (n && !addr->url().isEmpty() && !addr->note().isEmpty() && addr->addresses().count()) {
00635             GNOKII_DEBUG(QString(" DROPPED email %1 in favor of URLs, notes and addresses.\n")
00636                     .arg(s));
00637             continue;
00638         }
00639         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Email;
00640         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
00641         entry.subentries_count++;
00642         subentry++;
00643     }
00644     // add Adresses
00645     const KABC::Address::List addresses = addr->addresses();
00646     KABC::Address::List::ConstIterator it2;
00647     for ( it2 = addresses.begin(); it2 != addresses.end(); ++it2 ) {
00648         if (entry.subentries_count >= GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)
00649             break; // Phonebook full
00650         const KABC::Address *Addr = &(*it2);
00651         if (Addr->isEmpty()) continue;
00652         subentry->entry_type  = GN_PHONEBOOK_ENTRY_Postal;
00653         QStringList a;
00654         QChar sem(';');
00655         QString sem_repl(QString::fromLatin1(","));
00656             a.append( Addr->postOfficeBox().replace( sem, sem_repl ) );
00657         a.append( Addr->extended()     .replace( sem, sem_repl ) );
00658         a.append( Addr->street()       .replace( sem, sem_repl ) );
00659         a.append( Addr->locality()     .replace( sem, sem_repl ) );
00660         a.append( Addr->region()       .replace( sem, sem_repl ) );
00661         a.append( Addr->postalCode()   .replace( sem, sem_repl ) );
00662         a.append( Addr->country()      .replace( sem, sem_repl ) );
00663         s = a.join(sem);
00664         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
00665         entry.subentries_count++;
00666         subentry++;
00667     }
00668     // add Note
00669     s = addr->note().simplifyWhiteSpace();
00670     if (!s.isEmpty() && (entry.subentries_count<GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER)) {
00671         subentry->entry_type = GN_PHONEBOOK_ENTRY_Note;
00672         strncpy(subentry->data.number, GN_TO(s), sizeof(subentry->data.number)-1);
00673         entry.subentries_count++;
00674         subentry++;
00675     }
00676 
00677     // debug output
00678     for (int st=0; st<entry.subentries_count; st++) {
00679         gn_phonebook_subentry *subentry = &entry.subentries[st];
00680         GNOKII_DEBUG(QString(" SubTel #%1: entry_type=%2, number_type=%3, number=%4\n")
00681                         .arg(st).arg(subentry->entry_type)
00682                         .arg(subentry->number_type).arg(GN_FROM(subentry->data.number)));
00683     }
00684 
00685     data.phonebook_entry = &entry;
00686     gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
00687     GNOKII_CHECK_ERROR(error);
00688 
00689     return error;
00690 }
00691 
00692 
00693 static gn_error xxport_phone_delete_entry( int phone_location, gn_memory_type memtype )
00694 {
00695     gn_phonebook_entry entry;
00696     memset(&entry, 0, sizeof(entry));
00697     entry.empty = 1;
00698     entry.memory_type = memtype;
00699     entry.location = phone_location;
00700     data.phonebook_entry = &entry;
00701     GNOKII_DEBUG(QString("Deleting entry %1\n").arg(phone_location));
00702     gn_error error = gn_sm_functions(GN_OP_WritePhonebook, &data, &state);
00703     GNOKII_CHECK_ERROR(error);
00704     return error;
00705 }
00706 
00707 #endif
00708 
00709 bool GNOKIIXXPort::exportContacts( const KABC::AddresseeList &list, const QString & )
00710 {
00711 #ifndef HAVE_GNOKII_H
00712 
00713     Q_UNUSED(list);
00714     KMessageBox::error(parentWidget(), i18n("Gnokii interface is not available.\n"
00715         "Please ask your distributor to add gnokii at compile time."));
00716 
00717 #else
00718     if (KMessageBox::Continue != KMessageBox::warningContinueCancel(parentWidget(),
00719         i18n("<qt>Please connect your Mobile Phone to your computer and press "
00720              "<b>Continue</b> to start exporting the selected personal contacts.<br><br>"
00721              "Please note that if your Mobile Phone is not properly connected "
00722              "the following detection phase might take up to two minutes, during which "
00723              "KAddressbook will behave unresponsively.</qt>") ))
00724       return false;
00725 
00726     m_progressDlg = new KProgressDialog( parentWidget(), "importwidget",
00727         i18n("Mobile Phone Export"),
00728         i18n("<qt><center>Establishing connection to the Mobile Phone.<br><br>"
00729              "Please wait...</center></qt>") );
00730     m_progressDlg->setAllowCancel(true);
00731     m_progressDlg->progressBar()->setProgress(0);
00732     m_progressDlg->progressBar()->setCenterIndicator(true);
00733     m_progressDlg->setModal(true);
00734     m_progressDlg->setMinimumSize(450,350); // not honored yet - seems like bug in KProgressDialog
00735     m_progressDlg->show();
00736     processEvents();
00737 
00738     KProgress* progress = (KProgress*)m_progressDlg->progressBar();
00739 
00740     KABC::AddresseeList::ConstIterator it;
00741     QStringList failedList;
00742 
00743     gn_error error;
00744     bool deleteLabelInitialized = false;
00745 
00746 #if (QT_VERSION >= 0x030300)
00747     m_progressDlg->setCursor( Qt::BusyCursor );
00748 #endif
00749     QString errStr = businit();
00750     m_progressDlg->unsetCursor();
00751 
00752     if (!errStr.isEmpty()) {
00753         KMessageBox::error(parentWidget(), errStr);
00754         delete m_progressDlg;
00755         return false;
00756     }
00757 
00758     GNOKII_DEBUG("GNOKII export filter started.\n");
00759 
00760     gn_memory_type memtype = GN_MT_ME;  // internal phone memory
00761 
00762     int phone_count;    // num entries in phone
00763     bool overwrite_phone_entries = false;
00764     int phone_entry_no, entries_written;
00765     bool entry_empty;
00766 
00767     // get number of entries in this phone memory
00768     gn_memory_status memstat;
00769     error = read_phone_memstat(memtype, &memstat);
00770     if (error == GN_ERR_NONE) {
00771         GNOKII_DEBUG("Writing to internal phone memory.\n");
00772     } else {
00773         memtype = GN_MT_SM; // try SIM card instead
00774         error = read_phone_memstat(memtype, &memstat);
00775         if (error != GN_ERR_NONE)
00776             goto finish;
00777         GNOKII_DEBUG("Writing to SIM card memory.\n");
00778     }
00779     phone_count = memstat.used;
00780 
00781     if (memstat.free >= (int) list.count()) {
00782         if (KMessageBox::No == KMessageBox::questionYesNo(parentWidget(),
00783             i18n("<qt>Do you want the selected contacts to be <b>appended</b> to "
00784                  "the current mobile phonebook or should they <b>replace</b> all "
00785                  "currently existing phonebook entries ?<br><br>"
00786                  "Please note, that in case you choose to replace the phonebook "
00787                  "entries, every contact in your phone will be deleted and only "
00788                  "the newly exported contacts will be available from inside your phone.</qt>"),
00789             i18n("Export to Mobile Phone"),
00790             KGuiItem(i18n("&Append to Current Phonebook")),
00791             KGuiItem(i18n("&Replace Current Phonebook with New Contacts")) ) )
00792                 overwrite_phone_entries = true;
00793     }
00794 
00795     progress->setTotalSteps(list.count());
00796     entries_written = 0;
00797     progress->setProgress(entries_written);
00798     m_progressDlg->setButtonText(i18n("&Stop Export"));
00799     m_progressDlg->setLabel(i18n("<qt>Exporting <b>%1</b> contacts to the <b>%2</b> "
00800             "of the Mobile Phone.<br><br>%3</qt>")
00801         .arg(list.count())
00802         .arg(buildMemoryTypeString(memtype))
00803         .arg(buildPhoneInfoString(memstat)) );
00804 
00805     // Now run the loop...
00806     phone_entry_no = 1;
00807     for ( it = list.begin(); it != list.end(); ++it ) {
00808         const KABC::Addressee *addr = &(*it);
00809         if (addr->isEmpty())
00810             continue;
00811         // don't write back SIM-card entries !
00812         if (addr->custom(APP, "X_GSM_STORE_AT").startsWith("SM"))
00813             continue;
00814 
00815         progress->setProgress(entries_written++);
00816 
00817 try_next_phone_entry:
00818         this_filter->processEvents();
00819         if (m_progressDlg->wasCancelled())
00820             break;
00821 
00822         // End of phone memory reached ?
00823         if (phone_entry_no > (memstat.used + memstat.free))
00824             break;
00825 
00826         GNOKII_DEBUG(QString("Try to write entry '%1' at phone_entry_no=%2, phone_count=%3\n")
00827                 .arg(addr->realName()).arg(phone_entry_no).arg(phone_count));
00828 
00829         error = GN_ERR_NONE;
00830 
00831         // is this phone entry empty ?
00832         entry_empty = phone_entry_empty(phone_entry_no, memtype);
00833         if (overwrite_phone_entries) {
00834             // overwrite this phonebook entry ...
00835             if (!entry_empty)
00836                 phone_count--;
00837             error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
00838             phone_entry_no++;
00839         } else {
00840             // add this phonebook entry if possible ...
00841             if (entry_empty) {
00842                 error = xxport_phone_write_entry( phone_entry_no, memtype, addr);
00843                 phone_entry_no++;
00844             } else {
00845                 phone_entry_no++;
00846                 goto try_next_phone_entry;
00847             }
00848         }
00849 
00850         if (error != GN_ERR_NONE)
00851             failedList.append(addr->realName());
00852 
00853         // break if we got an error on the first entry
00854         if (error != GN_ERR_NONE && it==list.begin())
00855             break;
00856 
00857     } // for()
00858 
00859     // if we wanted to overwrite all entries, make sure, that we also
00860     // delete all remaining entries in the mobile phone.
00861     while (overwrite_phone_entries && error==GN_ERR_NONE && phone_count>0) {
00862         if (m_progressDlg->wasCancelled())
00863             break;
00864         if (!deleteLabelInitialized) {
00865             m_progressDlg->setLabel(
00866                 i18n("<qt><center>"
00867                      "All selected contacts have been sucessfully copied to "
00868                      "the Mobile Phone.<br><br>"
00869                      "Please wait until all remaining orphaned contacts from "
00870                      "the Mobile Phone have been deleted.</center></qt>") );
00871             m_progressDlg->setButtonText(i18n("&Stop Delete"));
00872             deleteLabelInitialized = true;
00873             progress->setTotalSteps(phone_count);
00874             entries_written = 0;
00875             progress->setProgress(entries_written);
00876             this_filter->processEvents();
00877         }
00878         if (phone_entry_no > (memstat.used + memstat.free))
00879             break;
00880         entry_empty = phone_entry_empty(phone_entry_no, memtype);
00881         if (!entry_empty) {
00882             error = xxport_phone_delete_entry(phone_entry_no, memtype);
00883             phone_count--;
00884             progress->setProgress(++entries_written);
00885             this_filter->processEvents();
00886         }
00887         phone_entry_no++;
00888     }
00889 
00890 finish:
00891     m_progressDlg->setLabel(i18n("Export to phone finished."));
00892     this_filter->processEvents();
00893 
00894     GNOKII_DEBUG("GNOKII export filter finished.\n");
00895 
00896     busterminate();
00897     delete m_progressDlg;
00898 
00899     if (!failedList.isEmpty()) {
00900         GNOKII_DEBUG(QString("Failed to export: %1\n").arg(failedList.join(", ")));
00901         KMessageBox::informationList(parentWidget(),
00902                         i18n("<qt>The following contacts could not be exported to the Mobile Phone. "
00903                  "Possible Reasons for this problem could be:<br><ul>"
00904                  "<li>The contacts contain more information per entry than the phone can store.</li>"
00905                  "<li>Your phone does not allow to store multiple addresses, emails, homepages, ...</li>"
00906                  "<li>other storage size related problems.</li>"
00907                  "</ul>"
00908                  "To avoid those kind of problems in the future please reduce the amount of different "
00909                  "fields in the above contacts.</qt>"),
00910             failedList,
00911             i18n("Mobile Phone Export") );
00912     }
00913 
00914 #endif
00915 
00916     return true;
00917 }
00918 
00919 #include "gnokii_xxport.moc"
00920 /* vim: set sts=4 ts=4 sw=4: */
00921 
KDE Logo
This file is part of the documentation for kaddressbook Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 2 09:54:30 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003