@Override
  public IQ handleIQ(IQ packet) throws UnauthorizedException {
    IQ reply = IQ.createResultIQ(packet);
    Element offlineRequest = packet.getChildElement();

    JID from = packet.getFrom();
    if (offlineRequest.element("purge") != null) {
      // User requested to delete all offline messages
      messageStore.deleteMessages(from.getNode());
    } else if (offlineRequest.element("fetch") != null) {
      // Mark that offline messages shouldn't be sent when the user becomes available
      stopOfflineFlooding(from);
      // User requested to receive all offline messages
      for (OfflineMessage offlineMessage : messageStore.getMessages(from.getNode(), false)) {
        sendOfflineMessage(from, offlineMessage);
      }
    } else {
      for (Iterator it = offlineRequest.elementIterator("item"); it.hasNext(); ) {
        Element item = (Element) it.next();
        Date creationDate = null;
        try {
          creationDate = xmppDateTime.parseString(item.attributeValue("node"));
        } catch (ParseException e) {
          Log.error("Error parsing date", e);
        }
        if ("view".equals(item.attributeValue("action"))) {
          // User requested to receive specific message
          OfflineMessage offlineMsg = messageStore.getMessage(from.getNode(), creationDate);
          if (offlineMsg != null) {
            sendOfflineMessage(from, offlineMsg);
          }
        } else if ("remove".equals(item.attributeValue("action"))) {
          // User requested to delete specific message
          if (messageStore.getMessage(from.getNode(), creationDate) != null) {
            messageStore.deleteMessage(from.getNode(), creationDate);
          } else {
            // If the requester is authorized but the node does not exist, the server MUST return a
            // <item-not-found/> error.
            reply.setError(PacketError.Condition.item_not_found);
          }
        }
      }
    }
    return reply;
  }