/* Copyright (c) 2001 Dawit Alemayehu This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License (LGPL) 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uachangerplugin.h" typedef KGenericFactory UAChangerPluginFactory; static const TDEAboutData aboutdata("uachangerplugin", I18N_NOOP("Change Browser Identification") , "1.0" ); K_EXPORT_COMPONENT_FACTORY (libuachangerplugin, UAChangerPluginFactory (&aboutdata)) #define UA_PTOS(x) (*it)->property(x).toString() #define TQFL1(x) TQString::fromLatin1(x) UAChangerPlugin::UAChangerPlugin( TQObject* parent, const char* name, const TQStringList & ) :KParts::Plugin( parent, name ), m_bSettingsLoaded(false), m_part(0L), m_config(0L) { setInstance(UAChangerPlugin::instance()); m_pUAMenu = new KActionMenu( i18n("Change Browser &Identification"), "agent", actionCollection(), "changeuseragent" ); m_pUAMenu->setDelayed( false ); connect( m_pUAMenu->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( slotAboutToShow() ) ); m_pUAMenu->setEnabled ( false ); if ( parent && parent->inherits( "KHTMLPart" ) ) { m_part = static_cast(parent); connect( m_part, TQT_SIGNAL(started(TDEIO::Job*)), this, TQT_SLOT(slotStarted(TDEIO::Job*)) ); } } UAChangerPlugin::~UAChangerPlugin() { saveSettings(); slotReloadDescriptions(); } void UAChangerPlugin::slotReloadDescriptions() { delete m_config; m_config = 0L; } void UAChangerPlugin::parseDescFiles() { KTrader::OfferList list = KTrader::self()->query("UserAgentStrings"); if ( list.count() == 0 ) return; m_mapAlias.clear(); m_lstAlias.clear(); m_lstIdentity.clear(); struct utsname utsn; uname( &utsn ); TQStringList languageList = TDEGlobal::locale()->languageList(); if ( languageList.count() ) { TQStringList::Iterator it = languageList.find(TQFL1("C")); if( it != languageList.end() ) { if( languageList.contains( TQFL1("en") ) > 0 ) languageList.remove( it ); else (*it) = TQFL1("en"); } } KTrader::OfferList::ConstIterator it = list.begin(); KTrader::OfferList::ConstIterator lastItem = list.end(); for ( ; it != lastItem; ++it ) { TQString tmp = UA_PTOS("X-TDE-UA-FULL"); TQString tag = UA_PTOS("X-TDE-UA-TAG"); if(tag != "IE" && tag != "NN" && tag != "MOZ") tag = "OTHER"; if ( (*it)->property("X-TDE-UA-DYNAMIC-ENTRY").toBool() ) { tmp.replace( TQFL1("appSysName"), TQFL1(utsn.sysname) ); tmp.replace( TQFL1("appSysRelease"), TQFL1(utsn.release) ); tmp.replace( TQFL1("appMachineType"), TQFL1(utsn.machine) ); tmp.replace( TQFL1("appLanguage"), languageList.join(TQFL1(", ")) ); tmp.replace( TQFL1("appPlatform"), TQFL1("X11") ); } if ( m_lstIdentity.contains(tmp) ) continue; // Ignore dups! m_lstIdentity << tmp; tmp = TQString("%1 %2").arg(UA_PTOS("X-TDE-UA-SYSNAME")).arg(UA_PTOS("X-TDE-UA-SYSRELEASE")); if ( tmp.stripWhiteSpace().isEmpty() ) { if(tag == "NN" || tag == "IE" || tag == "MOZ") tmp = i18n("Version %1").arg(UA_PTOS("X-TDE-UA-VERSION")); else tmp = TQString("%1 %2").arg(UA_PTOS("X-TDE-UA-NAME")).arg(UA_PTOS("X-TDE-UA-VERSION")); } else { if(tag == "NN" || tag == "IE" || tag == "MOZ") tmp = i18n("Version %1 on %2").arg(UA_PTOS("X-TDE-UA-VERSION")).arg(tmp); else tmp = i18n("%1 %2 on %3").arg(UA_PTOS("X-TDE-UA-NAME")).arg(UA_PTOS("X-TDE-UA-VERSION")).arg(tmp); } m_lstAlias << tmp; /* sort in this UA Alias alphabetically */ BrowserGroup ualist = m_mapAlias[tag]; BrowserGroup::Iterator e = ualist.begin(); while ( !tmp.isEmpty() && e != ualist.end() ) { if ( m_lstAlias[(*e)] > tmp ) { ualist.insert( e, m_lstAlias.count()-1 ); tmp = TQString(); } ++e; } if ( !tmp.isEmpty() ) ualist.append( m_lstAlias.count()-1 ); m_mapAlias[tag] = ualist; if(tag == "OTHER") m_mapBrowser[tag] = i18n("Other"); else m_mapBrowser[tag] = UA_PTOS("X-TDE-UA-NAME"); } } void UAChangerPlugin::slotStarted( TDEIO::Job* ) { m_currentURL = m_part->url(); // This plugin works on local files, http[s], and webdav[s]. TQString proto = m_currentURL.protocol(); if (m_currentURL.isLocalFile() || proto.startsWith("http") || proto.startsWith("webdav")) { if (!m_pUAMenu->isEnabled()) m_pUAMenu->setEnabled ( true ); } else { m_pUAMenu->setEnabled ( false ); } } void UAChangerPlugin::slotAboutToShow() { if (!m_config) { m_config = new TDEConfig( "tdeio_httprc" ); parseDescFiles(); } if (!m_bSettingsLoaded) loadSettings(); int count = 0; m_pUAMenu->popupMenu()->clear(); m_pUAMenu->popupMenu()->insertTitle(i18n("Identify As")); // imho title doesn't need colon.. TQString host = m_currentURL.isLocalFile() ? TQFL1("localhost") : m_currentURL.host(); m_currentUserAgent = KProtocolManager::userAgentForHost(host); //kdDebug(90130) << "User Agent: " << m_currentUserAgent << endl; int id = m_pUAMenu->popupMenu()->insertItem( i18n("Default Identification"), this, TQT_SLOT(slotDefault()), 0, ++count ); if( m_currentUserAgent == KProtocolManager::defaultUserAgent() ) m_pUAMenu->popupMenu()->setItemChecked(id, true); m_pUAMenu->popupMenu()->insertSeparator(); AliasConstIterator map = m_mapAlias.begin(); for( ; map != m_mapAlias.end(); ++map ) { KPopupMenu *browserMenu = new KPopupMenu; BrowserGroup::ConstIterator e = map.data().begin(); for( ; e != map.data().end(); ++e ) { int id = browserMenu->insertItem( m_lstAlias[*e], this, TQT_SLOT(slotItemSelected(int)), 0, *e ); if (m_lstIdentity[(*e)] == m_currentUserAgent) browserMenu->setItemChecked(id, true); } m_pUAMenu->popupMenu()->insertItem( m_mapBrowser[map.key()], browserMenu ); } m_pUAMenu->popupMenu()->insertSeparator(); /* useless here, imho.. m_pUAMenu->popupMenu()->insertItem( i18n("Reload Identifications"), this, TQT_SLOT(slotReloadDescriptions()), 0, ++count );*/ m_pUAMenu->popupMenu()->insertItem( i18n("Apply to Entire Site"), this, TQT_SLOT(slotApplyToDomain()), 0, ++count ); m_pUAMenu->popupMenu()->setItemChecked(count, m_bApplyToDomain); m_pUAMenu->popupMenu()->insertItem( i18n("Configure..."), this, TQT_SLOT(slotConfigure())); } void UAChangerPlugin::slotConfigure() { KService::Ptr service = KService::serviceByDesktopName ("useragent"); if (service) KRun::runCommand (service->exec ()); } void UAChangerPlugin::slotItemSelected( int id ) { if (m_lstIdentity[id] == m_currentUserAgent) return; TQString host; m_currentUserAgent = m_lstIdentity[id]; host = m_currentURL.isLocalFile() ? TQFL1("localhost") : filterHost( m_currentURL.host() ); m_config->setGroup( host.lower() ); m_config->writeEntry( "UserAgent", m_currentUserAgent ); m_config->sync(); // Update the io-slaves... updateIOSlaves (); // Reload the page with the new user-agent string m_part->openURL( m_currentURL ); } void UAChangerPlugin::slotDefault() { if( m_currentUserAgent == KProtocolManager::defaultUserAgent() ) return; // don't flicker! // We have no choice but delete all higher domain level settings here since it // affects what will be matched. TQStringList partList = TQStringList::split('.', m_currentURL.host(), false); if ( !partList.isEmpty() ) { partList.remove(partList.begin()); TQStringList domains; // Remove the exact name match... domains << m_currentURL.host (); while (partList.count()) { if (partList.count() == 2) if (partList[0].length() <=2 && partList[1].length() ==2) break; if (partList.count() == 1) break; domains << partList.join(TQFL1(".")); partList.remove(partList.begin()); } for (TQStringList::Iterator it = domains.begin(); it != domains.end(); it++) { //kdDebug () << "Domain to remove: " << *it << endl; if ( m_config->hasGroup(*it) ) m_config->deleteGroup(*it); else if( m_config->hasKey(*it) ) m_config->deleteEntry(*it); } } else if ( m_currentURL.isLocalFile() && m_config->hasGroup( "localhost" ) ) m_config->deleteGroup( "localhost" ); m_config->sync(); // Reset some internal variables and inform the http io-slaves of the changes. m_currentUserAgent = KProtocolManager::defaultUserAgent(); // Update the http io-slaves. updateIOSlaves(); // Reload the page with the default user-agent m_part->openURL( m_currentURL ); } void UAChangerPlugin::updateIOSlaves () { // Inform running http(s) io-slaves about the change... if (!DCOPRef("*", "TDEIO::Scheduler").send("reparseSlaveConfiguration", TQString())) kdWarning() << "UAChangerPlugin::updateIOSlaves: Unable to update running application!" << endl; } TQString UAChangerPlugin::filterHost(const TQString &hostname) { TQRegExp rx; // Check for IPv4 address rx.setPattern ("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); if (rx.exactMatch (hostname)) return hostname; // Check for IPv6 address here... rx.setPattern ("^\\[.*\\]$"); if (rx.exactMatch (hostname)) return hostname; // Return the TLD if apply to domain or return (m_bApplyToDomain ? findTLD(hostname): hostname); } TQString UAChangerPlugin::findTLD (const TQString &hostname) { TQStringList domains; TQStringList partList = TQStringList::split('.', hostname, false); if (partList.count()) partList.remove(partList.begin()); // Remove hostname while(partList.count()) { // We only have a TLD left. if (partList.count() == 1) break; if( partList.count() == 2 ) { // The .name domain uses ..name // Although the TLD is striclty speaking .name, for our purpose // it should be .name since people should not be able // to set cookies for everyone with the same surname. // Matches .name if( partList[1].lower() == TQFL1("name") ) { break; } else if( partList[1].length() == 2 ) { // If this is a TLD, we should stop. (e.g. co.uk) // We assume this is a TLD if it ends with .xx.yy or .x.yy if (partList[0].length() <= 2) break; // This is a TLD. // Catch some TLDs that we miss with the previous check // e.g. com.au, org.uk, mil.co TQCString t = partList[0].lower().utf8(); if ((t == "com") || (t == "net") || (t == "org") || (t == "gov") || (t == "edu") || (t == "mil") || (t == "int")) break; } } domains.append(partList.join(TQFL1("."))); partList.remove(partList.begin()); // Remove part } if( domains.isEmpty() ) return hostname; return domains[0]; } void UAChangerPlugin::saveSettings() { if(!m_bSettingsLoaded) return; TDEConfig cfg ("uachangerrc", false, false); cfg.setGroup ("General"); cfg.writeEntry ("applyToDomain", m_bApplyToDomain); } void UAChangerPlugin::loadSettings() { TDEConfig cfg ("uachangerrc", false, false); cfg.setGroup ("General"); m_bApplyToDomain = cfg.readBoolEntry ("applyToDomain", true); m_bSettingsLoaded = true; } void UAChangerPlugin::slotApplyToDomain() { m_bApplyToDomain = !m_bApplyToDomain; } #include "uachangerplugin.moc"