Skip to content

Commit

Permalink
Reassemble fragmented OTR messages
Browse files Browse the repository at this point in the history
When using an XMPP to IRC gateway such as bitlbee, a single OTR
message can get broken into multiple parts due to IRC message length
limit. Buffer such parts until a whole OTR message is received, and
only then pass it to libotr.

Note that this is unrelated to fragmentation defined in OTR protocol,
which is handled by libotr.
  • Loading branch information
jetomit committed Jul 3, 2019
1 parent d3d8262 commit 11cecd8
Showing 1 changed file with 36 additions and 0 deletions.
36 changes: 36 additions & 0 deletions otr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <regex>

using std::list;
using std::map;

#define PROTOCOL_ID "irc"

Expand Down Expand Up @@ -92,6 +94,9 @@ class COtrMod : public CModule {
VCString m_vsIgnored;
COtrTimer* m_pOtrTimer;

// per-sender buffer of received partial OTR messages
map<CString, CString> m_MessageBuffer;

// m_GenKeyRunning acts as a lock for members following it. We don't need
// an actual lock because it is accessed only from the main thread.
bool m_GenKeyRunning;
Expand Down Expand Up @@ -816,6 +821,10 @@ class COtrMod : public CModule {
return SendEncrypted(sTarget, sLine);
}

static bool HasOtrMessageEnd(const CString& sMessage) {
return sMessage.EndsWith(".") || sMessage.EndsWith(",");
}

EModRet OnPrivMsg(CNick& Nick, CString& sMessage) override {
int res;
char* newmessage = NULL;
Expand All @@ -827,6 +836,33 @@ class COtrMod : public CModule {
return CONTINUE;
}

// When using an XMPP to IRC gateway such as bitlbee, a single OTR
// message can get broken into multiple parts due to IRC message length
// limit. Buffer such parts until a whole OTR message is received, and
// only then pass it to libotr.
if (sMessage.StartsWith("?OTR")) {
if (!HasOtrMessageEnd(sMessage) && FindOtrQuery(sMessage).empty()) {
// received beginning of an incomplete OTR message (not a query);
// buffer it (replacing any existing data) and wait for rest
m_MessageBuffer[sNick] = sMessage;
return HALT;
}
} else {
auto buffer = m_MessageBuffer.find(sNick);
if (buffer != m_MessageBuffer.end()) {
// received the next part of a buffered OTR message
if (!HasOtrMessageEnd(sMessage)) {
// OTR message still incomplete, add new data to buffer
buffer->second += sMessage;
return HALT;
} else {
// this part completes a buffered OTR message
sMessage = buffer->second + sMessage;
m_MessageBuffer.erase(buffer);
}
}
}

const char* accountname = GetUser()->GetUserName().c_str();
res = otrl_message_receiving(m_pUserState, &m_xOtrOps, this,
accountname, PROTOCOL_ID, sNick.c_str(),
Expand Down

0 comments on commit 11cecd8

Please sign in to comment.