/* This file is part of libtdeabc. Copyright (c) 2003 Helge Deller <deller@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Useful links: - http://tldp.org/HOWTO/LDAP-Implementation-HOWTO/schemas.html - http://www.faqs.org/rfcs/rfc2849.html Not yet handled items: - objectclass microsoftaddressbook - info, - initials, - otherfacsimiletelephonenumber, - otherpager, - physicaldeliveryofficename, */ #include <tqstring.h> #include <tqstringlist.h> #include <tqregexp.h> #include <tqtextstream.h> #include <tdelocale.h> #include <kdebug.h> #include <kmdcodec.h> #include "addressee.h" #include "address.h" #include "ldif.h" #include "ldifconverter.h" #include "vcardconverter.h" using namespace TDEABC; /* generate LDIF stream */ bool LDIFConverter::addresseeToLDIF( const AddresseeList &addrList, TQString &str ) { AddresseeList::ConstIterator it; for ( it = addrList.begin(); it != addrList.end(); ++it ) { addresseeToLDIF( *it, str ); } return true; } static void ldif_out( TQTextStream &t, TQString formatStr, TQString value ) { if ( value.isEmpty() ) return; TQCString txt = LDIF::assembleLine( formatStr, value, 72 ); // write the string t << TQString::fromUtf8(txt) << "\n"; } bool LDIFConverter::addresseeToLDIF( const Addressee &addr, TQString &str ) { if ( addr.isEmpty() ) return false; TQTextStream t( str, IO_WriteOnly|IO_Append ); t.setEncoding( TQTextStream::UnicodeUTF8 ); const Address homeAddr = addr.address( Address::Home ); const Address workAddr = addr.address( Address::Work ); ldif_out( t, "dn", TQString( "cn=%1,mail=%2" ) .arg( TQString(addr.formattedName()).simplifyWhiteSpace() ) .arg( addr.preferredEmail() ) ); ldif_out( t, "givenname", addr.givenName() ); ldif_out( t, "sn", addr.familyName() ); ldif_out( t, "cn", TQString(addr.formattedName()).simplifyWhiteSpace() ); ldif_out( t, "uid", addr.uid() ); ldif_out( t, "nickname", addr.nickName() ); ldif_out( t, "xmozillanickname", addr.nickName() ); ldif_out( t, "mail", addr.preferredEmail() ); if ( addr.emails().count() > 1 ) ldif_out( t, "mozillasecondemail", addr.emails()[ 1 ] ); //ldif_out( t, "mozilla_AIMScreenName: %1\n", "screen_name" ); ldif_out( t, "telephonenumber", addr.phoneNumber( PhoneNumber::Work ).number() ); ldif_out( t, "facsimiletelephonenumber", addr.phoneNumber( PhoneNumber::Fax ).number() ); ldif_out( t, "homephone", addr.phoneNumber( PhoneNumber::Home ).number() ); ldif_out( t, "mobile", addr.phoneNumber( PhoneNumber::Cell ).number() ); // Netscape 7 ldif_out( t, "cellphone", addr.phoneNumber( PhoneNumber::Cell ).number() ); // Netscape 4.x ldif_out( t, "pager", addr.phoneNumber( PhoneNumber::Pager ).number() ); ldif_out( t, "pagerphone", addr.phoneNumber( PhoneNumber::Pager ).number() ); ldif_out( t, "streethomeaddress", homeAddr.street() ); ldif_out( t, "postalcode", workAddr.postalCode() ); ldif_out( t, "postofficebox", workAddr.postOfficeBox() ); TQStringList streets = TQStringList::split( '\n', homeAddr.street() ); if ( streets.count() > 0 ) ldif_out( t, "homepostaladdress", streets[ 0 ] ); // Netscape 7 if ( streets.count() > 1 ) ldif_out( t, "mozillahomepostaladdress2", streets[ 1 ] ); // Netscape 7 ldif_out( t, "mozillahomelocalityname", homeAddr.locality() ); // Netscape 7 ldif_out( t, "mozillahomestate", homeAddr.region() ); ldif_out( t, "mozillahomepostalcode", homeAddr.postalCode() ); ldif_out( t, "mozillahomecountryname", Address::ISOtoCountry(homeAddr.country()) ); ldif_out( t, "locality", workAddr.locality() ); ldif_out( t, "streetaddress", workAddr.street() ); // Netscape 4.x streets = TQStringList::split( '\n', workAddr.street() ); if ( streets.count() > 0 ) ldif_out( t, "postaladdress", streets[ 0 ] ); if ( streets.count() > 1 ) ldif_out( t, "mozillapostaladdress2", streets[ 1 ] ); ldif_out( t, "countryname", Address::ISOtoCountry(workAddr.country()) ); ldif_out( t, "l", workAddr.locality() ); ldif_out( t, "c", Address::ISOtoCountry(workAddr.country()) ); ldif_out( t, "st", workAddr.region() ); ldif_out( t, "title", addr.title() ); ldif_out( t, "vocation", addr.prefix() ); ldif_out( t, "ou", addr.role() ); ldif_out( t, "o", addr.organization() ); ldif_out( t, "organization", addr.organization() ); ldif_out( t, "organizationname", addr.organization() ); // Compatibility with older tdeabc versions. if ( !addr.department().isEmpty() ) ldif_out( t, "department", addr.department() ); else ldif_out( t, "department", addr.custom("KADDRESSBOOK", "X-Department") ); ldif_out( t, "workurl", addr.url().prettyURL() ); ldif_out( t, "homeurl", addr.url().prettyURL() ); ldif_out( t, "description", addr.note() ); if (addr.revision().isValid()) ldif_out(t, "modifytimestamp", dateToVCardString( addr.revision()) ); t << "objectclass: top\n"; t << "objectclass: person\n"; t << "objectclass: organizationalPerson\n"; t << "\n"; return true; } /* convert from LDIF stream */ bool LDIFConverter::LDIFToAddressee( const TQString &str, AddresseeList &addrList, TQDateTime dt ) { if (str.isEmpty()) return true; bool endldif = false, end = false; LDIF ldif; LDIF::ParseVal ret; const char *latinstr = str.latin1(); int latinstrlen = tqstrlen( latinstr ); TQByteArray data; Addressee a; Address homeAddr, workAddr; data.setRawData( latinstr, latinstrlen ); ldif.setLDIF( data ); if (!dt.isValid()) dt = TQDateTime::currentDateTime(); a.setRevision(dt); homeAddr = Address( Address::Home ); workAddr = Address( Address::Work ); do { ret = ldif.nextItem(); switch ( ret ) { case LDIF::Item: { TQString fieldname = ldif.attr().lower(); TQString value = TQString::fromUtf8( ldif.val(), ldif.val().size() ); evaluatePair( a, homeAddr, workAddr, fieldname, value ); break; } case LDIF::EndEntry: // if the new address is not empty, append it if ( !a.formattedName().isEmpty() || !a.name().isEmpty() || !a.familyName().isEmpty() ) { if ( !homeAddr.isEmpty() ) a.insertAddress( homeAddr ); if ( !workAddr.isEmpty() ) a.insertAddress( workAddr ); addrList.append( a ); } a = Addressee(); a.setRevision(dt); homeAddr = Address( Address::Home ); workAddr = Address( Address::Work ); break; case LDIF::MoreData: { if ( endldif ) end = true; else { ldif.endLDIF(); endldif = true; break; } } default: break; } } while ( !end ); data.resetRawData( latinstr, latinstrlen ); return true; } bool LDIFConverter::evaluatePair( Addressee &a, Address &homeAddr, Address &workAddr, TQString &fieldname, TQString &value ) { if ( fieldname == TQString::fromLatin1( "dn" ) ) // ignore & return false! return false; if ( fieldname.startsWith("#") ) { return true; } if ( fieldname.isEmpty() && !a.note().isEmpty() ) { // some LDIF export filters are borken and add additional // comments on stand-alone lines. Just add them to the notes for now. a.setNote( a.note() + "\n" + value ); return true; } if ( fieldname == TQString::fromLatin1( "givenname" ) ) { a.setGivenName( value ); return true; } if ( fieldname == TQString::fromLatin1( "xmozillanickname") || fieldname == TQString::fromLatin1( "nickname") ) { a.setNickName( value ); return true; } if ( fieldname == TQString::fromLatin1( "sn" ) ) { a.setFamilyName( value ); return true; } if ( fieldname == TQString::fromLatin1( "uid" ) ) { a.setUid( value ); return true; } if ( fieldname == TQString::fromLatin1( "mail" ) || fieldname == TQString::fromLatin1( "mozillasecondemail" ) ) { // mozilla if ( a.emails().findIndex( value ) == -1 ) a.insertEmail( value ); return true; } if ( fieldname == TQString::fromLatin1( "title" ) ) { a.setTitle( value ); return true; } if ( fieldname == TQString::fromLatin1( "vocation" ) ) { a.setPrefix( value ); return true; } if ( fieldname == TQString::fromLatin1( "cn" ) ) { a.setFormattedName( value ); return true; } if ( fieldname == TQString::fromLatin1( "o" ) || fieldname == TQString::fromLatin1( "organization" ) || // Exchange fieldname == TQString::fromLatin1( "organizationname" ) ) { // Exchange a.setOrganization( value ); return true; } if ( fieldname == TQString::fromLatin1( "description" ) ) { addComment: if ( !a.note().isEmpty() ) a.setNote( a.note() + "\n" ); a.setNote( a.note() + value ); return true; } if ( fieldname == TQString::fromLatin1( "custom1" ) || fieldname == TQString::fromLatin1( "custom2" ) || fieldname == TQString::fromLatin1( "custom3" ) || fieldname == TQString::fromLatin1( "custom4" ) ) { goto addComment; } if ( fieldname == TQString::fromLatin1( "homeurl" ) || fieldname == TQString::fromLatin1( "workurl" ) ) { if (a.url().isEmpty()) { a.setUrl( KURL( value ) ); return true; } if ( a.url().prettyURL() == KURL(value).prettyURL() ) return true; // TODO: current version of tdeabc only supports one URL. // TODO: change this with KDE 4 } if ( fieldname == TQString::fromLatin1( "homephone" ) ) { a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Home ) ); return true; } if ( fieldname == TQString::fromLatin1( "telephonenumber" ) ) { a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Work ) ); return true; } if ( fieldname == TQString::fromLatin1( "mobile" ) ) { // mozilla/Netscape 7 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Cell ) ); return true; } if ( fieldname == TQString::fromLatin1( "cellphone" ) ) { a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Cell ) ); return true; } if ( fieldname == TQString::fromLatin1( "pager" ) || // mozilla fieldname == TQString::fromLatin1( "pagerphone" ) ) { // mozilla a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Pager ) ); return true; } if ( fieldname == TQString::fromLatin1( "facsimiletelephonenumber" ) ) { a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Fax ) ); return true; } if ( fieldname == TQString::fromLatin1( "xmozillaanyphone" ) ) { // mozilla a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Work ) ); return true; } if ( fieldname == TQString::fromLatin1( "street" ) || fieldname == TQString::fromLatin1( "streethomeaddress" ) ) { homeAddr.setStreet( value ); return true; } if ( fieldname == TQString::fromLatin1( "postaladdress" ) ) { // mozilla workAddr.setStreet( value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillapostaladdress2" ) ) { // mozilla workAddr.setStreet( workAddr.street() + TQString::fromLatin1( "\n" ) + value ); return true; } if ( fieldname == TQString::fromLatin1( "postalcode" ) ) { workAddr.setPostalCode( value ); return true; } if ( fieldname == TQString::fromLatin1( "postofficebox" ) ) { workAddr.setPostOfficeBox( value ); return true; } if ( fieldname == TQString::fromLatin1( "homepostaladdress" ) ) { // Netscape 7 homeAddr.setStreet( value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillahomepostaladdress2" ) ) { // mozilla homeAddr.setStreet( homeAddr.street() + TQString::fromLatin1( "\n" ) + value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillahomelocalityname" ) ) { // mozilla homeAddr.setLocality( value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillahomestate" ) ) { // mozilla homeAddr.setRegion( value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillahomepostalcode" ) ) { // mozilla homeAddr.setPostalCode( value ); return true; } if ( fieldname == TQString::fromLatin1( "mozillahomecountryname" ) ) { // mozilla if ( value.length() <= 2 ) value = Address::ISOtoCountry(value); homeAddr.setCountry( value ); return true; } if ( fieldname == TQString::fromLatin1( "locality" ) ) { workAddr.setLocality( value ); return true; } if ( fieldname == TQString::fromLatin1( "streetaddress" ) ) { // Netscape 4.x workAddr.setStreet( value ); return true; } if ( fieldname == TQString::fromLatin1( "countryname" ) || fieldname == TQString::fromLatin1( "c" ) ) { // mozilla if ( value.length() <= 2 ) value = Address::ISOtoCountry(value); workAddr.setCountry( value ); return true; } if ( fieldname == TQString::fromLatin1( "l" ) ) { // mozilla workAddr.setLocality( value ); return true; } if ( fieldname == TQString::fromLatin1( "st" ) ) { workAddr.setRegion( value ); return true; } if ( fieldname == TQString::fromLatin1( "ou" ) ) { a.setRole( value ); return true; } if ( fieldname == TQString::fromLatin1( "department" ) ) { a.setDepartment( value ); return true; } if ( fieldname == TQString::fromLatin1( "member" ) ) { // this is a mozilla list member (cn=xxx, mail=yyy) TQStringList list( TQStringList::split( ',', value ) ); TQString name, email; TQStringList::Iterator it; for ( it = list.begin(); it != list.end(); ++it ) { if ( (*it).startsWith( "cn=" ) ) name = (*it).mid( 3 ).stripWhiteSpace(); if ( (*it).startsWith( "mail=" ) ) email = (*it).mid( 5 ).stripWhiteSpace(); } if ( !name.isEmpty() && !email.isEmpty() ) email = " <" + email + ">"; a.insertEmail( name + email ); a.insertCategory( i18n( "List of Emails" ) ); return true; } if ( fieldname == TQString::fromLatin1( "modifytimestamp" ) ) { if (value == TQString::fromLatin1("0Z")) // ignore return true; TQDateTime dt = VCardStringToDate( value ); if ( dt.isValid() ) { a.setRevision(dt); return true; } } if ( fieldname == TQString::fromLatin1( "objectclass" ) ) // ignore return true; kdWarning() << TQString(TQString("LDIFConverter: Unknown field for '%1': '%2=%3'\n") .arg(a.formattedName()).arg(fieldname).arg(value)); return true; } /* The following functions are obsoleted. Similar functionality can be found * in the LDIF class */ bool LDIFConverter::parseSingleLine( Addressee &a, Address &homeAddr, Address &workAddr, TQString &line ) { if ( line.isEmpty() ) return true; TQString fieldname, value; TQByteArray val; LDIF::splitLine( line.latin1(), fieldname, val ); value = TQString::fromUtf8( val.data(), val.size() ); return evaluatePair( a, homeAddr, workAddr, fieldname, value); } bool LDIFConverter::splitLine( TQString &line, TQString &fieldname, TQString &value) { TQByteArray val; bool ret = LDIF::splitLine( line.latin1(), fieldname, val ); value = TQString::fromUtf8( val.data(), val.size() ); return ret; } TQString LDIFConverter::makeLDIFfieldString( TQString formatStr, TQString value, bool allowEncode ) { if ( value.isEmpty() ) return TQString(); // append format if not given if (formatStr.find(':') == -1) formatStr.append(": %1\n"); // check if base64-encoding is needed bool printable = true; unsigned int i, len; len = value.length(); for (i = 0; i<len; ++i ) { if (!value[i].isPrint()) { printable = false; break; } } if (printable) // always encode if we find special chars... printable = (value.find('\n') == -1); if (!printable && allowEncode) { // encode to base64 value = KCodecs::base64Encode( value.utf8() ); int p = formatStr.find(':'); if (p>=0) formatStr.insert(p, ':'); } // generate the new string and split it to 72 chars/line TQCString txt = TQString(formatStr.arg(value)).utf8(); if (allowEncode) { len = txt.length(); if (len && txt[len-1] == '\n') --len; i = 72; while (i < len) { txt.insert(i, "\n "); i += 72+1; len += 2; } } return TQString::fromUtf8(txt); }