mirror of
				https://github.com/ShadowNinja/LuaIRC.git
				synced 2025-10-31 12:55:21 +01:00 
			
		
		
		
	Merge remote branch 'jacop/ssl'
Conflicts: asyncoperations.lua
This commit is contained in:
		| @@ -7,11 +7,7 @@ local meta = _META | |||||||
| function meta:send(fmt, ...) | function meta:send(fmt, ...) | ||||||
| 	local bytes, err = self.socket:send(fmt:format(...) .. "\r\n") | 	local bytes, err = self.socket:send(fmt:format(...) .. "\r\n") | ||||||
|  |  | ||||||
| 	if bytes then | 	if not bytes and err ~= "timeout" and err ~= "wantwrite" then | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	if err ~= "timeout" and err ~= "wantwrite" then |  | ||||||
| 		self:invoke("OnDisconnect", err, true) | 		self:invoke("OnDisconnect", err, true) | ||||||
| 		self:shutdown() | 		self:shutdown() | ||||||
| 		error(err, errlevel) | 		error(err, errlevel) | ||||||
| @@ -73,12 +69,12 @@ function meta:setMode(t) | |||||||
| 	assert(add or rem, "table contains neither 'add' nor 'remove'") | 	assert(add or rem, "table contains neither 'add' nor 'remove'") | ||||||
|  |  | ||||||
| 	if add then | 	if add then | ||||||
| 		mode = table.concat{"+", add} | 		mode = table.concat{"+", verify(add, 3)} | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	if rem then | 	if rem then | ||||||
| 		mode = table.concat{mode, "-", rem} | 		mode = table.concat{mode, "-", verify(rem, 3)} | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	self:send("MODE %s %s", verify(target, 3), verify(mode, 3)) | 	self:send("MODE %s %s", verify(target, 3), mode) | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										709
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						
									
										709
									
								
								init.lua
									
									
									
									
									
								
							| @@ -1,354 +1,355 @@ | |||||||
| local socket = require "socket" | local socket = require "socket" | ||||||
|  |  | ||||||
| local error = error | local error = error | ||||||
| local setmetatable = setmetatable | local setmetatable = setmetatable | ||||||
| local rawget = rawget | local rawget = rawget | ||||||
| local unpack = unpack | local unpack = unpack | ||||||
| local pairs = pairs | local pairs = pairs | ||||||
| local assert = assert | local assert = assert | ||||||
| local require = require | local require = require | ||||||
| local tonumber = tonumber | local tonumber = tonumber | ||||||
| local type = type | local type = type | ||||||
| local pcall = pcall | local pcall = pcall | ||||||
|  |  | ||||||
| module "irc" | module "irc" | ||||||
|  |  | ||||||
| local meta = {} | local meta = {} | ||||||
| meta.__index = meta | meta.__index = meta | ||||||
| _META = meta | _META = meta | ||||||
|  |  | ||||||
| require "irc.util" | require "irc.util" | ||||||
| require "irc.asyncoperations" | require "irc.asyncoperations" | ||||||
|  |  | ||||||
| local meta_preconnect = {} | local meta_preconnect = {} | ||||||
| function meta_preconnect.__index(o, k) | function meta_preconnect.__index(o, k) | ||||||
| 	local v = rawget(meta_preconnect, k) | 	local v = rawget(meta_preconnect, k) | ||||||
|  |  | ||||||
| 	if not v and meta[k] then | 	if not v and meta[k] then | ||||||
| 		error("field '"..k.."' is not accessible before connecting", 2) | 		error("field '"..k.."' is not accessible before connecting", 2) | ||||||
| 	end | 	end | ||||||
| 	return v | 	return v | ||||||
| end | end | ||||||
|  |  | ||||||
| function new(user) | function new(user) | ||||||
| 	local o = { | 	local o = { | ||||||
| 		nick = assert(user.nick, "Field 'nick' is required"); | 		nick = assert(user.nick, "Field 'nick' is required"); | ||||||
| 		username = user.username or "lua"; | 		username = user.username or "lua"; | ||||||
| 		realname = user.realname or "Lua owns"; | 		realname = user.realname or "Lua owns"; | ||||||
| 		hooks = {}; | 		hooks = {}; | ||||||
| 		track_users = true; | 		track_users = true; | ||||||
| 	} | 	} | ||||||
| 	return setmetatable(o, meta_preconnect) | 	return setmetatable(o, meta_preconnect) | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:hook(name, id, f) | function meta:hook(name, id, f) | ||||||
| 	f = f or id | 	f = f or id | ||||||
| 	self.hooks[name] = self.hooks[name] or {} | 	self.hooks[name] = self.hooks[name] or {} | ||||||
| 	self.hooks[name][id] = f | 	self.hooks[name][id] = f | ||||||
| 	return id or f | 	return id or f | ||||||
| end | end | ||||||
| meta_preconnect.hook = meta.hook | meta_preconnect.hook = meta.hook | ||||||
|  |  | ||||||
|  |  | ||||||
| function meta:unhook(name, id) | function meta:unhook(name, id) | ||||||
| 	local hooks = self.hooks[name] | 	local hooks = self.hooks[name] | ||||||
|  |  | ||||||
| 	assert(hooks, "no hooks exist for this event") | 	assert(hooks, "no hooks exist for this event") | ||||||
| 	assert(hooks[id], "hook ID not found") | 	assert(hooks[id], "hook ID not found") | ||||||
|  |  | ||||||
| 	hooks[id] = nil | 	hooks[id] = nil | ||||||
| end | end | ||||||
| meta_preconnect.unhook = meta.unhook | meta_preconnect.unhook = meta.unhook | ||||||
|  |  | ||||||
| function meta:invoke(name, ...) | function meta:invoke(name, ...) | ||||||
| 	local hooks = self.hooks[name] | 	local hooks = self.hooks[name] | ||||||
| 	if hooks then | 	if hooks then | ||||||
| 		for id,f in pairs(hooks) do | 		for id,f in pairs(hooks) do | ||||||
| 			if f(...) then | 			if f(...) then | ||||||
| 				return true | 				return true | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta_preconnect:connect(_host, _port) | function meta_preconnect:connect(_host, _port) | ||||||
| 	local host, port, password, secure, timeout | 	local host, port, password, secure, timeout | ||||||
|  |  | ||||||
| 	if type(_host) == "table" then | 	if type(_host) == "table" then | ||||||
| 		host = _host.host | 		host = _host.host | ||||||
| 		port = _host.port | 		port = _host.port | ||||||
| 		timeout = _host.timeout | 		timeout = _host.timeout | ||||||
| 		password = _host.password | 		password = _host.password | ||||||
| 		secure = _host.secure | 		secure = _host.secure | ||||||
| 	else | 	else | ||||||
| 		host = _host | 		host = _host | ||||||
| 		port = _port | 		port = _port | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	host = host or error("host name required to connect", 2) | 	host = host or error("host name required to connect", 2) | ||||||
| 	port = port or 6667 | 	port = port or 6667 | ||||||
|  |  | ||||||
| 	local s = socket.tcp() | 	local s = socket.tcp() | ||||||
|  |  | ||||||
| 	s:settimeout(timeout or 30) | 	s:settimeout(timeout or 30) | ||||||
| 	assert(s:connect(host, port)) | 	assert(s:connect(host, port)) | ||||||
|  |  | ||||||
| 	if secure then | 	if secure then | ||||||
| 		local work, ssl = pcall(require, "ssl") | 		local work, ssl = pcall(require, "ssl") | ||||||
| 		if not work then | 		if not work then | ||||||
| 			error("LuaSec required for secure connections", 2) | 			error("LuaSec required for secure connections", 2) | ||||||
| 		end | 		end | ||||||
|  |  | ||||||
| 		local params | 		local params | ||||||
| 		if type(secure) == "table" then | 		if type(secure) == "table" then | ||||||
| 			params = secure | 			params = secure | ||||||
| 		else | 		else | ||||||
| 			params = {mode="client", protocol="tlsv1"} | 			params = {mode = "client", protocol = "tlsv1"} | ||||||
| 		end | 		end | ||||||
|  |  | ||||||
| 		s = ssl.wrap(s, params) | 		s = ssl.wrap(s, params) | ||||||
| 		success, errmsg = s:dohandshake() | 		success, errmsg = s:dohandshake() | ||||||
| 		if not success then | 		if not success then | ||||||
| 			error(("could not make secure connection %s"):format(errmsg), 2) | 			error(("could not make secure connection: %s"):format(errmsg), 2) | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	self.socket = s | 	self.socket = s | ||||||
| 	setmetatable(self, meta) | 	setmetatable(self, meta) | ||||||
|  |  | ||||||
| 	if password then | 	if password then | ||||||
| 		self:send("PASS %s", password) | 		self:send("PASS %s", password) | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	self:send("USER %s 0 * :%s", self.username, self.realname) | 	self:send("USER %s 0 * :%s", self.username, self.realname) | ||||||
| 	self:send("NICK %s", self.nick) | 	self:send("NICK %s", self.nick) | ||||||
|  |  | ||||||
| 	self.channels = {} | 	self.channels = {} | ||||||
|  |  | ||||||
| 	s:settimeout(0) | 	s:settimeout(0) | ||||||
|  |  | ||||||
| 	repeat | 	repeat | ||||||
| 		self:think() | 		self:think() | ||||||
| 	until self.authed | 	until self.authed | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:disconnect(message) | function meta:disconnect(message) | ||||||
| 	local message = message or "Bye!" | 	message = message or "Bye!" | ||||||
|  |  | ||||||
| 	self:invoke("OnDisconnect", message, false) | 	self:invoke("OnDisconnect", message, false) | ||||||
| 	self:send("QUIT :%s", message) | 	self:send("QUIT :%s", message) | ||||||
|  |  | ||||||
| 	self:shutdown() | 	self:shutdown() | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:shutdown() | function meta:shutdown() | ||||||
| 	self.socket:close() | 	self.socket:close() | ||||||
| 	setmetatable(self, nil) | 	setmetatable(self, nil) | ||||||
| end | end | ||||||
|  |  | ||||||
| local function getline(self, errlevel) | local function getline(self, errlevel) | ||||||
| 	local line, err = self.socket:receive("*l") | 	local line, err = self.socket:receive("*l") | ||||||
|  |  | ||||||
| 	if line then | 	if line then | ||||||
| 		return line | 		return line | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	if err ~= "timeout" and err ~= "wantread" then | 	if err ~= "timeout" and err ~= "wantread" then | ||||||
| 		self:invoke("OnDisconnect", err, true) | 		self:invoke("OnDisconnect", err, true) | ||||||
| 		self:close() | 		self:close() | ||||||
| 		error(err, errlevel) | 		error(err, errlevel) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:think() | function meta:think() | ||||||
| 	while true do | 	while true do | ||||||
| 		local line = getline(self, 3) | 		local line = getline(self, 3) | ||||||
| 		if line then | 		if line then | ||||||
| 			if not self:invoke("OnRaw", line) then | 			if not self:invoke("OnRaw", line) then | ||||||
| 				self:handle(parse(line)) | 				self:handle(parse(line)) | ||||||
| 			end | 			end | ||||||
| 		else | 		else | ||||||
| 			break | 			break | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| local handlers = {} | local handlers = {} | ||||||
|  |  | ||||||
| handlers["PING"] = function(o, prefix, query) | handlers["PING"] = function(o, prefix, query) | ||||||
| 	o:send("PONG :%s", query) | 	o:send("PONG :%s", query) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["001"] = function(o) | handlers["001"] = function(o) | ||||||
| 	o.authed = true | 	o.authed = true | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["PRIVMSG"] = function(o, prefix, channel, message) | handlers["PRIVMSG"] = function(o, prefix, channel, message) | ||||||
| 	o:invoke("OnChat", parsePrefix(prefix), channel, message) | 	o:invoke("OnChat", parsePrefix(prefix), channel, message) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["NOTICE"] = function(o, prefix, channel, message) | handlers["NOTICE"] = function(o, prefix, channel, message) | ||||||
| 	o:invoke("OnNotice", parsePrefix(prefix), channel, message) | 	o:invoke("OnNotice", parsePrefix(prefix), channel, message) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["JOIN"] = function(o, prefix, channel) | handlers["JOIN"] = function(o, prefix, channel) | ||||||
| 	local user = parsePrefix(prefix) | 	local user = parsePrefix(prefix) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		if user.nick == o.nick then | 		if user.nick == o.nick then | ||||||
| 			o.channels[channel] = {users = {}} | 			o.channels[channel] = {users = {}} | ||||||
| 		else | 		else | ||||||
| 			o.channels[channel].users[user.nick] = user | 			o.channels[channel].users[user.nick] = user | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	o:invoke("OnJoin", user, channel) | 	o:invoke("OnJoin", user, channel) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["PART"] = function(o, prefix, channel, reason) | handlers["PART"] = function(o, prefix, channel, reason) | ||||||
| 	local user = parsePrefix(prefix) | 	local user = parsePrefix(prefix) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		if user.nick == o.nick then | 		if user.nick == o.nick then | ||||||
| 			o.channels[channel] = nil | 			o.channels[channel] = nil | ||||||
| 		else | 		else | ||||||
| 			o.channels[channel].users[user.nick] = nil | 			o.channels[channel].users[user.nick] = nil | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| 	o:invoke("OnPart", user, channel, reason) | 	o:invoke("OnPart", user, channel, reason) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["QUIT"] = function(o, prefix, msg) | handlers["QUIT"] = function(o, prefix, msg) | ||||||
| 	local user = parsePrefix(prefix) | 	local user = parsePrefix(prefix) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		for channel, v in pairs(o.channels) do | 		for channel, v in pairs(o.channels) do | ||||||
| 			v.users[user.nick] = nil | 			v.users[user.nick] = nil | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| 	o:invoke("OnQuit", user, msg) | 	o:invoke("OnQuit", user, msg) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["NICK"] = function(o, prefix, newnick) | handlers["NICK"] = function(o, prefix, newnick) | ||||||
| 	local user = parsePrefix(prefix) | 	local user = parsePrefix(prefix) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		for channel, v in pairs(o.channels) do | 		for channel, v in pairs(o.channels) do | ||||||
| 			local users = v.users | 			local users = v.users | ||||||
| 			local oldinfo = users[user.nick] | 			local oldinfo = users[user.nick] | ||||||
| 			if oldinfo then | 			if oldinfo then | ||||||
| 				users[newnick] = oldinfo | 				users[newnick] = oldinfo | ||||||
| 				users[user.nick] = nil | 				users[user.nick] = nil | ||||||
| 				o:invoke("NickChange", user, newnick, channel) | 				o:invoke("NickChange", user, newnick, channel) | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 	else | 	else | ||||||
| 		o:invoke("NickChange", user, newnick) | 		o:invoke("NickChange", user, newnick) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| --NAMES list | --NAMES list | ||||||
| handlers["353"] = function(o, prefix, me, chanType, channel, names) | handlers["353"] = function(o, prefix, me, chanType, channel, names) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		o.channels[channel] = o.channels[channel] or {users = {}, type = chanType} | 		o.channels[channel] = o.channels[channel] or {users = {}, type = chanType} | ||||||
|  |  | ||||||
| 		local users = o.channels[channel].users | 		local users = o.channels[channel].users | ||||||
| 		for nick in names:gmatch("(%S+)") do | 		for nick in names:gmatch("(%S+)") do | ||||||
| 			local access, name = parseNick(nick) | 			local access, name = parseNick(nick) | ||||||
| 			users[name] = {type = access} | 			users[name] = {type = access} | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| --end of NAMES | --end of NAMES | ||||||
| handlers["366"] = function(o, prefix, me, channel, msg) | handlers["366"] = function(o, prefix, me, channel, msg) | ||||||
| 	if o.track_users then | 	if o.track_users then | ||||||
| 		o:invoke("NameList", channel, msg) | 		o:invoke("NameList", channel, msg) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| --no topic | --no topic | ||||||
| handlers["331"] = function(o, prefix, me, channel) | handlers["331"] = function(o, prefix, me, channel) | ||||||
| 	o:invoke("OnTopic", channel, nil) | 	o:invoke("OnTopic", channel, nil) | ||||||
| end | end | ||||||
|  |  | ||||||
| --new topic | --new topic | ||||||
| handlers["TOPIC"] = function(o, prefix, channel, topic) | handlers["TOPIC"] = function(o, prefix, channel, topic) | ||||||
| 	o:invoke("OnTopic", channel, topic) | 	o:invoke("OnTopic", channel, topic) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["332"] = function(o, prefix, me, channel, topic) | handlers["332"] = function(o, prefix, me, channel, topic) | ||||||
| 	o:invoke("OnTopic", channel, topic) | 	o:invoke("OnTopic", channel, topic) | ||||||
| end | end | ||||||
|  |  | ||||||
| --topic creation info | --topic creation info | ||||||
| handlers["333"] = function(o, prefix, me, channel, nick, time) | handlers["333"] = function(o, prefix, me, channel, nick, time) | ||||||
| 	o:invoke("OnTopicInfo", channel, nick, tonumber(time)) | 	o:invoke("OnTopicInfo", channel, nick, tonumber(time)) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["KICK"] = function(o, prefix, channel, kicked, reason) | handlers["KICK"] = function(o, prefix, channel, kicked, reason) | ||||||
| 	o:invoke("OnKick", channel, kicked, parsePrefix(prefix), reason) | 	o:invoke("OnKick", channel, kicked, parsePrefix(prefix), reason) | ||||||
| end | end | ||||||
|  |  | ||||||
| --RPL_UMODEIS | --RPL_UMODEIS | ||||||
| --To answer a query about a client's own mode, RPL_UMODEIS is sent back | --To answer a query about a client's own mode, RPL_UMODEIS is sent back | ||||||
| handlers["221"] = function(o, prefix, modes) | handlers["221"] = function(o, prefix, modes) | ||||||
| 	o:invoke("OnUserModeIs", modes) | 	o:invoke("OnUserModeIs", modes) | ||||||
| end | end | ||||||
|  |  | ||||||
| --RPL_CHANNELMODEIS | --RPL_CHANNELMODEIS | ||||||
| --The result from common irc servers differs from that defined by the rfc | --The result from common irc servers differs from that defined by the rfc | ||||||
| handlers["324"] = function(o, prefix, user, channel, modes) | handlers["324"] = function(o, prefix, user, channel, modes) | ||||||
| 	o:invoke("OnChannelModeIs", user, channel, modes) | 	o:invoke("OnChannelModeIs", user, channel, modes) | ||||||
| end | end | ||||||
|  |  | ||||||
| handlers["ERROR"] = function(o, prefix, message) | handlers["ERROR"] = function(o, prefix, message) | ||||||
| 	o:invoke("OnDisconnect", message, true) | 	o:invoke("OnDisconnect", message, true) | ||||||
| 	o:shutdown() | 	o:shutdown() | ||||||
| 	error(message, 3) | 	error(message, 3) | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:handle(prefix, cmd, params) | function meta:handle(prefix, cmd, params) | ||||||
| 	local handler = handlers[cmd] | 	local handler = handlers[cmd] | ||||||
| 	if handler then | 	if handler then | ||||||
| 		return handler(self, prefix, unpack(params)) | 		return handler(self, prefix, unpack(params)) | ||||||
| 	end | 	end | ||||||
| end | end | ||||||
|  |  | ||||||
| local whoisHandlers = { | local whoisHandlers = { | ||||||
| 	["311"] = "userinfo"; | 	["311"] = "userinfo"; | ||||||
| 	["312"] = "node"; | 	["312"] = "node"; | ||||||
| 	["319"] = "channels"; | 	["319"] = "channels"; | ||||||
| 	["330"] = "account"; -- Freenode | 	["330"] = "account"; -- Freenode | ||||||
| 	["307"] = "registered"; -- Unreal | 	["307"] = "registered"; -- Unreal | ||||||
| } | } | ||||||
|  |  | ||||||
| function meta:whois(nick) | function meta:whois(nick) | ||||||
| 	self:send("WHOIS %s", nick) | 	self:send("WHOIS %s", nick) | ||||||
|  |  | ||||||
| 	local result = {} | 	local result = {} | ||||||
|  |  | ||||||
| 	while true do | 	while true do | ||||||
| 		local line = getline(self, 3) | 		local line = getline(self, 3) | ||||||
| 		if line then | 		if line then | ||||||
| 			local prefix, cmd, args = parse(line) | 			local prefix, cmd, args = parse(line) | ||||||
|  |  | ||||||
| 			local handler = whoisHandlers[cmd] | 			local handler = whoisHandlers[cmd] | ||||||
| 			if handler then | 			if handler then | ||||||
| 				result[handler] = args | 				result[handler] = args | ||||||
| 			elseif cmd == "318" then | 			elseif cmd == "318" then | ||||||
| 				break | 				break | ||||||
| 			else | 			else | ||||||
| 				self:handle(prefix, cmd, args) | 				self:handle(prefix, cmd, args) | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	if result.account then | 	if result.account then | ||||||
| 		result.account = result.account[3] | 		result.account = result.account[3] | ||||||
| 	elseif result.registered then | 	elseif result.registered then | ||||||
| 		result.account = result.registered[2] | 		result.account = result.registered[2] | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| 	return result | 	return result | ||||||
| end | end | ||||||
|  |  | ||||||
| function meta:topic(channel) | function meta:topic(channel) | ||||||
| 	self:send("TOPIC %s", channel) | 	self:send("TOPIC %s", channel) | ||||||
| end | end | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user