/* Copyright (C) 2007 One Laptop Per Child * Copyright (C) 2009 Marc Maurer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include #include #include #include #include #include #include } #include "DTubeUnixAccountHandler.h" #include "DTubeBuddy.h" static DBusHandlerResult s_dbus_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data); #define INTERFACE "com.abisource.abiword.abicollab.olpc" #define SEND_ALL_METHOD "SendAll" #define SEND_ONE_METHOD "SendOne" DTubeAccountHandler* DTubeAccountHandler::m_pHandler = NULL; DTubeAccountHandler* DTubeAccountHandler::getHandler() { return m_pHandler; } static bool is_usable_connection(TpConnection* conn) { GValue *value; // check if muc tubes are implemented for this connection // TODO: do we really need muc tubes? all our communication is 1-1 basically... if (tp_cli_dbus_properties_run_get (conn, -1, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, "RequestableChannelClasses", &value, NULL, NULL)) { GPtrArray *classes = (GPtrArray *) g_value_get_boxed (value); for (guint i = 0; i < classes->len; i++) { GValue class_ = {0,}; //GValue * chan_type; GValue * handle_type; GHashTable *fixed_prop; g_value_init (&class_, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_set_static_boxed (&class_, g_ptr_array_index (classes, i)); dbus_g_type_struct_get (&class_, 0, &fixed_prop, G_MAXUINT); /* we don't use stream tubes, so do not check for those chan_type = (GValue *) g_hash_table_lookup (fixed_prop, TP_IFACE_CHANNEL ".ChannelType"); if (chan_type == NULL || tp_strdiff (g_value_get_string (chan_type), "org.freedesktop.Telepathy.Channel.Type.StreamTube.DRAFT")) { continue; } */ handle_type = (GValue *) g_hash_table_lookup (fixed_prop, TP_IFACE_CHANNEL ".TargetHandleType"); if (handle_type == NULL || g_value_get_uint (handle_type) != TP_HANDLE_TYPE_ROOM) continue; return true; } } return false; } static void tp_connection_contacts_by_handle_cb(TpConnection * /*connection*/, guint n_contacts, TpContact * const *contacts, guint /*n_failed*/, const TpHandle * /*failed*/, const GError * /*error*/, gpointer user_data, GObject * /*weak_object*/) { UT_DEBUGMSG(("tp_connection_contacts_by_handle_cb()\n")); DTubeAccountHandler* pHandler = reinterpret_cast(user_data); UT_return_if_fail(pHandler); pHandler->getBuddiesAsync_cb(n_contacts, contacts); } static void tp_connection_get_contacts(TpConnection* conn, DTubeAccountHandler* pHandler) { UT_DEBUGMSG(("tp_connection_get_contacts()\n")); UT_return_if_fail(conn); GPtrArray *channels; // FIXME: tp_cli_connection_run_list_channels is deprecated // FIXME: only use those channels where TargetHandleType is LIST and TargetID is "stored" tp_cli_connection_run_list_channels (conn, -1, &channels, NULL, NULL); printf("Number of connection channels: %d\n", channels->len); for (guint i = 0; i < channels->len; i++) { GValueArray *chan_struct; const gchar *object_path; const gchar *channel_type; TpHandleType handle_type; guint handle; TpChannel *chan; chan_struct = (GValueArray *) g_ptr_array_index (channels, i); object_path = (const gchar *) g_value_get_boxed (g_value_array_get_nth (chan_struct, 0)); channel_type = g_value_get_string (g_value_array_get_nth (chan_struct, 1)); handle_type = (TpHandleType) g_value_get_uint (g_value_array_get_nth (chan_struct, 2)); handle = g_value_get_uint (g_value_array_get_nth (chan_struct, 3)); UT_DEBUGMSG(("Object path: %s\n", object_path)); UT_DEBUGMSG(("Channel type: %s\n", channel_type)); // FIXME: we're only interested in the 'stored' contact list, not in the publis or subscribe // one... surely we can filter on it, but I don't know how - MARCM if (handle_type != TP_HANDLE_TYPE_LIST || tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) { UT_DEBUGMSG(("This channel is not a contact list, skipping...\n")); continue; } // get the active member 'IDs' from the channel chan = tp_channel_new (conn, object_path, channel_type, handle_type, handle, NULL); tp_channel_run_until_ready (chan, NULL, NULL); // bad bad bad, should be _call_when_read (according to the TP people) const TpIntSet * set = tp_channel_group_get_members (chan); // convert the member ID set to a list of TpHandles TpIntSetIter iter; tp_intset_iter_init(&iter, set); const int n_elem = tp_intset_size(set); TpHandle handles[n_elem]; int k = 0; while (tp_intset_iter_next (&iter)) handles[k++] = iter.element; // fetch the TpContacts belonging to the TpHandles tp_connection_get_contacts_by_handle(conn, n_elem, handles, 0, NULL, tp_connection_contacts_by_handle_cb, pHandler, NULL, NULL); } } static void list_connection_names_cb (const gchar * const *bus_names, gsize n, const gchar * const * /*cms*/, const gchar * const * /*protocols*/, const GError *error, gpointer user_data, GObject * /*unused*/) { UT_DEBUGMSG(("list_connection_names_cb() n: %d\n", n)); DTubeAccountHandler* pHandler = reinterpret_cast(user_data); UT_return_if_fail(pHandler); if (error != NULL) return; TpDBusDaemon* bus = tp_dbus_daemon_new (tp_get_bus ()); MissionControl* mc = empathy_mission_control_dup_singleton (); // connection <-> account display name mapping for (guint i = 0; i < n; i++) { UT_DEBUGMSG(("Constructing connection for bus name %s\n", bus_names[i])); TpConnection* conn = tp_connection_new (bus, bus_names[i], NULL, NULL); if (conn == NULL || !is_usable_connection(conn)) continue; McAccount* account = mission_control_get_account_for_tpconnection (mc, conn, NULL); UT_UTF8String sAccountName = mc_account_get_display_name (account); UT_DEBUGMSG(("Found account that supports MUC: conn: %p, name: %s\n", conn, sAccountName.utf8_str())); tp_connection_get_contacts(conn, pHandler); } g_object_unref (mc); g_object_unref (bus); } static void tube_dbus_names_changed_cb (TpChannel * /*proxy*/, guint /*id*/, const GPtrArray *added, const GArray *removed, gpointer user_data, GObject * /*weak_object*/) { DTubeAccountHandler *obj = (DTubeAccountHandler *) user_data; guint i; UT_DEBUGMSG(("Tube D-Bus names changed\n")); for (i = 0; i < added->len; i++) { GValueArray *v; TpHandle handle; const gchar *name; v = (GValueArray *) g_ptr_array_index (added, i); handle = g_value_get_uint (g_value_array_get_nth (v, 0)); name = g_value_get_string (g_value_array_get_nth (v, 1)); UT_DEBUGMSG(("... added %s (%d)\n", name, handle)); UT_UTF8String buddyPath(name, strlen(name)); PD_Document *pDoc = static_cast(XAP_App::getApp()->getLastFocussedFrame()->getCurrentDoc()); obj->joinBuddy(pDoc, handle, buddyPath); } for (i = 0; i < removed->len; i++) { TpHandle handle; handle = g_array_index (removed, TpHandle, i); UT_DEBUGMSG(("... removed %d\n", handle)); /* TODO: call buddyLeft */ } } static void initiator_ready_cb (TpConnection * /*connection*/, guint /*n_contacts*/, TpContact * const *contacts, guint /*n_failed*/, const TpHandle * /*failed*/, const GError * /*error*/, gpointer user_data, GObject *weak_object) { DTubeAccountHandler *obj = (DTubeAccountHandler *) user_data; TpChannel *tube = TP_CHANNEL (weak_object); GtkWidget *dialog; gint response; guint id; TpChannel *chan; TpContact *initiator; GHashTable *parameters; GValue *title; initiator = contacts[0]; g_object_get (tube, "channel", &chan, "id", &id, "parameters", ¶meters, NULL); dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s offered you to join a collaboration session", tp_contact_get_alias (initiator)); // get document title title = (GValue *) g_hash_table_lookup (parameters, "title"); if (title != NULL) { gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "Document: %s", g_value_get_string (title)); } gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); response = gtk_dialog_run (GTK_DIALOG (dialog)); if (response == GTK_RESPONSE_OK) { UT_DEBUGMSG(("Tube accepted.\n")); obj->acceptTube(chan, id, tp_contact_get_handle (initiator)); } else { UT_DEBUGMSG(("Tube declined.\n")); g_object_unref (tube); } gtk_widget_destroy (dialog); g_hash_table_destroy (parameters); } static void new_tube_cb (EmpathyTubeHandler * /*thandler*/, TpChannel * tube, gpointer data) { UT_DEBUGMSG(("new_tube_cb()\n")); TpChannel *chan; DTubeAccountHandler *obj = (DTubeAccountHandler *) data; TpHandle initiator; TpContactFeature features[1] = {TP_CONTACT_FEATURE_ALIAS}; TpConnection *conn; g_object_get (tube, "channel", &chan, "initiator", &initiator, NULL); conn = tp_channel_borrow_connection (chan); tp_connection_run_until_ready (conn, FALSE, NULL, NULL); /* get the TpContact of the initiator */ tp_connection_get_contacts_by_handle (conn, 1, &initiator, 1, features, initiator_ready_cb, obj, NULL, G_OBJECT (tube)); g_object_ref (tube); } DTubeAccountHandler::DTubeAccountHandler() : AccountHandler(), m_bLocallyControlled(false) { UT_DEBUGMSG(("DTubeAccountHandler::DTubeAccountHandler()\n")); m_pHandler = this; empathy_gtk_init (); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); pManager->registerEventListener(this); tube_handler = empathy_tube_handler_new (TP_TUBE_TYPE_DBUS, "com.abisource.abiword.abicollab"); g_signal_connect (tube_handler, "new-tube", G_CALLBACK (new_tube_cb), this); handle_to_bus_name = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); } DTubeAccountHandler::~DTubeAccountHandler() { m_pHandler = NULL; } UT_UTF8String DTubeAccountHandler::getDescription() { return "Telepathy"; } UT_UTF8String DTubeAccountHandler::getDisplayType() { return "Telepathy"; } UT_UTF8String DTubeAccountHandler::getStorageType() { return "com.abisource.abiword.abicollab.backend.dtube"; } void DTubeAccountHandler::storeProperties() { // no need to implement this as we will be getting // all our info always directly from sugar } ConnectResult DTubeAccountHandler::connect() { UT_ASSERT_HARMLESS(UT_NOT_REACHED); return CONNECT_SUCCESS; } bool DTubeAccountHandler::disconnect() { UT_ASSERT_HARMLESS(UT_NOT_REACHED); return true; } bool DTubeAccountHandler::isOnline() { return true; } void DTubeAccountHandler::getBuddiesAsync() { UT_DEBUGMSG(("DTubeAccountHandler::getBuddiesAsync()\n")); // ask telepathy for the connection names TpDBusDaemon* bus = tp_dbus_daemon_new (tp_get_bus ()); tp_list_connection_names (bus, list_connection_names_cb, this, NULL, NULL); g_object_unref (bus); } void DTubeAccountHandler::getBuddiesAsync_cb(guint n_contacts, TpContact * const *contacts) { UT_DEBUGMSG(("DTubeAccountHandler::getBuddiesAsync_cb()\n")); UT_DEBUGMSG(("Got %d contacts\n", n_contacts)); for (UT_uint32 i = 0; i < n_contacts; i++) { TpContact* pContact = contacts[i]; UT_continue_if_fail(pContact); TelepathyBuddyPtr pBuddy = _getBuddy(pContact); if (!pBuddy) { UT_DEBUGMSG(("Detected new telepathy buddy with account name: %s\n", tp_contact_get_identifier (pContact))); pBuddy = boost::shared_ptr(new TelepathyBuddy(this, pContact)); addBuddy(pBuddy); } } } BuddyPtr DTubeAccountHandler::constructBuddy(const PropertyMap& /*props*/) { UT_DEBUGMSG(("DTubeAccountHandler::constructBuddy()\n")); UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return BuddyPtr(); } BuddyPtr DTubeAccountHandler::constructBuddy(const std::string& /*descriptor*/, BuddyPtr /*pBuddy*/) { UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return BuddyPtr(); } bool DTubeAccountHandler::recognizeBuddyIdentifier(const std::string& /*identifier*/) { UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return false; } void DTubeAccountHandler::forceDisconnectBuddy(BuddyPtr pBuddy) { UT_return_if_fail(pBuddy); UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED) } bool DTubeAccountHandler::startSession(PD_Document* pDoc, const std::vector& vAcl, AbiCollab** /*pSession*/) { UT_DEBUGMSG(("DTubeAccountHandler::startSession()\n")); UT_return_val_if_fail(pDoc, false); std::vector acl_; // this n^2 behavior shouldn't be too bad in practice: the ACL will never contain hundreds of elements for (std::vector::const_iterator cit = vAcl.begin(); cit != vAcl.end(); cit++) { for (std::vector::iterator it = getBuddies().begin(); it != getBuddies().end(); it++) { TelepathyBuddyPtr pBuddy = boost::static_pointer_cast(*it); UT_continue_if_fail(pBuddy); if (pBuddy->getDescriptor(false).utf8_str() == (*cit)) { acl_.push_back(pBuddy); break; } } } UT_UTF8String sTubeAddress; if (!_createAndOfferTube(pDoc, acl_, sTubeAddress)) return false; // TODO: store the tube address return true; } void DTubeAccountHandler::handleEvent(Session& /*pSession*/) { // TODO: implement me } /* static void announce_olpc_activity (TpChannel *text_chan, const gchar *doc_title) { GPtrArray *activities; GValue activity = {0,}; GHashTable *olpc_activity_properties = g_hash_table_new (g_str_hash, g_str_equal); GValue act_id = {0,}, type = {0,}, color = {0,}, priv = {0,}, name = {0,}; GError *err = NULL; TpHandle handle = tp_channel_get_handle (text_chan, NULL); TpConnection *conn = tp_channel_borrow_connection (text_chan); // FIXME: this is horrible #define ACTIVITY_ID "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // announce the actitiy activities = g_ptr_array_new (); g_value_init (&activity, ABI_STRUCT_TYPE_ACTIVITY); g_value_take_boxed (&activity, dbus_g_type_specialized_construct (ABI_STRUCT_TYPE_ACTIVITY)); dbus_g_type_struct_set (&activity, 0, ACTIVITY_ID, 1, handle, G_MAXUINT); g_ptr_array_add (activities, (void *) g_value_get_boxed (&activity)); abi_cli_olpc_buddy_info_call_set_activities (TP_PROXY (conn), -1, activities, NULL, NULL, NULL, NULL); g_ptr_array_free (activities, TRUE); // set activity's properties // id g_value_init (&act_id, G_TYPE_STRING); g_value_set_string (&act_id, ACTIVITY_ID); g_hash_table_insert (olpc_activity_properties, (void *) "id", (void *) &act_id); // type g_value_init (&type, G_TYPE_STRING); g_value_set_string (&type, "org.laptop.AbiWordActivity"); g_hash_table_insert (olpc_activity_properties, (void *) "type", (void *) &type); // name (use the title) g_value_init (&name, G_TYPE_STRING); g_value_set_string (&name, doc_title); g_hash_table_insert (olpc_activity_properties, (void *) "name", (void *) &name); // color g_value_init (&color, G_TYPE_STRING); g_value_set_string (&color, "#FF8F00,#00588C"); g_hash_table_insert (olpc_activity_properties, (void *) "color", (void *) &color); // private g_value_init (&priv, G_TYPE_BOOLEAN); g_value_set_boolean (&priv, FALSE); g_hash_table_insert (olpc_activity_properties, (void *) "private", (void *) &priv); abi_cli_olpc_activity_properties_call_set_properties (TP_PROXY (conn), -1, handle, olpc_activity_properties, NULL, NULL, NULL, NULL); g_hash_table_destroy (olpc_activity_properties); } */ static void set_muc_prop_cb (TpProxy * /*proxy*/, const GError * /*error*/, gpointer /*user_data*/, GObject * /*weak_object*/) { //TpChannel *text_chan = TP_CHANNEL (proxy); //const gchar *doc_title = (const gchar *) user_data; //announce_olpc_activity (text_chan, doc_title); } static void list_properties_cb (TpProxy *proxy, const GPtrArray *available_props, const GError *error, gpointer user_data, GObject * /*weak_object*/) { TpChannel *text_chan = TP_CHANNEL (proxy); guint i; //const gchar *doc_title = (const gchar *) user_data; if (error != NULL) { //announce_olpc_activity (text_chan, doc_title); return; } for (i = 0; i < available_props->len; i++) { GValue v = {0,}; guint prop_id; gchar *name; g_value_init (&v, TP_STRUCT_TYPE_PROPERTY_SPEC); g_value_set_static_boxed (&v, g_ptr_array_index (available_props, i)); dbus_g_type_struct_get (&v, 0, &prop_id, 1, &name, G_MAXUINT); if (!tp_strdiff (name, "anonymous")) { /* don't set the room anonyme */ GPtrArray *muc_props; GValue anonyme = {0,}, prop = {0,}; muc_props = g_ptr_array_new (); g_value_init (&anonyme, G_TYPE_BOOLEAN); g_value_set_boolean (&anonyme, FALSE); g_value_init (&prop, TP_STRUCT_TYPE_PROPERTY_VALUE); g_value_take_boxed (&prop, dbus_g_type_specialized_construct (TP_STRUCT_TYPE_PROPERTY_VALUE)); dbus_g_type_struct_set (&prop, 0, prop_id, 1, &anonyme, G_MAXUINT); g_ptr_array_add (muc_props, (void *) g_value_get_boxed (&prop)); tp_cli_properties_interface_call_set_properties (TP_PROXY (text_chan), -1, muc_props, set_muc_prop_cb, user_data, NULL, NULL); g_ptr_array_free (muc_props, TRUE); return; } } } static GHashTable* s_generate_hash(const std::map& props) { GHashTable* hash = g_hash_table_new (g_str_hash, g_str_equal); for (std::map::const_iterator cit = props.begin(); cit != props.end(); cit++) { GValue* value = g_new0 (GValue, 1); g_value_init (value, G_TYPE_STRING); g_value_set_string (value, (*cit).second.c_str()); g_hash_table_insert (hash, g_strdup((*cit).first.c_str()), value); } return hash; } void DTubeAccountHandler::signal(const Event& /*event*/, BuddyPtr /*pSource*/) { UT_DEBUGMSG(("DTubeAccountHandler::signal()\n")); // NOTE: do NOT let AccountHandler::signal() send broadcast packets! // It will send them to all buddies, including the ones we created just // to be able to list the available Telepathy contacts: TelepathyBuddies. // They are just fake buddies however, and can't receive real packets. // Only DTubeBuddy's can be sent packets /* AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_if_fail(pManager); // we just want to listen for when we get a document handle from the other side // (this obviously only makes sense for a joining party, not an offering one; // the offering party should never even receive such an event if (event.getClassType() == PCT_AccountBuddyAddDocumentEvent) { UT_DEBUGMSG(("We received a document handle from an offering party; let's join it immediately!\n")); AccountBuddyAddDocumentEvent& abade = (AccountBuddyAddDocumentEvent&)event; if (!m_bLocallyControlled) { DocHandle* pDocHandle = abade.getDocHandle(); if (pDocHandle) { UT_DEBUGMSG(("Got dochandle, going to initiate a join on it!\n")); // FIXME: remove const cast pManager->joinSessionInitiate(pSource, pDocHandle); } else { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } } else { UT_ASSERT_HARMLESS(UT_NOT_REACHED); } } else if (event.getClassType() == PCT_StartSessionEvent) { UT_DEBUGMSG(("Sharing document. Offer tube\n")); DisplayShareDialog(); }*/ } bool DTubeAccountHandler::send(const Packet* pPacket) { UT_DEBUGMSG(("DTubeAccountHandler::send(const Packet* pPacket)\n")); UT_return_val_if_fail(pPacket, false); UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return true; } bool DTubeAccountHandler::send(const Packet* pPacket, BuddyPtr pBuddy) { UT_DEBUGMSG(("DTubeAccountHandler::send(const Packet* pPacket, BuddyPtr pBuddy\n")); UT_return_val_if_fail(pPacket, false); UT_return_val_if_fail(pBuddy, false); DTubeBuddyPtr pDTubeBuddy = boost::static_pointer_cast(pBuddy); UT_DEBUGMSG(("Sending packet to d-tube buddy on dbus addess: %s\n", pDTubeBuddy->getDBusName().utf8_str())); DBusMessage* pMessage = dbus_message_new_method_call (pDTubeBuddy->getDBusName().utf8_str(), "/org/laptop/DTube/Presence/Buddies", INTERFACE, SEND_ONE_METHOD); // TODO: check dst dbus_message_set_destination(pMessage, pDTubeBuddy->getDBusName().utf8_str()); UT_DEBUGMSG(("Destination (%s) set on message\n", pDTubeBuddy->getDBusName().utf8_str())); // we don't want replies, because they easily run into dbus timeout problems // when sending large packets // TODO: this means we should probably use signals though dbus_message_set_no_reply(pMessage, TRUE); // make to-be-send-stream once std::string data; _createPacketStream( data, pPacket ); const char* packet_contents = &data[0]; dbus_message_append_args(pMessage, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &packet_contents, data.size(), DBUS_TYPE_INVALID); UT_DEBUGMSG(("Appended packet contents\n")); bool sent = dbus_connection_send(pDTubeBuddy->getTube(), pMessage, NULL); UT_ASSERT_HARMLESS(sent); if (sent) dbus_connection_flush(pDTubeBuddy->getTube()); dbus_message_unref(pMessage); return sent; } bool DTubeAccountHandler::_startSession(PD_Document* pDoc, const UT_UTF8String& tubeDBusAddress) { UT_DEBUGMSG(("DTubeAccountHandler::_startSession() - tubeDBusAddress: %s", tubeDBusAddress.utf8_str())); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); // TODO: check that we aren't already in a session; this backend can only host one session at a time (for now) UT_return_val_if_fail(pDoc, false); UT_DEBUGMSG(("Got tube address: %s\n", tubeDBusAddress.utf8_str())); //DBusError error; DBusConnection* pTube = dbus_connection_open(tubeDBusAddress.utf8_str(), NULL); if (pTube) { UT_DEBUGMSG(("Opened a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str())); UT_DEBUGMSG(("Adding dbus handlers to the main loop\n")); dbus_connection_setup_with_g_main(pTube, NULL); UT_DEBUGMSG(("Adding message filter\n")); dbus_connection_add_filter(pTube, s_dbus_handle_message, this, NULL); m_bLocallyControlled = true; // we are "connected" now, time to start sending out, and listening to messages (such as events) //pManager->registerEventListener(this); // start hosting a session on the current document //UT_UTF8String sID; //pManager->startSession(pDoc, sID, NULL); return true; } else UT_DEBUGMSG(("Failed to open a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str())); return false; } bool DTubeAccountHandler::joinTube(const UT_UTF8String& tubeDBusAddress) { UT_DEBUGMSG(("DTubeAccountHandler::joinTube()\n")); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); // TODO: check that we aren't already in a session; this backend can only join one session at a time (for now) //DBusError error; DBusConnection* pTube = dbus_connection_open(tubeDBusAddress.utf8_str(), NULL); if (pTube) { UT_DEBUGMSG(("Opened a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str())); UT_DEBUGMSG(("Adding dbus handlers to the main loop\n")); dbus_connection_setup_with_g_main(pTube, NULL); UT_DEBUGMSG(("Adding message filter\n")); dbus_connection_add_filter(pTube, s_dbus_handle_message, this, NULL); m_bLocallyControlled = false; // we are "connected" now, time to start sending out, and listening to messages (such as events) //pManager->registerEventListener(this); } else UT_DEBUGMSG(("Failed to open a dbus connection for tube: %s\n", tubeDBusAddress.utf8_str())); return false; } bool DTubeAccountHandler::joinBuddy(PD_Document* /*pDoc*/, TpHandle handle, const UT_UTF8String& buddyDBusAddress) { UT_DEBUGMSG(("DTubeAccountHandler::joinBuddy()\n")); if (g_hash_table_lookup (handle_to_bus_name, GUINT_TO_POINTER (handle)) != NULL) return false; g_hash_table_insert (handle_to_bus_name, GUINT_TO_POINTER (handle), (void *) &buddyDBusAddress); // TODO: implement me properly return false; /* DTubeBuddyPtr pBuddy = boost::shared_ptr(new DTubeBuddy(this, buddyDBusAddress)); addBuddy(pBuddy); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); if (m_bLocallyControlled) { AbiCollab* pSession = pManager->getSession(pDoc); UT_return_val_if_fail(pSession, false); pSession->addCollaborator(pBuddy); return true; } else { UT_DEBUGMSG(("Buddy joined, while we are NOT hosting a session; requesting sessions from buddy: %s\n", pBuddy->getDescriptor(false).utf8_str())); getSessionsAsync(pBuddy); return true; } return false;*/ } bool DTubeAccountHandler::disjoinBuddy(FV_View* pView, const UT_UTF8String& buddyDBusAddress) { UT_DEBUGMSG(("DTubeAccountHandler::disjoinBuddy()\n")); UT_return_val_if_fail(pView, false); AbiCollabSessionManager* pManager = AbiCollabSessionManager::getManager(); UT_return_val_if_fail(pManager, false); PD_Document * pDoc = pView->getDocument(); UT_return_val_if_fail(pDoc, false); if (m_bLocallyControlled) { UT_DEBUGMSG(("Dropping buddy %s from the session!", buddyDBusAddress.utf8_str())); AbiCollab* pSession = pManager->getSessionFromDocumentId(pDoc->getDocUUIDString()); if (pSession) { UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); DTubeBuddyPtr pTmpBuddy;// = _getBuddy(buddyDBusAddress); pSession->removeCollaborator(pTmpBuddy); return true; } } else { UT_DEBUGMSG(("The session owner (%s) left!", buddyDBusAddress.utf8_str())); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); return true; } return false; } void DTubeAccountHandler::acceptTube(TpChannel *chan, guint id, TpHandle initiator) { gboolean result; gchar *address; GPtrArray *names; guint i; result = tp_cli_channel_type_tubes_run_accept_d_bus_tube (chan, -1, id, &address, NULL, NULL); g_assert (result); UT_UTF8String tubeDBusAddress(address, strlen (address)); joinTube(tubeDBusAddress); tp_cli_channel_type_tubes_connect_to_d_bus_names_changed (chan, tube_dbus_names_changed_cb, this, NULL, NULL, NULL); /* add initiator */ result = tp_cli_channel_type_tubes_run_get_d_bus_names (chan, -1, id, &names, NULL, NULL); for (i = 0; i < names->len; i++) { GValueArray *v; TpHandle handle; const gchar *name; v = (GValueArray *) g_ptr_array_index (names, i); handle = g_value_get_uint (g_value_array_get_nth (v, 0)); name = g_value_get_string (g_value_array_get_nth (v, 1)); if (handle != initiator) continue; UT_DEBUGMSG(("Found initiator %s (%d)\n", name, handle)); UT_UTF8String buddyPath(name, strlen(name)); PD_Document *pDoc = static_cast(XAP_App::getApp()->getLastFocussedFrame()->getCurrentDoc()); joinBuddy(pDoc, handle, buddyPath); break; } } void DTubeAccountHandler::handleMessage(const char* senderDBusAddress, const char* packet_data, int packet_size) { UT_DEBUGMSG(("DTubeAccountHandler::handleMessage()\n")); // get the buddy for this session UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); DTubeBuddyPtr pBuddy;// = _getBuddy(senderDBusAddress); UT_return_if_fail(pBuddy); // TODO: shouldn't we just disconnect here? // construct the packet // FIXME: inefficient copying of data std::string packet_str(packet_size, ' '); memcpy(&packet_str[0], packet_data, packet_size); FREEP(packet_data); Packet* pPacket = _createPacket(packet_str, pBuddy); UT_return_if_fail(pPacket); // TODO: shouldn't we just disconnect here? // handle! AccountHandler::handleMessage(pPacket, pBuddy); } DBusHandlerResult s_dbus_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data) { UT_DEBUGMSG(("s_dbus_handle_message()\n")); UT_return_val_if_fail(connection, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); UT_return_val_if_fail(message, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); UT_return_val_if_fail(user_data, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); DTubeAccountHandler* pHandler = reinterpret_cast(user_data); if (dbus_message_is_method_call(message, INTERFACE, SEND_ONE_METHOD)) { UT_DEBUGMSG(("%s message accepted!\n", SEND_ONE_METHOD)); const char* senderDBusAddress = dbus_message_get_sender(message); DBusError error; dbus_error_init (&error); const char* packet_data = 0; int packet_size = 0; if (dbus_message_get_args(message, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &packet_data, &packet_size, DBUS_TYPE_INVALID)) { UT_DEBUGMSG(("Received packet from %s\n", senderDBusAddress)); pHandler->handleMessage(senderDBusAddress, packet_data, packet_size); //dbus_free(packet); return DBUS_HANDLER_RESULT_HANDLED; } else UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } UT_DEBUGMSG(("Unhandled message\n")); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } // TODO: cleanup after errors bool DTubeAccountHandler::_createAndOfferTube(PD_Document* pDoc, const std::vector& vBuddies, UT_UTF8String& sTubeAddress) { GError* error = NULL; GHashTable* params; gchar* object_path; GHashTable* channel_properties; gboolean result; gchar *address; GValue title = {0,}; const gchar *doc_title; UT_return_val_if_fail(pDoc, false); UT_return_val_if_fail(vBuddies.size() > 0, false); // get some connection belonging to this contact // TODO: we probably want to change this to some user selectable thingy TpContact* pContact = vBuddies[0]->getContact(); TpConnection * conn = tp_contact_get_connection (pContact); // // create a room, so we can invite some members in it // // first setup some properties for this room... std::map h_params; h_params["org.freedesktop.Telepathy.Channel.ChannelType"] = TP_IFACE_CHANNEL_TYPE_DBUS_TUBE; //h_params["org.freedesktop.Telepathy.Channel.TargetHandleType"] = TP_HANDLE_TYPE_ROOM; h_params["org.freedesktop.Telepathy.Channel.TargetID"] = "abicollabtest@conference.jabber.org"; h_params["org.freedesktop.Telepathy.Channel.Type.DBusTube.ServiceName"] = "com.abisource.abiword.abicollab"; params = s_generate_hash(h_params); // org.freedesktop.Telepathy.Channel.TargetHandleType GValue target_handle_type = {0,}; g_value_init (&target_handle_type, G_TYPE_INT); g_value_set_int (&target_handle_type, TP_HANDLE_TYPE_ROOM); g_hash_table_insert (params, (void *) "org.freedesktop.Telepathy.Channel.TargetHandleType", (void *) &target_handle_type); // ... then actually create the room if (!tp_cli_connection_interface_requests_run_create_channel (conn, -1, params, &object_path, &channel_properties, &error, NULL)) { UT_DEBUGMSG(("Error creating room: %s\n", error ? error->message : "(null)")); return false; } UT_DEBUGMSG(("Got a room, path: %s\n", object_path)); // get a channel to the new room TpChannel* chan = tp_channel_new_from_properties (conn, object_path, channel_properties, NULL); UT_return_val_if_fail(chan, ""); tp_channel_run_until_ready (chan, NULL, NULL); // TODO: check for errors UT_DEBUGMSG(("Channel created to the room\n")); // add members to the room GArray* members = g_array_new (FALSE, FALSE, sizeof(TpHandle)); for (UT_uint32 i = 0; i < vBuddies.size(); i++) { TelepathyBuddyPtr pBuddy = vBuddies[i]; UT_continue_if_fail(pBuddy && pBuddy->getContact()); TpHandle handle = tp_contact_get_handle(pBuddy->getContact()); UT_DEBUGMSG(("Adding %s to the invite list\n", tp_contact_get_identifier(pBuddy->getContact()))); g_array_append_val(members, handle); } UT_DEBUGMSG(("Inviting members to the room...\n")); if (!tp_cli_channel_interface_group_run_add_members (chan, -1, members, "Hi there!", &error, NULL)) { UT_DEBUGMSG(("Error inviting room members: %s\n", error ? error->message : "(null)")); return false; } UT_DEBUGMSG(("Members invited\n")); params = g_hash_table_new (g_str_hash, g_str_equal); /* Document title */ /* HACK * cassidy: but NOTE that if the current focussed document is other than the doc you shared, you'll get a fsckup * cassidy: so mark it with "HACK", so we will add the document to the event signal it belongs to */ g_value_init (&title, G_TYPE_STRING); doc_title = XAP_App::getApp()->getLastFocussedFrame()->getTitle().utf8_str(); g_value_set_string (&title, doc_title); g_hash_table_insert (params, (void *) "title", (void *) &title); // offer this tube to every participant in the room result = tp_cli_channel_type_dbus_tube_run_offer (chan, -1, params, TP_SOCKET_ACCESS_CONTROL_LOCALHOST, &address, &error, NULL); if (!result) { UT_DEBUGMSG(("Error offering tube to room participants: %s\n", error ? error->message : "(null)")); return false; } g_hash_table_destroy (params); UT_DEBUGMSG(("Tube offered, address: %s\n", address)); sTubeAddress = address; // start listening on the tube for people entering and leaving it tp_cli_channel_type_tubes_connect_to_d_bus_names_changed (chan, tube_dbus_names_changed_cb, this, NULL, NULL, NULL); return true; } // FIXME: this can't be right TelepathyBuddyPtr DTubeAccountHandler::_getBuddy(TpContact* pContact) { for (std::vector::iterator it = getBuddies().begin(); it != getBuddies().end(); it++) { TelepathyBuddyPtr pB = boost::static_pointer_cast(*it); UT_continue_if_fail(pB); if (pB->equals(pContact)) return pB; } return TelepathyBuddyPtr(); }