Skip to content

Commit a7c3b34

Browse files
authored
Merge pull request #74 from tchoutri/add-whois
Implementation of Whois feature.
2 parents a2f3e39 + 868244e commit a7c3b34

File tree

6 files changed

+141
-22
lines changed

6 files changed

+141
-22
lines changed

lib/exirc/channels.ex

+5-5
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ defmodule ExIrc.Channels do
7272
Update the type of a tracked channel when it changes
7373
"""
7474
def set_type(channel_tree, channel_name, channel_type) when is_binary(channel_type) do
75-
set_type(channel_tree, channel_name, String.to_char_list(channel_type))
75+
set_type(channel_tree, channel_name, String.to_charlist(channel_type))
7676
end
7777
def set_type(channel_tree, channel_name, channel_type) do
7878
name = downcase(channel_name)
@@ -104,7 +104,7 @@ defmodule ExIrc.Channels do
104104
Add multiple users to a tracked channel (used primarily in conjunction with the NAMES command)
105105
"""
106106
def users_join(channel_tree, channel_name, nicks) do
107-
pnicks = strip_rank(nicks)
107+
pnicks = trim_rank(nicks)
108108
manipfn = fn(channel_nicks) -> :lists.usort(channel_nicks ++ pnicks) end
109109
users_manip(channel_tree, channel_name, manipfn)
110110
end
@@ -113,13 +113,13 @@ defmodule ExIrc.Channels do
113113
Remove a user from a tracked channel when they leave
114114
"""
115115
def user_part(channel_tree, channel_name, nick) do
116-
pnick = strip_rank([nick])
116+
pnick = trim_rank([nick])
117117
manipfn = fn(channel_nicks) -> :lists.usort(channel_nicks -- pnick) end
118118
users_manip(channel_tree, channel_name, manipfn)
119119
end
120120

121121
def user_quit(channel_tree, nick) do
122-
pnick = strip_rank([nick])
122+
pnick = trim_rank([nick])
123123
manipfn = fn(channel_nicks) -> :lists.usort(channel_nicks -- pnick) end
124124
foldl = fn(channel_name, new_channel_tree) ->
125125
name = downcase(channel_name)
@@ -217,7 +217,7 @@ defmodule ExIrc.Channels do
217217
end
218218
end
219219

220-
defp strip_rank(nicks) do
220+
defp trim_rank(nicks) do
221221
nicks |> Enum.map(fn(n) -> case n do
222222
<< "@", nick :: binary >> -> nick
223223
<< "+", nick :: binary >> -> nick

lib/exirc/client.ex

+83-7
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ defmodule ExIrc.Client do
3333
debug?: false,
3434
retries: 0,
3535
inet: :inet,
36-
owner: nil
36+
owner: nil,
37+
whois_buffers: %{}
3738
end
3839

3940
#################
@@ -167,6 +168,16 @@ defmodule ExIrc.Client do
167168
def names(client, channel) do
168169
GenServer.call(client, {:names, channel}, :infinity)
169170
end
171+
172+
@doc """
173+
Ask the server for the user's informations.
174+
"""
175+
@spec whois(client :: pid, user :: binary) :: :ok | {:error, atom()}
176+
def whois(client, user) do
177+
GenServer.call(client, {:whois, user}, :infinity)
178+
end
179+
180+
170181
@doc """
171182
Change mode for a user or channel
172183
"""
@@ -330,7 +341,7 @@ defmodule ExIrc.Client do
330341
# Set SSL mode
331342
state = %{state | ssl?: ssl}
332343
# Open a new connection
333-
case Transport.connect(state, String.to_char_list(server), port, [:list, {:packet, :line}, {:keepalive, true}] ++ options) do
344+
case Transport.connect(state, String.to_charlist(server), port, [:list, {:packet, :line}, {:keepalive, true}] ++ options) do
334345
{:ok, socket} ->
335346
send_event {:connected, server, port}, state
336347
{:reply, :ok, %{state | connected?: true, server: server, port: port, socket: socket}}
@@ -397,6 +408,12 @@ defmodule ExIrc.Client do
397408
Transport.send(state, names!(channel))
398409
{:reply, :ok, state}
399410
end
411+
412+
def handle_call({:whois, user}, _from, state) do
413+
Transport.send(state, whois!(user))
414+
{:reply, :ok, state}
415+
end
416+
400417
# Handles a call to change mode for a user or channel
401418
def handle_call({:mode, channel_or_nick, flags, args}, _from, state) do
402419
Transport.send(state, mode!(channel_or_nick, flags, args))
@@ -551,7 +568,7 @@ defmodule ExIrc.Client do
551568
end
552569
# Called when the client enters a channel
553570
def handle_data(%IrcMessage{nick: nick, cmd: "JOIN"} = msg, %ClientState{nick: nick} = state) do
554-
channel = msg.args |> List.first |> String.strip
571+
channel = msg.args |> List.first |> String.trim
555572
if state.debug?, do: debug "JOINED A CHANNEL #{channel}"
556573
channels = Channels.join(state.channels, channel)
557574
new_state = %{state | channels: channels}
@@ -561,7 +578,7 @@ defmodule ExIrc.Client do
561578
# Called when another user joins a channel the client is in
562579
def handle_data(%IrcMessage{nick: user_nick, cmd: "JOIN", host: host, user: user} = msg, state) do
563580
sender = %SenderInfo{nick: user_nick, host: host, user: user}
564-
channel = msg.args |> List.first |> String.strip
581+
channel = msg.args |> List.first |> String.trim
565582
if state.debug?, do: debug "ANOTHER USER JOINED A CHANNEL: #{channel} - #{user_nick}"
566583
channels = Channels.user_join(state.channels, channel, user_nick)
567584
new_state = %{state | channels: channels}
@@ -589,6 +606,65 @@ defmodule ExIrc.Client do
589606
send_event {:topic_changed, channel, topic}, new_state
590607
{:noreply, new_state}
591608
end
609+
610+
611+
## WHOIS
612+
613+
614+
def handle_data(%IrcMessage{cmd: @rpl_whoisuser, args: [_sender, nickname, username, hostname, _, realname]}, state) do
615+
user = %{nickname: nickname, username: username, hostname: hostname, realname: realname}
616+
{:noreply, %ClientState{state|whois_buffers: Map.put(state.whois_buffers, nickname, user)}}
617+
end
618+
619+
def handle_data(%IrcMessage{cmd: @rpl_whoiscertfp, args: [_sender, nickname, "has client certificate fingerprint "<> fingerprint]}, state) do
620+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :certfp], fingerprint)}}
621+
end
622+
623+
def handle_data(%IrcMessage{cmd: @rpl_whoisregnick, args: [_sender, nickname, _message]}, state) do
624+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :registered_nick?], true)}}
625+
end
626+
627+
def handle_data(%IrcMessage{cmd: @rpl_whoishelpop, args: [_sender, nickname, _message]}, state) do
628+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :helpop?], true)}}
629+
end
630+
631+
def handle_data(%IrcMessage{cmd: @rpl_whoischannels, args: [_sender, nickname, channels]}, state) do
632+
chans = String.split(channels, " ")
633+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :channels], chans)}}
634+
end
635+
636+
def handle_data(%IrcMessage{cmd: @rpl_whoisserver, args: [_sender, nickname, server_addr, server_name]}, state) do
637+
new_buffer = state.whois_buffers
638+
|> put_in([nickname, :server_name], server_name)
639+
|> put_in([nickname, :server_address], server_addr)
640+
{:noreply, %ClientState{state|whois_buffers: new_buffer}}
641+
end
642+
643+
def handle_data(%IrcMessage{cmd: @rpl_whoisoperator, args: [_sender, nickname, _message]}, state) do
644+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :ircop?], true)}}
645+
end
646+
647+
def handle_data(%IrcMessage{cmd: @rpl_whoisaccount, args: [_sender, nickname, account_name, _message]}, state) do
648+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :account_name], account_name)}}
649+
end
650+
651+
def handle_data(%IrcMessage{cmd: @rpl_whoissecure, args: [_sender, nickname, _message]}, state) do
652+
{:noreply, %ClientState{state|whois_buffers: put_in(state.whois_buffers, [nickname, :tls?], true)}}
653+
end
654+
655+
def handle_data(%IrcMessage{cmd: @rpl_whoisidle, args: [_sender, nickname, idling_time, signon_time, _message]}, state) do
656+
new_buffer = state.whois_buffers
657+
|> put_in([nickname, :idling_time], idling_time)
658+
|> put_in([nickname, :signon_time], signon_time)
659+
{:noreply, %ClientState{state|whois_buffers: new_buffer}}
660+
end
661+
662+
def handle_data(%IrcMessage{cmd: @rpl_endofwhois, args: [_sender, nickname, _message]}, state) do
663+
buffer = struct(Irc.Whois, state.whois_buffers[nickname])
664+
send_event {:whois, buffer}, state
665+
{:noreply, %ClientState{state|whois_buffers: Map.delete(state.whois_buffers, nickname)}}
666+
end
667+
592668
def handle_data(%IrcMessage{cmd: @rpl_notopic, args: [channel]}, state) do
593669
if state.debug? do
594670
debug "INITIAL TOPIC MSG"
@@ -645,7 +721,7 @@ defmodule ExIrc.Client do
645721
end
646722
# Called when we leave a channel
647723
def handle_data(%IrcMessage{cmd: "PART", nick: nick} = msg, %ClientState{nick: nick} = state) do
648-
channel = msg.args |> List.first |> String.strip
724+
channel = msg.args |> List.first |> String.trim
649725
if state.debug?, do: debug "WE LEFT A CHANNEL: #{channel}"
650726
channels = Channels.part(state.channels, channel)
651727
new_state = %{state | channels: channels}
@@ -655,7 +731,7 @@ defmodule ExIrc.Client do
655731
# Called when someone else in our channel leaves
656732
def handle_data(%IrcMessage{cmd: "PART", nick: from, host: host, user: user} = msg, state) do
657733
sender = %SenderInfo{nick: from, host: host, user: user}
658-
channel = msg.args |> List.first |> String.strip
734+
channel = msg.args |> List.first |> String.trim
659735
if state.debug?, do: debug "#{from} LEFT A CHANNEL: #{channel}"
660736
channels = Channels.user_part(state.channels, channel, from)
661737
new_state = %{state | channels: channels}
@@ -729,7 +805,7 @@ defmodule ExIrc.Client do
729805
{:noreply, state}
730806
end
731807
# Called when a NOTICE is received by the client.
732-
def handle_data(%IrcMessage{nick: from, cmd: "NOTICE", args: [target, message], host: host, user: user} = _msg, state) do
808+
def handle_data(%IrcMessage{nick: from, cmd: "NOTICE", args: [_target, message], host: host, user: user} = _msg, state) do
733809
sender = %SenderInfo{nick: from,
734810
host: host,
735811
user: user}

lib/exirc/commands.ex

+25
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ defmodule Irc.Commands do
4949
#one of two replies is sent. If the topic is set, RPL_TOPIC is sent back else
5050
#RPL_NOTOPIC.
5151
#"""
52+
@rpl_whoiscertfp "276"
53+
@rpl_whoisregnick "307"
54+
@rpl_whoishelpop "310"
55+
@rpl_whoisuser "311"
56+
@rpl_whoisserver "312"
57+
@rpl_whoisoperator "313"
58+
@rpl_whoisidle "317"
59+
@rpl_endofwhois "318"
60+
@rpl_whoischannels "319"
61+
@rpl_whoisaccount "330"
5262
@rpl_notopic "331"
5363
@rpl_topic "332"
5464
#@doc """
@@ -77,6 +87,8 @@ defmodule Irc.Commands do
7787
@rpl_motd "372"
7888
@rpl_motdstart "375"
7989
@rpl_endofmotd "376"
90+
@rpl_whoishost "378"
91+
@rpl_whoismodes "379"
8092

8193
################
8294
# Error Codes
@@ -150,6 +162,8 @@ defmodule Irc.Commands do
150162
#"""
151163
@err_restricted "484"
152164

165+
@rpl_whoissecure "671"
166+
153167
###############
154168
# Code groups
155169
###############
@@ -158,6 +172,12 @@ defmodule Irc.Commands do
158172
@err_nickname_in_use, @err_nick_collision,
159173
@err_unavail_resource, @err_need_more_params,
160174
@err_already_registered, @err_restricted ]
175+
176+
@whois_rpls [ @rpl_whoisuser, @rpl_whoishost,
177+
@rpl_whoishost, @rpl_whoisserver,
178+
@rpl_whoismodes, @rpl_whoisidle,
179+
@rpl_endofwhois
180+
]
161181
end
162182

163183
end
@@ -182,6 +202,11 @@ defmodule Irc.Commands do
182202

183203
# IRC Commands
184204

205+
@doc """
206+
Send a WHOIS request about a user
207+
"""
208+
def whois!(user), do: command! ['WHOIS ', user]
209+
185210
@doc """
186211
Send password to server
187212
"""

lib/exirc/logger.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ defmodule ExIrc.Logger do
88
"""
99
@spec info(binary) :: :ok
1010
def info(msg) do
11-
:error_logger.info_report String.to_char_list(msg)
11+
:error_logger.info_report String.to_charlist(msg)
1212
end
1313

1414
@doc """
1515
Log a warning message report
1616
"""
1717
@spec warning(binary) :: :ok
1818
def warning(msg) do
19-
:error_logger.warning_report String.to_char_list("#{IO.ANSI.yellow()}#{msg}#{IO.ANSI.reset()}")
19+
:error_logger.warning_report String.to_charlist("#{IO.ANSI.yellow()}#{msg}#{IO.ANSI.reset()}")
2020
end
2121

2222
@doc """
2323
Log an error message report
2424
"""
2525
@spec error(binary) :: :ok
2626
def error(msg) do
27-
:error_logger.error_report String.to_char_list("#{IO.ANSI.red()}#{msg}#{IO.ANSI.reset()}")
27+
:error_logger.error_report String.to_charlist("#{IO.ANSI.red()}#{msg}#{IO.ANSI.reset()}")
2828
end
2929
end

lib/exirc/utils.ex

+7-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ defmodule ExIrc.Utils do
1313
message = ExIrc.Utils.parse data
1414
assert "irc.example.org" = message.server
1515
"""
16-
@spec parse(raw_data :: char_list) :: IrcMessage.t
16+
@spec parse(raw_data :: charlist) :: IrcMessage.t
1717
def parse(raw_data) do
1818
data = :string.substr(raw_data, 1, length(raw_data))
1919
case data do
@@ -137,7 +137,7 @@ defmodule ExIrc.Utils do
137137
end
138138
defp isup_param("PREFIX=" <> user_prefixes, state) do
139139
prefixes = Regex.run(~r/\((.*)\)(.*)/, user_prefixes, capture: :all_but_first)
140-
|> Enum.map(&String.to_char_list/1)
140+
|> Enum.map(&String.to_charlist/1)
141141
|> List.zip
142142
%{state | user_prefixes: prefixes}
143143
end
@@ -167,15 +167,15 @@ defmodule ExIrc.Utils do
167167
' ',
168168
:lists.nth(m, @months_of_year),
169169
' ',
170-
:io_lib.format("~2..0s", [Integer.to_char_list(d)]),
170+
:io_lib.format("~2..0s", [Integer.to_charlist(d)]),
171171
' ',
172-
:io_lib.format("~2..0s", [Integer.to_char_list(h)]),
172+
:io_lib.format("~2..0s", [Integer.to_charlist(h)]),
173173
':',
174-
:io_lib.format("~2..0s", [Integer.to_char_list(n)]),
174+
:io_lib.format("~2..0s", [Integer.to_charlist(n)]),
175175
':',
176-
:io_lib.format("~2..0s", [Integer.to_char_list(s)]),
176+
:io_lib.format("~2..0s", [Integer.to_charlist(s)]),
177177
' ',
178-
Integer.to_char_list(y)] |> List.flatten |> List.to_string
178+
Integer.to_charlist(y)] |> List.flatten |> List.to_string
179179
end
180180

181181
defp trim_crlf(charlist) do

lib/exirc/whois.ex

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule Irc.Whois do
2+
defstruct [account_name: nil,
3+
channels: [],
4+
helpop?: false,
5+
hostname: nil,
6+
idling_time: 0,
7+
ircop?: false,
8+
nickname: nil,
9+
realname: nil,
10+
registered_nick?: false,
11+
server_address: nil,
12+
server_name: nil,
13+
signon_time: 0,
14+
tls?: false,
15+
username: nil,
16+
]
17+
end
18+

0 commit comments

Comments
 (0)