Finished writing the buffering/parsing code for handling messages from the IRC server
authorMatt Mullins <mmullins@mmlx.us>
Thu, 19 May 2011 23:00:45 +0000 (18:00 -0500)
committerMatt Mullins <mmullins@mmlx.us>
Thu, 19 May 2011 23:02:14 +0000 (18:02 -0500)
irc/irc_conn.erl

index 3af4399..ab7c6ed 100644 (file)
@@ -49,8 +49,32 @@ handle_cast({send_command, Command}, State) ->
                 ),
     {noreply, State}.
 
-handle_info(_Message, _State) ->
-    {noreply, _State}.
+%% Handle incoming data
+handle_info({tcp, _Socket, Data}, State = #irc_state{ socket = _Socket }) ->
+    Buf1 = State#irc_state.buffer ++ Data,
+    Buf2 = case parse_lines(Buf1) of
+        {ok, Lines, Rem} ->
+            lists:foreach(fun do_line/1, Lines),
+            Rem;
+        none ->
+            Buf1
+        end,
+    {noreply, State#irc_state{buffer = Buf2}}.
+
+%% @doc Handles a line received from the IRC server.
+do_line(Line) ->
+    Command = irc_util:list_to_command(Line),
+    case Command#irc_command.command of
+        "PING" ->
+            error_logger:info_msg("Received a PING from server ~p~n", [Line]),
+            % return the command to the IRC server with a PONG
+            % leaving the rest of the line (usually a timestamp) alone
+            NewCommand = Command#irc_command{prefix = none, command = "PONG"},
+            send_command(NewCommand);
+        _ ->
+            % for now, just know that it happened
+            error_logger:info_msg("Received a line ~p~n", [Line])
+    end.
 
 terminate(_Reason, _State) ->
     ok.
@@ -64,3 +88,27 @@ send_command(Command) ->
 join_channel(Channel) ->
     Command = #irc_command{command = "JOIN", middles = [Channel]},
     send_command(Command).
+
+%% @doc Splits a buffer into full lines, keeping whatever is left over
+%% Returns either {ok, [Line], Remaining} or none.
+parse_lines(Buffer) ->
+    % "if" would be more appropriate here, but I can't use member() in a guard
+    case lists:member($\n, Buffer) of
+        true ->
+            {Line, RHS} = lists:splitwith(
+                            fun (Char) -> (Char /= $\n) and (Char /= $\r) end,
+                            Buffer),
+            Rem = case RHS of
+                "\r\n" ++ Rest -> Rest;  % Strip off CR+LF, as specified by RFC
+                "\n" ++ Rest -> Rest     % Accept simple LF separators too
+                % If the RHS doesn't contain a newline, then crash, because
+                % something is horribly wrong.
+            end,
+            % recurse and put together the results
+            case parse_lines(Rem) of
+                {ok, Lines, FinalRem} -> {ok, [Line|Lines], FinalRem};
+                none -> {ok, [Line], Rem}
+            end;
+        false ->
+            none
+    end.