1 /* Copyright (C) 2007 One Laptop Per Child
2  * Author: Marc Maurer <uwog@uwog.net>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA.
18  */
19 
20 #include "SugarUnixAccountHandler.h"
21 #include "SugarBuddy.h"
22 #include <account/xp/AccountEvent.h>
23 #include <account/xp/Event.h>
24 #include <core/account/xp/SessionEvent.h>
25 #include <session/xp/AbiCollabSessionManager.h>
26 #include <session/xp/AbiCollab.h>
27 #include <ev_EditMethod.h>
28 #include <xap_App.h>
29 #include <fv_View.h>
30 
31 // some fucntion prototype declarations
32 static bool s_offerTube(AV_View* v, EV_EditMethodCallData *d);
33 static bool s_joinTube(AV_View* v, EV_EditMethodCallData *d);
34 static bool s_disconnectTube(AV_View* v, EV_EditMethodCallData *d);
35 static bool s_buddyJoined(AV_View* v, EV_EditMethodCallData *d);
36 static bool s_buddyLeft(AV_View* v, EV_EditMethodCallData *d);
37 static DBusHandlerResult s_dbus_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data);
38 
39 #define INTERFACE "com.abisource.abiword.abicollab.olpc"
40 #define SEND_ALL_METHOD "SendAll"
41 #define SEND_ONE_METHOD "SendOne"
42 
43 SugarAccountHandler* SugarAccountHandler::m_pHandler = NULL;
getHandler()44 SugarAccountHandler* SugarAccountHandler::getHandler() { return m_pHandler; }
45 
SugarAccountHandler()46 SugarAccountHandler::SugarAccountHandler()
47 	: AccountHandler(),
48 	m_pTube(NULL),
49 	m_bIsInSession(false)
50 {
51 	UT_DEBUGMSG(("SugarAccountHandler::SugarAccountHandler()\n"));
52 	m_pHandler = this;
53 	_registerEditMethods();
54 }
55 
~SugarAccountHandler()56 SugarAccountHandler::~SugarAccountHandler()
57 {
58 	m_pHandler = NULL;
59 	disconnect();
60 }
61 
getDescription()62 UT_UTF8String SugarAccountHandler::getDescription()
63 {
64 	return "Sugar Presence Service";
65 }
66 
getDisplayType()67 UT_UTF8String SugarAccountHandler::getDisplayType()
68 {
69 	return "Sugar Presence Service";
70 }
71 
getStaticStorageType()72 UT_UTF8String SugarAccountHandler::getStaticStorageType()
73 {
74 	return SUGAR_STATIC_STORAGE_TYPE;
75 }
76 
loadProperties()77 void SugarAccountHandler::loadProperties()
78 {
79 	// no need to implement this as we will be getting
80 	// all our info always directly from sugar
81 }
82 
storeProperties()83 void SugarAccountHandler::storeProperties()
84 {
85 	// no need to implement this as we will be getting
86 	// all our info always directly from sugar
87 }
88 
connect()89 ConnectResult SugarAccountHandler::connect()
90 {
91 	UT_ASSERT_HARMLESS(UT_NOT_REACHED);
92 	return CONNECT_SUCCESS;
93 }
94 
disconnect()95 bool SugarAccountHandler::disconnect()
96 {
97 	if (m_pTube)
98 	{
99 		dbus_connection_unref(m_pTube);
100 		m_pTube = NULL;
101 	}
102 	return true;
103 }
104 
isOnline()105 bool SugarAccountHandler::isOnline()
106 {
107 	return true;
108 }
109 
constructBuddy(const PropertyMap & props)110 BuddyPtr SugarAccountHandler::constructBuddy(const PropertyMap& props)
111 {
112 	UT_DEBUGMSG(("SugarAccountHandler::constructBuddy()\n"));
113 
114 	PropertyMap::const_iterator cit = props.find("dbusAddress");
115 	UT_return_val_if_fail(cit != props.end(), SugarBuddyPtr());
116 	UT_return_val_if_fail(cit->second.size() > 0, SugarBuddyPtr());
117 
118 	UT_DEBUGMSG(("Constructing SugarBuddy (dbusAddress: %s)\n", cit->second.c_str()));
119 	// NOTE: the buddy name must uniquely identify a buddy, and I can't
120 	// guarantee at the moment that the name we could get from the sugar
121 	// presence framework would always be unique to one buddy; hence the
122 	// dbus address will do for now
123 	return boost::shared_ptr<SugarBuddy>(new SugarBuddy(this, cit->second.c_str()));
124 }
125 
constructBuddy(const std::string & descriptor,BuddyPtr)126 BuddyPtr SugarAccountHandler::constructBuddy(const std::string& descriptor, BuddyPtr /*pBuddy*/)
127 {
128 	UT_DEBUGMSG(("SugarAccountHandler::constructBuddy() - descriptor: %s\n", descriptor.c_str()));
129 
130 	std::string uri_id = "sugar://";
131 	UT_return_val_if_fail(descriptor.size() > uri_id.size(), SugarBuddyPtr());
132 
133 	std::string dbusAddress = descriptor.substr(uri_id.size());
134 	SugarBuddyPtr pBuddy = getBuddy(dbusAddress.c_str());
135 	UT_return_val_if_fail(pBuddy, SugarBuddyPtr());
136 
137 	return pBuddy;
138 }
139 
recognizeBuddyIdentifier(const std::string & identifier)140 bool SugarAccountHandler::recognizeBuddyIdentifier(const std::string& identifier)
141 {
142 	std::string uri_id = "sugar://";
143 
144 	if (identifier.compare(0, uri_id.size(), uri_id) != 0)
145 		return false;
146 
147 	// The rest of the buddy descriptor contains the dbus address, which we
148 	// can't really check.
149 
150 	return true;
151 }
152 
handleEvent(Session &)153 void  SugarAccountHandler::handleEvent(Session& /*pSession*/)
154 {
155 	// TODO: implement me
156 }
157 
signal(const Event & event,BuddyPtr pSource)158 void SugarAccountHandler::signal(const Event& event, BuddyPtr pSource)
159 {
160 	UT_DEBUGMSG(("SugarAccountHandler::signal()\n"));
161 
162 	AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager();
163 	UT_return_if_fail(pManager);
164 
165 	switch (event.getClassType())
166 	{
167 		case PCT_CloseSessionEvent:
168 			{
169 				UT_DEBUGMSG(("Got a PCT_CloseSessionEvent\n"));
170 				const CloseSessionEvent cse = static_cast<const CloseSessionEvent&>(event);
171 				UT_return_if_fail(!pSource); // we shouldn't receive these events over the wire on this backend
172 
173 				// If the session that is closed was started by us, then disconnect from
174 				// the tube. Otherwise, just drop the event on the floor....
175 
176 				if (cse.getSessionId() == m_sSessionId)
177 				{
178 					UT_DEBUGMSG(("We host session %s, disconnecting...\n", cse.getSessionId().utf8_str()));
179 					disconnect();
180 				}
181 			}
182 			break;
183 
184 		case PCT_AccountBuddyAddDocumentEvent:
185 			{
186 				// Prevent joining other dochandles that come over the wire after having
187 				// already joined the one we received just now. This should ofcourse never
188 				// happen, but it will in practice because of a Write/Activity bug.
189 				// See ... for details.
190 				if (m_bIsInSession)
191 				{
192 					UT_DEBUGMSG(("Received a bogus AccountBuddyAddDocumentEvent: we are already connected to a session.\n"));
193 					return;
194 				}
195 
196 				// We've received a document handle from the other side. This obviously only
197 				// makes sense for a joining party, not an offering one: the offering party
198 				// should never even receive such an event
199 
200 				UT_DEBUGMSG(("We received a document handle from an offering party; let's join it immediately!\n"));
201 				AccountBuddyAddDocumentEvent& abade = (AccountBuddyAddDocumentEvent&)event;
202 
203 				// FIXME: should we check if we were waiting for a document to come our way?
204 
205 				DocHandle* pDocHandle = abade.getDocHandle();
206 				UT_return_if_fail(pDocHandle);
207 
208 				UT_DEBUGMSG(("Got dochandle, going to initiate a join on it!\n"));
209 				pManager->joinSessionInitiate(pSource, pDocHandle);
210 				m_bIsInSession = true;
211 			}
212 			break;
213 
214 		default:
215 			AccountHandler::signal(event, pSource);
216 			break;
217 	}
218 }
219 
send(const Packet * pPacket)220 bool SugarAccountHandler::send(const Packet* pPacket)
221 {
222 	UT_DEBUGMSG(("SugarAccountHandler::send(const Packet* pPacket)\n"));
223 	UT_return_val_if_fail(pPacket, false);
224 	UT_return_val_if_fail(m_pTube, false);
225 
226 	return _send(pPacket, NULL);
227 }
228 
send(const Packet * pPacket,BuddyPtr pBuddy)229 bool SugarAccountHandler::send(const Packet* pPacket, BuddyPtr pBuddy)
230 {
231 	UT_DEBUGMSG(("SugarAccountHandler::send(const Packet* pPacket, const Buddy& buddy)\n"));
232 	UT_return_val_if_fail(pPacket, false);
233 	UT_return_val_if_fail(m_pTube, false);
234 
235 	SugarBuddyPtr pSugarBuddy = boost::static_pointer_cast<SugarBuddy>(pBuddy);
236 	UT_DEBUGMSG(("Sending packet to sugar buddy on dbus addess: %s\n", pSugarBuddy->getDBusAddress().utf8_str()));
237 
238 	return _send(pPacket, pSugarBuddy->getDBusAddress().utf8_str());
239 }
240 
createPacket(const std::string & packet,BuddyPtr pBuddy)241 Packet* SugarAccountHandler::createPacket(const std::string& packet, BuddyPtr pBuddy)
242 {
243 	return _createPacket(packet, pBuddy);
244 }
245 
_send(const Packet * pPacket,const char * dbusAddress)246 bool SugarAccountHandler::_send(const Packet* pPacket, const char* dbusAddress)
247 {
248 	UT_DEBUGMSG(("SugarAccountHandler::_send() - dbusAddress: %s\n", dbusAddress ? dbusAddress : "(broadcast)"));
249 	UT_return_val_if_fail(pPacket, false);
250 	UT_return_val_if_fail(m_pTube, false);
251 
252 	DBusMessage* pMessage = dbus_message_new_method_call(dbusAddress, "/org/laptop/Sugar/Presence/Buddies", INTERFACE, SEND_ONE_METHOD);
253 	if (dbusAddress)
254 	{
255 		// TODO: isn't this redudant? we already set a destination in dbus_message_new_method_call()
256 		if (!dbus_message_set_destination(pMessage, dbusAddress))
257 		{
258 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
259 			dbus_message_unref(pMessage);
260 			return false;
261 		}
262 		UT_DEBUGMSG(("Destination (%s) set on message\n", dbusAddress));
263 	}
264 
265 	// we don't want replies, because then then easily run into dbus timeout problems
266 	// when sending large packets
267 	// TODO: this means we should probably use signals though
268 	dbus_message_set_no_reply(pMessage, TRUE);
269 
270 	// make to-be-send-stream once
271 	std::string data;
272 	_createPacketStream( data, pPacket );
273 
274 	const char* packet_contents = &data[0];
275 	if (!dbus_message_append_args(pMessage, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &packet_contents, data.size(), DBUS_TYPE_INVALID))
276 	{
277 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
278 		dbus_message_unref(pMessage);
279 		return false;
280 	}
281 
282 	UT_DEBUGMSG(("Appended packet contents\n"));
283 
284 	bool sent = dbus_connection_send(m_pTube, pMessage, NULL);
285 	UT_ASSERT_HARMLESS(sent);
286 	if (sent)
287 		dbus_connection_flush(m_pTube);
288 	dbus_message_unref(pMessage);
289 
290 	return sent;
291 }
292 
_registerEditMethods()293 void SugarAccountHandler::_registerEditMethods()
294 {
295 	UT_DEBUGMSG(("SugarAccountHandler::_registerEditMethods()\n"));
296 
297     // First we need to get a pointer to the application itself.
298     XAP_App *pApp = XAP_App::getApp();
299     EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer();
300 
301 	EV_EditMethod *emOfferTube = new EV_EditMethod (
302 		"com.abisource.abiword.abicollab.olpc.offerTube",     // name of callback function
303 		s_offerTube,       // callback function itself.
304 		0,                      // no additional data required.
305 		""                      // description -- allegedly never used for anything
306 	);
307 	pEMC->addEditMethod(emOfferTube);
308 
309 	EV_EditMethod *emJoinTube = new EV_EditMethod (
310 		"com.abisource.abiword.abicollab.olpc.joinTube",     // name of callback function
311 		s_joinTube,       // callback function itself.
312 		0,                      // no additional data required.
313 		""                      // description -- allegedly never used for anything
314 	);
315 	pEMC->addEditMethod(emJoinTube);
316 
317 	EV_EditMethod *emDisconnectTube = new EV_EditMethod (
318 		"com.abisource.abiword.abicollab.olpc.disconnectTube",     // name of callback function
319 		s_disconnectTube,       // callback function itself.
320 		0,                      // no additional data required.
321 		""                      // description -- allegedly never used for anything
322 	);
323 	pEMC->addEditMethod(emDisconnectTube);
324 
325 	EV_EditMethod *emBuddyJoined = new EV_EditMethod (
326 		"com.abisource.abiword.abicollab.olpc.buddyJoined",     // name of callback function
327 		s_buddyJoined,       // callback function itself.
328 		0,                      // no additional data required.
329 		""                      // description -- allegedly never used for anything
330 	);
331 	pEMC->addEditMethod(emBuddyJoined);
332 
333 	EV_EditMethod *emBuddyLeft = new EV_EditMethod (
334 		"com.abisource.abiword.abicollab.olpc.buddyLeft",     // name of callback function
335 		s_buddyLeft,       // callback function itself.
336 		0,                      // no additional data required.
337 		""                      // description -- allegedly never used for anything
338 	);
339 	pEMC->addEditMethod(emBuddyLeft);
340 
341 }
342 
_handlePacket(Packet * packet,BuddyPtr buddy)343 void SugarAccountHandler::_handlePacket(Packet* packet, BuddyPtr buddy)
344 {
345 	UT_DEBUGMSG(("SugarAccountHandler::_handlePacket()\n"));
346 
347 	UT_return_if_fail(packet);
348 	UT_return_if_fail(buddy);
349 
350 	switch (packet->getClassType())
351 	{
352 		case PCT_JoinSessionRequestResponseEvent:
353 		{
354 			JoinSessionRequestResponseEvent* jsre = static_cast<JoinSessionRequestResponseEvent*>( packet );
355 			m_sSessionId = jsre->getSessionId();
356 			// Let the AccountHandler::_handlePacket() call below handle the actual joining.
357 			// This could mean that when the actual joining fails (unlikely), we have
358 			// a bogus session ID stored. I doubt it will ever really be a problem though.
359 			break;
360 		}
361 
362 		default:
363 			break;
364 	}
365 
366 	AccountHandler::_handlePacket(packet, buddy);
367 }
368 
offerTube(FV_View * pView,const UT_UTF8String & tubeDBusAddress)369 bool SugarAccountHandler::offerTube(FV_View* pView, const UT_UTF8String& tubeDBusAddress)
370 {
371 	UT_DEBUGMSG(("SugarAccountHandler::offerTube()\n"));
372 	UT_return_val_if_fail(pView, false);
373 
374 	AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager();
375 	UT_return_val_if_fail(pManager, false);
376 
377 	// TODO: check that we aren't already in a session; this backend can only host one session at a time (for now)
378 
379 	PD_Document * pDoc = pView->getDocument();
380 	UT_return_val_if_fail(pDoc, false);
381 
382 	UT_DEBUGMSG(("Got tube address: %s\n", tubeDBusAddress.utf8_str()));
383 
384 	m_pTube = dbus_connection_open(tubeDBusAddress.utf8_str(), NULL);
385 	UT_return_val_if_fail(m_pTube, false);
386 
387 	UT_DEBUGMSG(("Opened a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str()));
388 
389 	UT_DEBUGMSG(("Adding dbus handlers to the main loop\n"));
390 	dbus_connection_setup_with_g_main(m_pTube, NULL);
391 
392 	UT_DEBUGMSG(("Adding message filter\n"));
393 	dbus_connection_add_filter(m_pTube, s_dbus_handle_message, this, NULL);
394 
395 	// start hosting a session on the current document
396 	UT_return_val_if_fail(m_sSessionId == "", false);
397 	AbiCollab* pSession = pManager->startSession(pDoc, m_sSessionId, this, true, NULL, "");
398 	UT_return_val_if_fail(pSession, false);
399 
400 	// we are "connected" now, time to start sending out, and listening to messages (such as events)
401 	pManager->registerEventListener(this);
402 
403 	m_bIsInSession = true;
404 
405 	return true;
406 }
407 
joinTube(FV_View * pView,const UT_UTF8String & tubeDBusAddress)408 bool SugarAccountHandler::joinTube(FV_View* pView, const UT_UTF8String& tubeDBusAddress)
409 {
410 	UT_DEBUGMSG(("SugarAccountHandler::joinTube()\n"));
411 	UT_return_val_if_fail(pView, false);
412 
413 	AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager();
414 	UT_return_val_if_fail(pManager, false);
415 
416 	// TODO: check that we aren't already in a session; this backend can only join one session at a time (for now)
417 
418 	m_pTube = dbus_connection_open(tubeDBusAddress.utf8_str(), NULL);
419 	UT_return_val_if_fail(m_pTube, false);
420 
421 	UT_DEBUGMSG(("Opened a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str()));
422 
423 	UT_DEBUGMSG(("Adding dbus handlers to the main loop\n"));
424 	dbus_connection_setup_with_g_main(m_pTube, NULL);
425 
426 	UT_DEBUGMSG(("Adding message filter\n"));
427 	dbus_connection_add_filter(m_pTube, s_dbus_handle_message, this, NULL);
428 
429 	// we are "connected" now, time to start sending out, and listening to messages (such as events)
430 	pManager->registerEventListener(this);
431 
432 	// broadcast a request for sessions; if everything is alright then we should
433 	// receive exactly 1 session in all the responses combined
434 	UT_DEBUGMSG(("Sending a broadcast GetSessionsEvent\n"));
435 	GetSessionsEvent event;
436 	send(&event);
437 
438 	return true;
439 }
440 
disconnectTube(FV_View * pView)441 bool SugarAccountHandler::disconnectTube(FV_View* pView)
442 {
443 	UT_DEBUGMSG(("SugarAccountHandler::disconnectTube()\n"));
444 	UT_return_val_if_fail(pView, false);
445 
446 	AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager();
447 	UT_return_val_if_fail(pManager, false);
448 
449 	PD_Document * pDoc = pView->getDocument();
450 	UT_return_val_if_fail(pDoc, false);
451 
452 	AbiCollab* pSession = pManager->getSession(pDoc);
453 	UT_return_val_if_fail(pSession, false);
454 	pManager->disconnectSession(pSession);
455 
456 	return true;
457 }
458 
joinBuddy(FV_View * pView,const UT_UTF8String & buddyDBusAddress)459 bool SugarAccountHandler::joinBuddy(FV_View* pView, const UT_UTF8String& buddyDBusAddress)
460 {
461 	UT_DEBUGMSG(("SugarAccountHandler::joinBuddy() - buddyDBusAddress: %s\n", buddyDBusAddress.utf8_str()));
462 	UT_return_val_if_fail(pView, false);
463 
464 	SugarBuddyPtr pBuddy = boost::shared_ptr<SugarBuddy>(new SugarBuddy(this, buddyDBusAddress));
465 	addBuddy(pBuddy);
466 
467 	return true;
468 }
469 
disjoinBuddy(FV_View * pView,const UT_UTF8String & buddyDBusAddress)470 bool SugarAccountHandler::disjoinBuddy(FV_View* pView, const UT_UTF8String& buddyDBusAddress)
471 {
472 	UT_DEBUGMSG(("SugarAccountHandler::disjoinBuddy()\n"));
473 	UT_return_val_if_fail(pView, false);
474 
475 	AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager();
476 	UT_return_val_if_fail(pManager, false);
477 
478 	PD_Document * pDoc = pView->getDocument();
479 	UT_return_val_if_fail(pDoc, false);
480 
481 	m_ignoredBuddies.erase( buddyDBusAddress ); // buddy name is buddyDBusAddress!
482 
483 	BuddyPtr pBuddy = getBuddy(buddyDBusAddress);
484 	UT_return_val_if_fail(pBuddy, false);
485 
486 	pManager->removeBuddy(pBuddy, false);
487 
488 	// TODO: shouldn't we remove this buddy from our own buddy list?
489 
490 	return true;
491 }
492 
forceDisconnectBuddy(BuddyPtr pBuddy)493 void SugarAccountHandler::forceDisconnectBuddy(BuddyPtr pBuddy)
494 {
495 	UT_return_if_fail(pBuddy);
496 	m_ignoredBuddies.insert(pBuddy->getDescriptor(false));
497 }
498 
hasAccess(const std::vector<std::string> &,BuddyPtr pBuddy)499 bool SugarAccountHandler::hasAccess(const std::vector<std::string>& /*vAcl*/, BuddyPtr pBuddy)
500 {
501 	UT_DEBUGMSG(("SugarAccountHandler::hasAccess() - pBuddy: %s\n", pBuddy->getDescriptor(false).utf8_str()));
502 	UT_return_val_if_fail(pBuddy, false);
503 
504 	// The sugar presence service is responsible for access control. Just do a quick
505 	// check here to see if we know the buddy on this account, and
506 	// then be done with it.
507 	SugarBuddyPtr pSugarBuddy = boost::dynamic_pointer_cast<SugarBuddy>(pBuddy);
508 	UT_return_val_if_fail(pSugarBuddy, false);
509 
510 	SugarBuddyPtr pExistingBuddy = getBuddy(pSugarBuddy->getDBusAddress());
511 	if (!pExistingBuddy)
512 		return false;
513 
514 	return true;
515 }
516 
getBuddy(const UT_UTF8String & dbusAddress)517 SugarBuddyPtr SugarAccountHandler::getBuddy(const UT_UTF8String& dbusAddress)
518 {
519 	for (std::vector<BuddyPtr>::iterator it = getBuddies().begin(); it != getBuddies().end(); it++)
520 	{
521 		SugarBuddyPtr pBuddy = boost::static_pointer_cast<SugarBuddy>(*it);
522 		UT_continue_if_fail(pBuddy);
523 		if (pBuddy->getDBusAddress() == dbusAddress)
524 			return pBuddy;
525 	}
526 	return SugarBuddyPtr();
527 }
528 
s_offerTube(AV_View * v,EV_EditMethodCallData * d)529 static bool s_offerTube(AV_View* v, EV_EditMethodCallData *d)
530 {
531 	UT_DEBUGMSG(("s_offerTube()\n"));
532 	UT_return_val_if_fail(v, false);
533 	UT_return_val_if_fail(d && d->m_pData && d->m_dataLength > 0, false);
534 
535 	FV_View* pView = static_cast<FV_View *>(v);
536 	UT_UTF8String tubeDBusAddress(d->m_pData, d->m_dataLength);
537 
538 	SugarAccountHandler* pHandler = SugarAccountHandler::getHandler();
539 	UT_return_val_if_fail(pHandler, false);
540 	return pHandler->offerTube(pView, tubeDBusAddress);
541 }
542 
s_joinTube(AV_View * v,EV_EditMethodCallData * d)543 static bool s_joinTube(AV_View* v, EV_EditMethodCallData *d)
544 {
545 	UT_DEBUGMSG(("s_joinTube()\n"));
546 	UT_return_val_if_fail(v, false);
547 	UT_return_val_if_fail(d && d->m_pData && d->m_dataLength > 0, false);
548 
549 	FV_View* pView = static_cast<FV_View *>(v);
550 	UT_UTF8String tubeDBusAddress(d->m_pData, d->m_dataLength);
551 	UT_DEBUGMSG(("Got tube address: %s\n", tubeDBusAddress.utf8_str()));
552 
553 	SugarAccountHandler* pHandler = SugarAccountHandler::getHandler();
554 	UT_return_val_if_fail(pHandler, false);
555 	return pHandler->joinTube(pView, tubeDBusAddress);
556 }
557 
s_disconnectTube(AV_View * v,EV_EditMethodCallData *)558 static bool s_disconnectTube(AV_View* v, EV_EditMethodCallData */*d*/)
559 {
560 	UT_DEBUGMSG(("s_disconnectTube()\n"));
561 	UT_return_val_if_fail(v, false);
562 	FV_View* pView = static_cast<FV_View *>(v);
563 
564 	SugarAccountHandler* pHandler = SugarAccountHandler::getHandler();
565 	UT_return_val_if_fail(pHandler, false);
566 	return pHandler->disconnectTube(pView);
567 }
568 
s_buddyJoined(AV_View * v,EV_EditMethodCallData * d)569 static bool s_buddyJoined(AV_View* v, EV_EditMethodCallData *d)
570 {
571 	UT_DEBUGMSG(("s_buddyJoined()\n"));
572 	UT_return_val_if_fail(SugarAccountHandler::getHandler(), false);
573 	UT_return_val_if_fail(d && d->m_pData && d->m_dataLength > 0, false);
574 
575 	FV_View* pView = static_cast<FV_View *>(v);
576 	UT_UTF8String buddyPath(d->m_pData, d->m_dataLength);
577 	UT_DEBUGMSG(("Adding buddy with dbus path: %s\n", buddyPath.utf8_str()));
578 
579 	SugarAccountHandler* pHandler = SugarAccountHandler::getHandler();
580 	UT_return_val_if_fail(pHandler, false);
581 	return pHandler->joinBuddy(pView, buddyPath);
582 }
583 
s_buddyLeft(AV_View * v,EV_EditMethodCallData * d)584 static bool s_buddyLeft(AV_View* v, EV_EditMethodCallData *d)
585 {
586 	UT_DEBUGMSG(("s_buddyLeft()\n"));
587 	UT_return_val_if_fail(SugarAccountHandler::getHandler(), false);
588 	UT_return_val_if_fail(d && d->m_pData && d->m_dataLength > 0, false);
589 
590 	FV_View* pView = static_cast<FV_View *>(v);
591 	UT_UTF8String buddyPath(d->m_pData, d->m_dataLength);
592 	UT_DEBUGMSG(("Removing buddy with dbus path %s\n", buddyPath.utf8_str()));
593 
594 	SugarAccountHandler* pHandler = SugarAccountHandler::getHandler();
595 	UT_return_val_if_fail(pHandler, false);
596 
597 	return pHandler->disjoinBuddy(pView, buddyPath);
598 }
599 
s_dbus_handle_message(DBusConnection * connection,DBusMessage * message,void * user_data)600 DBusHandlerResult s_dbus_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data)
601 {
602 	UT_DEBUGMSG(("s_dbus_handle_message()\n"));
603 	UT_return_val_if_fail(connection, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
604 	UT_return_val_if_fail(message, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
605 	UT_return_val_if_fail(user_data, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
606 	SugarAccountHandler* pHandler = reinterpret_cast<SugarAccountHandler*>(user_data);
607 
608 	if (dbus_message_is_method_call(message, INTERFACE, SEND_ONE_METHOD))
609 	{
610 		UT_DEBUGMSG(("%s message accepted!\n", SEND_ONE_METHOD));
611 
612 		const char* senderDBusAddress = dbus_message_get_sender(message);
613 
614 		DBusError error;
615 		dbus_error_init (&error);
616 		const char* packet_data = 0;
617 		int packet_size = 0;
618 	    if (dbus_message_get_args(message, &error,
619 					DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &packet_data, &packet_size,
620 					DBUS_TYPE_INVALID))
621 		{
622 			UT_DEBUGMSG(("Received packet from %s\n", senderDBusAddress));
623 
624 			if (!pHandler->isIgnoredBuddy(senderDBusAddress))
625 			{
626 				// import the packet
627 				BuddyPtr pBuddy = pHandler->getBuddy(senderDBusAddress);
628 				if (!pBuddy)
629 				{
630 					// this can actually happen, for example when joining a tube
631 					// we send out a broadcast GetSessionsEvent. Responses from
632 					// that can return before joinBuddy() was called for that buddy.
633 					pBuddy = boost::shared_ptr<SugarBuddy>(new SugarBuddy( pHandler, senderDBusAddress));
634 					pHandler->addBuddy(pBuddy);
635 				}
636 
637 				// FIXME: inefficient copying of data
638 				std::string packet_str(packet_size, ' ');
639 				memcpy(&packet_str[0], packet_data, packet_size);
640 				Packet* pPacket = pHandler->createPacket(packet_str, pBuddy);
641 				UT_return_val_if_fail(pPacket, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); // TODO: shouldn't we just disconnect here?
642 
643 				// handle!
644 				pHandler->handleMessage(pPacket, pBuddy);
645 			}
646 
647 			//dbus_free(packet);
648 			return DBUS_HANDLER_RESULT_HANDLED;
649 		}
650 		else
651 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
652 	}
653 
654 	UT_DEBUGMSG(("Unhandled message\n"));
655 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
656 }
657