<commit_before>/***************************************************************************
 *   Copyright (C) 2006 by Till Adam <adam@kde.org>                        *
 *   Copyright (C) 2013 by Volker Krause <vkrause@kde.org>                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/
#include "akonadiconnection.h"

#include <QtCore/QDebug>
#include <QtCore/QEventLoop>
#include <QtCore/QLatin1String>
#include <QSettings>

#include "storage/datastore.h"
#include "handler.h"
#include "response.h"
#include "tracer.h"
#include "clientcapabilityaggregator.h"

#include "imapstreamparser.h"
#include "shared/akdebug.h"
#include "shared/akcrash.h"

#include <akstandarddirs.h>

#include <assert.h>

#define AKONADI_PROTOCOL_VERSION "35"

using namespace Akonadi;

AkonadiConnection::AkonadiConnection( quintptr socketDescriptor, QObject *parent )
    : QThread( parent )
    , m_socketDescriptor( socketDescriptor )
    , m_socket( 0 )
    , m_currentHandler( 0 )
    , m_connectionState( NonAuthenticated )
    , m_backend( 0 )
    , m_selectedConnection( 0 )
    , m_streamParser( 0 )
    , m_verifyCacheOnRetrieval( false )
{
    m_identifier.sprintf( "%p", static_cast<void *>( this ) );
    ClientCapabilityAggregator::addSession( m_clientCapabilities );

    const QSettings settings( AkStandardDirs::serverConfigFile(), QSettings::IniFormat );
    m_verifyCacheOnRetrieval = settings.value( QLatin1String( "Cache/VerifyOnRetrieval" ), m_verifyCacheOnRetrieval ).toBool();
}

DataStore *Akonadi::AkonadiConnection::storageBackend()
{
    if ( !m_backend ) {
      m_backend = DataStore::self();
    }
    return m_backend;
}

AkonadiConnection::~AkonadiConnection()
{
    ClientCapabilityAggregator::removeSession( m_clientCapabilities );
    Tracer::self()->endConnection( m_identifier, QString() );
}

void AkonadiConnection::run()
{
    m_socket = new QLocalSocket();

    if ( !m_socket->setSocketDescriptor( m_socketDescriptor ) ) {
        qWarning() << "AkonadiConnection(" << m_identifier
                   << ")::run: failed to set socket descriptor: "
                   << m_socket->error() << "(" << m_socket->errorString() << ")";
        delete m_socket;
        m_socket = 0;
        return;
    }

    /* Start a local event loop and start processing incoming data. Whenever
     * a full command has been read, it is delegated to the responsible
     * handler and processed by that. If that command needs to do something
     * asynchronous such as ask the db for data, it returns and the input
     * queue can continue to be processed. Whenever there is something to
     * be sent back to the user it is queued in the form of a Response object.
     * All this is meant to make it possible to process large incoming or
     * outgoing data transfers in a streaming manner, without having to
     * hold them in memory 'en gros'. */

    connect( m_socket, SIGNAL(readyRead()),
             this, SLOT(slotNewData()), Qt::DirectConnection );
    connect( m_socket, SIGNAL(disconnected()),
             this, SLOT(slotDisconnected()), Qt::DirectConnection );

    m_streamParser = new ImapStreamParser( m_socket );
    m_streamParser->setTracerIdentifier( m_identifier );

    Response greeting;
    greeting.setUntagged();
    greeting.setString( "OK Akonadi Almost IMAP Server [PROTOCOL " AKONADI_PROTOCOL_VERSION "]" );
    // don't send before the event loop is active, since waitForBytesWritten() can cause interesting reentrancy issues
    // TODO should be QueueConnection, but unfortunately that doesn't work (yet), since
    // "this" belongs to the wrong thread, but that requires a slightly larger refactoring
    QMetaObject::invokeMethod( this, "slotResponseAvailable", Qt::DirectConnection, Q_ARG( Akonadi::Response, greeting ) );

    exec();
    delete m_socket;
    m_socket = 0;
    delete m_streamParser;
    m_streamParser = 0;
}

void AkonadiConnection::slotDisconnected()
{
    quit();
}

void AkonadiConnection::slotNewData()
{
  // On Windows, calling readLiteralPart() triggers the readyRead() signal recursively and leads to parse errors
  if ( m_currentHandler ) {
    return;
  }

  while ( m_socket->bytesAvailable() > 0 || !m_streamParser->readRemainingData().isEmpty() ) {
    try {
      const QByteArray tag = m_streamParser->readString();
      // deal with stray newlines
      if ( tag.isEmpty() && m_streamParser->atCommandEnd() ) {
        continue;
      }
      const QByteArray command = m_streamParser->readString();
      if ( command.isEmpty() ) {
        throw Akonadi::Exception("empty command");
      }
      Tracer::self()->connectionInput( m_identifier, ( tag + ' ' + command + ' ' + m_streamParser->readRemainingData() ) );
      m_currentHandler = findHandlerForCommand( command );
      assert( m_currentHandler );
      connect( m_currentHandler, SIGNAL(responseAvailable(Akonadi::Response)),
               this, SLOT(slotResponseAvailable(Akonadi::Response)), Qt::DirectConnection );
      connect( m_currentHandler, SIGNAL(connectionStateChange(ConnectionState)),
              this, SLOT(slotConnectionStateChange(ConnectionState)),
              Qt::DirectConnection );
      m_currentHandler->setTag( tag );
      m_currentHandler->setStreamParser( m_streamParser );
      if ( !m_currentHandler->parseStream() ) {
        m_streamParser->skipCurrentCommand();
      }
    } catch ( const Akonadi::HandlerException &e ) {
      m_currentHandler->failureResponse( e.what() );
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    } catch ( const Akonadi::Exception &e ) {
      if ( m_currentHandler ) {
        m_currentHandler->failureResponse( QByteArray( e.type() ) + QByteArray( ": " ) + QByteArray( e.what() ) );
      }
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    } catch ( ... ) {
      akError() << "Unknown exception caught: " << akBacktrace();
      if ( m_currentHandler ) {
        m_currentHandler->failureResponse( "Unknown exception caught" );
      }
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    }
    delete m_currentHandler;
    m_currentHandler = 0;

    if ( m_streamParser->readRemainingData().startsWith( '\n' ) || m_streamParser->readRemainingData().startsWith( "\r\n" ) ) {
      try {
        m_streamParser->readUntilCommandEnd(); //just eat the ending newline
      } catch ( ... ) {}
    }
  }
}

void AkonadiConnection::writeOut( const QByteArray &data )
{
    QByteArray block = data + "\r\n";
    m_socket->write( block );
    m_socket->waitForBytesWritten();

    Tracer::self()->connectionOutput( m_identifier, block );
}

Handler *AkonadiConnection::findHandlerForCommand( const QByteArray &command )
{
    Handler *handler = Handler::findHandlerForCommandAlwaysAllowed( command );
    if ( handler ) {
      handler->setConnection( this );
      return handler;
    }

    switch ( m_connectionState ) {
    case NonAuthenticated:
        handler =  Handler::findHandlerForCommandNonAuthenticated( command );
        break;
    case Authenticated:
        handler =  Handler::findHandlerForCommandAuthenticated( command, m_streamParser );
        break;
    case Selected:
        break;
    case LoggingOut:
        break;
    }
    // we didn't have a handler for this, let the default one do its thing
    if ( !handler ) {
        handler = new UnknownCommandHandler( command );
    }
    handler->setConnection( this );
    return handler;
}

void AkonadiConnection::slotResponseAvailable( const Akonadi::Response &response )
{
    // FIXME handle reentrancy in the presence of continuation. Something like:
    // "if continuation pending, queue responses, once continuation is done, replay them"
    writeOut( response.asString() );
}

void AkonadiConnection::slotConnectionStateChange( ConnectionState state )
{
    if ( state == m_connectionState ) {
        return;
    }
    m_connectionState = state;
    switch ( m_connectionState ) {
    case NonAuthenticated:
        assert( 0 ); // can't happen, it's only the initial state, we can't go back to it
        break;
    case Authenticated:
        break;
    case Selected:
        break;
    case LoggingOut:
        m_socket->disconnectFromServer();
        break;
    }
}

qint64 Akonadi::AkonadiConnection::selectedCollectionId() const
{
    return m_selectedConnection;
}

void Akonadi::AkonadiConnection::setSelectedCollection( qint64 collection )
{
    m_selectedConnection = collection;
}

const Collection Akonadi::AkonadiConnection::selectedCollection()
{
  return Collection::retrieveById( selectedCollectionId() );
}

void Akonadi::AkonadiConnection::addStatusMessage( const QByteArray &msg )
{
    m_statusMessageQueue.append( msg );
}

void Akonadi::AkonadiConnection::flushStatusMessageQueue()
{
    for ( int i = 0; i < m_statusMessageQueue.count(); ++i ) {
      Response response;
      response.setUntagged();
      response.setString( m_statusMessageQueue[i] );

      slotResponseAvailable( response );
    }

    m_statusMessageQueue.clear();
}

void AkonadiConnection::setSessionId( const QByteArray &id )
{
  m_identifier.sprintf( "%s (%p)", id.data(), static_cast<void *>( this ) );
  Tracer::self()->beginConnection( m_identifier, QString() );
  m_streamParser->setTracerIdentifier( m_identifier );

  m_sessionId = id;
  storageBackend()->setSessionId( id );
  storageBackend()->notificationCollector()->setSessionId( id );
}

QByteArray AkonadiConnection::sessionId() const
{
  return m_sessionId;
}

Resource Akonadi::AkonadiConnection::resourceContext() const
{
  return m_resourceContext;
}

void AkonadiConnection::setResourceContext( const Resource &res )
{
  m_resourceContext = res;
}

bool AkonadiConnection::isOwnerResource( const PimItem &item ) const
{
  if ( resourceContext().isValid() && item.collection().resourceId() == resourceContext().id() ) {
    return true;
  }
  // fallback for older resources
  if ( sessionId() == item.collection().resource().name().toUtf8() ) {
    return true;
  }
  return false;
}

bool AkonadiConnection::isOwnerResource( const Collection &collection ) const
{
  if ( resourceContext().isValid() && collection.resourceId() == resourceContext().id() ) {
    return true;
  }
  if ( sessionId() == collection.resource().name().toUtf8() ) {
    return true;
  }
  return false;
}

const ClientCapabilities &AkonadiConnection::capabilities() const
{
  return m_clientCapabilities;
}

void AkonadiConnection::setCapabilities( const ClientCapabilities &capabilities )
{
  ClientCapabilityAggregator::removeSession( m_clientCapabilities );
  m_clientCapabilities = capabilities;
  ClientCapabilityAggregator::addSession( m_clientCapabilities );
}

bool AkonadiConnection::verifyCacheOnRetrieval() const
{
  return m_verifyCacheOnRetrieval;
}
<commit_msg>Bump Akonadi protocol version to 36<commit_after>/***************************************************************************
 *   Copyright (C) 2006 by Till Adam <adam@kde.org>                        *
 *   Copyright (C) 2013 by Volker Krause <vkrause@kde.org>                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/
#include "akonadiconnection.h"

#include <QtCore/QDebug>
#include <QtCore/QEventLoop>
#include <QtCore/QLatin1String>
#include <QSettings>

#include "storage/datastore.h"
#include "handler.h"
#include "response.h"
#include "tracer.h"
#include "clientcapabilityaggregator.h"

#include "imapstreamparser.h"
#include "shared/akdebug.h"
#include "shared/akcrash.h"

#include <akstandarddirs.h>

#include <assert.h>

#define AKONADI_PROTOCOL_VERSION "36"

using namespace Akonadi;

AkonadiConnection::AkonadiConnection( quintptr socketDescriptor, QObject *parent )
    : QThread( parent )
    , m_socketDescriptor( socketDescriptor )
    , m_socket( 0 )
    , m_currentHandler( 0 )
    , m_connectionState( NonAuthenticated )
    , m_backend( 0 )
    , m_selectedConnection( 0 )
    , m_streamParser( 0 )
    , m_verifyCacheOnRetrieval( false )
{
    m_identifier.sprintf( "%p", static_cast<void *>( this ) );
    ClientCapabilityAggregator::addSession( m_clientCapabilities );

    const QSettings settings( AkStandardDirs::serverConfigFile(), QSettings::IniFormat );
    m_verifyCacheOnRetrieval = settings.value( QLatin1String( "Cache/VerifyOnRetrieval" ), m_verifyCacheOnRetrieval ).toBool();
}

DataStore *Akonadi::AkonadiConnection::storageBackend()
{
    if ( !m_backend ) {
      m_backend = DataStore::self();
    }
    return m_backend;
}

AkonadiConnection::~AkonadiConnection()
{
    ClientCapabilityAggregator::removeSession( m_clientCapabilities );
    Tracer::self()->endConnection( m_identifier, QString() );
}

void AkonadiConnection::run()
{
    m_socket = new QLocalSocket();

    if ( !m_socket->setSocketDescriptor( m_socketDescriptor ) ) {
        qWarning() << "AkonadiConnection(" << m_identifier
                   << ")::run: failed to set socket descriptor: "
                   << m_socket->error() << "(" << m_socket->errorString() << ")";
        delete m_socket;
        m_socket = 0;
        return;
    }

    /* Start a local event loop and start processing incoming data. Whenever
     * a full command has been read, it is delegated to the responsible
     * handler and processed by that. If that command needs to do something
     * asynchronous such as ask the db for data, it returns and the input
     * queue can continue to be processed. Whenever there is something to
     * be sent back to the user it is queued in the form of a Response object.
     * All this is meant to make it possible to process large incoming or
     * outgoing data transfers in a streaming manner, without having to
     * hold them in memory 'en gros'. */

    connect( m_socket, SIGNAL(readyRead()),
             this, SLOT(slotNewData()), Qt::DirectConnection );
    connect( m_socket, SIGNAL(disconnected()),
             this, SLOT(slotDisconnected()), Qt::DirectConnection );

    m_streamParser = new ImapStreamParser( m_socket );
    m_streamParser->setTracerIdentifier( m_identifier );

    Response greeting;
    greeting.setUntagged();
    greeting.setString( "OK Akonadi Almost IMAP Server [PROTOCOL " AKONADI_PROTOCOL_VERSION "]" );
    // don't send before the event loop is active, since waitForBytesWritten() can cause interesting reentrancy issues
    // TODO should be QueueConnection, but unfortunately that doesn't work (yet), since
    // "this" belongs to the wrong thread, but that requires a slightly larger refactoring
    QMetaObject::invokeMethod( this, "slotResponseAvailable", Qt::DirectConnection, Q_ARG( Akonadi::Response, greeting ) );

    exec();
    delete m_socket;
    m_socket = 0;
    delete m_streamParser;
    m_streamParser = 0;
}

void AkonadiConnection::slotDisconnected()
{
    quit();
}

void AkonadiConnection::slotNewData()
{
  // On Windows, calling readLiteralPart() triggers the readyRead() signal recursively and leads to parse errors
  if ( m_currentHandler ) {
    return;
  }

  while ( m_socket->bytesAvailable() > 0 || !m_streamParser->readRemainingData().isEmpty() ) {
    try {
      const QByteArray tag = m_streamParser->readString();
      // deal with stray newlines
      if ( tag.isEmpty() && m_streamParser->atCommandEnd() ) {
        continue;
      }
      const QByteArray command = m_streamParser->readString();
      if ( command.isEmpty() ) {
        throw Akonadi::Exception("empty command");
      }
      Tracer::self()->connectionInput( m_identifier, ( tag + ' ' + command + ' ' + m_streamParser->readRemainingData() ) );
      m_currentHandler = findHandlerForCommand( command );
      assert( m_currentHandler );
      connect( m_currentHandler, SIGNAL(responseAvailable(Akonadi::Response)),
               this, SLOT(slotResponseAvailable(Akonadi::Response)), Qt::DirectConnection );
      connect( m_currentHandler, SIGNAL(connectionStateChange(ConnectionState)),
              this, SLOT(slotConnectionStateChange(ConnectionState)),
              Qt::DirectConnection );
      m_currentHandler->setTag( tag );
      m_currentHandler->setStreamParser( m_streamParser );
      if ( !m_currentHandler->parseStream() ) {
        m_streamParser->skipCurrentCommand();
      }
    } catch ( const Akonadi::HandlerException &e ) {
      m_currentHandler->failureResponse( e.what() );
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    } catch ( const Akonadi::Exception &e ) {
      if ( m_currentHandler ) {
        m_currentHandler->failureResponse( QByteArray( e.type() ) + QByteArray( ": " ) + QByteArray( e.what() ) );
      }
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    } catch ( ... ) {
      akError() << "Unknown exception caught: " << akBacktrace();
      if ( m_currentHandler ) {
        m_currentHandler->failureResponse( "Unknown exception caught" );
      }
      try {
        m_streamParser->skipCurrentCommand();
      } catch ( ... ) {}
    }
    delete m_currentHandler;
    m_currentHandler = 0;

    if ( m_streamParser->readRemainingData().startsWith( '\n' ) || m_streamParser->readRemainingData().startsWith( "\r\n" ) ) {
      try {
        m_streamParser->readUntilCommandEnd(); //just eat the ending newline
      } catch ( ... ) {}
    }
  }
}

void AkonadiConnection::writeOut( const QByteArray &data )
{
    QByteArray block = data + "\r\n";
    m_socket->write( block );
    m_socket->waitForBytesWritten();

    Tracer::self()->connectionOutput( m_identifier, block );
}

Handler *AkonadiConnection::findHandlerForCommand( const QByteArray &command )
{
    Handler *handler = Handler::findHandlerForCommandAlwaysAllowed( command );
    if ( handler ) {
      handler->setConnection( this );
      return handler;
    }

    switch ( m_connectionState ) {
    case NonAuthenticated:
        handler =  Handler::findHandlerForCommandNonAuthenticated( command );
        break;
    case Authenticated:
        handler =  Handler::findHandlerForCommandAuthenticated( command, m_streamParser );
        break;
    case Selected:
        break;
    case LoggingOut:
        break;
    }
    // we didn't have a handler for this, let the default one do its thing
    if ( !handler ) {
        handler = new UnknownCommandHandler( command );
    }
    handler->setConnection( this );
    return handler;
}

void AkonadiConnection::slotResponseAvailable( const Akonadi::Response &response )
{
    // FIXME handle reentrancy in the presence of continuation. Something like:
    // "if continuation pending, queue responses, once continuation is done, replay them"
    writeOut( response.asString() );
}

void AkonadiConnection::slotConnectionStateChange( ConnectionState state )
{
    if ( state == m_connectionState ) {
        return;
    }
    m_connectionState = state;
    switch ( m_connectionState ) {
    case NonAuthenticated:
        assert( 0 ); // can't happen, it's only the initial state, we can't go back to it
        break;
    case Authenticated:
        break;
    case Selected:
        break;
    case LoggingOut:
        m_socket->disconnectFromServer();
        break;
    }
}

qint64 Akonadi::AkonadiConnection::selectedCollectionId() const
{
    return m_selectedConnection;
}

void Akonadi::AkonadiConnection::setSelectedCollection( qint64 collection )
{
    m_selectedConnection = collection;
}

const Collection Akonadi::AkonadiConnection::selectedCollection()
{
  return Collection::retrieveById( selectedCollectionId() );
}

void Akonadi::AkonadiConnection::addStatusMessage( const QByteArray &msg )
{
    m_statusMessageQueue.append( msg );
}

void Akonadi::AkonadiConnection::flushStatusMessageQueue()
{
    for ( int i = 0; i < m_statusMessageQueue.count(); ++i ) {
      Response response;
      response.setUntagged();
      response.setString( m_statusMessageQueue[i] );

      slotResponseAvailable( response );
    }

    m_statusMessageQueue.clear();
}

void AkonadiConnection::setSessionId( const QByteArray &id )
{
  m_identifier.sprintf( "%s (%p)", id.data(), static_cast<void *>( this ) );
  Tracer::self()->beginConnection( m_identifier, QString() );
  m_streamParser->setTracerIdentifier( m_identifier );

  m_sessionId = id;
  storageBackend()->setSessionId( id );
  storageBackend()->notificationCollector()->setSessionId( id );
}

QByteArray AkonadiConnection::sessionId() const
{
  return m_sessionId;
}

Resource Akonadi::AkonadiConnection::resourceContext() const
{
  return m_resourceContext;
}

void AkonadiConnection::setResourceContext( const Resource &res )
{
  m_resourceContext = res;
}

bool AkonadiConnection::isOwnerResource( const PimItem &item ) const
{
  if ( resourceContext().isValid() && item.collection().resourceId() == resourceContext().id() ) {
    return true;
  }
  // fallback for older resources
  if ( sessionId() == item.collection().resource().name().toUtf8() ) {
    return true;
  }
  return false;
}

bool AkonadiConnection::isOwnerResource( const Collection &collection ) const
{
  if ( resourceContext().isValid() && collection.resourceId() == resourceContext().id() ) {
    return true;
  }
  if ( sessionId() == collection.resource().name().toUtf8() ) {
    return true;
  }
  return false;
}

const ClientCapabilities &AkonadiConnection::capabilities() const
{
  return m_clientCapabilities;
}

void AkonadiConnection::setCapabilities( const ClientCapabilities &capabilities )
{
  ClientCapabilityAggregator::removeSession( m_clientCapabilities );
  m_clientCapabilities = capabilities;
  ClientCapabilityAggregator::addSession( m_clientCapabilities );
}

bool AkonadiConnection::verifyCacheOnRetrieval() const
{
  return m_verifyCacheOnRetrieval;
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-fts/QueryAnalyzer.h"
#include "common.h"

using namespace fnord;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  //flags.defineFlag(
  //    "lang",
  //    cli::FlagParser::T_STRING,
  //    true,
  //    NULL,
  //    NULL,
  //    "language",
  //    "<lang>");
  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  fts::StopwordDictionary stopwords;
  fts::QueryAnalyzer analyzer(&stopwords);

  for (String line; std::getline(std::cin, line); ) {
    Set<String> tokens;
    analyzer.analyze(Language::GERMAN, line, &tokens);
    fnord::iputs("\ninput: $0\noutput: $1", line, tokens);
  }

  return 0;
}

<commit_msg>filter stopwords<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-fts/QueryAnalyzer.h"
#include "common.h"

using namespace fnord;

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "lang",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "language",
      "<lang>");

  flags.defineFlag(
      "stopwords",
      cli::FlagParser::T_STRING,
      false,
      NULL,
      NULL,
      "stopwords",
      "<path>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  auto lang = languageFromString(flags.getString("lang"));

  fts::StopwordDictionary stopwords;
  if (flags.isSet("stopwords")) {
    stopwords.loadStopwordFile(flags.getString("stopwords"));
  } else {
    fnord::logWarning("cm-tokenize", "no stopword file provided");
  }

  fts::QueryAnalyzer analyzer(&stopwords);
  for (String line; std::getline(std::cin, line); ) {
    Set<String> tokens;
    analyzer.analyze(lang, line, &tokens);
    fnord::iputs("\ninput: $0\noutput: $1", line, tokens);
  }

  return 0;
}

<|endoftext|>


<commit_before>#include "test_main.hpp"

#include "../chrono.hpp"
#include "../server_thread.hpp"
#include "../request_filter.hpp"
#include "../router.hpp"
#include "../work_queue.hpp"

#include <stdio.h>

#include <boost/thread/future.hpp>

namespace msrv {

class EchoController : ControllerBase
{
public:
    static void defineRoutes(Router* router)
    {
        auto routes = router->defineRoutes<EchoController>();

        routes.createWith([](Request* request) { return new EchoController(request); });

        routes.get("", &EchoController::handle);
        routes.get(":p*", &EchoController::handle);
        routes.post("", ControllerAction<EchoController>(&EchoController::handle));
        routes.post(":p*", ControllerAction<EchoController>(&EchoController::handle));
    }

    EchoController(Request* request)
        : ControllerBase(request)
    {
    }

    ResponsePtr handle()
    {
        if (optionalParam<bool>("eventstream", false))
        {
            return Response::eventStream([this] { return buildResponse(); });
        }
        else if (auto delay = optionalParam<int32_t>("delay"))
        {
            if (delay <= 0)
                throw InvalidRequestException("delay should greater than 0");

            return Response::async(boost::async([this, delay]
            {
                std::this_thread::sleep_for(std::chrono::seconds(*delay));
                return ResponsePtr(Response::json(buildResponse()));
            }));
        }

        return Response::json(buildResponse());
    }

    Json buildResponse()
    {
        Json response;

        response["method"] = toString(request()->method);
        response["path"] = request()->path;
        response["headers"] = request()->headers;
        response["queryParams"] = request()->queryParams;
        response["body"] = request()->postData;
        response["ticks"] = steadyTime().time_since_epoch().count();

        return response;
    }
};

class EchoServer
{
public:
    static constexpr int PORT = 8882;

    EchoServer()
    {
        EchoController::defineRoutes(&router_);
        filters_.addFilter(std::make_unique<ExecuteHandlerFilter>());

        auto config = std::make_unique<ServerConfig>();

        config->allowRemote = false;
        config->port = PORT;
        config->router = &router_;
        config->filters = &filters_;
        config->defaultWorkQueue = &workQueue_;

        thread_.restart(std::move(config));
    }

    ~EchoServer() = default;

    void dispatchEvents()
    {
        thread_.dispatchEvents();
    }

private:
    Router router_;
    RequestFilterChain filters_;
    ThreadWorkQueue workQueue_;
    ServerThread thread_;

    MSRV_NO_COPY_AND_ASSIGN(EchoServer);
};

int testMain(int, char**)
{
    EchoServer server;

    logInfo("running server at port %d", EchoServer::PORT);
    logInfo("press q<ENTER> to stop, press e<ENTER> to dispatch events");

    while (true)
    {
        switch (int ch = ::getchar())
        {
        case 'q':
        case 'Q':
            return 0;

        case 'e':
        case 'E':
            server.dispatchEvents();
            break;
        }
    }
}

}

<commit_msg>echo_server: echo headers<commit_after>#include "test_main.hpp"

#include "../chrono.hpp"
#include "../server_thread.hpp"
#include "../request_filter.hpp"
#include "../router.hpp"
#include "../work_queue.hpp"

#include <stdio.h>

#include <boost/thread/future.hpp>

namespace msrv {

class EchoHeadersFilter : public RequestFilter
{
protected:
    void endRequest(Request* request) override
    {
        auto* response = request->response.get();

        if (!response)
            return;

        for (auto& pair : request->headers)
            response->headers.emplace("X-Echo-" + pair.first, pair.second);
    }
};

class EchoController : ControllerBase
{
public:
    static void defineRoutes(Router* router)
    {
        auto routes = router->defineRoutes<EchoController>();

        routes.createWith([](Request* request) { return new EchoController(request); });

        routes.get("", &EchoController::handle);
        routes.get(":p*", &EchoController::handle);
        routes.post("", ControllerAction<EchoController>(&EchoController::handle));
        routes.post(":p*", ControllerAction<EchoController>(&EchoController::handle));
    }

    EchoController(Request* request)
        : ControllerBase(request)
    {
    }

    ResponsePtr handle()
    {
        if (optionalParam<bool>("eventstream", false))
        {
            return Response::eventStream([this] { return buildResponse(); });
        }
        else if (auto delay = optionalParam<int32_t>("delay"))
        {
            if (delay <= 0)
                throw InvalidRequestException("delay should greater than 0");

            return Response::async(boost::async([this, delay]
            {
                std::this_thread::sleep_for(std::chrono::seconds(*delay));
                return ResponsePtr(Response::json(buildResponse()));
            }));
        }

        return Response::json(buildResponse());
    }

    Json buildResponse()
    {
        Json response;

        response["method"] = toString(request()->method);
        response["path"] = request()->path;
        response["headers"] = request()->headers;
        response["queryParams"] = request()->queryParams;
        response["body"] = request()->postData;
        response["ticks"] = steadyTime().time_since_epoch().count();

        return response;
    }
};

class EchoServer
{
public:
    static constexpr int PORT = 8882;

    EchoServer()
    {
        EchoController::defineRoutes(&router_);
        filters_.addFilter(std::make_unique<EchoHeadersFilter>());
        filters_.addFilter(std::make_unique<ExecuteHandlerFilter>());

        auto config = std::make_unique<ServerConfig>();

        config->allowRemote = false;
        config->port = PORT;
        config->router = &router_;
        config->filters = &filters_;
        config->defaultWorkQueue = &workQueue_;

        thread_.restart(std::move(config));
    }

    ~EchoServer() = default;

    void dispatchEvents()
    {
        thread_.dispatchEvents();
    }

private:
    Router router_;
    RequestFilterChain filters_;
    ThreadWorkQueue workQueue_;
    ServerThread thread_;

    MSRV_NO_COPY_AND_ASSIGN(EchoServer);
};

int testMain(int, char**)
{
    EchoServer server;

    logInfo("running server at port %d", EchoServer::PORT);
    logInfo("press q<ENTER> to stop, press e<ENTER> to dispatch events");

    while (true)
    {
        switch (int ch = ::getchar())
        {
        case 'q':
        case 'Q':
            return 0;

        case 'e':
        case 'E':
            server.dispatchEvents();
            break;
        }
    }
}

}

<|endoftext|>


<commit_before>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 100 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;
	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 0;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(0, &stReadFDS);

	base = 0;
	int finale = -1;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			cout << "begin loop no. " << x << endl;
			int t = select(1, &stReadFDS, NULL, NULL, &stTimeOut);
			if (t != 0) {
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT" << endl;
				}
			} else {
				cout << "=== ACK TIMEOUT" << endl;
			}
			if(b[0] == 0) continue;
			if(isAck()) {
				handleAck();
			} else {
				handleAck();
				//handleNak(x);
			}
			if(finale > 0 && base == finale) break;
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<commit_msg>who knows anymore<commit_after>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 100 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;
	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 0;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(0, &stReadFDS);

	base = 0;
	int finale = -1;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			cout << "begin loop no. " << x << endl;
			int t = select(1, &stReadFDS, NULL, NULL, &stTimeOut);
			if (t != 0) {
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT" << endl;
				}
			} else {
				cout << "=== ACK TIMEOUT" << endl;
			}
			//if(b[0] == 0) continue;
			if(isAck()) {
				handleAck();
			} else {
				handleAck();
				//handleNak(x);
			}
			if(finale > 0 && base == finale) break;
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<|endoftext|>


<commit_before>#include <map>
#include <Poco/StreamCopier.h>
#include <Poco/Net/NetException.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>

#include "service/AuthService.h"
#include "server/Route.h"
#include "model/JSONSerializer.h"
#include "UIServerModule.h"
#include "Debug.h"

using namespace std;
using namespace Poco;
using namespace Poco::Net;
using namespace Poco::JSON;
using namespace Poco::Dynamic;
using namespace BeeeOn;

void handleNoRoute(UIRouteContext &context)
{
	TRACE_FUNC();

	context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
	context.response().setReason("Not found");
}

static void handleNoOperation(UIRouteContext &context)
{
	TRACE_FUNC();

	context.response().setStatus(UIResponse::HTTP_BAD_REQUEST);
	context.response().setReason("Bad request");
}

static void verifyAuthorized(const UIRequest &request,
		const UIRoute &route, UIServerModule &module)
{
	TRACE_FUNC();

	throw Poco::Net::NotAuthenticatedException("not implemented");
}

void handleAuth(UIRouteContext &context)
{
	TRACE_FUNC();

	if (context.request().hasCredentials()) {
		string scheme;
		string authInfo;
		context.request().getCredentials(scheme, authInfo);

		// TODO: user is already logged in? leave it
		// XXX: is this secure to just echo the value?
		context.response().sendBuffer(authInfo.c_str(), authInfo.size());
		return;
	}

	string jsonData;
	StreamCopier::copyToString(context.request().stream(), jsonData);

	Parser parser;
	parser.parse(jsonData);
	const Var result = parser.result();
	const Object::Ptr data = result.extract<Object::Ptr>();
	const Var provider = data->get("provider");
	const Var authCode = data->get("authCode");

	AuthCodeCredentials cred(provider.toString(), authCode.toString());
	const string &sessionId = context.userData()
					.authService().login(cred);

	context.response().sendBuffer(sessionId.c_str(), sessionId.size());
}

static void handleGetUser(UIRouteContext &context)
{
	UIRoute::Params::const_iterator it;

	it = context.params().find("userId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	User user(UserID::parse(it->second));
	if (!context.userData().userService().fetch(user)) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	JSONObjectSerializer serializer;
	user.toWeb(serializer);
	const string &userData = serializer.toString();

	context.response().sendBuffer(userData.c_str(), userData.size());
}

static void handleCreateUser(UIRouteContext &context)
{
	std::string userData;
	StreamCopier::copyToString(context.request().stream(), userData);
	JSONObjectSerializer deserializer;
	User user;
	user.fromWeb(deserializer);

	context.userData().userService().create(user);
	JSONObjectSerializer serializer;
	user.toWeb(serializer);
	const string &result = serializer.toString();

	context.response().sendBuffer(result.c_str(), result.size());
}

static void handleGetDevice(UIRouteContext &context)
{
	UIRoute::Params::const_iterator it;

	it = context.params().find("placeId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	Place place(PlaceID::parse(it->second));

	it = context.params().find("deviceId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	Device device(DeviceID::parse(it->second));
	if (!context.userData().deviceService().fetch(device)) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	JSONObjectSerializer serializer;
	device.toWeb(serializer);
	const string &result = serializer.toString();

	context.response().sendBuffer(result.c_str(), result.size());
}

namespace BeeeOn {

void factorySetup(UIServerRequestHandlerFactory &factory)
{
	factory.noRoute(handleNoRoute);
	factory.noOperation(handleNoOperation);
	factory.sessionVerifier(verifyAuthorized);
	factory.POST("/auth", handleAuth, false);
	factory.DELETE("/auth", handleAuth);
	factory.POST("/users", handleCreateUser);
	factory.GET("/users/:userId", handleGetUser);
	factory.GET("/:placeId/devices/:deviceId", handleGetDevice);
}

BEEEON_OBJECT(UIServerModule, BeeeOn::UIServerModule)

}
<commit_msg>UIServerModule: use class Auth to handle authorization<commit_after>#include <map>
#include <Poco/StreamCopier.h>
#include <Poco/Net/NetException.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>

#include "service/AuthService.h"
#include "server/Route.h"
#include "model/JSONSerializer.h"
#include "UIServerModule.h"
#include "Debug.h"

#include "Auth.h"

using namespace std;
using namespace Poco;
using namespace Poco::Net;
using namespace Poco::JSON;
using namespace Poco::Dynamic;
using namespace BeeeOn;

void handleNoRoute(UIRouteContext &context)
{
	TRACE_FUNC();

	context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
	context.response().setReason("Not found");
}

static void handleNoOperation(UIRouteContext &context)
{
	TRACE_FUNC();

	context.response().setStatus(UIResponse::HTTP_BAD_REQUEST);
	context.response().setReason("Bad request");
}

static void verifyAuthorized(const UIRequest &request,
		const UIRoute &route, UIServerModule &module)
{
	TRACE_FUNC();

	throw Poco::Net::NotAuthenticatedException("not implemented");
}

void handleAuth(UIRouteContext &context)
{
	TRACE_FUNC();

	if (context.request().hasCredentials()) {
		string scheme;
		string authInfo;
		context.request().getCredentials(scheme, authInfo);

		// TODO: user is already logged in? leave it
		// XXX: is this secure to just echo the value?
		context.response().sendBuffer(authInfo.c_str(), authInfo.size());
		return;
	}

	string jsonData;
	StreamCopier::copyToString(context.request().stream(), jsonData);

	Parser parser;
	parser.parse(jsonData);
	const Var result = parser.result();
	const Object::Ptr data = result.extract<Object::Ptr>();
	const Var provider = data->get("provider");
	const Var authCode = data->get("authCode");

	AuthCodeCredentials cred(provider.toString(), authCode.toString());
	const string &sessionId = context.userData()
					.authService().login(cred);

	context.response().sendBuffer(sessionId.c_str(), sessionId.size());
}

static void handleGetUser(UIRouteContext &context)
{
	UIRoute::Params::const_iterator it;

	it = context.params().find("userId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	User user(UserID::parse(it->second));
	if (!context.userData().userService().fetch(user)) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	JSONObjectSerializer serializer;
	user.toWeb(serializer);
	const string &userData = serializer.toString();

	context.response().sendBuffer(userData.c_str(), userData.size());
}

static void handleCreateUser(UIRouteContext &context)
{
	std::string userData;
	StreamCopier::copyToString(context.request().stream(), userData);
	JSONObjectSerializer deserializer;
	User user;
	user.fromWeb(deserializer);

	context.userData().userService().create(user);
	JSONObjectSerializer serializer;
	user.toWeb(serializer);
	const string &result = serializer.toString();

	context.response().sendBuffer(result.c_str(), result.size());
}

static void handleGetDevice(UIRouteContext &context)
{
	UIRoute::Params::const_iterator it;

	it = context.params().find("placeId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	Place place(PlaceID::parse(it->second));

	it = context.params().find("deviceId");
	if (it == context.params().end()) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	Device device(DeviceID::parse(it->second));
	if (!context.userData().deviceService().fetch(device)) {
		context.response().setStatus(UIResponse::HTTP_NOT_FOUND);
		context.response().setReason("Not found");
		return;
	}

	JSONObjectSerializer serializer;
	device.toWeb(serializer);
	const string &result = serializer.toString();

	context.response().sendBuffer(result.c_str(), result.size());
}

namespace BeeeOn {

void factorySetup(UIServerRequestHandlerFactory &factory)
{
	factory.noRoute(handleNoRoute);
	factory.noOperation(handleNoOperation);
	factory.sessionVerifier(verifyAuthorized);
	factory.POST("/auth", Auth::handlePost, false);
	factory.DELETE("/auth", Auth::handleDelete);
	factory.POST("/users", handleCreateUser);
	factory.GET("/users/:userId", handleGetUser);
	factory.GET("/:placeId/devices/:deviceId", handleGetDevice);
}

BEEEON_OBJECT(UIServerModule, BeeeOn::UIServerModule)

}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void RemoveUser(User *user) {
  for(auto channel : kChannels) {
    for(auto channel_user : channel.second->users) {
      if (channel_user->name == user->name) {
        channel.second->users.remove(channel_user);
        break;
      }
    }
  }
  for(auto current_user : kUsers) {
    if (current_user.second->name == user->name) {
      kUsers.erase(user->name);
    }
  }
}

/**
 * Logs in a user.
 *
 * @buffer is the login_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLoginRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request_login login_request;
  memcpy(&login_request, buffer, sizeof(struct request_login));

  User *current_user = new User(login_request.req_username, request_address, request_port);
  RemoveUser(current_user);
  kUsers.insert({std::string(login_request.req_username), current_user});

  std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
}

/**
 * Logs out a user.
 *
 * @buffer is the logout_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLogoutRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request_logout logout_request;
  memcpy(&logout_request, buffer, sizeof(struct request_logout));

  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      std::cout << "server: " << user.first << " logs out" << std::endl;
      kUsers.erase(user.first);
      break;
    }
  }
}

/**
 * Removes a user from the requested channel.
 *
 * @buffer is the logout_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLeaveRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  bool is_channel;
  struct request_leave leave_request;

  memcpy(&leave_request, buffer, sizeof(struct request_leave));
  current_channel = leave_request.req_channel;

  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      is_channel = false;
      for(auto ch : kChannels) {
        if (ch.first == current_channel) {
          std::cout << "channel found" << std::endl;
          is_channel = true;
          break;
        }
      }

      if (is_channel) {
        channel = kChannels[current_channel];

        for (it = channel->users.begin(); it != channel->users.end(); ++it) {
          if ((*it)->name == user.first) {
            break;
          }
        }

        if (it != channel->users.end()) {
          channel->users.remove(*it);
          std::cout << user.first << " leaves channel " << channel->name << std::endl;
          if (channel->users.size() == 0) {
            kChannels.erase(channel->name);
            std::cout << "server: removing empty channel " << channel->name << std::endl;
          }
        }
        for (auto u : channel->users) {
          std::cout << "user: " << u->name << std::endl;
        }
        break;
      } else {
        std::cout << "server: " << user.first << " trying to leave non-existent channel " << current_channel << std::endl;
      }

    }
  }
}

/**
 * Sends a text list packet containing every channel to the requesting user.
 *
 * @server_socket is the socket to send on.
 * @request_address is the address to send to.
 * @request_port is the port to send to.
 */
void HandleListRequest(int server_socket, in_addr_t request_address, unsigned short request_port) {
  struct sockaddr_in client_addr;
  const size_t list_size = sizeof(text_list) + (kChannels.size() * sizeof(channel_info));
  struct text_list *list = (text_list *) malloc(list_size);
  memset(list, '\0', list_size);;

  list->txt_type = TXT_LIST;
  list->txt_nchannels = (int) kChannels.size();

  // Fills the packet's channels array.
  int i = 0;
  for (auto ch : kChannels) {
    strncpy(list->txt_channels[i++].ch_channel, ch.first.c_str(), CHANNEL_MAX);
  }

  std::cout << "Channels" << std::endl;
  for (i = 0; i < list->txt_nchannels; i++) {
    std::cout << list->txt_channels[i].ch_channel << std::endl;
  }

  // Finds the requesting users address and port and sends the packet.
  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      memset(&client_addr, 0, sizeof(struct sockaddr_in));
      client_addr.sin_family = AF_INET;
      client_addr.sin_port = current_port;
      client_addr.sin_addr.s_addr = current_address;

      size_t message_size = sizeof(list);

      if (sendto(server_socket, &list, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
        Error("server: failed to send list\n");
      }

      std::cout << "server: " << user.first << " lists channels" << std::endl;
      break;
    }
  }

  free(list);
}

struct sockaddr_in* CreateSockAddr(unsigned short port, in_addr_t address) {
  struct sockaddr_in *client_addr;
  memset(&client_addr, 0, sizeof(struct sockaddr_in));
  client_addr->sin_family = AF_INET;
  client_addr->sin_port = port;
  client_addr->sin_addr.s_addr = address;
  return client_addr;
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
  Channel *channel;
//  std::string current_channel;
//  std::list<User *>::const_iterator it;
  bool is_new_channel;
//  bool is_channel;
  bool is_channel_user;

  memcpy(&current_request, buffer, sizeof(struct request));
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      HandleLoginRequest(buffer, request_address, request_port);
      break;
    case REQ_LOGOUT:
      HandleLogoutRequest(buffer, request_address, request_port);
      break;
    case REQ_LEAVE:
      HandleLeaveRequest(buffer, request_address, request_port);
      break;
    case REQ_JOIN:
      struct request_join join_request;
      memcpy(&join_request, buffer, sizeof(struct request_join));
      is_new_channel = true;

      // If channel does exists in global map, set local channel to channel from kChannels
      for (auto ch : kChannels) {
        if (join_request.req_channel == ch.second->name) {
          is_new_channel = false;
          channel = ch.second;
          break;
        }
      }

      // If channel is new create a new channel
      if (is_new_channel) {
        channel = new Channel(join_request.req_channel);
      }

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;
        if (current_port == request_port && current_address == request_address) {
          std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

          is_channel_user = false;
          for(auto u : channel->users) {
            if (u->name == user.second->name) {
              is_channel_user = true;
              break;
            }
          }

          if (!is_channel_user) {
            channel->users.push_back(user.second);
          }

          // Otherwise
          if (is_new_channel) {
            kChannels.insert({channel->name, channel});
          }
          break;
        }

      }
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          for(auto channel_user : kChannels[say_request.req_channel]->users) {
            struct sockaddr_in client_addr;
            struct text_say say;
            memcpy(&say, buffer, sizeof(struct text_say));

//        client_addr = CreateSockAddr(user->port, user->address);
            memset(&client_addr, 0, sizeof(struct sockaddr_in));
            client_addr.sin_family = AF_INET;
            client_addr.sin_port = channel_user->port;
            client_addr.sin_addr.s_addr = channel_user->address;

            // copy message into struct being sent
            strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
            strncpy(say.txt_text, say_request.req_text, SAY_MAX);
            say.txt_type = TXT_SAY;
            strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

            size_t message_size = sizeof(struct text_say);

            if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
              Error("server: failed to send say\n");
            }
          }
          std::cout << current_user->name << " sends say message in " << say_request.req_channel << std::endl;
          break;
        }
      }

      break;
    case REQ_LIST:
      HandleListRequest(server_socket, request_address, request_port);
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);

    if (receive_len > 0) {
      buffer[receive_len] = 0;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<commit_msg>move join to function<commit_after>#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <cstring>
#include <stdio.h>
#include <string>
#include <iostream>
#include <list>
#include <map>
#include <set>

#include "server.h"
#include "duckchat.h"

// TODO Handle domain
// Server can accept connections.
// Server handles Login and Logout from users, and keeps records of which users are logged in.
// Server handles Join and Leave from users, keeps records of which channels a user belongs to,
// and keeps records of which users are in a channel.
// TODO Server handles the Say message.
// TODO Server correctly handles List and Who.
// TODO Create copies of your client and server source. Modify them to send invalid packets to your good client
// and server, to see if you can make your client or server crash. Fix any bugs you find.

class Channel {
public:
  std::string name;
  std::list<User *> users;

  Channel(std::string name): name(name) {};
};

class User {
public:
  std::string name;
  in_addr_t address;
  unsigned short port;

  User(std::string name, in_addr_t address, unsigned short port): name(name), address(address), port(port) {};
};

std::map<std::string, User *> kUsers;
std::map<std::string, Channel *> kChannels;

void Error(const char *msg) {
  perror(msg);
  exit(1);
}

void RemoveUser(User *user) {
  for(auto channel : kChannels) {
    for(auto channel_user : channel.second->users) {
      if (channel_user->name == user->name) {
        channel.second->users.remove(channel_user);
        break;
      }
    }
  }
  for(auto current_user : kUsers) {
    if (current_user.second->name == user->name) {
      kUsers.erase(user->name);
    }
  }
}

/**
 * Logs in a user.
 *
 * @buffer is the login_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLoginRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request_login login_request;
  memcpy(&login_request, buffer, sizeof(struct request_login));

  User *current_user = new User(login_request.req_username, request_address, request_port);
  RemoveUser(current_user);
  kUsers.insert({std::string(login_request.req_username), current_user});

  std::cout << "server: " << login_request.req_username << " logs in" << std::endl;
}

/**
 * Logs out a user.
 *
 * @buffer is the logout_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLogoutRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request_logout logout_request;
  memcpy(&logout_request, buffer, sizeof(struct request_logout));

  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      std::cout << "server: " << user.first << " logs out" << std::endl;
      kUsers.erase(user.first);
      break;
    }
  }
}

/**
 * Removes a user from the requested channel.
 *
 * @buffer is the logout_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleLeaveRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  Channel *channel;
  std::string current_channel;
  std::list<User *>::const_iterator it;
  bool is_channel;
  struct request_leave leave_request;

  memcpy(&leave_request, buffer, sizeof(struct request_leave));
  current_channel = leave_request.req_channel;

  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      is_channel = false;
      for(auto ch : kChannels) {
        if (ch.first == current_channel) {
          std::cout << "channel found" << std::endl;
          is_channel = true;
          break;
        }
      }

      if (is_channel) {
        channel = kChannels[current_channel];

        for (it = channel->users.begin(); it != channel->users.end(); ++it) {
          if ((*it)->name == user.first) {
            break;
          }
        }

        if (it != channel->users.end()) {
          channel->users.remove(*it);
          std::cout << user.first << " leaves channel " << channel->name << std::endl;
          if (channel->users.size() == 0) {
            kChannels.erase(channel->name);
            std::cout << "server: removing empty channel " << channel->name << std::endl;
          }
        }
        for (auto u : channel->users) {
          std::cout << "user: " << u->name << std::endl;
        }
        break;
      } else {
        std::cout << "server: " << user.first << " trying to leave non-existent channel " << current_channel << std::endl;
      }

    }
  }
}

/**
 * Adds a user to the requested channel.
 *
 * @buffer is the logout_request
 * @request_address is the user's address.
 * @request_port is the user's port.
 */
void HandleJoinRequest(void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request_join join_request;
  memcpy(&join_request, buffer, sizeof(struct request_join));
  bool is_new_channel = true;
  bool is_channel_user;
  Channel *channel;

  // If channel does exists in global map, set local channel to channel from kChannels
  for (auto ch : kChannels) {
    if (join_request.req_channel == ch.second->name) {
      is_new_channel = false;
      channel = ch.second;
      break;
    }
  }

  // If channel is new create a new channel
  if (is_new_channel) {
    channel = new Channel(join_request.req_channel);
  }

  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;
    if (current_port == request_port && current_address == request_address) {
      std::cout << "server: " << user.first << " joins channel "<< channel->name << std::endl;

      is_channel_user = false;
      for(auto u : channel->users) {
        if (u->name == user.second->name) {
          is_channel_user = true;
          break;
        }
      }

      if (!is_channel_user) {
        channel->users.push_back(user.second);
      }

      // Otherwise
      if (is_new_channel) {
        kChannels.insert({channel->name, channel});
      }
      break;
    }
  }
}

/**
 * Sends a text list packet containing every channel to the requesting user.
 *
 * @server_socket is the socket to send on.
 * @request_address is the address to send to.
 * @request_port is the port to send to.
 */
void HandleListRequest(int server_socket, in_addr_t request_address, unsigned short request_port) {
  struct sockaddr_in client_addr;
  const size_t list_size = sizeof(text_list) + (kChannels.size() * sizeof(channel_info));
  struct text_list *list = (text_list *) malloc(list_size);
  memset(list, '\0', list_size);;

  list->txt_type = TXT_LIST;
  list->txt_nchannels = (int) kChannels.size();

  // Fills the packet's channels array.
  int i = 0;
  for (auto ch : kChannels) {
    strncpy(list->txt_channels[i++].ch_channel, ch.first.c_str(), CHANNEL_MAX);
  }

  std::cout << "Channels" << std::endl;
  for (i = 0; i < list->txt_nchannels; i++) {
    std::cout << list->txt_channels[i].ch_channel << std::endl;
  }

  // Finds the requesting users address and port and sends the packet.
  for (auto user : kUsers) {
    unsigned short current_port = user.second->port;
    in_addr_t current_address = user.second->address;

    if (current_port == request_port && current_address == request_address) {
      memset(&client_addr, 0, sizeof(struct sockaddr_in));
      client_addr.sin_family = AF_INET;
      client_addr.sin_port = current_port;
      client_addr.sin_addr.s_addr = current_address;

      size_t message_size = sizeof(list);

      if (sendto(server_socket, &list, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
        Error("server: failed to send list\n");
      }

      std::cout << "server: " << user.first << " lists channels" << std::endl;
      break;
    }
  }

  free(list);
}

struct sockaddr_in* CreateSockAddr(unsigned short port, in_addr_t address) {
  struct sockaddr_in *client_addr;
  memset(&client_addr, 0, sizeof(struct sockaddr_in));
  client_addr->sin_family = AF_INET;
  client_addr->sin_port = port;
  client_addr->sin_addr.s_addr = address;
  return client_addr;
}

void ProcessRequest(int server_socket, void *buffer, in_addr_t request_address, unsigned short request_port) {
  struct request current_request;
  User *current_user;
//  Channel *channel;
//  std::string current_channel;
//  std::list<User *>::const_iterator it;
//  bool is_new_channel;
//  bool is_channel;

  memcpy(&current_request, buffer, sizeof(struct request));
  request_t request_type = current_request.req_type;

  switch(request_type) {
    case REQ_LOGIN:
      HandleLoginRequest(buffer, request_address, request_port);
      break;
    case REQ_LOGOUT:
      HandleLogoutRequest(buffer, request_address, request_port);
      break;
    case REQ_LEAVE:
      HandleLeaveRequest(buffer, request_address, request_port);
      break;
    case REQ_JOIN:
      HandleJoinRequest();
      break;
    case REQ_SAY:
      struct request_say say_request;
      memcpy(&say_request, buffer, sizeof(struct request_say));

      for (auto user : kUsers) {
        unsigned short current_port = user.second->port;
        in_addr_t current_address = user.second->address;

        if (current_port == request_port && current_address == request_address) {
          current_user = user.second;
          for(auto channel_user : kChannels[say_request.req_channel]->users) {
            struct sockaddr_in client_addr;
            struct text_say say;
            memcpy(&say, buffer, sizeof(struct text_say));

//        client_addr = CreateSockAddr(user->port, user->address);
            memset(&client_addr, 0, sizeof(struct sockaddr_in));
            client_addr.sin_family = AF_INET;
            client_addr.sin_port = channel_user->port;
            client_addr.sin_addr.s_addr = channel_user->address;

            // copy message into struct being sent
            strncpy(say.txt_channel, say_request.req_channel, CHANNEL_MAX);
            strncpy(say.txt_text, say_request.req_text, SAY_MAX);
            say.txt_type = TXT_SAY;
            strncpy(say.txt_username, current_user->name.c_str(), USERNAME_MAX);

            size_t message_size = sizeof(struct text_say);

            if (sendto(server_socket, &say, message_size, 0, (struct sockaddr*) &client_addr, sizeof(client_addr)) < 0) {
              Error("server: failed to send say\n");
            }
          }
          std::cout << current_user->name << " sends say message in " << say_request.req_channel << std::endl;
          break;
        }
      }

      break;
    case REQ_LIST:
      HandleListRequest(server_socket, request_address, request_port);
      break;
    default:
      break;
  }
}

int main(int argc, char *argv[]) {
  struct sockaddr_in server_addr;

  int server_socket;
  int receive_len;
  void* buffer[kBufferSize];
  int port;
//  std::string domain;

  if (argc < 3) {
    std::cerr << "Usage: ./server domain_name port_num" << std::endl;
    exit(1);
  }

//  domain = argv[1];
  port = atoi(argv[2]);

  memset((char *) &server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(port);

  if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    Error("server: can't open socket\n");
  }

  if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
    Error("server: bind failed\n");
  }

  printf("server: waiting on port %d\n", port);
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    receive_len = recvfrom(server_socket, buffer, kBufferSize, 0, (struct sockaddr *) &client_addr, &client_addr_len);

    if (receive_len > 0) {
      buffer[receive_len] = 0;

      ProcessRequest(server_socket, buffer, client_addr.sin_addr.s_addr, client_addr.sin_port);
    }
  }
}
<|endoftext|>