/***************************************************************************
 *   Copyright (C) 2004 - 2005 by Raphael Langerhorst                      *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#include "GXmppNetwork.h"

#include <qapplication.h>
#include <qptrlist.h>
#include <qevent.h>
#include <qtimer.h>

#include <exception>

//TODO implement all methods

namespace GWE
{
  
class QNetworkSendEvent : public QCustomEvent
{
  private:
    QString Message;
  public:
    QNetworkSendEvent(QString message) : QCustomEvent(65001),Message(message) {}
    virtual ~QNetworkSendEvent() {}
    
    QString message() { return Message; }
};

class GXmppMessageQueue
{
  private:
    QMap<QString,QStringList > Queues;
  public:
    void add(QString server, QString message);
    
    /**
     * The order is from front to back (start at begin()).
     * Queue is automatically removed from Queues.
     */
    QStringList takeMessagesForServer(QString server);
    
    bool hasMessages(QString server);
};

void GXmppMessageQueue::add(QString server, QString message)
{
  QStringList queue;
  if (Queues.contains(server))
  {
    queue = Queues[server];
  }
  queue.append(message);
  Queues.insert(server,queue);
}

QStringList GXmppMessageQueue::takeMessagesForServer(QString server)
{
  QStringList msgs;
  if (Queues.contains(server))
  {
    msgs = Queues[server];
    Queues.remove(server);
  }
  return msgs;
}

bool GXmppMessageQueue::hasMessages(QString server)
{
  return Queues.contains(server);
}

GXmppNetwork::GXmppNetwork(QString full_jid, QObject *parent, const char *name)
: GXmlNetwork(parent,name),
  Active(FALSE),
  Connected(FALSE),
  Connecting(FALSE),
  XmppLayerCreated(FALSE),
  XmppConnector(NULL),
  Tls(NULL),
  TlsHandler(NULL),
  Stream(NULL),
  NetworkId(full_jid),
  MessageQueue(new GXmppMessageQueue()),
  StayConnected(FALSE)
{
  connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(closeNetwork()));

//   QTimer* heartbeat_timer = new QTimer(this,"heartbeat timer");
//   connect(heartbeat_timer,SIGNAL(timeout()),this,SLOT(sendHeartbeat()));
//   heartbeat_timer->start(55000);
  // see XMPP::ClientStream::setNoopTime() which is now used
}

GXmppNetwork::~GXmppNetwork()
{
  deleteXmppLayer(true);
  if (MessageQueue)
  {
    delete MessageQueue;
    MessageQueue=NULL;
  }
}

bool GXmppNetwork::createXmppLayer()
{
  if (XmppLayerCreated)
  {
    qWarning("XMPP Layer already created! Destroying it first...");
    this->deleteXmppLayer(true);
    qWarning("XMPP Layer now destroyed");
  }
    
  //XMPP related setup
  try
  {
    qDebug("Creating XMPP layer");
    XmppConnector = new XMPP::AdvancedConnector();
    Q_CHECK_PTR(XmppConnector);

    //@todo something causes a segfault if the jabber server is not found.
    
    if (XmppConnector==NULL)
    {
      throw std::exception();
    }
    
    connect(XmppConnector,SIGNAL(srvLookup(const QString &)),this,SLOT(connectorServerLookup(const QString&)));
    connect(XmppConnector,SIGNAL(srvResult(bool)),this,SLOT(connectorServerResult(bool)));
    connect(XmppConnector,SIGNAL(httpSyncStarted()),this,SLOT(connectorHttpSyncStarted()));
    connect(XmppConnector,SIGNAL(httpSyncFinished()),this,SLOT(connectorHttpSyncFinished()));
    
    if (QCA::isSupported(QCA::CAP_TLS))
    {
      qDebug("CAP TLS supported");
      Tls = new QCA::TLS();
      Q_CHECK_PTR(Tls);
      
      if (Tls==NULL)
      {
        throw std::exception();
      }
      
      TlsHandler = new XMPP::QCATLSHandler(Tls);
      Q_CHECK_PTR(TlsHandler);
      
      if (TlsHandler == NULL)
      {
        throw std::exception();
      }
      connect(TlsHandler,SIGNAL(tlsHandshaken()),this,SLOT(tlsHandshaken()));
    }
    
    this->Stream = new XMPP::ClientStream(this->XmppConnector,this->TlsHandler);
    Q_CHECK_PTR(Stream);
    
    if (Stream == NULL)
    {
      throw std::exception();
    }
    
    connect(Stream,SIGNAL(connected()),SLOT(clientStreamConnected()));
    connect(Stream,SIGNAL(securityLayerActivated(int)),SLOT(clientStreamSecurityLayerActivated(int)));
    connect(Stream,SIGNAL(needAuthParams(bool,bool,bool)),SLOT(clientStreamNeedAuthenticationParameters(bool,bool,bool)));
    connect(Stream,SIGNAL(authenticated()),SLOT(clientStreamAuthenticated()));
    connect(Stream,SIGNAL(connectionClosed()),SLOT(clientStreamConnectionClosed()));
    connect(Stream,SIGNAL(delayedCloseFinished()),SLOT(clientStreamDelayedCloseFinished()));
    connect(Stream,SIGNAL(readyRead()),SLOT(clientStreamReadyRead()));
    connect(Stream,SIGNAL(stanzaWritten()),SLOT(clientStreamStanzaWritten()));
    connect(Stream,SIGNAL(warning(int)),SLOT(clientStreamWarning(int)));
    connect(Stream,SIGNAL(error(int)),SLOT(clientStreamError(int)));
    
    this->XmppLayerCreated = true;
    qDebug("XMPP Layer now created");
    return true;
  }
  catch (std::exception e)
  {
    deleteXmppLayer(true);
    return false;
  }
  
  //active == false && connected == false, they are set in initNetwork()
}

bool GXmppNetwork::deleteXmppLayer(bool force)
{
  if (force==FALSE)
  {
    if (!XmppLayerCreated)
    {
      qWarning("XMPP layer not even created! Nothing to delete.");
      return true;
    }
    if (Active)
    {
      qWarning("XMPP network is active! Not deleting objects!");
      return false;
    }
    if (Connected)
    {
      qWarning("XMPP network is still connected! Not deleting objects!");
      return false;
    }
  }
    
  if (Stream)
  {
    delete Stream;
    Stream = NULL;
  }
  if (TlsHandler)
  {
    delete TlsHandler;
    TlsHandler = NULL;
  }
  if (Tls)
  {
    delete Tls;
    Tls = NULL;
  }
  if (XmppConnector)
  {
    delete XmppConnector;
    XmppConnector=NULL;
  }
  
  this->Connected = false;
  this->Connecting = false;
  this->Active = false;
  this->XmppLayerCreated = false;
  
  return true;
}

void GXmppNetwork::customEvent(QCustomEvent* event)
{
  if (event->type()==65001)
  {
    if (!this->isConnected())
    {
      qWarning("Can't send data because network is not connected!");
    }
    else
    {
//       qDebug("sending message:");
//       qDebug(((QNetworkSendEvent*)event)->message());
      Stream->writeDirect(((QNetworkSendEvent*)event)->message());
    }
  }
  else
    qWarning(QString("Received unknown custom event type %1").arg(QString::number(event->type())));
}

bool GXmppNetwork::isSubscribed(QString destination)
{
  if (this->SubscriptionStates.contains(destination))
  {
    if (this->SubscriptionStates[destination]=="subscribed")
      return true;
    else
      return false;
  }
  else
    return false;
}

void GXmppNetwork::sendQueuedMessages(QString destination)
{
  QStringList msgs = MessageQueue->takeMessagesForServer(destination);
  QStringList::iterator it;
  for (it = msgs.begin(); it != msgs.end(); ++it)
  {
    this->send(*it);
  }
}

void GXmppNetwork::sendHeartbeat()
{
  if (this->isConnected())
    this->send("<presence/>");
}

// GXmlNetwork implementation BEGIN //

bool GXmppNetwork::initNetwork()
{
  if (this->Connecting)
  {
    qWarning("Already in the process of connecting the network");
    return false;
  }
  
  this->Connecting = true;
  
  QMutexLocker lock(this);
  if (!this->createXmppLayer())
  {
    qWarning("Failed to initialize XMPP layer");
    return false;
  }
  if (Active)
  {
    qWarning("XMPP layer already active");
    return true;
  }
  Active = true;
  
  // HACK don't use XMPP 1.0 even if advertised (jabberd 1.4.4 is bugged)
  this->Stream->setOldOnly(true);
  
  //XMPP initialization - without SSL
  this->XmppConnector->setOptHostPort(NetworkId.domain(),5222);
  
  //@todo get option from a parameter!
  this->XmppConnector->setOptSSL(false);
  
  this->Stream->setAllowPlain(true);

  this->Stream->setResourceBinding(true);
  
  //for use with SSL
//   this->XmppConnector->setOptHostPort(NetworkId.domain(),5223);
//   this->XmppConnector->setOptSSL(true);
  
  if (this->Tls)
  {
    QPtrList<QCA::Cert> certStore;
    Tls->setCertificateStore(certStore);
  }
  
  Stream->setNoopTime(55000); // every 55 seconds
  qDebug("Connecting XMPP network with JID " + this->getNetworkId());
  qDebug("Warning: if you get a segmentation fault next, this probably means");
  qDebug("         that the server you want to connect to (the domain part");
  qDebug("         of the JID) does not have an XMPP/Jabber server running.");
  qDebug("         In this case make sure the JID is correct and/or contact");
  qDebug("         the G System Team.");
  
  this->Stream->connectToServer(this->NetworkId);
  return true;
}

void GXmppNetwork::reconnectNetwork()
{
  QTimer::singleShot(100,this,SLOT(initNetwork()));
}

bool GXmppNetwork::closeNetwork()
{
  QMutexLocker lock(this);
  this->StayConnected = false;
  if (this->Connected)
  {
    qDebug("Disconnecting XMPP Stream...");
    this->Stream->close();
    this->Connected = false;
    this->Active = false; // correct place?
    this->Connecting = false;
    return true;
  }
  else
  {
    qWarning("Not closing network: not connected");
    return true; //it's fine, not connected after all
  }
}

QString GXmppNetwork::getNetworkId() const
{
  return this->NetworkId.full();
}

void GXmppNetwork::setPassword(const QString& password)
{
  this->Password = password;
}

bool GXmppNetwork::send(QDomElement data, const QString& destination)
{
  if (destination.isEmpty())
  {
    qWarning("Destination is empty, not sending message!");
    return false;
  }
  
  //@todo this doesn't seem to be functional(??)
  XMPP::Jid destjid(destination);
  if (!destjid.isValid())
  {
    qWarning(QString("Destination %1 not a valid JID, not sending message!").arg(destination));
    return false;
  }
  
//   qDebug(QString("type range for user events: %1 to %2").arg(QString::number(QEvent::User)).arg(QString::number(QEvent::MaxUser)));
  QDomDocument d;
  QDomElement e = d.createElement("message");
  d.appendChild(e);
  e.setAttribute("to",destination);
  e.appendChild(d.importNode(data,"true"));
  
  if (!this->isSubscribed(destination) || !this->isConnected())
  {
    if (!this->isConnected() && this->StayConnected)
    {
      qWarning("Network is not connected! Reconnecting...");
      this->reconnectNetwork();
    }
    else
    {
      qWarning(QString("Delaying sending of message, destination %1 is not yet available").arg(destination));
      this->makeDestinationAvailable(destination);
    }
    this->MessageQueue->add(destination,d.toString());
  }
  else
  {
    this->send(d.toString());
//     QApplication::postEvent(this,new QNetworkSendEvent(d.toString()));
  }

  return true;
}

bool GXmppNetwork::send(const QString& data)
{
  QApplication::postEvent(this,new QNetworkSendEvent(data));
  return true;
}

bool GXmppNetwork::flushOutput()
{
  //assume everything is fine (I don't know about a method to flush output)
  //@todo implement
  return true;
}

void GXmppNetwork::makeDestinationAvailable(const QString& destination)
{
  if (this->SubscriptionStates.contains(destination))
  {
    if (this->SubscriptionStates[destination] == "subscribed")
    {
//       XMPP::Stanza s = this->Stream->createStanza(XMPP::Stanza::Presence, destination, "probe");
//       QString msg = s.toString();
      
      QString msg = QString("<presence type=\"probe\" to=\"%1\"></presence>").arg(destination);
      
      this->send(msg);
    }
    else
    {
//       XMPP::Stanza s = this->Stream->createStanza(XMPP::Stanza::Presence, destination, "subscribe");
//       QString msg = s.toString();
      
      QString msg = QString("<presence type=\"subscribe\" to=\"%1\"></presence>").arg(destination);
      
      this->send(msg);
    }
  }
  else
  {
//     XMPP::Stanza s = this->Stream->createStanza(XMPP::Stanza::Presence, destination, "subscribe");
//     QString msg = s.toString();
    this->SubscriptionStates.insert(destination,"unsubscribed");
    
    // use this for XMPP 1.0
    QString msg = QString("<presence type=\"subscribe\" to=\"%1\"></presence>").arg(destination);
    
    this->send(msg);
    
    // use this HACK for pre XMPP 1.0
    msg = QString("<presence to=\"%1\"></presence>").arg(destination);
    this->send(msg);
  }
}

bool GXmppNetwork::isConnected()
{
  if (this->Active && this->Connected && Stream->isAuthenticated() && Stream->isActive())
    return true;
  else
    return false;
}

//END GXmlNetwork implementation //

//BEGIN XMPP slots and signals for network management //

// CONNECTOR SLOTS //
  
void GXmppNetwork::connectorServerLookup(const QString& server)
{
  qDebug("XMPP Server Lookup: " + server);
}

void GXmppNetwork::connectorServerResult(bool success)
{
  if (success)
  {
    qDebug("XMPP Server Result OK");
  }
  else
  {
    qDebug("XMPP Server Result FAILURE");
  }
}

void GXmppNetwork::connectorHttpSyncStarted()
{
  qDebug("XMPP Connector HTTP sync started");
}

void GXmppNetwork::connectorHttpSyncFinished()
{
  qDebug("XMPP Connector HTTP sync finished");
}

// TLS SLOTS //

void GXmppNetwork::tlsHandshaken()
{
  qDebug("XMPP TLS handshake complete");
}

// STREAM SLOTS //

void GXmppNetwork::clientStreamConnected()
{
  this->Connecting = false;
  this->Connected = true;
  this->StayConnected = true;
  qDebug("XMPP Stream connected");
}

void GXmppNetwork::clientStreamSecurityLayerActivated(int l)
{
  qDebug("XMPP Stream Security Layer " + QString::number(l) + " activated");
}

void GXmppNetwork::clientStreamNeedAuthenticationParameters(bool a,bool b,bool c)
{
  qDebug("XMPP needs authentication " + QString::number(a) + " " + QString::number(b) + " " + QString::number(c));
  qDebug("Continuing to authenticate");
  if (this->Stream)
  {
    if (a)
    {
      qDebug("username: " + this->NetworkId.full());
      this->Stream->setUsername(this->NetworkId.full());
    }
    if (b)
    {
//       qDebug("password: (not shown)");
      this->Stream->setPassword(this->Password);
    }
    if (c)
    {
      qDebug("domain: " + this->NetworkId.domain());
      this->Stream->setRealm(this->NetworkId.domain());
    }
    this->Stream->continueAfterParams();
  }
}

void GXmppNetwork::clientStreamAuthenticated()
{
  qDebug("XMPP Stream authenticated");

  //well, just let everyone know we are online
  this->Stream->writeDirect("<presence><show></show></presence>");
  
  emit this->networkConnected();

  //@todo: send an apropriate message to the master (define a server-server-protocol!!)
}

void GXmppNetwork::clientStreamConnectionClosed()
{
  qDebug("XMPP Stream connection closed.");
  if (this->StayConnected)
  {
    qDebug("Using timer for delayed recreation of the XMPP layer...");
    this->reconnectNetwork();
  }
}

void GXmppNetwork::clientStreamDelayedCloseFinished()
{
  qDebug("XMPP Stream delayed close finished");
}

void GXmppNetwork::clientStreamReadyRead()
{
  qDebug("XMPP Stream ready read");
  
  bool stanza_printed = false;

  while(Stream->stanzaAvailable())
  {
    this->lock();
    XMPP::Stanza s = Stream->read();
    this->unlock();
//     qDebug(s.toString());
//     stanza_printed=true;
    
    QDomElement data = s.element();
    QString from = s.from().full();
    QString type = s.type();
//     qDebug(QString("XMPP message received, data tag name: %1").arg(data.tagName()));
    if (data.tagName() == "message")  //extract all child elements
    {
      //make sure presence is subscribed
      if (this->SubscriptionStates.contains(from))
      {
        if (this->SubscriptionStates[from] != "subscribed")
          this->makeDestinationAvailable(from);
      }
      else
        this->makeDestinationAvailable(from);
      
      //@todo improve detection of unneccessary offline messages,
      //      more clearly state which offline messages should go through (body does for now)
      bool discard = false;
      bool isbody = false;
      if (data.elementsByTagName("error").count()>0 || type == "error")
        discard=true;
      else if (data.elementsByTagName("body").count()>0)  //do not emit body tags when error tags are included - thus else if
        isbody=true;
      else  //filter out all other delayed messages
      {
        QDomNodeList dom_list = data.elementsByTagName("x");
        for (unsigned int i=0; i<dom_list.count(); i++)
        {
//           if (dom_list.item(i).toElement().attribute("xmlns","none") == "jabber:x:delay")
          if (dom_list.item(i).toElement().text() == "Offline Storage")
          {
  //           qWarning("Discarding offline message");
            discard = true;
            break;
          }
        }
      }
      if (!discard || isbody)
      {
        QDomNode node = data.firstChild();
        while(!node.isNull())
        {
          if (node.isElement())
          {
            if (node.toElement().tagName() != "x")  //do not forward x tags
            {
              qDebug(QString("emitting data available, message: %1").arg(node.toElement().tagName()));
              emit this->dataAvailable(node.toElement(),from);
            }
            else
            {
              qDebug("x tag received, ignoring!");
              if (!stanza_printed)
              {
                qDebug("Stanza data:");
                qDebug(s.toString());
                stanza_printed=true;
              }
            }
          }
          node = node.nextSibling();
        }
      }
      else
      {
        qWarning("Discarding offline message");
      }
    }
    else if (data.tagName() == "presence")
    {
//       qDebug(QString("Received presence stanza from %1").arg(from));
      if (!data.hasAttribute("type"))
      {
        //this is a HACK to work with pre XMPP 1.0 (old) servers
        if (!this->isSubscribed(from))
        {
          QString msg = QString("<presence to=\"%1\"></presence>").arg(from);
          this->send(msg);
          this->SubscriptionStates.insert(from,"subscribed");
          qDebug(QString("%1 is now subscribed and available.").arg(from));
        }
        this->sendQueuedMessages(from);
        
        emit this->presenceChanged(from,true);
      }
      else if (type == "error")
      {
          //@todo errors should be communicated to the data controller, somehow
        qWarning("Presence error:");
        if (!stanza_printed)
        {
          qDebug("Stanza data:");
          qDebug(s.toString());
          stanza_printed=true;
        }
      }
      else
      {
        if (type == "unavailable")
        {
          qDebug(QString("%1 is unavailable.").arg(from));
          //this is a HACK to work with pre XMPP 1.0 (old) servers
          this->SubscriptionStates.remove(from);
          emit this->presenceChanged(from,false);
        }
        else if (type == "subscribe")
        {
          qDebug(QString("%1 wants to subscribe.").arg(from));
          QString msg = QString("<presence to=\"%1\" type=\"subscribed\"></presence>").arg(from);
          this->send(msg);
          if (!this->SubscriptionStates.contains(from))
          {
            this->SubscriptionStates.insert(from,"unsubscribed");
          }
          if (this->SubscriptionStates[from] == "unsubscribed")
          {
            msg = QString("<presence to=\"%1\" type=\"subscribe\"></presence>").arg(this->getNetworkId()).arg(from);
            this->send(msg);
          }
        }
        else if (type == "subscribed")
        {
          qDebug(QString("Subscribed to %1.").arg(from));
          this->SubscriptionStates.insert(from,"subscribed");
          this->makeDestinationAvailable(from);
        }
        else if (type == "unsubscribe")
        {
          qDebug(QString("Unsubscribing presence to %1").arg(from));
          QString msg = QString("<presence to=\"%1\" type=\"subscribed\"></presence>").arg(from);
          this->send(msg);
          if (this->SubscriptionStates.contains(from))
          {
            if (this->SubscriptionStates[from] != "unsubscribed")
            {
              msg = QString("<presence to=\"%1\" type=\"unsubscribe\"></presence>").arg(from);
              this->send(msg);
            }
          }
        }
        else if (type == "unsubscribed")
        {
          qDebug(QString("Unsubscribed from %1.").arg(from));
          this->SubscriptionStates.insert(from,"unsubscribed");
//           qDebug(QString("Unsubscribed presence from %1").arg(from));
          emit this->presenceChanged(from,false);
        }
      }
    }
    else
    {
      qDebug(QString("%1 stanza received, ignoring!").arg(data.tagName()));
      if (!stanza_printed)
      {
         qDebug("Stanza data:");
         qDebug(s.toString());
         stanza_printed=true;
      }
//       emit this->dataAvailable(data,from);  //if it's not a "message", pass everything on
    }
  }
}

void GXmppNetwork::clientStreamStanzaWritten()
{
  qDebug("XMPP Stream Stanza written");
}

void GXmppNetwork::clientStreamWarning(int w)
{
  qDebug("XMPP Stream warning " + QString::number(w));
  qDebug("continuing after warning");
  if (this->Stream)
    this->Stream->continueAfterWarning();
}

void GXmppNetwork::clientStreamError(int err)
{
  // copied with small adaptions from the IRIS conntest example
  // LGPL license, (c) Justin Karneges
  // http://delta.affinix.com/iris/
  
  QString s;
  
  if(err == XMPP::ClientStream::ErrParse)
  {
    s = "XML parsing error";
  }
  else if(err == XMPP::ClientStream::ErrProtocol)
  {
    s = "XMPP protocol error";
  }
  else if(err == XMPP::ClientStream::ErrStream)
  {
    int x = Stream->errorCondition();
    if(x == XMPP::Stream::GenericStreamError)
      s = "generic stream error";
    else if(x == XMPP::ClientStream::Conflict)
      s = "conflict (remote login replacing this one)";
    else if(x == XMPP::ClientStream::ConnectionTimeout)
      s = "timed out from inactivity";
    else if(x == XMPP::ClientStream::InternalServerError)
      s = "internal server error";
    else if(x == XMPP::ClientStream::InvalidXml)
      s = "invalid XML";
    else if(x == XMPP::ClientStream::PolicyViolation)
      s = "policy violation.  go to jail!";
    else if(x == XMPP::ClientStream::ResourceConstraint)
      s = "server out of resources";
    else if(x == XMPP::ClientStream::SystemShutdown)
      s = "system is shutting down NOW";
    s = QString("XMPP stream error: ") + s;
  }
  else if(err == XMPP::ClientStream::ErrConnection)
  {
    int x = XmppConnector->errorCode();
    QString s;
    if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
      s = "unable to connect to server";
    else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
      s = "host not found";
    else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
      s = "proxy connect";
    else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
      s = "proxy negotiating";
    else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
      s = "proxy authorization";
    else if(x == XMPP::AdvancedConnector::ErrStream)
      s = "stream error";
    s = QString("Connection error: ") + s;
  }
  else if(err == XMPP::ClientStream::ErrNeg)
  {
    int x = Stream->errorCondition();
    QString s;
    if(x == XMPP::ClientStream::HostGone)
      s = "host no longer hosted";
    else if(x == XMPP::ClientStream::HostUnknown)
      s = "host unknown";
    else if(x == XMPP::ClientStream::RemoteConnectionFailed)
      s = "a required remote connection failed";
    else if(x == XMPP::ClientStream::SeeOtherHost)
      s = QString("see other host: [%1]").arg(Stream->errorText());
    else if(x == XMPP::ClientStream::UnsupportedVersion)
      s = "server does not support proper xmpp version";
    s = QString("Stream negotiation error: ") + s;
  }
  else if(err == XMPP::ClientStream::ErrTLS)
  {
    int x = Stream->errorCondition();
    QString s;
    if(x == XMPP::ClientStream::TLSStart)
      s = "server rejected STARTTLS";
    else if(x == XMPP::ClientStream::TLSFail)
    {
      int t = TlsHandler->tlsError();
      if(t == QCA::TLS::ErrHandshake)
        s = "TLS handshake error";
      else
        s = "broken security layer (TLS)";
    }
    //nothing to add
  }
  else if(err == XMPP::ClientStream::ErrAuth)
  {
    int x = Stream->errorCondition();
    
    if(x == XMPP::ClientStream::GenericAuthError)
      s = "unable to login";
    else if(x == XMPP::ClientStream::NoMech)
      s = "no appropriate auth mechanism available for given security settings";
    else if(x == XMPP::ClientStream::BadProto)
      s = "bad server response";
    else if(x == XMPP::ClientStream::BadServ)
      s = "server failed mutual authentication";
    else if(x == XMPP::ClientStream::EncryptionRequired)
      s = "encryption required for chosen SASL mechanism";
    else if(x == XMPP::ClientStream::InvalidAuthzid)
      s = "invalid authzid";
    else if(x == XMPP::ClientStream::InvalidMech)
      s = "invalid SASL mechanism";
    else if(x == XMPP::ClientStream::InvalidRealm)
      s = "invalid realm";
    else if(x == XMPP::ClientStream::MechTooWeak)
      s = "SASL mechanism too weak for authzid";
    else if(x == XMPP::ClientStream::NotAuthorized)
      s = "not authorized";
    else if(x == XMPP::ClientStream::TemporaryAuthFailure)
      s = "temporary auth failure";
    s = QString("Authentication error: ") + s;
  }
  else if(err == XMPP::ClientStream::ErrSecurityLayer)
    s = "broken security layer (SASL)";

  qWarning(s);
  
  if (!this->isConnected() && this->StayConnected)
  {
    qWarning("XMPP Stream got disconnected, reconnecting...");
    QTimer::singleShot(100,this,SLOT(initNetwork()));
  }
}

//END XMPP slots and signals for network management //

}


syntax highlighted by Code2HTML, v. 0.9.1