summaryrefslogtreecommitdiff
path: root/src/bridge/bridge.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bridge/bridge.cpp')
-rw-r--r--src/bridge/bridge.cpp181
1 files changed, 150 insertions, 31 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp
index d16875f..8849ef9 100644
--- a/src/bridge/bridge.cpp
+++ b/src/bridge/bridge.cpp
@@ -1,5 +1,4 @@
#include <bridge/bridge.hpp>
-#include <bridge/list_element.hpp>
#include <xmpp/biboumi_component.hpp>
#include <network/poller.hpp>
#include <utils/empty_if_fixed_server.hpp>
@@ -10,6 +9,7 @@
#include <utils/split.hpp>
#include <xmpp/jid.hpp>
#include <database/database.hpp>
+#include "result_set_management.hpp"
using namespace std::string_literals;
@@ -386,45 +386,164 @@ void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick)
irc->send_nick_command(new_nick);
}
-void Bridge::send_irc_channel_list_request(const Iid& iid, const std::string& iq_id,
- const std::string& to_jid)
+void Bridge::send_irc_channel_list_request(const Iid& iid, const std::string& iq_id, const std::string& to_jid,
+ ResultSetInfo rs_info)
{
- IrcClient* irc = this->get_irc_client(iid.get_server());
+ auto& list = channel_list_cache[iid.get_server()];
- irc->send_list_command();
+ // We fetch the list from the IRC server only if we have a complete
+ // cached list that needs to be invalidated (that is, when the request
+ // doesn’t have a after or before, or when the list is empty).
+ // If the list is not complete, this means that a request is already
+ // ongoing, so we just need to add the callback.
+ // By default the list is complete and empty.
+ if (list.complete &&
+ (list.channels.empty() || (rs_info.after.empty() && rs_info.before.empty())))
+ {
+ IrcClient* irc = this->get_irc_client(iid.get_server());
+ irc->send_list_command();
+
+ // Add a callback that will populate our list
+ list.channels.clear();
+ list.complete = false;
+ irc_responder_callback_t cb = [this, iid](const std::string& irc_hostname,
+ const IrcMessage& message) -> bool
+ {
+ if (irc_hostname != iid.get_server())
+ return false;
- std::vector<ListElement> list;
+ auto& list = channel_list_cache[iid.get_server()];
+
+ if (message.command == "263" || message.command == "RPL_TRYAGAIN" || message.command == "ERR_TOOMANYMATCHES"
+ || message.command == "ERR_NOSUCHSERVER")
+ {
+ list.complete = true;
+ return true;
+ }
+ else if (message.command == "322" || message.command == "RPL_LIST")
+ { // Add element to list
+ if (message.arguments.size() == 4)
+ {
+ list.channels.emplace_back(message.arguments[1] + utils::empty_if_fixed_server("%" + iid.get_server()),
+ message.arguments[2], message.arguments[3]);
+ }
+ return false;
+ }
+ else if (message.command == "323" || message.command == "RPL_LISTEND")
+ { // Send the iq response with the content of the list
+ list.complete = true;
+ return true;
+ }
+ return false;
+ };
- irc_responder_callback_t cb = [this, iid, iq_id, to_jid, list=std::move(list)](const std::string& irc_hostname,
- const IrcMessage& message) mutable -> bool
+ this->add_waiting_irc(std::move(cb));
+ }
+
+ // If the list is complete, we immediately send the answer.
+ // Otherwise, we install a callback, that will populate our list and send
+ // the answer when we can.
+ if (list.complete)
{
- if (irc_hostname != iid.get_server())
+ this->send_matching_channel_list(list, rs_info, iq_id, to_jid, std::to_string(iid));
+ }
+ else
+ {
+ // Add a callback to answer the request as soon as we can
+ irc_responder_callback_t cb = [this, iid, iq_id, to_jid,
+ rs_info=std::move(rs_info)](const std::string& irc_hostname,
+ const IrcMessage& message) -> bool
+ {
+ if (irc_hostname != iid.get_server())
+ return false;
+
+ if (message.command == "263" || message.command == "RPL_TRYAGAIN" || message.command == "ERR_TOOMANYMATCHES"
+ || message.command == "ERR_NOSUCHSERVER")
+ {
+ std::string text;
+ if (message.arguments.size() >= 2)
+ text = message.arguments[1];
+ this->xmpp.send_stanza_error("iq", to_jid, std::to_string(iid), iq_id, "wait", "service-unavailable", text, false);
+ return true;
+ }
+ else if (message.command == "322" || message.command == "RPL_LIST")
+ {
+ auto& list = channel_list_cache[iid.get_server()];
+ const auto res = this->send_matching_channel_list(list, rs_info, iq_id, to_jid, std::to_string(iid));
+ log_debug("We added a new channel in our list, can we send the result? ", std::boolalpha, res);
+ return res;
+ }
+ else if (message.command == "323" || message.command == "RPL_LISTEND")
+ { // Send the iq response with the content of the list
+ auto& list = channel_list_cache[iid.get_server()];
+ this->send_matching_channel_list(list, rs_info, iq_id, to_jid, std::to_string(iid));
+ return true;
+ }
return false;
- if (message.command == "263" || message.command == "RPL_TRYAGAIN" ||
- message.command == "ERR_TOOMANYMATCHES" || message.command == "ERR_NOSUCHSERVER")
+ };
+
+ this->add_waiting_irc(std::move(cb));
+ }
+}
+
+bool Bridge::send_matching_channel_list(const ChannelList& channel_list, const ResultSetInfo& rs_info,
+ const std::string& id, const std::string& to_jid, const std::string& from)
+{
+ auto begin = channel_list.channels.begin();
+ auto end = channel_list.channels.begin();
+ if (channel_list.complete)
+ {
+ begin = std::find_if(channel_list.channels.begin(), channel_list.channels.end(), [this, &rs_info](const ListElement& element)
+ {
+ return rs_info.after == element.channel + "@" + this->xmpp.get_served_hostname();
+ });
+ if (begin == channel_list.channels.end())
+ begin = channel_list.channels.begin();
+ else
+ begin = std::next(begin);
+ end = std::find_if(channel_list.channels.begin(), channel_list.channels.end(), [this, &rs_info](const ListElement& element)
+ {
+ return rs_info.before == element.channel + "@" + this->xmpp.get_served_hostname();
+ });
+ if (rs_info.max >= 0)
{
- std::string text;
- if (message.arguments.size() >= 2)
- text = message.arguments[1];
- this->xmpp.send_stanza_error("iq", to_jid, std::to_string(iid), iq_id,
- "wait", "service-unavailable", text, false);
- return true;
+ if (std::distance(begin, end) >= rs_info.max)
+ end = begin + rs_info.max;
}
- else if (message.command == "322" || message.command == "RPL_LIST")
- { // Add element to list
- if (message.arguments.size() == 4)
- list.emplace_back(message.arguments[1], message.arguments[2],
- message.arguments[3]);
- return false;
+ }
+ else
+ {
+ if (rs_info.after.empty() && rs_info.before.empty() && rs_info.max < 0)
+ return false;
+ if (!rs_info.after.empty())
+ {
+ begin = std::find_if(channel_list.channels.begin(), channel_list.channels.end(), [this, &rs_info](const ListElement& element)
+ {
+ return rs_info.after == element.channel + "@" + this->xmpp.get_served_hostname();
+ });
+ if (begin == channel_list.channels.end())
+ return false;
+ begin = std::next(begin);
}
- else if (message.command == "323" || message.command == "RPL_LISTEND")
- { // Send the iq response with the content of the list
- this->xmpp.send_iq_room_list_result(iq_id, to_jid, std::to_string(iid), list);
- return true;
+ if (!rs_info.before.empty())
+ {
+ end = std::find_if(channel_list.channels.begin(), channel_list.channels.end(), [this, &rs_info](const ListElement& element)
+ {
+ return rs_info.before == element.channel + "@" + this->xmpp.get_served_hostname();
+ });
+ if (end == channel_list.channels.end())
+ return false;
}
- return false;
- };
- this->add_waiting_irc(std::move(cb));
+ if (rs_info.max >= 0)
+ {
+ if (std::distance(begin, end) < rs_info.max)
+ return false;
+ else
+ end = begin + rs_info.max;
+ }
+ }
+ this->xmpp.send_iq_room_list_result(id, to_jid, from, channel_list, begin, end, rs_info);
+ return true;
}
void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason,
@@ -1002,4 +1121,4 @@ void Bridge::set_record_history(const bool val)
{
this->record_history = val;
}
-#endif \ No newline at end of file
+#endif