/***************************************************************************
                                   elogthreadsubmit.cpp
                             -------------------
    begin                : Feb 09 2004
    copyright            : (C) 2004 The University of British Columbia
    email                :
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>

#include <qiodevice.h>
#include <qbuffer.h>
#include <qcstring.h>
#include <qtextstream.h>
#include <qdatastream.h>
#include <qmessagebox.h>

#include <kaction.h>
#include <klocale.h>
#include <kmdcodec.h>
#include <kmdimainfrm.h>
#include <kmessagebox.h>

#include "elogthreadsubmit.h"
#include <kstdebug.h>
#include <kst.h>
#include <kstevents.h>

ElogThreadSubmit::ElogThreadSubmit(KstELOG* elog,
                                  bool bIncludeCapture,
                                  bool bIncludeConfiguration,
                                  bool bIncludeDebugInfo,
                                  QByteArray* pByteArrayCapture,
                                  const QString& strMessage,
                                  const QString& strUserName,
                                  const QString& strUserPassword,
                                  const QString& strWritePassword,
                                  const QString& strLogbook,
                                  const QString& strAttributes,
                                  bool bSubmitAsHTML,
                                  bool bSuppressEmail)
: ElogThread(elog) {
  _byteArrayCapture.duplicate( *pByteArrayCapture );
  _bIncludeCapture        = bIncludeCapture;
  _bIncludeConfiguration  = bIncludeConfiguration;
  _bIncludeDebugInfo      = bIncludeDebugInfo;
  _strMessage             = strMessage;
  _strUserName            = strUserName;
  _strUserPassword        = strUserPassword;
  _strWritePassword       = strWritePassword;
  _strLogbook             = strLogbook;
  _strAttributes          = strAttributes;
  _bSubmitAsHTML          = bSubmitAsHTML;
  _bSuppressEmail         = bSuppressEmail;
}

ElogThreadSubmit::~ElogThreadSubmit() {
}

void ElogThreadSubmit::doTransmit(int sock) {
  QByteArray	byteArrayConfigure;
  QTextStream textStreamConfigure( byteArrayConfigure, IO_ReadWrite );
  QByteArray	byteArrayDebugInfo;
  QTextStream textStreamDebugInfo( byteArrayDebugInfo, IO_ReadWrite );
  QCustomEvent eventConfigure(KstELOGConfigureEvent);
  QCustomEvent eventDebugInfo(KstELOGDebugInfoEvent);
  QStringList::iterator it;
  QStringList	strListAttributes;
  QStringList	strListAttribute;
  char* content;
  char request[100000];
  char response[100000];
  char boundary[80], *p;
  int i, n, header_length, content_length;
  int iAttachment = 0;

  content_length 			  = 100000;
  
  if( _bIncludeCapture ) {  
    if( _byteArrayCapture.size() > 0 ) {
      content_length += _byteArrayCapture.size();
    } else {
      _bIncludeCapture = false;
    }
  }
 
  if( _bIncludeConfiguration ) {
    eventConfigure.setData( &textStreamConfigure );
    QApplication::sendEvent( (QObject*)_elog->app(), (QEvent*)&eventConfigure );
    content_length += byteArrayConfigure.size();
  }
  
  if( _bIncludeDebugInfo ) {
    eventDebugInfo.setData( &textStreamDebugInfo );
    QApplication::sendEvent( (QObject*)_elog->app(), (QEvent*)&eventDebugInfo );
    content_length += byteArrayDebugInfo.size();
  }
  
  content = (char*)malloc(content_length);
  if (content != NULL) {    
    srand((unsigned) time(NULL));
    sprintf(boundary, "---------------------------%04X%04X%04X", rand(), rand(), rand());
    strcpy(content, boundary);
    strcat(content, "\r\nContent-Disposition: form-data; name=\"cmd\"\r\n\r\nSubmit\r\n");
    
    //
    // add the attributes...
    //
    addAttribute( content, boundary, "unm", _strUserName, false );
    addAttribute( content, boundary, "upwd", _strUserPassword, true );
    addAttribute( content, boundary, "exp", _strLogbook, false );
    
    strListAttributes = QStringList::split( '\n', _strAttributes, FALSE ); 
    for ( it = strListAttributes.begin(); it != strListAttributes.end(); ++it ) {
      strListAttribute = QStringList::split( '=', *it, FALSE );
      if( strListAttribute.count() == 2 ) {
        addAttribute( content, boundary, 
                      strListAttribute.first().stripWhiteSpace().ascii(),
                      strListAttribute.last().stripWhiteSpace(), false );
      }
    }
    if( _bSubmitAsHTML ) {
      addAttribute( content, boundary, "html", "1", false );      
    }
    if( _bSuppressEmail ) {
      addAttribute( content, boundary, "suppress", "1", false );      
    }

    addAttribute( content, boundary, "Text", _strMessage, false );
    sprintf( content + strlen(content), "%s\r\n", boundary );
    
    content_length = strlen(content);
    p = content + content_length;
    
    //
    // add the attachments...
    //
    if( _bIncludeCapture ) {
      iAttachment++;
      addAttachment( &content_length, &p, boundary, &_byteArrayCapture, iAttachment, "Capture.png" );
    }
    if( _bIncludeConfiguration ) {
      iAttachment++;
      addAttachment( &content_length, &p, boundary, &byteArrayConfigure, iAttachment, "Configure.kst" );
    }
    if( _bIncludeDebugInfo ) {
      iAttachment++;
      addAttachment( &content_length, &p, boundary, &byteArrayDebugInfo, iAttachment, "DebugInfo.txt" );
    }

    strcpy(request, "POST /");
    if (!_strLogbook.isEmpty()) {
      sprintf(request + strlen(request), "%s/", _strLogbook.ascii());
    }
    strcat(request, " HTTP/1.0\r\n");
    
    sprintf(request + strlen(request), "Content-Type: multipart/form-data; boundary=%s\r\n", boundary);
    sprintf(request + strlen(request), "Host: %s\r\n", _host_name);
    sprintf(request + strlen(request), "User-Agent: ELOG\r\n");
    sprintf(request + strlen(request), "Content-Length: %d\r\n", content_length);
    
    if (!_strWritePassword.isEmpty()) {
      QCString enc = KCodecs::base64Encode(_strWritePassword.ascii());
      sprintf(request + strlen(request), "Cookie: wpwd=%s\r\n", enc.data());
    }

    strcat(request, "\r\n");
    
    header_length = strlen(request);
    
    send(sock, request, header_length, 0);
    send(sock, content, content_length, 0);
    
    //
    // handle the response...
    //
    i = recv(sock, response, 10000, 0);
    if (i >= 0) {    
      n = i;
      while (i > 0) {
        i = recv(sock, response + n, 10000, 0);
        if (i > 0) {
          n += i;
        }
      }
      response[n] = 0;
      
      doResponseError(response);
    }
    else
    {
      doError( tr2i18n("ELOG entry: unable to receive response"), KstDebug::Notice );
    }
  }
}

void ElogThreadSubmit::run( ) {
  int status, sock, error;
  
  error = makeConnection( &sock, &status );
  if( status == 0 ) {
    doTransmit( sock );
    close( sock );
  }
  else {
    switch( error ) {
      case -1:
        doError( tr2i18n("Failed to add ELOG entry: failed to connect to host.") );
        break;
      case -2:
        doError( tr2i18n("Failed to add ELOG entry: failed to get host name.") );
        break;
      case -3:
        doError( tr2i18n("Failed to add ELOG entry: failed to create socket.") );
        break;
      case -4:
        doError( tr2i18n("Failed to add ELOG entry: failed to get host by address.") );
        break;
      case -5:
        doError( tr2i18n("Failed to add ELOG entry: failed to get host by name.") );
        break;
      default:
        doError( tr2i18n("Failed to add ELOG entry: unknown error.") );
        break;
    }
  }

  delete this;
}

bool ElogThreadSubmit::doResponseError( const char* response ) {
  QString strError;
  char str[80];
  
  if (strstr(response, "302 Found")) {
    if (strstr(response, "Location:")) {
      if (strstr(response, "wpwd")) {
        doError( tr2i18n("Failed to add ELOG entry: invalid password") );
      } else if (strstr(response, "wusr")) {
        doError( tr2i18n("Failed to add ELOG entry: invalid user name") );
      } else {
        strncpy(str, strstr(response, "Location:") + 10, sizeof(str));
        if (strchr(str, '?')) {
          *strchr(str, '?') = 0;
        }
        if (strchr(str, '\n')) {
          *strchr(str, '\n') = 0;
        }
        if (strchr(str, '\r')) {
          *strchr(str, '\r') = 0;
        }
        
        if (strrchr(str, '/')) {
          strError = tr2i18n("Successfully added ELOG entry: ID=%1\n").arg( strrchr(str, '/') + 1 );
          doError( strError, KstDebug::Notice );
        } else {
          strError = tr2i18n("Successfully added ELOG entry: ID=%1\n").arg( str );
          doError( strError, KstDebug::Notice );
        }
      }
    } else {
      doError( tr2i18n("Successfully added ELOG entry"), KstDebug::Notice );
    }
  } else if (strstr(response, "Logbook Selection"))
    doError( tr2i18n("Failed to add ELOG entry: no logbook specified") );
  else if (strstr(response, "enter password")) {
    doError( tr2i18n("Failed to add ELOG entry: missing or invalid password") );
  }
  else if (strstr(response, "form name=form1")) {
    doError( tr2i18n("Failed to add ELOG entry: missing or invalid user name/password") );
  }
  else if (strstr(response, "Error: Attribute")) {
    strncpy(str, strstr(response, "Error: Attribute") + 20, sizeof(str));
    if (strchr(str, '<')) {
      *strchr(str, '<') = 0;
    }
    strError = tr2i18n("Failed to add ELOG entry: missing required attribute \"%1\"").arg( str );
    doError( strError );
  } else {
    doError( tr2i18n("Failed to add ELOG entry: error transmitting message") ); 
  }
  
  return TRUE;
}
// vim: ts=2 sw=2 et
