void lmcMessaging::checkPendingMsg(void) { for(int index = 0; index < pendingList.count(); index++) { // check if message has timed out if(pendingList[index].active && pendingList[index].timeStamp.msecsTo(QDateTime::currentDateTime()) > nTimeout) { if(pendingList[index].retry < nMaxRetry) { // send the message once more pendingList[index].retry++; pendingList[index].timeStamp = QDateTime::currentDateTime(); resendMessage(pendingList[index].type, pendingList[index].msgId, &pendingList[index].userId, &pendingList[index].xmlMessage); } else { XmlMessage statusMsg; // max retries exceeded. mark message as failed. switch(pendingList[index].type) { case MT_Message: emit messageReceived(MT_Failed, &pendingList[index].userId, &pendingList[index].xmlMessage); break; case MT_Ping: statusMsg.addData(XN_STATUS, statusCode[ST_COUNT - 1]); emit messageReceived(MT_Status, &pendingList[index].userId, &statusMsg); emit messageReceived(MT_Depart, &pendingList[index].userId, NULL); removeUser(pendingList[index].userId); break; default: break; } pendingList[index].active = false; pendingList.removeAt(index); index--; // since next item will have this index now } } } }
bool lmcMessaging::addUser(QString szUserId, QString szVersion, QString szAddress, QString szName, QString szStatus, QString szAvatar, QString szNote, QString szCaps) { for(int index = 0; index < userList.count(); index++) if(userList[index].id.compare(szUserId) == 0) return false; lmcTrace::write("Adding new user: "******", " + szVersion + ", " + szAddress); if(!userGroupMap.contains(szUserId) || !groupList.contains(Group(userGroupMap.value(szUserId)))) userGroupMap.insert(szUserId, GRP_DEFAULT_ID); int nAvatar = szAvatar.isNull() ? -1 : szAvatar.toInt(); userList.append(User(szUserId, szVersion, szAddress, szName, szStatus, userGroupMap[szUserId], nAvatar, szNote, QString::null, szCaps)); if(!szStatus.isNull()) { XmlMessage xmlMessage; xmlMessage.addHeader(XN_FROM, szUserId); xmlMessage.addData(XN_STATUS, szStatus); // send a status message to app layer, this is different from announce message emit messageReceived(MT_Status, &szUserId, &xmlMessage); int statusIndex = Helper::statusIndexFromCode(szStatus); if(statusType[statusIndex] == StatusTypeOffline) // offline status return false; // no need to send a new user message to app layer } emit messageReceived(MT_Announce, &szUserId, NULL); return true; }
void lmcChatRoomWindow::removeUser(QString* lpszUserId) { QTreeWidgetItem* pItem = getUserItem(lpszUserId); if(!pItem) return; QTreeWidgetItem* pGroup = pItem->parent(); pGroup->removeChild(pItem); QString userId = peerIds.value(*lpszUserId); QString userName = peerNames.value(*lpszUserId); peerIds.remove(*lpszUserId); peerNames.remove(*lpszUserId); if(groupMode) { XmlMessage xmlMessage; xmlMessage.addData(XN_THREAD, threadId); xmlMessage.addData(XN_GROUPMSGOP, GroupMsgOpNames[GMO_Leave]); appendMessageLog(MT_Leave, &userId, &userName, &xmlMessage); setWindowTitle(getWindowTitle()); } // If the local user is removed for some reason, prevent sending any further messages if(userId.compare(localId) == 0) ui.txtMessage->setEnabled(false); }
void lmcWebNetwork::raiseError(ErrorType type) { XmlMessage xmlMessage; xmlMessage.addHeader(XN_TYPE, MessageTypeNames[MT_WebFailed]); xmlMessage.addData(XN_ERROR, ErrorTypeNames[type]); QString szMessage = xmlMessage.toString(); emit messageReceived(&szMessage); }
void lmcChatRoomWindow::selectContacts(QStringList* selectedContacts) { XmlMessage xmlMessage; xmlMessage.addData(XN_THREAD, threadId); xmlMessage.addData(XN_GROUPMSGOP, GroupMsgOpNames[GMO_Request]); for(int index = 0; index < selectedContacts->count(); index++) { QString userId = selectedContacts->value(index); emit messageSent(MT_GroupMessage, &userId, &xmlMessage); } }
void lmcMessaging::updateUser(MessageType type, QString szUserId, QString szUserData) { User* pUser = getUser(&szUserId); if(!pUser) return; XmlMessage updateMsg; switch(type) { case MT_Status: if(pUser->status.compare(szUserData) != 0) { QString oldStatus = pUser->status; pUser->status = szUserData; int statusIndex = Helper::statusIndexFromCode(oldStatus); if(statusType[statusIndex] == StatusTypeOffline) // old status is offline emit messageReceived(MT_Announce, &szUserId, NULL); updateMsg.addData(XN_STATUS, pUser->status); emit messageReceived(MT_Status, &szUserId, &updateMsg); statusIndex = Helper::statusIndexFromCode(pUser->status); if(statusType[statusIndex] == StatusTypeOffline) { // new status is offline // Send a dummy xml message. A non null xml message implies that the // user is only in offline status, and not actually offline. XmlMessage xmlMessage; emit messageReceived(MT_Depart, &szUserId, &xmlMessage); } } break; case MT_UserName: if(pUser->name.compare(szUserData) != 0) { pUser->name = szUserData; updateMsg.addData(XN_NAME, pUser->name); emit messageReceived(MT_UserName, &szUserId, &updateMsg); } break; case MT_Note: if(pUser->note.compare(szUserData) != 0) { pUser->note = szUserData; updateMsg.addData(XN_NOTE, pUser->note); emit messageReceived(MT_Note, &szUserId, &updateMsg); } break; case MT_Group: pUser->group = szUserData; userGroupMap.insert(pUser->id, pUser->group); saveGroups(); break; case MT_Avatar: pUser->avatarPath = szUserData; break; default: break; } }
void lmcMessaging::removeUser(QString szUserId) { for(int index = 0; index < userList.count(); index++) if(userList.value(index).id.compare(szUserId) == 0) { XmlMessage statusMsg; statusMsg.addData(XN_STATUS, statusCode[ST_COUNT - 1]); emit messageReceived(MT_Status, &szUserId, &statusMsg); emit messageReceived(MT_Depart, &szUserId, NULL); userList.removeAt(index); return; } }
void lmcChatRoomWindow::addUser(User* pUser) { if(!pUser) return; // Do not add user if user's version is 1.2.10 or less. These versions do not // support Public Chat feature. if(Helper::compareVersions(pUser->version, "1.2.10") <= 0) return; // Do not add user if user is already in the list of participants if(peerIds.contains(pUser->id)) return; peerIds.insert(pUser->id, pUser->id); peerNames.insert(pUser->id, pUser->name); int index = Helper::statusIndexFromCode(pUser->status); lmcUserTreeWidgetUserItem *pItem = new lmcUserTreeWidgetUserItem(); pItem->setData(0, IdRole, pUser->id); pItem->setData(0, TypeRole, "User"); pItem->setData(0, StatusRole, index); pItem->setData(0, SubtextRole, pUser->note); pItem->setText(0, pUser->name); if(index != -1) pItem->setIcon(0, QIcon(QPixmap(statusPic[index], "PNG"))); lmcUserTreeWidgetGroupItem* pGroupItem = (lmcUserTreeWidgetGroupItem*)getGroupItem(&GroupId); pGroupItem->addChild(pItem); pGroupItem->sortChildren(0, Qt::AscendingOrder); // this should be called after item has been added to tree setUserAvatar(&pUser->id); if(groupMode) { XmlMessage xmlMessage; xmlMessage.addData(XN_THREAD, threadId); xmlMessage.addData(XN_GROUPMSGOP, GroupMsgOpNames[GMO_Join]); appendMessageLog(MT_Join, &pUser->id, &pUser->name, &xmlMessage); setWindowTitle(getWindowTitle()); emit messageSent(MT_GroupMessage, NULL, &xmlMessage); } // Local user cannot participate in public chat if status is offline if(!groupMode && pUser->id.compare(localId) == 0) { bool offline = (statusType[Helper::statusIndexFromCode(pUser->status)] == StatusTypeOffline); ui.txtMessage->setEnabled(!offline); ui.txtMessage->setFocus(); } }
void lmcMessaging::settingsChanged(void) { nTimeout = pSettings->value(IDS_TIMEOUT, IDS_TIMEOUT_VAL).toInt() * 1000; nMaxRetry = pSettings->value(IDS_MAXRETRIES, IDS_MAXRETRIES_VAL).toInt(); pNetwork->settingsChanged(); QString userName = getUserName(); if(localUser->name.compare(userName) != 0) { localUser->name = userName; XmlMessage xmlMessage; xmlMessage.addData(XN_NAME, userName); sendMessage(MT_UserName, NULL, &xmlMessage); } }
void lmcChatRoomWindow::userFileAction_triggered(void) { QString userId = ui.tvUserList->currentItem()->data(0, IdRole).toString(); QString dir = pSettings->value(IDS_OPENPATH, IDS_OPENPATH_VAL).toString(); QString fileName = QFileDialog::getOpenFileName(this, QString(), dir); if(!fileName.isEmpty()) { pSettings->setValue(IDS_OPENPATH, QFileInfo(fileName).dir().absolutePath()); XmlMessage xmlMessage; xmlMessage.addData(XN_MODE, FileModeNames[FM_Send]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Request]); xmlMessage.addData(XN_FILEPATH, fileName); emit messageSent(MT_LocalFile, &userId, &xmlMessage); } }
void lmcChatRoomWindow::closeEvent(QCloseEvent* pEvent) { if(groupMode) { XmlMessage xmlMessage; xmlMessage.addData(XN_THREAD, threadId); xmlMessage.addData(XN_GROUPMSGOP, GroupMsgOpNames[GMO_Leave]); emit messageSent(MT_GroupMessage, NULL, &xmlMessage); // call stop procedure to save history stop(); emit closed(&threadId); } QWidget::closeEvent(pEvent); }
// A message is to be sent void lmcMessaging::sendMessage(MessageType type, QString* lpszUserId, XmlMessage* pMessage) { QString data = QString::null; XmlMessage message; switch(type) { case MT_Group: data = pMessage->data(XN_GROUP); updateUser(type, *lpszUserId, data); break; case MT_Status: case MT_UserName: case MT_Note: case MT_PublicMessage: for(int index = 0; index < userList.count(); index++) prepareMessage(type, msgId, false, &userList[index].id, pMessage); msgId++; break; case MT_GroupMessage: if(lpszUserId) prepareMessage(type, msgId, false, lpszUserId, pMessage); else { for(int index = 0; index < userList.count(); index++) prepareMessage(type, msgId, false, &userList[index].id, pMessage); } msgId++; break; case MT_Avatar: // if user id is specified send to that user alone, else send to all if(lpszUserId) { if(pMessage->data(XN_FILEOP) == FileOpNames[FO_Request]) pMessage->addData(XN_FILEID, getUuid() ); prepareMessage(type, msgId, false, lpszUserId, pMessage); } else { for(int index = 0; index < userList.count(); index++) { message = pMessage->clone(); message.addData( XN_FILEID, getUuid() ); prepareMessage(type, msgId, false, &userList[index].id, &message); } } msgId++; break; case MT_Version: sendWebMessage(type, pMessage); break; default: prepareMessage(type, msgId, false, lpszUserId, pMessage); msgId++; break; } }
void lmcTransferWindow::btnCancel_clicked(void) { FileView* view = ui.lvTransferList->currentItem(); int mode = view->mode == FileView::TM_Send ? FM_Send : FM_Receive; XmlMessage xmlMessage; xmlMessage.addData(XN_MODE, FileModeNames[mode]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Cancel]); xmlMessage.addData(XN_FILEID, view->id); emit messageSent((MessageType)view->type, &view->userId, &xmlMessage); view->state = FileView::TS_Cancel; ui.lvTransferList->itemChanged(ui.lvTransferList->currentRow()); setButtonState(view->state); }
void lmcMessaging::receiveProgress(QString* lpszUserId, QString* lpszData) { XmlMessage xmlMessage(*lpszData); int fileMode = indexOf( FileModeNames, FM_Max, xmlMessage.data(XN_MODE) ); int fileOp = indexOf( FileOpNames, FO_Max, xmlMessage.data(XN_FILEOP) ); int fileType = indexOf( FileTypeNames, FT_Max, xmlMessage.data(XN_FILETYPE) ); QString fileId = xmlMessage.data(XN_FILEID); // determine type of message to be sent to app layer based on file type MessageType type; switch(fileType) { case FT_Normal: type = MT_File; break; case FT_Avatar: type = MT_Avatar; break; default: type = MT_Blank; break; } XmlMessage reply; switch(fileOp) { case FO_Error: reply.addData(XN_MODE, FileModeNames[fileMode]); reply.addData(XN_FILETYPE, FileTypeNames[fileType]); reply.addData(XN_FILEOP, FileOpNames[FO_Abort]); reply.addData(XN_FILEID, fileId); sendMessage(type, lpszUserId, &reply); emit messageReceived(type, lpszUserId, &xmlMessage); break; case FO_Progress: case FO_Complete: emit messageReceived(type, lpszUserId, &xmlMessage); break; } }
// send the broadcast message to all selected users void lmcBroadcastWindow::sendMessage(void) { // return if text box is empty if(ui.txtMessage->document()->isEmpty()) return; // send only if connected if(bConnected) { QString szHtmlMessage(ui.txtMessage->toHtml()); encodeMessage(&szHtmlMessage); QTextDocument docMessage; docMessage.setHtml(szHtmlMessage); QString szMessage(docMessage.toPlainText()); // send broadcast int sendCount = 0; XmlMessage xmlMessage; xmlMessage.addData(XN_BROADCAST, szMessage); for(int index = 0; index < ui.tvUserList->topLevelItemCount(); index++) { for(int childIndex = 0; childIndex < ui.tvUserList->topLevelItem(index)->childCount(); childIndex++) { QTreeWidgetItem* item = ui.tvUserList->topLevelItem(index)->child(childIndex); if(item->checkState(0) == Qt::Checked) { QString szUserId = item->data(0, IdRole).toString(); emit messageSent(MT_Broadcast, &szUserId, &xmlMessage); sendCount++; } } } if(sendCount == 0) { QMessageBox::warning(this, tr("No recipient selected"), tr("Please select at least one recipient to send a broadcast.")); return; } ui.txtMessage->clear(); close(); } }
void lmcTransferWindow::stop(void) { // Cancel all active transfers for(int index = 0; index < ui.lvTransferList->count(); index++) { FileView* view = ui.lvTransferList->item(index); if(view->state < FileView::TS_Complete) { int mode = view->mode == FileView::TM_Send ? FM_Send : FM_Receive; XmlMessage xmlMessage; xmlMessage.addData(XN_MODE, FileModeNames[mode]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Cancel]); xmlMessage.addData(XN_FILEID, view->id); emit messageSent((MessageType)view->type, &view->userId, &xmlMessage); view->state = FileView::TS_Cancel; } } bool saveHistory = pSettings->value(IDS_FILEHISTORY, IDS_FILEHISTORY_VAL).toBool(); if(saveHistory) ui.lvTransferList->saveData(StdLocation::transferHistory()); pSettings->setValue(IDS_WINDOWTRANSFERS, saveGeometry()); }
void lmcMessageLog::updateFileMessage(FileMode mode, FileOp op, QString fileId) { QString szMessage = getFileStatusMessage(mode, op); QWebFrame* frame = page()->mainFrame(); QWebElement document = frame->documentElement(); QWebElement body = document.findFirst("body"); QString selector = "span#"; QString tempId = (mode == FM_Send) ? "send" : "receive"; tempId.append(fileId); selector.append(tempId); QWebElement span = body.findFirst(selector); span.setPlainText(szMessage); // update the entry in message log for(int index = 0; index < messageLog.count(); index++) { SingleMessage msg = messageLog.at(index); if(tempId.compare(msg.id) == 0) { XmlMessage xmlMessage = msg.message; xmlMessage.removeData(XN_FILEOP); xmlMessage.addData(XN_FILEOP, FileOpNames[op]); msg.message = xmlMessage; break; } } }
void lmcMessaging::sendUserData(MessageType type, QueryOp op, QString* lpszUserId, QString* lpszAddress) { lmcTrace::write("Sending local user details to user " + *lpszUserId + " at " + *lpszAddress); XmlMessage xmlMessage; xmlMessage.addData(XN_USERID, localUser->id); xmlMessage.addData(XN_NAME, localUser->name); xmlMessage.addData(XN_ADDRESS, localUser->address); xmlMessage.addData(XN_VERSION, localUser->version); xmlMessage.addData(XN_STATUS, localUser->status); xmlMessage.addData(XN_NOTE, localUser->note); xmlMessage.addData(XN_USERCAPS, QString::number(localUser->caps)); xmlMessage.addData(XN_QUERYOP, QueryOpNames[op]); QString szMessage = Message::addHeader(type, msgId, &localUser->id, lpszUserId, &xmlMessage); pNetwork->sendMessage(lpszUserId, lpszAddress, &szMessage); }
void lmcChatRoomWindow::sendMessage(void) { if(ui.txtMessage->document()->isEmpty()) return; if(bConnected) { QString szHtmlMessage(ui.txtMessage->toHtml()); encodeMessage(&szHtmlMessage); QTextDocument docMessage; docMessage.setHtml(szHtmlMessage); QString szMessage = docMessage.toPlainText(); QFont font = ui.txtMessage->font(); font.setPointSize(ui.txtMessage->fontPointSize()); MessageType type = groupMode ? MT_GroupMessage : MT_PublicMessage; XmlMessage xmlMessage; xmlMessage.addHeader(XN_TIME, QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch())); xmlMessage.addData(XN_FONT, font.toString()); xmlMessage.addData(XN_COLOR, messageColor.name()); xmlMessage.addData(XN_MESSAGE, szMessage); if(groupMode) { xmlMessage.addData(XN_THREAD, threadId); xmlMessage.addData(XN_GROUPMSGOP, GroupMsgOpNames[GMO_Message]); } appendMessageLog(type, &localId, &localName, &xmlMessage); if(groupMode) { QHash<QString, QString>::const_iterator index = peerIds.constBegin(); while (index != peerIds.constEnd()) { QString userId = index.value(); emit messageSent(type, &userId, &xmlMessage); index++; } } else emit messageSent(type, NULL, &xmlMessage); } else appendMessageLog(MT_Error, NULL, NULL, NULL); ui.txtMessage->clear(); ui.txtMessage->setFocus(); }
void lmcChatRoomWindow::userInfoAction_triggered(void) { QString userId = ui.tvUserList->currentItem()->data(0, IdRole).toString(); XmlMessage xmlMessage; xmlMessage.addData(XN_QUERYOP, QueryOpNames[QO_Get]); emit messageSent(MT_Query, &userId, &xmlMessage); }
void lmcMessageLog::fileOperation(QString fileId, QString action) { XmlMessage xmlMessage; if(action.compare("fileaccept", Qt::CaseInsensitive) == 0) { XmlMessage fileData = receiveFileMap.value(fileId); xmlMessage.addData(XN_MODE, FileModeNames[FM_Receive]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Accept]); xmlMessage.addData(XN_FILEID, fileData.data(XN_FILEID)); xmlMessage.addData(XN_FILEPATH, fileData.data(XN_FILEPATH)); xmlMessage.addData(XN_FILENAME, fileData.data(XN_FILENAME)); xmlMessage.addData(XN_FILESIZE, fileData.data(XN_FILESIZE)); } else if(action.compare("filedecline", Qt::CaseInsensitive) == 0) { XmlMessage fileData = receiveFileMap.value(fileId); xmlMessage.addData(XN_MODE, FileModeNames[FM_Receive]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Decline]); xmlMessage.addData(XN_FILEID, fileData.data(XN_FILEID)); } else if(action.compare("filecancel", Qt::CaseInsensitive) == 0) { XmlMessage fileData = receiveFileMap.value(fileId); xmlMessage.addData(XN_MODE, FileModeNames[FM_Send]); xmlMessage.addData(XN_FILETYPE, FileTypeNames[FT_Normal]); xmlMessage.addData(XN_FILEOP, FileOpNames[FO_Cancel]); xmlMessage.addData(XN_FILEID, fileData.data(XN_FILEID)); } emit messageSent(MT_LocalFile, &peerId, &xmlMessage); }
void lmcMessaging::processMessage(MessageHeader* pHeader, XmlMessage* pMessage) { QString msgId; QString data = QString::null; XmlMessage reply; lmcTrace::write("Processing message type " + QString::number(pHeader->type) + " from user " + pHeader->userId); switch(pHeader->type) { case MT_UserData: if(pMessage->data(XN_QUERYOP) == QueryOpNames[QO_Get]) sendUserData(pHeader->type, QO_Result, &pHeader->userId, &pHeader->address); // add the user only after sending back user data, this way both parties will have added each other addUser(pMessage->data(XN_USERID), pMessage->data(XN_VERSION), pMessage->data(XN_ADDRESS), pMessage->data(XN_NAME), pMessage->data(XN_STATUS), QString::null, pMessage->data(XN_NOTE), pMessage->data(XN_USERCAPS)); break; case MT_Broadcast: emit messageReceived(pHeader->type, &pHeader->userId, pMessage); break; case MT_Status: data = pMessage->data(XN_STATUS); updateUser(pHeader->type, pHeader->userId, data); break; case MT_UserName: data = pMessage->data(XN_NAME); updateUser(pHeader->type, pHeader->userId, data); break; case MT_Note: data = pMessage->data(XN_NOTE); updateUser(pHeader->type, pHeader->userId, data); break; case MT_Message: // add message to received message list if(addReceivedMsg(pHeader->id, pHeader->userId)) { emit messageReceived(pHeader->type, &pHeader->userId, pMessage); } // send an acknowledgement msgId = QString::number(pHeader->id); reply.addData(XN_MESSAGEID, msgId); sendMessage(MT_Acknowledge, &pHeader->userId, &reply); break; case MT_GroupMessage: emit messageReceived(pHeader->type, &pHeader->userId, pMessage); break; case MT_PublicMessage: emit messageReceived(pHeader->type, &pHeader->userId, pMessage); break; case MT_Ping: // send an acknowledgement msgId = QString::number(pHeader->id); reply.addData(XN_MESSAGEID, msgId); sendMessage(MT_Acknowledge, &pHeader->userId, &reply); break; case MT_Query: // send a reply cum acknowledgement if its a 'get' query if(pMessage->data(XN_QUERYOP) == QueryOpNames[QO_Get]) { msgId = QString::number(pHeader->id); reply.addData(XN_MESSAGEID, msgId); reply.addData(XN_QUERYOP, QueryOpNames[QO_Result]); sendMessage(pHeader->type, &pHeader->userId, &reply); } else if(pMessage->data(XN_QUERYOP) == QueryOpNames[QO_Result]) { msgId = pMessage->data(XN_MESSAGEID); removePendingMsg(msgId.toLongLong()); // Add the path to the user's avatar image stored locally data = "avt_" + pHeader->userId + ".png"; data = QDir(StdLocation::cacheDir()).absoluteFilePath(data); pMessage->addData(XN_AVATAR, data); emit messageReceived(pHeader->type, &pHeader->userId, pMessage); } break; case MT_ChatState: emit messageReceived(pHeader->type, &pHeader->userId, pMessage); break; case MT_Acknowledge: // remove message from pending list msgId = pMessage->data(XN_MESSAGEID); removePendingMsg(msgId.toLongLong()); break; case MT_File: case MT_Avatar: processFile(pHeader, pMessage); break; case MT_Folder: processFolder(pHeader, pMessage); break; default: break; } lmcTrace::write("Message processing done"); }