kaddressbook

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