summaryrefslogtreecommitdiffstats
path: root/src/jabber_whiteboard/callbacks.cpp
blob: 8216f33c6cacc4780ea168ac9f76dee415acceab (plain)
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
/**
 * Whiteboard session manager
 * Message dispatch devices and timeout triggers
 *
 * Authors:
 * David Yip <yipdw@rose-hulman.edu>
 *
 * Copyright (c) 2005 Authors
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

extern "C" {
#include <loudmouth/loudmouth.h>
}

#include <glibmm/i18n.h>

#include "message-stack.h"
#include "document.h"
#include "desktop-handles.h"

#include "jabber_whiteboard/undo-stack-observer.h"
#include "jabber_whiteboard/jabber-handlers.h"
#include "jabber_whiteboard/defines.h"
#include "jabber_whiteboard/typedefs.h"
#include "jabber_whiteboard/session-manager.h"
#include "jabber_whiteboard/message-queue.h"
#include "jabber_whiteboard/message-handler.h"
#include "jabber_whiteboard/message-node.h"
#include "jabber_whiteboard/callbacks.h"

namespace Inkscape {

namespace Whiteboard {

Callbacks::Callbacks(SessionManager* sm) : _sm(sm) 
{
	this->_sd = this->_sm->session_data;
}

Callbacks::~Callbacks() 
{
}

bool
Callbacks::dispatchSendQueue()
{
	// If we're not in a whiteboard session, don't dispatch anything
	if (!(this->_sd->status[IN_WHITEBOARD])) {
		return false;
	}

	// If the connection is not open, inform the user that an error has occurred
	// and stop the queue
	LmConnectionState state = lm_connection_get_state(this->_sd->connection);

	if (state != LM_CONNECTION_STATE_OPEN && state != LM_CONNECTION_STATE_AUTHENTICATED) {
		SP_DT_MSGSTACK(this->_sm->desktop())->flash(Inkscape::INFORMATION_MESSAGE, _("Jabber connection lost."));
		return false;
	}

	// If there's nothing to send, don't do anything
	if (this->_sd->send_queue->empty()) {
		return true;
	}

	// otherwise, send out the first change
	MessageNode* first = this->_sd->send_queue->first();

	SP_DT_MSGSTACK(this->_sm->desktop())->flashF(Inkscape::NORMAL_MESSAGE,
                                                     ngettext("Sending message; %u message remaining in send queue.",
                                                              "Sending message; %u messages remaining in send queue.",
                                                              this->_sd->send_queue->size()),
                                                     this->_sd->send_queue->size());

	if (this->_sd->send_queue->empty()) {
		SP_DT_MSGSTACK(this->_sm->desktop())->flash(Inkscape::NORMAL_MESSAGE, _("Receive queue empty."));
	}

	switch (first->type()) {
		case CHANGE_REPEATABLE:
		case CHANGE_NOT_REPEATABLE:
		case CHANGE_COMMIT:
		case DOCUMENT_BEGIN:
		case DOCUMENT_END:
			this->_sm->sendMessage(first->type(), first->sequence(), first->message(), first->recipient().c_str(), first->chatroom());
			break;
		default:
			g_warning("MessageNode with unknown change type found in send queue; discarding message.  This may lead to desynchronization!");
			break;
	}

	this->_sd->send_queue->popFront();

	return true;
}

bool
Callbacks::dispatchReceiveQueue()
{
	CommitsQueue& rcq = this->_sd->recipients_committed_queue;
	// See if we have any commits submitted. 
	if (!rcq.empty()) {
		// Pick the first one off the queue.
		ReceivedCommitEvent& committer = rcq.front();

		// Find the commit event sender's receive queue.
		ReceiveMessageQueue* rmq = this->_sd->receive_queues[committer];

		if (rmq != NULL) {
			if (!rmq->empty()) {
				// Get the first message off the sender's receive queue.
				MessageNode* msg = rmq->first();

				// There are a few message change types that demand special processing;
				// handle them here.
				//
				// TODO: clean this up.  This should be a simple dispatching routine,
				// and should not be performing operations like it's doing right now.
				// These really should go into connection-establishment.cpp
				// (although that should only happen after SessionManager itself
				// is cleaned up).
				switch(msg->type()) {
					case CHANGE_COMMIT:
						rcq.pop_front();
						break;
					case DOCUMENT_BEGIN:
						if (this->_sm->session_data->status[WAITING_TO_SYNC_TO_CHAT]) {
							this->_sm->session_data->status.set(WAITING_TO_SYNC_TO_CHAT, 0);
							this->_sm->session_data->status.set(SYNCHRONIZING_WITH_CHAT, 1);
						}
						break;
					case DOCUMENT_END:
						rcq.pop_front();
						if (this->_sm->session_data->status[SYNCHRONIZING_WITH_CHAT]) {
							this->_sm->sendMessage(CONNECTED_SIGNAL, 0, "", this->_sm->session_data->recipient, true);
							this->_sm->session_data->status.set(SYNCHRONIZING_WITH_CHAT, 0);
							this->_sm->session_data->status.set(IN_CHATROOM, 1);
						} else {
							this->_sm->sendMessage(CONNECTED_SIGNAL, 0, "", msg->sender().c_str(), false);
						}
						break;
					case CHANGE_REPEATABLE:
					case CHANGE_NOT_REPEATABLE:
					default:
						break;
				}
				

				// Pass the message to the received change handler.
				this->_sm->receiveChange(msg->message());
				SP_DT_MSGSTACK(this->_sm->desktop())->flashF(Inkscape::NORMAL_MESSAGE,
                                                                             ngettext("Receiving change; %u change left to process.",
                                                                                      "Receiving change; %u changes left to process.",
                                                                                      rmq->size()),
                                                                             rmq->size());

				
				// Register this message as the latest message received from this
				// sender.
				rmq->setLatestProcessedPacket(msg->sequence());

				// Pop this message off the receive queue.
				rmq->popFront();
				return true;
			} else {
				// This really shouldn't happen.
				// If we have a commit request from a valid sender, there should
				// be something in the receive queue to process.  However, 
				// if a client is buggy or has managed to trick us into accepting
				// a commit, we should handle the event gracefully.
				g_warning("Processing commit, but no changes to commit were found; ignoring commit event.");

				// Remove this sender from the commit list.  If they want to commit
				// later, they can.
				rcq.pop_front();
				return true;
			}
		} else {
			// If the receive queue returned is NULL, then we don't know about
			// this sender.  Remove the sender from the commit list.
			g_warning("Attempting to process commit from unknown sender; ignoring.");
			rcq.pop_front();
			return true;
		}
	} else {
		return true;
	}
}

}

}


/*
  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 :