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 *************************************************************************
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. *
15 *************************************************************************
18 #include "bonjouraccount.h"
23 #include <kactionmenu.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
);
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
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()
82 KActionMenu
* BonjourAccount::actionMenu()
84 KActionMenu
*mActionMenu
= Kopete::Account::actionMenu();
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 */ )
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
)
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
)
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()
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
;
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 :)
194 kDebug()<<"Publish Successful";
196 kDebug()<<"Publish Failed";
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."));
214 if (! startLocalServer())
219 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOnline
);
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
235 QMap
<QString
, QByteArray
> map
= pointer
->textData();
236 QString cfirst
= map
["1st"];
237 QString clast
= map
["last"];
240 if (! cfirst
.isEmpty() && ! clast
.isEmpty())
241 display
= cfirst
+ ' ' + clast
;
242 else if (! cfirst
.isEmpty())
244 else if (! clast
.isEmpty())
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
)
278 // In case we have lost connection, this may return NULL
279 Kopete::Contact
*c
= contacts()[pointer
->serviceName()];
282 c
->setOnlineStatus(Kopete::OnlineStatus::Offline
);
285 void BonjourAccount::wipeOutContact(Kopete::Contact
*c
)
287 if (c
== myself() || c
== NULL
)
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
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
++)
310 void BonjourAccount::disconnect()
312 wipeOutAllContacts();
320 localServer
->close();
333 myself()->setOnlineStatus( BonjourProtocol::protocol()->bonjourOffline
);
336 void BonjourAccount::slotGoOnline ()
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 ()
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 ()
377 void BonjourAccount::receivedMessage( const QString
&message
)
379 // Look up the contact the message is 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
413 const QByteArray
BonjourAccount::getfirstName() const
418 const QByteArray
BonjourAccount::getlastName() const
423 const QByteArray
BonjourAccount::getemailAddress() const
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
))
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
;
463 if (! (c
= verifyUser(conn
, user
))) {
464 kDebug()<<"Ignoring Unverified User: "<<user
;
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
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
))
501 BonjourContact
*c
= (BonjourContact
*) contacts()[user
];
503 if (c
->getremoteAddress() != conn
->getHostAddress())
510 #include "bonjouraccount.moc"