anchor.diff:      {Id, {suback, QoSes}};
   make_reply(#unsuback{ message_id = Id }) ->
       {Id, unusback}.
  +
  +do_ack(S, #publish{ qos = QoS }) ->
  +    case QoS of
  +        at_most_once ->
  +            S;
  +        #qos{ level = at_least_once,
  +              message_id = Id } ->
  +            Ack = #puback{ message_id = Id },
  +            write(S, Ack),
  +            S;
  +        #qos{ level = exactly_once,
  +              message_id = Id } ->
  +            #state{ rpcs = RPC } = S,
  +            Ack = #pubrec{ message_id = Id },
  +            write(S, Ack),
  +            RPC1 = gb_trees:insert({server, Id}, Ack, RPC),
  +            S#state{ rpcs = RPC1 }
  +    end.
   
   %% `recv` is used to synchronously get another frame from the
   %% socket. Once the connection is open, there's no need to do this,
  
positive.code: 
  opened({frame, Frame = #publish{}}, S0) ->
      %% TODO deal with QoS
      #state{ receiver = Receiver } = S0,
      Receiver ! {frame, Frame},
      {next_state, opened, S0};
  
  %% This is step one of "exactly once" delivery
  opened({frame, #pubrec{ message_id = Id }}, S0) ->
      #state{ rpcs = RPC } = S0,
      #publish{} = gb_trees:get(Id, RPC),
      Ack = #pubrel{ message_id = Id },
      RPC1 = gb_trees:update(Id, Ack, RPC),
      write(S0, Ack),
      {next_state, opened, S0#state{ rpcs = RPC1 }};
  
  %% These frames end a "guaranteed delivery" exchange.
  opened({frame, #puback{ message_id = Id }}, S0) ->
      #state{ rpcs = RPC } = S0,
      #publish{} = gb_trees:get(Id, RPC),
      RPC1 = gb_trees:delete(Id, RPC),
      {next_state, opened, S0#state{ rpcs = RPC1 }};
  opened({frame, #pubcomp{ message_id = Id }}, S0) ->
      #state{ rpcs = RPC } = S0,
      #pubrel{} = gb_trees:get(Id, RPC),
      RPC1 = gb_trees:delete(Id, RPC),
      {next_state, opened, S0#state{ rpcs = RPC1 }};
  
  %% Remaining: #suback, #unsuback
  opened({frame, Frame}, S0) ->
      #state{ rpcs = RPC0 } = S0,
      {Id, Reply} = make_reply(Frame),
      {Ref, From} = gb_trees:get(Id, RPC0),
      RPC1 = gb_trees:delete(Id, RPC0),
      From ! {Ref, Reply},
      {next_state, opened, S0#state{ rpcs = RPC1 }};
  
  
negative.code:             #qos{ level = L } ->
                  Qos = #qos{ level = L,
                              message_id = NextId },
                  RPCS1 = gb_trees:insert({client, NextId}, P0, RPCS),
                  {P0#publish{ qos = Qos },
                   S0#state{
                     rpcs = RPCS1,
  