diff --git a/main.lua b/main.lua index 493094e..6710c4a 100644 --- a/main.lua +++ b/main.lua @@ -37,6 +37,7 @@ local IrcChatView = TextViewer:extend{ _ordered_targets = nil, -- ordered list of targets for UI _current_target = nil, -- active target ("*" for server console) _server_label = nil, + _unread = nil, -- map target => count of unseen messages } function IrcChatView:init() @@ -62,6 +63,9 @@ function IrcChatView:init() self._ordered_targets = {} self._current_target = nil self._server_label = self._server and (self._server.name or (self._server.host .. ":" .. tostring(self._server.port or 6667))) or "IRC" + self._unread = {} + -- default to server console target + self:switchTarget("*") self:updateTitle() TextViewer.init(self) -- Start connection after UI init so we can show logs @@ -71,7 +75,7 @@ end function IrcChatView:updateTitle() local tgt = self._current_target if tgt and tgt ~= "*" then - self.title = T(_("%1 — %2"), self._server_label, tgt) + self.title = tgt else self.title = self._server_label end @@ -83,6 +87,9 @@ function IrcChatView:ensureBuffer(target) self._buffers[target] = "" table.insert(self._ordered_targets, target) end + if self._unread[target] == nil then + self._unread[target] = 0 + end end function IrcChatView:switchTarget(target) @@ -94,6 +101,8 @@ function IrcChatView:switchTarget(target) self.scroll_text_w.text_widget:setText(self._buffers[target] or "") self.scroll_text_w:scrollToBottom() end + -- reset unread counter when focusing this target + self._unread[target] = 0 end function IrcChatView:appendLine(line, target) @@ -109,6 +118,9 @@ function IrcChatView:appendLine(line, target) self.scroll_text_w.text_widget:setText(buf) self.scroll_text_w:scrollToBottom() end + else + -- increment unread counter for background target + self._unread[target] = (self._unread[target] or 0) + 1 end end @@ -124,7 +136,7 @@ function IrcChatView:startConnection() return end - -- Do a blocking connect with a short timeout, then switch to non-blocking. + -- Do a blocking connect with a short timeout, then switch to non-blocking (and TLS if needed). sock:settimeout(10, 't') local ok, cerr = sock:connect(host, port) if not ok then @@ -132,8 +144,43 @@ function IrcChatView:startConnection() pcall(function() sock:close() end) return end - sock:settimeout(0, 'b') - self._sock = sock + local use_tls = (tonumber(port) == 6697) or (self._server.tls == true) + if use_tls then + local okreq, ssl = pcall(require, "ssl") + if not okreq or not ssl then + self:appendLine(_("TLS requested but LuaSec is unavailable.")) + sock:settimeout(0, 'b') + self._sock = sock + else + local params = { + mode = "client", + protocol = "tlsv1_2", + verify = "none", + options = "all", + -- SNI support + server = host, + } + local ssock, werr = ssl.wrap(sock, params) + if not ssock then + self:appendLine(T(_("TLS wrap failed: %1"), tostring(werr))) + sock:settimeout(0, 'b') + self._sock = sock + else + ssock:settimeout(10, 't') + local hok, herr = ssock:dohandshake() + if not hok then + self:appendLine(T(_("TLS handshake failed: %1"), tostring(herr))) + pcall(function() ssock:close() end) + return + end + ssock:settimeout(0, 'b') + self._sock = ssock + end + end + else + sock:settimeout(0, 'b') + self._sock = sock + end -- Authenticate if configured, then send NICK/USER; Join will be sent after welcome (001) self._connected = true @@ -328,13 +375,26 @@ function IrcChatView:handleLine(line) end return elseif command == "001" then -- welcome (registered) - local msg = rest:match("^%S+%s+:(.+)$") or rest + local target_nick, msg = rest:match("^(%S+)%s+:(.+)$") + if target_nick then + self._nick = target_nick + else + msg = rest + end self._registered = true self:appendLine(msg) if self._pending_join and #self._pending_join > 0 then self:sendRaw(string.format("JOIN %s\r\n", self._pending_join)) end return + elseif command == "NICK" then -- someone changed nick; update ours if it's us + local newnick = rest:match("^:(.+)$") or rest + local oldnick = prefix:match("^([^!]+)!") or prefix + if oldnick == self._nick and newnick and #newnick > 0 then + self._nick = newnick + self:appendLine(T(_("You are now known as %1"), newnick)) + end + return elseif command == "376" or command == "422" then -- End of MOTD / No MOTD: safe to join if not yet joined if not self._registered then self._registered = true end @@ -382,7 +442,14 @@ function IrcChatView:onShowMenu() for _, tgt in ipairs(self._ordered_targets) do table.insert(buttons, { { - text = tgt, + text_func = function() + local n = self._unread[tgt] or 0 + if n > 0 then + return string.format("%s (%d)", tgt, n) + else + return tgt + end + end, checked_func = function() return self._current_target == tgt end, callback = function() self:switchTarget(tgt)