fix session id generator
[makneto-zunavac1.git] / src / backend / session.cpp
blobdd49b88d64c7570f6e9e19949e86d5c89e21f3bf
1 /*
2 <one line to give the library's name and an idea of what it does.>
3 Copyright (C) 2011 Vojta Kulicka <vojtechkulicka@gmail.com>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <TelepathyQt4/ChannelRequest>
22 #include <TelepathyQt4/TextChannel>
23 #include <TelepathyQt4/StreamedMediaChannel>
24 #include <TelepathyQt4/Message>
25 #include <TelepathyQt4/PendingSendMessage>
26 #include <TelepathyQt4/Contact>
27 #include <TelepathyQt4/Account>
28 #include <TelepathyQt4/ReferencedHandles>
30 #include <QString>
32 #include "session.h"
33 #include "voip/callchannelhandler.h"
34 #include "telepathy-client.h"
35 #include <TelepathyQt4/FileTransferChannel>
36 #include <TelepathyQt4/IncomingFileTransferChannel>
37 #include <TelepathyQt4/OutgoingFileTransferChannel>
39 using namespace MaknetoBackend;
41 //Constructor for sessions created as result of an incoming channel
43 Session::Session(const Tp::ChannelPtr &channel, SesstionTypes chatType, QObject *parent)
44 : QObject(parent) {
45 m_contacts = new QSet<Tp::ContactPtr > ();
46 m_account = TelepathyClient::Instance()->getAccountforChannel(channel);
48 m_chatType = (SessionTypeNone | chatType);
50 m_mediaChannel.reset();
51 m_textChannel.reset();
52 m_fileTransferChannel.reset();
53 m_callChannelHandler = NULL;
55 m_myContact = channel->groupSelfContact();
57 //set the peer's contactPtr
58 if (m_chatType != SessionTypeMuc) {
59 //for one-to-one chat contains two contacts, me and peer
60 //for calls there is just the peer contact
61 Tp::Contacts contacts = channel->groupContacts();
63 foreach(Tp::ContactPtr contact, contacts) {
64 if (contact == channel->groupSelfContact())
65 continue;
66 else {
67 m_contact = contact;
68 qDebug() << "Session: " << contact->alias() << " contactId: " << contact->id();
71 if (!m_contact) {
72 qWarning() << "Session: start session with NULL contact?!";
75 m_sessionName = m_contact ? m_contact->alias() : "undefined";
76 m_uniqueSessionName = Session::sessionName(m_account, m_contact);
77 }//MUC
78 else {
79 Tp::Contacts contacts = channel->groupContacts();
81 foreach(Tp::ContactPtr contact, contacts) {
82 if (contact == channel->groupSelfContact())
83 continue;
84 else {
85 m_contacts->insert(contact);
86 qDebug() << "Session: " << contact->alias() << " id: " << contact->id();
89 m_sessionName = channel->targetId();
90 m_uniqueSessionName = Session::sessionNameForMuc(m_account, channel->targetId());
93 //now is only informative
94 switch (m_chatType) {
96 case SessionTypeText:
97 qDebug() << "Session: Text";
98 break;
99 case SessionTypeAudio:
100 qDebug() << "Session: Audio";
101 break;
102 case SessionTypeVideo:
103 qDebug() << "Session: Video";
104 break;
105 case SessionTypeMuc:
106 qDebug() << "Session: MUC";
107 break;
108 default:
109 qDebug() << "Default session type. Something is wrong";
110 Q_ASSERT(false);
113 if (m_textChannel || m_mediaChannel)
114 qDebug() << "Session: initialized";
118 //Constructor for sessions created as result of an outgoing channel
120 Session::Session(const Tp::AccountPtr &account, const Tp::ContactPtr &contact, SesstionTypes sessionType, QObject *parent)
121 : QObject(parent),
122 m_account(account),
123 m_contact(contact) {
124 //most likely is not neccessary here
125 m_contacts = new QSet<Tp::ContactPtr > ();
126 m_myContact = account->connection()->selfContact();
128 m_chatType = (SessionTypeNone | sessionType);
130 m_mediaChannel.reset();
131 m_textChannel.reset();
132 m_fileTransferChannel.reset();
133 m_callChannelHandler = NULL;
135 //set the peer's contactPtr
136 m_sessionName = contact->alias();
137 m_uniqueSessionName = Session::sessionName(m_account, m_contact); // FIXME: how to get name for outgoing MUC here?
138 qDebug() << "Session: name " << m_sessionName << " id: " << m_uniqueSessionName << "";
140 switch (m_chatType) {
142 case SessionTypeText:
143 startTextChat();
144 break;
145 case SessionTypeAudio:
146 startMediaCall(false);
147 break;
148 case SessionTypeVideo:
149 startMediaCall(true);
150 break;
151 case SessionTypeMuc:
152 qDebug() << "MUC";
153 //startTextChatroom();
154 break;
155 default:
156 qDebug() << "Session: Default session type. Something is wrong.";
157 Q_ASSERT(false);
160 qDebug() << "Session: initialized";
163 Session::~Session() {
164 if (m_callChannelHandler && (!m_mediaChannel.isNull())) // FIXME: it is correct?
165 delete m_callChannelHandler;
168 void Session::onAddCapability(Session::SesstionTypes chatType) {
170 switch (chatType) {
172 case SessionTypeText:
173 startTextChat();
174 break;
175 case SessionTypeAudio:
176 startMediaCall(false);
177 break;
178 case SessionTypeVideo:
179 startMediaCall(true);
180 break;
181 case SessionTypeMuc:
182 qDebug() << "MUC";
183 //startTextChatroom();
184 break;
185 default:
186 qDebug() << "Session: Default session type. Something is wrong.";
187 Q_ASSERT(false);
189 m_chatType = m_chatType | chatType;
193 ///-----------------------------------------------------------------------------------------------------------------------
194 ///************************************************** Text Chat ******************************************************
195 ///-----------------------------------------------------------------------------------------------------------------------
197 void Session::startTextChat() {
198 qDebug() << "Session: Start text chat (one-to-one)";
199 //returns PendingChannel object, which emits succeeded signal, see if it would be better
200 //to wait for that instead of distributing the channel through handleChannels.
201 m_account->ensureTextChat(m_contact, QDateTime::currentDateTime(), "org.freedesktop.Telepathy.Client.Makneto");
204 void Session::onTextChannelReady(Tp::TextChannelPtr& textChannel) {
205 qDebug() << "Session: Text Channel ready";
206 if (m_textChannel.isNull()) {
207 m_textChannel = textChannel;
208 m_chatType.operator|=(SessionTypeText);
210 connect(m_textChannel.data(),
211 SIGNAL(messageReceived(const Tp::ReceivedMessage &)),
212 SLOT(onMessageReceived(const Tp::ReceivedMessage &)));
214 //chat status changed
215 connect(m_textChannel.data(),
216 SIGNAL(chatStateChanged(Tp::ContactPtr, Tp::ChannelChatState)),
217 SLOT(onChatStateChanged(Tp::ContactPtr, Tp::ChannelChatState)));
220 //QStringList *messages = new QStringList();
222 foreach(Tp::ReceivedMessage message, m_textChannel->messageQueue()) {
224 //messages->append(message.text());
225 emit messageReceived(message.text(), message.sender()->alias());
226 m_textChannel->acknowledge(QList<Tp::ReceivedMessage > () << message);
228 //emit textChatReady(messages);
232 void Session::onMessageReceived(const Tp::ReceivedMessage &message) {
233 //qDebug() << "Message received: " << message.text();
234 QString sender;
235 if (message.sender() && message.text() != QString()) {
236 sender = message.sender()->alias();
237 emit messageReceived(message.text(), sender);
239 //message acknowledging causes the messages to be deleted from pendingMessageQueue
240 m_textChannel->acknowledge(QList<Tp::ReceivedMessage > () << message);
244 void Session::sendMessage(const QString& message) {
246 if (message == QString())
247 return;
249 if (m_textChannel.isNull()){
250 qWarning() << "Sesssion: m_textChannel shared pointer is NULL !!!";
251 return;
254 //qDebug() << "Session: Sending message: " << message;
255 connect(m_textChannel->send(message), SIGNAL(finished(Tp::PendingOperation *)),
256 this, SLOT(onMessageSent(Tp::PendingOperation *)));
259 void Session::onMessageSent(Tp::PendingOperation *op) {
260 if (op->isError()) {
261 Tp::PendingSendMessage *pendingMessage = qobject_cast<Tp::PendingSendMessage *>(op);
262 qDebug() << "Session: Error sending message: " << pendingMessage->message().text()
263 << "\nError: " << pendingMessage->errorMessage();
264 } else
265 emit messageSent();
268 void Session::onChatStateChanged(Tp::ContactPtr contact, Tp::ChannelChatState chatState) {
269 qDebug() << "Session: Channel chat state changed for: " << contact->alias() << " to: " << chatState;
270 //the contact alias is for MUC - I think
271 switch (chatState) {
273 case Tp::ChannelChatStateComposing:
274 emit chatStateChanged(ChatStateComposing, contact->alias());
275 break;
276 case Tp::ChannelChatStateGone:
277 emit chatStateChanged(ChatStateGone, contact->alias());
278 break;
279 case Tp::ChannelChatStateActive:
280 emit chatStateChanged(ChatStateActive, contact->alias());
281 break;
282 case Tp::ChannelChatStateInactive:
283 emit chatStateChanged(ChatStateInactive, contact->alias());
284 break;
285 case Tp::ChannelChatStatePaused:
286 emit chatStateChanged(ChatStatePaused, contact->alias());
287 break;
288 default:
289 qDebug() << "Session: Warning: Unknown chat state";
296 ///-----------------------------------------------------------------------------------------------------------------------
297 ///************************************************** MUC ************************************************************
298 ///-----------------------------------------------------------------------------------------------------------------------
301 //probably useless
303 void Session::startTextChatroom(const QString &chatRoomName) {
304 qDebug() << "Session: Start text chat chatroom (MUC)";
305 m_account->ensureTextChatroom(chatRoomName, QDateTime::currentDateTime(), "org.freedesktop.Telepathy.Client.Makneto");
308 void Session::onTextChannelReady(Tp::TextChannelPtr& textChannel, bool isMUC) {
309 qDebug() << "Session: Text Channel ready";
310 if (m_textChannel.isNull()) {
311 m_textChannel = textChannel;
313 isMUC ? m_chatType = SessionTypeMuc
314 : m_chatType.operator|=(SessionTypeText);
316 connect(m_textChannel.data(),
317 SIGNAL(messageReceived(const Tp::ReceivedMessage &)),
318 SLOT(onMessageReceived(const Tp::ReceivedMessage &)));
320 //chat status changed TODO
321 connect(m_textChannel.data(),
322 SIGNAL(chatStateChanged(Tp::ContactPtr, Tp::ChannelChatState)),
323 SLOT(onChatStateChanged(Tp::ContactPtr, Tp::ChannelChatState)));
325 foreach(Tp::ReceivedMessage message, m_textChannel->messageQueue()) {
326 emit messageReceived(message.text(), message.sender()->alias());
327 m_textChannel->acknowledge(QList<Tp::ReceivedMessage > () << message);
332 bool Session::isMUC() {
333 return m_chatType.testFlag(SessionTypeMuc);
337 ///-----------------------------------------------------------------------------------------------------------------------
338 ///************************************************** VoIP **********************************************************
339 ///-----------------------------------------------------------------------------------------------------------------------
341 void Session::startMediaCall(bool video) {
342 qDebug() << "Session: start media call (" << m_uniqueSessionName << ")";
344 if (m_contact.isNull()){
345 qWarning() << "Session: m_contact shared pointer is NULL !!!";
346 return;
349 QVariantMap request;
350 request.insert(TELEPATHY_INTERFACE_CHANNEL ".ChannelType",
351 TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
352 request.insert(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType", Tp::HandleTypeContact);
353 request.insert(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle", m_contact->handle()[0]);
354 request.insert(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", true);
355 if (video)
356 request.insert(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", true);
358 m_account->ensureChannel(request, QDateTime::currentDateTime(), "org.freedesktop.Telepathy.Client.Makneto");
362 //TODO
364 void Session::onIncomingCallChannel(Tp::StreamedMediaChannelPtr& mediaChannel, SessionType mediaType) {
366 qDebug() << "Session: Incoming call channel";
368 if (!m_mediaChannel.isNull()) {
369 qWarning() << "previous channel is not cleaned?!";
370 return;
373 m_chatType.operator|=(mediaType);
374 m_mediaChannel = mediaChannel;
376 //TODO connect channel's signals
378 if (m_mediaChannel->isRequested())
379 emit callReady();
380 else
381 emit incomingCall();
384 void Session::acceptCall() {
385 qDebug() << "Session: Accept Call";
386 if (m_mediaChannel.isNull() && m_callChannelHandler == NULL)
387 return;
389 m_mediaChannel->acceptCall();
391 m_callChannelHandler = new CallChannelHandler(m_mediaChannel, this);
393 connect(m_callChannelHandler, SIGNAL(callEnded(QString)),
394 this, SLOT(onCallEnded(QString)));
395 connect(m_callChannelHandler, SIGNAL(error(QString)),
396 this, SLOT(onCallError(QString)));
397 connect(m_callChannelHandler, SIGNAL(participantLeft(CallParticipant*)),
398 this, SLOT(onCallParticipantLeft(CallParticipant*)));
399 connect(m_callChannelHandler, SIGNAL(participantJoined(CallParticipant*)),
400 this, SLOT(onCallParticipantJoined(CallParticipant*)));
403 void Session::rejectCall() {
404 qDebug() << "Session: Rejecting call from: " << m_contact->alias();
405 if (!m_mediaChannel.isNull()) {
406 m_mediaChannel->hangupCall();
407 m_mediaChannel.reset();
409 if (m_chatType.testFlag(SessionTypeVideo))
410 m_chatType.operator^=(SessionTypeVideo);
411 m_chatType.operator^=(SessionTypeAudio);
414 void MaknetoBackend::Session::onCallEnded(const QString &message) {
415 qDebug() << "Session: Call ended";
416 //TODO figure out what to do with the channel
417 if (m_callChannelHandler)
418 m_callChannelHandler->deleteLater(); //TODO check out
420 m_mediaChannel.reset();
421 if (m_chatType.testFlag(SessionTypeVideo))
422 m_chatType.operator^=(SessionTypeVideo);
423 m_chatType.operator^=(SessionTypeAudio);
425 emit callEnded(message);
428 void Session::onHangup() {
429 qDebug() << "Session: Hang up";
430 //TODO check if the call still lasts
431 if (!m_mediaChannel.isNull() && m_mediaChannel->isValid()) {
432 m_callChannelHandler->hangup();
434 m_mediaChannel.reset();
435 if (m_chatType.testFlag(SessionTypeVideo))
436 m_chatType.operator^=(SessionTypeVideo);
437 m_chatType.operator^=(SessionTypeAudio);
440 void Session::onCallError(const QString &msg) {
441 // just forward to higher layer
442 Q_EMIT callError(msg);
445 void Session::onCallParticipantLeft(CallParticipant *participant) {
446 // FIXME: should we do something? all streams are removed already
449 void Session::onCallParticipantJoined(CallParticipant *participant) {
450 connect(participant, SIGNAL(videoStreamAdded(CallParticipant*)),
451 this, SLOT(onVideoStreamAdded(CallParticipant*)));
452 connect(participant, SIGNAL(videoStreamRemoved(CallParticipant*)),
453 this, SLOT(onVideoStreamRemoved(CallParticipant*)));
456 void Session::onVideoStreamAdded(CallParticipant *participant) {
457 if (!participant->videoWidget()) {
458 qWarning() << "video stream added, but we dont have videoWidget!";
459 return;
462 if (participant->isMyself()) {
463 Q_EMIT videoPreviewAvailable(participant->videoWidget());
464 } else {
465 Q_EMIT videoAvailable(participant->videoWidget());
469 void Session::onVideoStreamRemoved(CallParticipant *participant) {
470 if (participant->isMyself()) {
471 Q_EMIT videoPreviewRemoved();
472 } else {
473 Q_EMIT videoRemoved();
478 ///-----------------------------------------------------------------------------------------------------------------------
479 ///********************************************** File Transfer ******************************************************
480 ///-----------------------------------------------------------------------------------------------------------------------
482 void Session::startFileTransfer(const QString &filename) {
483 if (m_chatType.testFlag(SessionTypeMuc)) {
485 qDebug() << "Session: File transfer does not work with MUC";
486 return;
488 if (!m_fileTransferChannel.isNull() && m_fileTransferChannel->state() != Tp::FileTransferStateNone) {
489 qDebug() << "Session: There already is an unfinished file transfer.";
490 emit fileTransferAlreadyInProgress();
491 return;
494 m_fileTransferFile.setFileName(filename);
495 QFileInfo fileInfo(m_fileTransferFile);
496 Tp::FileTransferChannelCreationProperties fileTransferProperties = Tp::FileTransferChannelCreationProperties(fileInfo.fileName(),
497 QString("application/octet-stream"),
498 (qulonglong) fileInfo.size());
499 m_account->createFileTransfer(m_contact,
500 fileTransferProperties,
501 QDateTime::currentDateTime(),
502 "org.freedesktop.Telepathy.Client.Makneto");
505 void Session::onFileTransfer(Tp::FileTransferChannelPtr& fileTransferChannel) {
506 //check if there is not an active file transfer
507 if (!m_fileTransferChannel.isNull() && m_fileTransferChannel->state() != Tp::FileTransferStateNone) {
508 qDebug() << "Session: Incoming file transfer ignored, there is already one in progress";
509 return;
512 m_fileTransferChannel = fileTransferChannel;
513 if (m_fileTransferChannel->isRequested()) {
515 if (!m_fileTransferFile.open(QIODevice::ReadOnly)) { //TODO let GUI know
516 qDebug() << "Session: error opening file: " << m_fileTransferFile.fileName();
517 m_fileTransferChannel->cancel();
518 m_fileTransferChannel.reset();
519 return;
521 Tp::OutgoingFileTransferChannelPtr outgoingFileTransfer = Tp::OutgoingFileTransferChannelPtr::dynamicCast(m_fileTransferChannel);
522 if (outgoingFileTransfer)
523 outgoingFileTransfer->provideFile(&m_fileTransferFile);
525 connect(m_fileTransferChannel.data(), SIGNAL(stateChanged(Tp::FileTransferState, Tp::FileTransferStateChangeReason)),
526 this, SLOT(onFileTransferStateChanged(Tp::FileTransferState, Tp::FileTransferStateChangeReason)));
527 connect(m_fileTransferChannel.data(), SIGNAL(transferredBytesChanged(qulonglong)),
528 this, SLOT(onFileTransferTransferredBytesChanged(qulonglong)));
529 emit fileTransferCreated();
530 } else {
531 emit incomingFileTransfer(fileTransferChannel->fileName());
535 void Session::onAcceptFileTransfer(bool accept, QString filename) {
536 if (accept) {
537 Tp::IncomingFileTransferChannelPtr incomingFT = Tp::IncomingFileTransferChannelPtr::dynamicCast(m_fileTransferChannel);
539 if (incomingFT) {
541 if (filename == QString()) {
542 filename = incomingFT->fileName();
544 m_fileTransferFile.setFileName(filename);
545 if (m_fileTransferFile.open(QIODevice::WriteOnly)) {
546 qDebug() << "Session: Could not open/create file: " << m_fileTransferFile.fileName();
548 incomingFT->acceptFile(0, &m_fileTransferFile); //the first number is the offset of the file
550 connect(m_fileTransferChannel.data(), SIGNAL(stateChanged(Tp::FileTransferState, Tp::FileTransferStateChangeReason)),
551 this, SLOT(onFileTransferStateChanged(Tp::FileTransferState, Tp::FileTransferStateChangeReason)));
552 connect(m_fileTransferChannel.data(), SIGNAL(transferredBytesChanged(qulonglong)),
553 SLOT(onFileTransferTransferredBytesChanged(qulonglong)));
556 } else {
557 //return PendingOperation object - possibly check if it finished ok
558 m_fileTransferChannel->cancel(); //TODO check if this is the method to call, or just wait it out - not do anything
559 m_fileTransferChannel.reset();
563 void Session::onFileTransferStateChanged(Tp::FileTransferState state, Tp::FileTransferStateChangeReason reason) {
564 Q_UNUSED(reason);
565 switch (state) {
566 case Tp::FileTransferStateAccepted:
567 //TODO provide file
568 emit fileTransferStateChanged(fileTransferAccepted);
569 break;
570 case Tp::FileTransferStateCancelled:
571 emit fileTransferStateChanged(fileTransferCancelled);
572 m_fileTransferChannel.reset();
573 m_fileTransferFile.close();
574 break;
575 case Tp::FileTransferStateCompleted:
576 emit fileTransferStateChanged(fileTransferCompleted);
577 m_fileTransferChannel.reset();
578 m_fileTransferFile.close();
579 break;
580 case Tp::FileTransferStateOpen:
581 emit fileTransferStateChanged(fileTransferOpen);
582 break;
583 default: qDebug() << "Session: File transfer channel in an for now unsupported state";
588 QString Session::sessionNameForChannel(const Tp::AccountPtr &account, const Tp::ChannelPtr &channel) {
589 Tp::Contacts allContacts = channel->groupContacts();
590 if (allContacts.size() == 2) { //one-to-one chat
592 foreach(Tp::ContactPtr contact, allContacts) {
593 if (contact != channel->groupSelfContact())
594 return sessionName(account, contact);
596 } else if (allContacts.size() == 1) {
597 Tp::Contacts pendingContacts = channel->groupRemotePendingContacts();
598 if (pendingContacts.size() == 1) {
599 return (account ? account->uniqueIdentifier() : "undefinedAccount") + "/" +(pendingContacts.begin()->constData()->id());
602 //groupChat
603 return sessionNameForMuc(account, channel->targetId());
606 QString Session::sessionName(const Tp::AccountPtr &account, const Tp::ContactPtr &contact) {
607 return (account ? account->uniqueIdentifier() : "undefinedAccount") + "/" +(contact ? contact->id() : "undefinedContact");
610 QString Session::sessionNameForMuc(const Tp::AccountPtr &account, const QString &mucName){
611 return (account ? account->uniqueIdentifier() : "undefinedAccount") + "/MUC/" +(mucName);
615 void Session::onFileTransferTransferredBytesChanged(qulonglong bytes) {
616 qDebug() << "Session: Transferred bytes: " << bytes;
617 emit fileTransferTransferredBytesChanged(bytes);
620 QString Session::getName() {
621 return m_sessionName;
624 QString Session::getUniqueName() {
625 return m_uniqueSessionName;
628 QString Session::getMyId() {
629 return m_myContact->id();
632 QString Session::getMyNick() {
633 return m_myContact->alias();
636 Session::SesstionTypes Session::getChatType() {
637 return m_chatType;
640 QString Session::getIcon(){
641 if (m_contact.isNull())
642 return QString();
643 return m_contact->avatarData().fileName;
647 #include "session.moc"