|
| 1 | +%%% |
| 2 | +%%% Simple SMTP server in Erlang |
| 3 | +%%% |
| 4 | +%%% Author: Maarten Oelering |
| 5 | +%%% |
| 6 | + |
| 7 | +-module(smtp_server). |
| 8 | + |
| 9 | +-behaviour(gen_server). |
| 10 | + |
| 11 | +%% API |
| 12 | +-export([start_link/1]). |
| 13 | + |
| 14 | +%% callbacks |
| 15 | +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, |
| 16 | + terminate/2, code_change/3]). |
| 17 | + |
| 18 | +-record(state, {lsock, hostname, session}). |
| 19 | + |
| 20 | +%%%=================================================================== |
| 21 | +%%% API |
| 22 | +%%%=================================================================== |
| 23 | + |
| 24 | +start_link({LSock, Hostname}) -> |
| 25 | + gen_server:start_link(?MODULE, [{LSock, Hostname}], []). |
| 26 | + |
| 27 | +%%%=================================================================== |
| 28 | +%%% callbacks |
| 29 | +%%%=================================================================== |
| 30 | + |
| 31 | +init([{LSock, Hostname}]) -> |
| 32 | + % force timeout and wait for connect in handle_info/2 |
| 33 | + {ok, #state{lsock = LSock, hostname = Hostname}, 0}. |
| 34 | + |
| 35 | +handle_call(Request, _From, State) -> |
| 36 | + Reply = {ok, Request}, |
| 37 | + {reply, Reply, State}. |
| 38 | + |
| 39 | +handle_cast(stop, State) -> |
| 40 | + {stop, normal, State}. |
| 41 | + |
| 42 | +%% data received on socket |
| 43 | +handle_info({tcp, _Socket, Data}, #state{session = Session} = State) -> |
| 44 | + case smtp_session:data_line(Session, Data) of |
| 45 | + {ok, NewSession} -> |
| 46 | + {noreply, State#state{session = NewSession}}; |
| 47 | + {stop, NewSession} -> |
| 48 | + {stop, normal, State#state{session = NewSession}} |
| 49 | + end; |
| 50 | + |
| 51 | +%% socket closed by peer |
| 52 | +handle_info({tcp_closed, _Socket}, State) -> |
| 53 | + {stop, normal, State}; |
| 54 | + |
| 55 | +handle_info(timeout, #state{lsock = LSock, hostname = Hostname} = State) -> |
| 56 | + % wait for connection |
| 57 | + {ok, Socket} = gen_tcp:accept(LSock), |
| 58 | + {ok, Session} = smtp_session:connect(Socket, Hostname), |
| 59 | + % start new acceptor process |
| 60 | + smtpd_sup:start_child(), |
| 61 | + {noreply, State#state{session = Session}}; |
| 62 | + |
| 63 | +handle_info(_Info, State) -> |
| 64 | + {noreply, State}. |
| 65 | + |
| 66 | +terminate(_Reason, #state{session = Session} = _State) -> |
| 67 | + smtp_session:disconnect(Session), |
| 68 | + % ?? gen_tcp:close(Socket), |
| 69 | + ok. |
| 70 | + |
| 71 | +code_change(_OldVsn, State, _Extra) -> |
| 72 | + {ok, State}. |
| 73 | + |
| 74 | +%%%=================================================================== |
| 75 | +%%% helpers |
| 76 | +%%%=================================================================== |
| 77 | + |
0 commit comments