Bonjour nick name is in Latin1 encoding
[kdenetwork.git] / kopete / protocols / bonjour / bonjouraccount.cpp
blob39afbf948d83bfa481a5a8ba0116c0b7b00d0be7
1 /*
2 bonjouraccount.cpp - Kopete Bonjour Protocol
4 Copyright (c) 2007 by Tejas Dinkar <tejas@gja.in>
5 Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
6 Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
8 *************************************************************************
9 * *
10 * This library is free software; you can redistribute it and/or *
11 * modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either *
13 * version 2 of the License, or (at your option) any later version. *
14 * *
15 *************************************************************************
18 #include "bonjouraccount.h"
20 #include <kaction.h>
21 #include <kdebug.h>
22 #include <klocale.h>
23 #include <kactionmenu.h>
24 #include <kmenu.h>
25 #include <kicon.h>
26 #include <kmessagebox.h>
28 #include <dnssd/publicservice.h>
29 #include <dnssd/servicebrowser.h>
31 #include "kopetemetacontact.h"
32 #include "kopetecontactlist.h"
33 #include "kopetedeletecontacttask.h"
34 #include "kopeteuiglobal.h"
36 #include "bonjourcontact.h"
37 #include "bonjourprotocol.h"
38 #include "bonjourcontactconnection.h"
41 static const char AvailabilityStatusAvailId[] = "avail";
42 static const char AvailabilityStatusAwayId[] = "away";
43 // TODO: add this status to the account
44 // static const char AvailabilityStatusDnDId[] = "dnd";
47 BonjourAccount::BonjourAccount( BonjourProtocol *parent, const QString& accountID )
48 : Kopete::Account ( parent, accountID )
50 // Init the myself contact
51 setMyself( new BonjourContact( this, accountId(), accountId(), Kopete::ContactList::self()->myself() ) );
52 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOffline );
54 service = NULL;
55 localServer = NULL;
56 listeningPort = 0;
57 browser = NULL;
59 // All Contacts Go To The Bonjour Group
60 bonjourGroup = Kopete::ContactList::self()->findGroup("Bonjour");
62 // Clean out Contacts from last time when kopete starts up
63 wipeOutAllContacts();
65 parseConfig();
68 void BonjourAccount::parseConfig()
70 username = configGroup()->readEntry("username").toLatin1();
71 firstName = configGroup()->readEntry("firstName").toLatin1();
72 lastName = configGroup()->readEntry("lastName").toLatin1();
73 emailAddress = configGroup()->readEntry("emailAddress").toLatin1();
76 BonjourAccount::~BonjourAccount()
78 if (isConnected())
79 disconnect();
81 #if 0
82 KActionMenu* BonjourAccount::actionMenu()
84 KActionMenu *mActionMenu = Kopete::Account::actionMenu();
86 return mActionMenu;
88 #endif
89 bool BonjourAccount::createContact(const QString& contactId, Kopete::MetaContact* parentContact)
91 BonjourContact* newContact = new BonjourContact( this, contactId, parentContact->displayName(), parentContact );
92 return newContact != 0L;
95 void BonjourAccount::setAway( bool away, const QString & /* reason */ )
97 if ( away )
98 slotGoAway();
99 else
100 slotGoOnline();
103 void BonjourAccount::setOnlineStatus(const Kopete::OnlineStatus& status, const Kopete::StatusMessage &reason, const OnlineStatusOptions& options)
105 if ( status.status() == Kopete::OnlineStatus::Online &&
106 myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline )
107 slotGoOnline();
108 else if (status.status() == Kopete::OnlineStatus::Online &&
109 myself()->onlineStatus().status() == Kopete::OnlineStatus::Away )
110 setAway( false, reason.message() );
111 else if ( status.status() == Kopete::OnlineStatus::Offline )
112 slotGoOffline();
113 else if ( status.status() == Kopete::OnlineStatus::Away )
114 slotGoAway( /* reason */ );
117 void BonjourAccount::setStatusMessage(const Kopete::StatusMessage& statusMessage)
119 Q_UNUSED(statusMessage);
120 /* Not used in bonjour */
123 // This Function Starts a new Local Server
124 // It runs on local port listeningPort
125 // Make Sure IP Tables lets this port through!!
126 bool BonjourAccount::startLocalServer()
128 int port = 5298;
130 localServer = new QTcpServer();
132 while (port < 5305) // No of Attempts
133 if (localServer->listen(QHostAddress::Any, port)) {
134 QObject::connect(localServer, SIGNAL(newConnection()),
135 this, SLOT(newIncomingConnection()));
136 listeningPort = port;
137 break;
139 else
140 port++;
142 kDebug()<<"Listening On Port: "<<listeningPort;
144 return localServer->isListening();
147 void BonjourAccount::startBrowse()
149 // Delete All Contacts Before we start looking for new ones
150 wipeOutAllContacts();
152 browser = new DNSSD::ServiceBrowser("_presence._tcp");
154 QObject::connect(browser,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),
155 this,SLOT(comingOnline(DNSSD::RemoteService::Ptr)));
156 QObject::connect(browser,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr)),
157 this,SLOT(goingOffline(DNSSD::RemoteService::Ptr)));
159 kDebug()<<"Starting Browser";
160 browser->startBrowse();
163 void BonjourAccount::startPublish()
165 if (! username.contains('@')) {
166 username.append("@");
167 username.append(DNSSD::ServiceBrowser::getLocalHostName().toUtf8());
170 service = new DNSSD::PublicService(username, "_presence._tcp", listeningPort);
172 QMap <QString, QByteArray> map;
173 map.insert("1st", firstName);
174 map.insert("email", emailAddress);
175 map.insert("last", lastName);
176 map.insert("node", "kopete");
177 map.insert("port.p2pj", QByteArray::number(listeningPort)); // This Number Actually Ignored
178 map.insert("status", AvailabilityStatusAvailId);
179 map.insert("txtvers", "1");
180 map.insert("vc", "!");
181 map.insert("ver", "0.0.1");
183 service->setTextData(map);
185 kDebug()<<"Starting Publish";
186 QObject::connect(service, SIGNAL(published(bool)), this, SLOT(published(bool)));
187 service->publishAsync();
190 void BonjourAccount::published(bool success)
192 // If we have sucessfully published, great :)
193 if (success) {
194 kDebug()<<"Publish Successful";
195 } else {
196 kDebug()<<"Publish Failed";
197 disconnect();
198 KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(), KMessageBox::Error,
199 i18n("Unable to publish Bonjour service. Currently the Bonjour plugin only works with Avahi."));
203 void BonjourAccount::connect( const Kopete::OnlineStatus& /* initialStatus */ )
205 if (username.isEmpty())
206 username = accountId().toUtf8();
208 if (DNSSD::ServiceBrowser::isAvailable() != DNSSD::ServiceBrowser::Working) {
209 KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(), KMessageBox::Error,
210 i18n("Unable to connect to the local mDNS server. Please ensure the Avahi daemon is running."));
211 return;
214 if (! startLocalServer())
215 return;
217 startPublish();
219 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOnline );
221 startBrowse();
224 void BonjourAccount::comingOnline(DNSSD::RemoteService::Ptr pointer)
226 if (! pointer->resolve()) {
227 kDebug()<<"Unable to Resolve! Dumping Contact";
230 kDebug()<<"Coming Online:"<<pointer->serviceName();
232 if (pointer->serviceName() == username) // Don't Add Ourselves
233 return;
235 QMap <QString, QByteArray> map = pointer->textData();
236 QString cfirst = map["1st"];
237 QString clast = map["last"];
239 QString display;
240 if (! cfirst.isEmpty() && ! clast.isEmpty())
241 display = cfirst + ' ' + clast;
242 else if (! cfirst.isEmpty())
243 display = cfirst;
244 else if (! clast.isEmpty())
245 display = clast;
246 else
247 display = pointer->serviceName().split('@')[0];
249 QString hostName = pointer->hostName();
250 kDebug()<<"Hostname is:"<<hostName;
251 if (! hostName.isEmpty()) {
252 QHostAddress hostAddress = DNSSD::ServiceBrowser::resolveHostName(hostName);
253 kDebug()<<"Host Address is:"<<hostAddress;
255 if (hostAddress != QHostAddress() ) {
256 Kopete::MetaContact *mc;
258 // FIXME: The Standard Has Specifications on What To Do in case of a clash
259 // We Ignore them over here.
260 mc = addContact(pointer->serviceName(), display, bonjourGroup);
262 BonjourContact *c = (BonjourContact *) mc->contacts()[0];
264 c->setremoteHostName(hostName);
265 c->setremoteAddress(hostAddress);
266 c->setremotePort(pointer->port());
267 c->settextdata(pointer->textData());
268 c->setusername(pointer->serviceName());
269 c->setOnlineStatus(Kopete::OnlineStatus::Online);
274 void BonjourAccount::goingOffline(DNSSD::RemoteService::Ptr pointer)
276 pointer->resolve();
278 // In case we have lost connection, this may return NULL
279 Kopete::Contact *c = contacts()[pointer->serviceName()];
281 if (c)
282 c->setOnlineStatus(Kopete::OnlineStatus::Offline);
285 void BonjourAccount::wipeOutContact(Kopete::Contact *c)
287 if (c == myself() || c == NULL)
288 return;
290 Kopete::MetaContact *mc = c->metaContact();
292 c->setOnlineStatus(Kopete::OnlineStatus::Offline);
293 mc->removeContact(c);
295 // FIXME: DeleteContact task should be extended and used
296 c->deleteLater();
298 if (mc->contacts().isEmpty())
299 Kopete::ContactList::self()->removeMetaContact(mc);
302 void BonjourAccount::wipeOutAllContacts()
304 QList <Kopete::Contact *> list = contacts().values();
306 for (QList <Kopete::Contact *>::Iterator i = list.begin(); i != list.end(); i++)
307 wipeOutContact(*i);
310 void BonjourAccount::disconnect()
312 wipeOutAllContacts();
314 if (browser) {
315 delete browser;
316 browser = NULL;
319 if (localServer) {
320 localServer->close();
321 delete localServer;
322 localServer = NULL;
325 listeningPort = 0;
327 if (service) {
328 service->stop();
329 delete service;
330 service = NULL;
333 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOffline );
336 void BonjourAccount::slotGoOnline ()
338 kDebug();
340 if (!isConnected())
341 connect();
342 else {
343 if (service) {
344 QMap <QString, QByteArray> map = service->textData();
345 map["status"] = AvailabilityStatusAvailId;
346 service->setTextData(map);
348 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOnline );
352 void BonjourAccount::slotGoAway ()
354 kDebug();
356 if (!isConnected ())
357 connect();
359 if (service) {
360 QMap <QString, QByteArray> map = service->textData();
361 map["status"] = AvailabilityStatusAwayId; // "dnd" would be another option here
362 service->setTextData(map);
365 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourAway );
369 void BonjourAccount::slotGoOffline ()
371 kDebug();
373 if (isConnected ())
374 disconnect ();
377 void BonjourAccount::receivedMessage( const QString &message )
379 // Look up the contact the message is from
380 QString from;
381 BonjourContact* messageSender;
383 from = message.section( ':', 0, 0 );
384 Kopete::Contact* contact = contacts()[from];
385 messageSender = dynamic_cast<BonjourContact *>( contact );
388 void BonjourAccount::setusername(const QByteArray &n_username)
390 username = n_username;
393 void BonjourAccount::setfirstName(const QByteArray &n_firstName)
395 firstName = n_firstName;
398 void BonjourAccount::setlastName(const QByteArray &n_lastName)
400 lastName = n_lastName;
403 void BonjourAccount::setemailAddress(const QByteArray &n_emailAddress)
405 emailAddress = n_emailAddress;
408 const QByteArray BonjourAccount::getusername() const
410 return username;
413 const QByteArray BonjourAccount::getfirstName() const
415 return firstName;
418 const QByteArray BonjourAccount::getlastName() const
420 return lastName;
423 const QByteArray BonjourAccount::getemailAddress() const
425 return emailAddress;
428 QList <BonjourContact *> BonjourAccount::getContactsByAddress(const QHostAddress &addr)
430 QList <BonjourContact *> list;
432 QList <Kopete::Contact *> c = contacts().values();
434 for (QList <Kopete::Contact *>::iterator i = c.begin(); i != c.end(); i++) {
435 BonjourContact *c = (BonjourContact *) *i;
436 if (c->isRemoteAddress(addr))
437 list<<c;
440 return list;
443 void BonjourAccount::newIncomingConnection()
445 // Get Next Connection
446 QTcpSocket *sock = localServer->nextPendingConnection();
448 BonjourContactConnection *bcc = new BonjourContactConnection(sock);
449 QObject::connect(bcc, SIGNAL(discoveredUserName(BonjourContactConnection *, const QString &)),
450 this, SLOT(discoveredUserName(BonjourContactConnection *, const QString &)));;
451 QObject::connect(bcc, SIGNAL(usernameNotInStream(BonjourContactConnection *)),
452 this, SLOT(usernameNotInStream(BonjourContactConnection *)));;
454 unknownConnections << bcc;
457 void BonjourAccount::discoveredUserName(BonjourContactConnection *conn, const QString &user)
459 kDebug()<<"User Making Contact (unverified): "<<user;
461 BonjourContact *c;
463 if (! (c = verifyUser(conn, user))) {
464 kDebug()<<"Ignoring Unverified User: "<<user;
465 return;
468 kDebug()<<"User Verified: "<<user;
470 unknownConnections.removeAll(conn);
472 c->setConnection(conn);
475 void BonjourAccount::usernameNotInStream(BonjourContactConnection *conn)
477 QList <BonjourContact *> list = getContactsByAddress(conn->getHostAddress());
479 kDebug()<<"Looking Up Via IP Address"<<conn->getHostAddress()<<list;
481 // Set this connection to first user in the list
482 if (list.size()) {
483 BonjourContact *c = list[0];
485 kDebug()<<"Assigned to Contact: "<<c->getusername();
487 unknownConnections.removeAll(conn);
489 conn->setRemoteAndLocal(c->getusername(), username);
490 c->setConnection(conn);
495 BonjourContact *BonjourAccount::verifyUser(BonjourContactConnection *conn, const QString &user)
497 // First Check the User Exists
498 if (! contacts().keys().contains(user))
499 return NULL;
501 BonjourContact *c = (BonjourContact *) contacts()[user];
503 if (c->getremoteAddress() != conn->getHostAddress())
504 return NULL;
506 return c;
510 #include "bonjouraccount.moc"