1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
|
/**
* Whiteboard session manager
* Methods for establishing connections and notifying the user of events
*
* Authors:
* David Yip <yipdw@rose-hulman.edu>
*
* Copyright (c) 2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "util/ucompose.hpp"
#include <glibmm/i18n.h>
#include <gtkmm/dialog.h>
#include <gtkmm/messagedialog.h>
#include "desktop.h"
#include "file.h"
#include "document.h"
#include "xml/repr.h"
#include "jabber_whiteboard/defines.h"
#include "jabber_whiteboard/typedefs.h"
#include "jabber_whiteboard/node-tracker.h"
#include "jabber_whiteboard/chat-handler.h"
#include "jabber_whiteboard/message-queue.h"
#include "jabber_whiteboard/session-manager.h"
#include "jabber_whiteboard/invitation-confirm-dialog.h"
namespace Inkscape {
namespace Whiteboard {
void
SessionManager::sendRequestToUser(std::string const& recipientJID)
{
/*
Glib::ustring doccopy;
if (document != NULL) {
doccopy = *document;
}
*/
this->session_data->status.set(WAITING_FOR_INVITE_RESPONSE, 1);
this->sendMessage(CONNECT_REQUEST_USER, 0, "", recipientJID.c_str(), false);
}
void
SessionManager::sendRequestToChatroom(Glib::ustring const& server, Glib::ustring const& chatroom, Glib::ustring const& handle, Glib::ustring const& password)
{
// We do not yet use the Basic MUC Protocol for connection establishment etc
// <http://www.jabber.org/jeps/jep-0045.html>. The protocol we use is the
// old GroupChat system; extension to MUC is TODO
// Compose room@service/nick string for "to" field
Glib::ustring dest = String::ucompose("%1@%2/%3", chatroom, server, handle);
LmMessage* presence_req = lm_message_new(dest.data(), LM_MESSAGE_TYPE_PRESENCE);
// Add 'from' attribute
LmMessageNode* preq_root = lm_message_get_node(presence_req);
lm_message_node_set_attribute(preq_root, "from", lm_connection_get_jid(this->session_data->connection));
// Add <x xmlns='http://jabber.org/protocol/muc/' />
// (Not anymore: we don't speak it! -- yipdw)
LmMessageNode* xmlns_node = lm_message_node_add_child(preq_root, "x", "");
lm_message_node_set_attribute(xmlns_node, "xmlns", "http://jabber.org/protocol/muc/");
// If a password was supplied, add it to xmlns_node
if (password != NULL) {
lm_message_node_add_child(xmlns_node, "password", password.c_str());
}
// Create chat message handler and node tracker
if (!this->_myChatHandler) {
this->_myChatHandler = new ChatMessageHandler(this);
}
// Flag ourselves as connecting to a chatroom (but not yet connected)
this->session_data->status.set(CONNECTING_TO_CHAT, 1);
// Send the message
GError *error = NULL;
if (!lm_connection_send(this->session_data->connection, presence_req, &error)) {
g_error("Presence message could not be sent to %s: %s", dest.data(), error->message);
this->session_data->status.set(CONNECTING_TO_CHAT, 0);
}
this->session_data->chat_handle = handle;
this->session_data->chat_server = server;
this->session_data->chat_name = chatroom;
this->setRecipient(String::ucompose("%1@%2", chatroom, server).data());
lm_message_unref(presence_req);
}
void
SessionManager::sendConnectRequestResponse(char const* recipientJID, gboolean accepted_request)
{
if (accepted_request == TRUE) {
this->setRecipient(recipientJID);
this->session_data->status.set(IN_WHITEBOARD, 1);
}
this->sendMessage(CONNECT_REQUEST_RESPONSE_USER, accepted_request, "", recipientJID, false);
}
// When this method is invoked, it means that the user has received an invitation from another peer
// to engage in a whiteboard session (i.e. 1:1 communication). The user may accept or reject this invitation.
void
SessionManager::receiveConnectRequest(gchar const* requesterJID)
{
int x, y;
Gdk::ModifierType mt;
Gdk::Display::get_default()->get_pointer(x, y, mt);
if (mt) {
// Attach a polling timeout
this->_notify_incoming_request = Glib::signal_timeout().connect(sigc::bind< 0 >(sigc::mem_fun(*this, &SessionManager::_pollReceiveConnectRequest), requesterJID), 50);
return;
}
if (this->session_data->status[WAITING_FOR_INVITE_RESPONSE]) {
// Whoops. Someone tried to invite us while we were inviting someone else
// (maybe it was the someone we were trying to invite, maybe it was someone else).
//
// Our response is to reject the second request, as we can only handle one
// invitation at a time. Also, we notify the user (who is waiting for an invitation
// response) of the rejection event.
this->sendMessage(CONNECT_REQUEST_REFUSED_BY_PEER, 0, "", requesterJID, false);
Glib::ustring primary = "<span weight=\"bold\" size=\"larger\">";
// TRANSLATORS: This string is used to inform an Inkboard user that the following
// scenario has occurred:
// 1. Alice invites Bob to an Inkboard session.
// 2. While Alice's invitation is en route, Bob invites Alice to an Inkboard session.
//
// Or, we might have the following scenario:
// 1. Alice invites Bob to an Inkboard session.
// 2. While Alice is waiting for Bob's response, Carol sends Alice an invitation.
//
// In the current implementation, we can only handle one invitation at a time,
// so we reject all others.
//
// This is a fix for bug #1352522. Probably not the friendliest, but it's about
// the best we can do without changing the protocol.
primary += _("<b>An invitation conflict has occurred.</b>");
primary += "</span>\n\n";
// TRANSLATORS: %1 is the JID of the user who sent us the invitation request.
primary += String::ucompose(_("The Jabber user <b>%1</b> attempted to invite you to a whiteboard session while you were waiting on an invitation response.\n\nThe invitation from <b>%1</b> has been rejected."), requesterJID);
Gtk::MessageDialog dialog(primary, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, false);
dialog.run();
return;
}
if (this->session_data->status[IN_WHITEBOARD]) {
this->sendMessage(ALREADY_IN_SESSION, 0, "", requesterJID, false);
return;
}
// Check to see if the user made any modifications to this document. If so,
// we want to give them the option of (1) letting us clear their document or (2)
// opening a new, blank document for the whiteboard session.
Glib::ustring primary = "<span weight=\"bold\" size=\"larger\">" + String::ucompose(_("<b>%1</b> has invited you to a whiteboard session."), requesterJID) + "</span>\n\n";
Glib::ustring title = String::ucompose(_("Incoming whiteboard invitation from %1"), requesterJID);
if (sp_document_repr_root(this->_myDoc)->attribute("sodipodi:modified") == NULL) {
primary += String::ucompose(_("Do you wish to accept <b>%1</b>'s whiteboard session invitation?"), requesterJID);
} else {
primary += String::ucompose(_("Would you like to accept %1's invitation in a new document window?\nAccepting the invitation in your current window will discard unsaved changes."), requesterJID);
}
// Construct confirmation dialog
InvitationConfirmDialog dialog(primary);
dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
dialog.add_button(_("Accept invitation in new document window"), ACCEPT_INVITATION_IN_NEW_WINDOW);
bool undecided = true;
InvitationResponses resp = static_cast< InvitationResponses >(dialog.run());
while(undecided) {
if (resp == ACCEPT_INVITATION) {
undecided = false;
this->clearDocument();
// Create a receive queue for the initiator of this request
this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
this->setupInkscapeInterface();
if (dialog.useSessionFile()) {
this->session_data->sessionFile = dialog.getSessionFilePath();
this->_tryToStartLog();
}
this->sendConnectRequestResponse(requesterJID, TRUE);
} else if (resp == ACCEPT_INVITATION_IN_NEW_WINDOW) {
SPDesktop* newdesktop = sp_file_new_default();
if (newdesktop != NULL) {
undecided = false;
// Swap desktops around
// Destroy the new desktop's session manager and add this one in
delete newdesktop->_whiteboard_session_manager;
newdesktop->_whiteboard_session_manager = this;
// Assign a new session manager to our old desktop
this->_myDesktop->_whiteboard_session_manager = new SessionManager(this->_myDesktop);
// Reset our desktop and document pointers
this->setDesktop(newdesktop);
// Prepare document and send acceptance notification
this->session_data->receive_queues[requesterJID] = new ReceiveMessageQueue(this);
this->clearDocument();
this->setupInkscapeInterface();
if (dialog.useSessionFile()) {
this->session_data->sessionFile = dialog.getSessionFilePath();
this->_tryToStartLog();
}
this->sendConnectRequestResponse(requesterJID, TRUE);
} else {
// We could not create a new desktop; ask the user if she or he wants to
// replace the current document and accept the invitation, or reject the invitation.
// TRANSLATORS: %1 is a userid here
Glib::ustring msg = "<span weight=\"bold\" size=\"larger\">" + String::ucompose(_("A new document window could not be opened for a whiteboard session with <b>%1</b>"), requesterJID) + ".</span>\n\nWould you like to accept the whiteboard connection in the active document or refuse the invitation?";
InvitationConfirmDialog replace_dialog(msg);
dialog.add_button(_("Accept invitation"), ACCEPT_INVITATION);
dialog.add_button(_("Decline invitation"), DECLINE_INVITATION);
resp = static_cast< InvitationResponses >(dialog.run());
}
} else {
undecided = false;
this->sendMessage(CONNECT_REQUEST_REFUSED_BY_PEER, 0, "", requesterJID, false);
}
}
}
// When this method is invoked, it means that the other peer
// has accepted our request.
void
SessionManager::receiveConnectRequestResponse(InvitationResponses response, std::string& sender)
{
this->session_data->status.set(WAITING_FOR_INVITE_RESPONSE, 0);
switch(response) {
case ACCEPT_INVITATION:
{
// Create a receive queue for the other peer.
this->session_data->receive_queues[sender] = new ReceiveMessageQueue(this);
KeyToNodeMap newids;
NodeToKeyMap newnodes;
this->_myTracker = new XMLNodeTracker(this);
this->setupInkscapeInterface();
this->_tryToStartLog();
this->resendDocument(this->session_data->recipient, newids, newnodes);
this->_myTracker->put(newids, newnodes);
// this->_myTracker->dump();
this->setupCommitListener();
break;
}
case DECLINE_INVITATION:
{
// TRANSLATORS: %1 is the peer whom refused our invitation.
Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> has refused your whiteboard invitation.</span>\n\n"), sender);
// TRANSLATORS: %1 is the peer whom refused our invitation, %2 is our Jabber identity.
Glib::ustring secondary = String::ucompose(_("You are still connected to a Jabber server as <b>%2</b>, and may send an invitation to <b>%1</b> again, or you may send an invitation to a different user."), sender, lm_connection_get_jid(this->session_data->connection));
Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
dialog.run();
break;
}
case PEER_ALREADY_IN_SESSION:
{
// TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
Glib::ustring primary = String::ucompose(_("<span weight=\"bold\" size=\"larger\">The user <b>%1</b> is already in a whiteboard session.</span>\n\n"), sender);
// TRANSLATORS: %1 is the peer whom we tried to contact, but is already in a whiteboard session.
Glib::ustring secondary = String::ucompose(_("You are still connected to a Jabber server as <b>%1</b>, and may send an invitation to a different user."), sender);
Gtk::MessageDialog dialog(primary + secondary, true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, false);
dialog.run();
break;
}
default:
break;
}
}
void
SessionManager::receiveConnectRequestResponseChat(gchar const* recipient)
{
// When responding to connection request responses in chatrooms,
// the responding user is already established in the whiteboard session.
// Therefore we do not need to perform any setup of observers or dispatchers; the requesting user
// will do that.
KeyToNodeMap newids;
NodeToKeyMap newnodes;
this->resendDocument(recipient, newids, newnodes);
}
bool
SessionManager::_pollReceiveConnectRequest(Glib::ustring const recipientJID)
{
int x, y;
Gdk::ModifierType mt;
Gdk::Display::get_default()->get_pointer(x, y, mt);
if (mt) {
return true;
} else {
this->receiveConnectRequest(recipientJID.c_str());
return false;
}
}
}
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
|