<commit_before>/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.

   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; version 2 of the License.

   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 General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "handshake.h"

#include <mysql.h> // for MYSQL structure

/// Client-side context for authentication handshake

class Handshake_client: public Handshake
{
  /**
    Name of the server's service for which we authenticate.

    The service name is sent by server in the initial packet. If no
    service name is used, this member is @c NULL.
  */
  SEC_WCHAR  *m_service_name;

  /// Buffer for storing service name obtained from server.
  SEC_WCHAR   m_service_name_buf[MAX_SERVICE_NAME_LENGTH];

  Connection &m_con;

public:

  Handshake_client(Connection &con, const char *target, size_t len);
  ~Handshake_client();

  Blob  first_packet();
  Blob  process_data(const Blob&);

  Blob read_packet();
  int write_packet(Blob &data);
};

/**
  Create authentication handshake context for client.

  @param con     connection for communication with the peer
  @param target  name of the target service with which we will authenticate
                 (can be NULL if not used)

  Some security packages (like Kerberos) require providing explicit name
  of the service with which a client wants to authenticate. The server-side
  authentication plugin sends this name in the greeting packet
  (see @c win_auth_handshake_{server,client}() functions).
*/

Handshake_client::Handshake_client(Connection &con,
                                   const char *target, size_t len)
: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con)
{
  if (!target || 0 == len)
    return;

  // Convert received UPN to internal WCHAR representation.

  m_service_name= utf8_to_wchar(target, &len);

  if (m_service_name)
    DBUG_PRINT("info", ("Using target service: %S\n", m_service_name));
  else
  {
    /*
      Note: we ignore errors here - m_target will be NULL, the target name
      will not be used and system will fall-back to NTLM authentication. But
      we leave trace in error log.
    */
    ERROR_LOG(WARNING, ("Could not decode UPN sent by the server"
                        "; target service name will not be used"
                        " and Kerberos authentication will not work"));
  }
}

Handshake_client::~Handshake_client()
{
  if (m_service_name)
    free(m_service_name);
}

Blob Handshake_client::read_packet()
{
  /*
    We do a fake read in the first round because first
    packet from the server containing UPN must be read
    before the handshake context is created and the packet
    processing loop starts. We return an empty blob here
    and process_data() function will ignore it.
  */
  if (m_round == 1)
    return Blob();

  // Otherwise we read packet from the connection.

  Blob packet= m_con.read();
  m_error= m_con.error();
  if (!m_error && packet.is_null())
    m_error= true;  // (no specific error code assigned)

  if (m_error)
    return Blob();

  DBUG_PRINT("dump", ("Got the following bytes"));
  DBUG_DUMP("dump", packet.ptr(), packet.len());
  return packet;
}

int Handshake_client::write_packet(Blob &data)
{
  /*
   Length of the first data payload send by client authentication plugin is
   limited to 255 bytes (because it is wrapped inside client authentication
   packet and is length-encoded with 1 byte for the length).

   If the data payload is longer than 254 bytes, then it is sent in two parts:
   first part of length 255 will be embedded in the authentication packet,
   second part will be sent in the following packet. Byte 255 of the first
   part contains information about the total length of the payload. It is a
   number of blocks of size 512 bytes which is sufficient to store the
   combined packets.

   Server's logic for reading first client's payload is as follows
   (see Handshake_server::read_packet()):
   1. Read data from the authentication packet, if it is shorter than 255 bytes
      then that is all data sent by client.
   2. If there is 255 bytes of data in the authentication packet, read another
      packet and append it to the data, skipping byte 255 of the first packet
      which can be used to allocate buffer of appropriate size.
  */

  size_t len2= 0;   // length of the second part of first data payload
  byte saved_byte;  // for saving byte 255 in which data length is stored

  if (m_round == 1 && data.len() > 254)
  {
    len2= data.len() - 254;
    DBUG_PRINT("info", ("Splitting first packet of length %lu"
                        ", %lu bytes will be sent in a second part",
                        data.len(), len2));
    /*
      Store in byte 255 the number of 512b blocks that are needed to
      keep all the data.
    */
    unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0);
    DBUG_ASSERT(block_count < (unsigned)0x100);
    saved_byte= data[254];
    data[254] = block_count;

    data.trim(255);
  }

  DBUG_PRINT("dump", ("Sending the following data"));
  DBUG_DUMP("dump", data.ptr(), data.len());
  int ret= m_con.write(data);

  if (ret)
    return ret;

  // Write second part if it is present.
  if (len2)
  {
    data[254]= saved_byte;
    Blob data2(data.ptr() + 254, len2);
    DBUG_PRINT("info", ("Sending second part of data"));
    DBUG_DUMP("info", data2.ptr(), data2.len());
    ret= m_con.write(data2);
  }

  return ret;
}

/**
  Process data sent by server.

  @param[in]  data  blob with data from server

  This method analyses data sent by server during authentication handshake.
  If client should continue packet exchange, this method returns data to
  be sent to the server next. If no more data needs to be exchanged, an
  empty blob is returned and @c is_complete() is @c true. In case of error
  an empty blob is returned and @c error() gives non-zero error code.

  When invoked for the first time (in the first round of the handshake)
  there is no data from the server (data blob is null) and the intial
  packet is generated without an input.

  @return Data to be sent to the server next or null blob if no more data
  needs to be exchanged or in case of error.
*/

Blob Handshake_client::process_data(const Blob &data)
{
#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
  /*
    Code for testing the logic for sending the first client payload.

    A fake data of length given by environment variable TEST_PACKET_LENGTH
    (or default 255 bytes) is sent to the server. First 2 bytes of the
    payload contain its total length (LSB first). The length of test data
    is limited to 2048 bytes.

    Upon receiving test data, server will check that data is correct and
    refuse connection. If server detects data errors it will crash on
    assertion.

    This code is executed if debug flag "winauth_first_packet_test" is
    set, e.g. using client option:

     --debug="d,winauth_first_packet_test"

     The same debug flag must be enabled in the server, e.g. using
     statement:

     SET GLOBAL debug= '+d,winauth_first_packet_test';
  */

  static byte test_buf[2048];

  if (m_round == 1
      && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false))
  {
    const char *env= getenv("TEST_PACKET_LENGTH");
    size_t len= env ? atoi(env) : 0;
    if (!len)
      len= 255;
    if (len > sizeof(test_buf))
      len= sizeof(test_buf);

    // Store data length in first 2 bytes.
    byte *ptr= test_buf;
    *ptr++= len & 0xFF;
    *ptr++= len >> 8;

    // Fill remaining bytes with known values.
    for (byte b= 0; ptr < test_buf + len; ++ptr, ++b)
      *ptr= b;

    return Blob(test_buf, len);
  };

#endif

  Security_buffer  input(data);
  SECURITY_STATUS  ret;

  m_output.free();

  ret= InitializeSecurityContextW(
         &m_cred,
         m_round == 1 ? NULL : &m_sctx,        // partial context
         m_service_name,                       // service name
         ASC_REQ_ALLOCATE_MEMORY,              // requested attributes
         0,                                    // reserved
         SECURITY_NETWORK_DREP,                // data representation
         m_round == 1 ? NULL : &input,         // input data
         0,                                    // reserved
         &m_sctx,                              // context
         &m_output,                            // output data
         &m_atts,                              // attributes
         &m_expire);                           // expire date

  if (process_result(ret))
  {
    DBUG_PRINT("error",
               ("InitializeSecurityContext() failed with error %X", ret));
    return Blob();
  }

  return m_output.as_blob();
}

/**********************************************************************/

/**
  Perform authentication handshake from client side.

  @param[in]  vio    pointer to @c MYSQL_PLUGIN_VIO instance to be used
                     for communication with the server
  @param[in]  mysql  pointer to a MySQL connection for which we authenticate

  After reading the initial packet from server, containing its UPN to be
  used as service name, client starts packet exchange by sending the first
  packet in this exchange. While handshake is not yet completed, client
  reads packets sent by the server and process them, possibly generating new
  data to be sent to the server.

  This function reports errors.

  @return 0 on success.
*/

int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
  DBUG_ENTER("win_auth_handshake_client");

  /*
    Check if we should enable logging.
  */
  {
    const char *opt= getenv("AUTHENTICATION_WIN_LOG");
    int opt_val= opt ? atoi(opt) : 0;
    if (opt && !opt_val)
    {
      if (!strncasecmp("on", opt, 2))    opt_val= 2;
      if (!strncasecmp("yes", opt, 3))   opt_val= 2;
      if (!strncasecmp("true", opt, 4))  opt_val= 2;
      if (!strncasecmp("debug", opt, 5)) opt_val= 4;
      if (!strncasecmp("dbug", opt, 4))  opt_val= 4;
    }
    set_log_level(opt_val);
  }

  ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user));

  // Create connection object.

  Connection con(vio);
  DBUG_ASSERT(!con.error());

  // Read initial packet from server containing service name.

  Blob service_name= con.read();

  if (con.error() || service_name.is_null())
  {
    ERROR_LOG(ERROR, ("Error reading initial packet"));
    DBUG_RETURN(CR_ERROR);
  }
  DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len()));

  // Create authentication handshake context using the given service name.

  Handshake_client hndshk(con,
                          service_name[0] ? (char *)service_name.ptr() : NULL,
                          service_name.len());
  if (hndshk.error())
  {
    ERROR_LOG(ERROR, ("Could not create authentication handshake context"));
    DBUG_RETURN(CR_ERROR);
  }

  DBUG_ASSERT(!hndshk.error());

  /*
    Read and process packets from server until handshake is complete.
    Note that the first read from server is dummy
    (see Handshake_client::read_packet()) as we already have read the
    first packet to establish service name.
  */
  if (hndshk.packet_processing_loop())
    DBUG_RETURN(CR_ERROR);

  DBUG_ASSERT(!hndshk.error() && hndshk.is_complete());

  DBUG_RETURN(CR_OK);
}
<commit_msg>Bug#12982926  CLIENT CAN OVERRIDE ZERO-LENGTH-ALLOCATE BUFFER<commit_after>/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.

   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; version 2 of the License.

   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 General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "handshake.h"

#include <mysql.h> // for MYSQL structure

/// Client-side context for authentication handshake

class Handshake_client: public Handshake
{
  /**
    Name of the server's service for which we authenticate.

    The service name is sent by server in the initial packet. If no
    service name is used, this member is @c NULL.
  */
  SEC_WCHAR  *m_service_name;

  /// Buffer for storing service name obtained from server.
  SEC_WCHAR   m_service_name_buf[MAX_SERVICE_NAME_LENGTH];

  Connection &m_con;

public:

  Handshake_client(Connection &con, const char *target, size_t len);
  ~Handshake_client();

  Blob  first_packet();
  Blob  process_data(const Blob&);

  Blob read_packet();
  int write_packet(Blob &data);
};

/**
  Create authentication handshake context for client.

  @param con     connection for communication with the peer
  @param target  name of the target service with which we will authenticate
                 (can be NULL if not used)

  Some security packages (like Kerberos) require providing explicit name
  of the service with which a client wants to authenticate. The server-side
  authentication plugin sends this name in the greeting packet
  (see @c win_auth_handshake_{server,client}() functions).
*/

Handshake_client::Handshake_client(Connection &con,
                                   const char *target, size_t len)
: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con)
{
  if (!target || 0 == len)
    return;

  // Convert received UPN to internal WCHAR representation.

  m_service_name= utf8_to_wchar(target, &len);

  if (m_service_name)
    DBUG_PRINT("info", ("Using target service: %S\n", m_service_name));
  else
  {
    /*
      Note: we ignore errors here - m_target will be NULL, the target name
      will not be used and system will fall-back to NTLM authentication. But
      we leave trace in error log.
    */
    ERROR_LOG(WARNING, ("Could not decode UPN sent by the server"
                        "; target service name will not be used"
                        " and Kerberos authentication will not work"));
  }
}

Handshake_client::~Handshake_client()
{
  if (m_service_name)
    free(m_service_name);
}

Blob Handshake_client::read_packet()
{
  /*
    We do a fake read in the first round because first
    packet from the server containing UPN must be read
    before the handshake context is created and the packet
    processing loop starts. We return an empty blob here
    and process_data() function will ignore it.
  */
  if (m_round == 1)
    return Blob();

  // Otherwise we read packet from the connection.

  Blob packet= m_con.read();
  m_error= m_con.error();
  if (!m_error && packet.is_null())
    m_error= true;  // (no specific error code assigned)

  if (m_error)
    return Blob();

  DBUG_PRINT("dump", ("Got the following bytes"));
  DBUG_DUMP("dump", packet.ptr(), packet.len());
  return packet;
}

int Handshake_client::write_packet(Blob &data)
{
  /*
   Length of the first data payload send by client authentication plugin is
   limited to 255 bytes (because it is wrapped inside client authentication
   packet and is length-encoded with 1 byte for the length).

   If the data payload is longer than 254 bytes, then it is sent in two parts:
   first part of length 255 will be embedded in the authentication packet,
   second part will be sent in the following packet. Byte 255 of the first
   part contains information about the total length of the payload. It is a
   number of blocks of size 512 bytes which is sufficient to store the
   combined packets.

   Server's logic for reading first client's payload is as follows
   (see Handshake_server::read_packet()):
   1. Read data from the authentication packet, if it is shorter than 255 bytes
      then that is all data sent by client.
   2. If there is 255 bytes of data in the authentication packet, read another
      packet and append it to the data, skipping byte 255 of the first packet
      which can be used to allocate buffer of appropriate size.
  */

  size_t len2= 0;   // length of the second part of first data payload
  byte saved_byte;  // for saving byte 255 in which data length is stored

  if (m_round == 1 && data.len() > 254)
  {
    len2= data.len() - 254;
    DBUG_PRINT("info", ("Splitting first packet of length %lu"
                        ", %lu bytes will be sent in a second part",
                        data.len(), len2));
    /*
      Store in byte 255 the number of 512b blocks that are needed to
      keep all the data.
    */
    unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0);

#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)

    /*
      For testing purposes, use wrong block count to see how server
      handles this.
    */
    DBUG_EXECUTE_IF("winauth_first_packet_test",{
      block_count= data.len() == 601 ? 0 :
                   data.len() == 602 ? 1 :
                   block_count;
    });

#endif

    DBUG_ASSERT(block_count < (unsigned)0x100);
    saved_byte= data[254];
    data[254] = block_count;

    data.trim(255);
  }

  DBUG_PRINT("dump", ("Sending the following data"));
  DBUG_DUMP("dump", data.ptr(), data.len());
  int ret= m_con.write(data);

  if (ret)
    return ret;

  // Write second part if it is present.
  if (len2)
  {
    data[254]= saved_byte;
    Blob data2(data.ptr() + 254, len2);
    DBUG_PRINT("info", ("Sending second part of data"));
    DBUG_DUMP("info", data2.ptr(), data2.len());
    ret= m_con.write(data2);
  }

  return ret;
}

/**
  Process data sent by server.

  @param[in]  data  blob with data from server

  This method analyses data sent by server during authentication handshake.
  If client should continue packet exchange, this method returns data to
  be sent to the server next. If no more data needs to be exchanged, an
  empty blob is returned and @c is_complete() is @c true. In case of error
  an empty blob is returned and @c error() gives non-zero error code.

  When invoked for the first time (in the first round of the handshake)
  there is no data from the server (data blob is null) and the intial
  packet is generated without an input.

  @return Data to be sent to the server next or null blob if no more data
  needs to be exchanged or in case of error.
*/

Blob Handshake_client::process_data(const Blob &data)
{
#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
  /*
    Code for testing the logic for sending the first client payload.

    A fake data of length given by environment variable TEST_PACKET_LENGTH
    (or default 255 bytes) is sent to the server. First 2 bytes of the
    payload contain its total length (LSB first). The length of test data
    is limited to 2048 bytes.

    Upon receiving test data, server will check that data is correct and
    refuse connection. If server detects data errors it will crash on
    assertion.

    This code is executed if debug flag "winauth_first_packet_test" is
    set, e.g. using client option:

     --debug="d,winauth_first_packet_test"

     The same debug flag must be enabled in the server, e.g. using
     statement:

     SET GLOBAL debug= '+d,winauth_first_packet_test';
  */

  static byte test_buf[2048];

  if (m_round == 1
      && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false))
  {
    const char *env= getenv("TEST_PACKET_LENGTH");
    size_t len= env ? atoi(env) : 0;
    if (!len)
      len= 255;
    if (len > sizeof(test_buf))
      len= sizeof(test_buf);

    // Store data length in first 2 bytes.
    byte *ptr= test_buf;
    *ptr++= len & 0xFF;
    *ptr++= len >> 8;

    // Fill remaining bytes with known values.
    for (byte b= 0; ptr < test_buf + len; ++ptr, ++b)
      *ptr= b;

    return Blob(test_buf, len);
  };

#endif

  Security_buffer  input(data);
  SECURITY_STATUS  ret;

  m_output.free();

  ret= InitializeSecurityContextW(
         &m_cred,
         m_round == 1 ? NULL : &m_sctx,        // partial context
         m_service_name,                       // service name
         ASC_REQ_ALLOCATE_MEMORY,              // requested attributes
         0,                                    // reserved
         SECURITY_NETWORK_DREP,                // data representation
         m_round == 1 ? NULL : &input,         // input data
         0,                                    // reserved
         &m_sctx,                              // context
         &m_output,                            // output data
         &m_atts,                              // attributes
         &m_expire);                           // expire date

  if (process_result(ret))
  {
    DBUG_PRINT("error",
               ("InitializeSecurityContext() failed with error %X", ret));
    return Blob();
  }

  return m_output.as_blob();
}

/**********************************************************************/

/**
  Perform authentication handshake from client side.

  @param[in]  vio    pointer to @c MYSQL_PLUGIN_VIO instance to be used
                     for communication with the server
  @param[in]  mysql  pointer to a MySQL connection for which we authenticate

  After reading the initial packet from server, containing its UPN to be
  used as service name, client starts packet exchange by sending the first
  packet in this exchange. While handshake is not yet completed, client
  reads packets sent by the server and process them, possibly generating new
  data to be sent to the server.

  This function reports errors.

  @return 0 on success.
*/

int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
  DBUG_ENTER("win_auth_handshake_client");

  /*
    Check if we should enable logging.
  */
  {
    const char *opt= getenv("AUTHENTICATION_WIN_LOG");
    int opt_val= opt ? atoi(opt) : 0;
    if (opt && !opt_val)
    {
      if (!strncasecmp("on", opt, 2))    opt_val= 2;
      if (!strncasecmp("yes", opt, 3))   opt_val= 2;
      if (!strncasecmp("true", opt, 4))  opt_val= 2;
      if (!strncasecmp("debug", opt, 5)) opt_val= 4;
      if (!strncasecmp("dbug", opt, 4))  opt_val= 4;
    }
    set_log_level(opt_val);
  }

  ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user));

  // Create connection object.

  Connection con(vio);
  DBUG_ASSERT(!con.error());

  // Read initial packet from server containing service name.

  Blob service_name= con.read();

  if (con.error() || service_name.is_null())
  {
    ERROR_LOG(ERROR, ("Error reading initial packet"));
    DBUG_RETURN(CR_ERROR);
  }
  DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len()));

  // Create authentication handshake context using the given service name.

  Handshake_client hndshk(con,
                          service_name[0] ? (char *)service_name.ptr() : NULL,
                          service_name.len());
  if (hndshk.error())
  {
    ERROR_LOG(ERROR, ("Could not create authentication handshake context"));
    DBUG_RETURN(CR_ERROR);
  }

  DBUG_ASSERT(!hndshk.error());

  /*
    Read and process packets from server until handshake is complete.
    Note that the first read from server is dummy
    (see Handshake_client::read_packet()) as we already have read the
    first packet to establish service name.
  */
  if (hndshk.packet_processing_loop())
    DBUG_RETURN(CR_ERROR);

  DBUG_ASSERT(!hndshk.error() && hndshk.is_complete());

  DBUG_RETURN(CR_OK);
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** This file is a modified version of part of an example program for Qt.
** This file may be used, distributed and modified without limitation.
**
** Don Sanders <sanders@kde.org>
**
*****************************************************************************/

#include "smtp.h"

#include <qtextstream.h>
#include <qsocket.h>
#include <qtimer.h>
#include <kapp.h>
#include <kmessagebox.h>
#include <klocale.h>

Smtp::Smtp( const QString &from, const QStringList &to,
	    const QString &aMessage,
	    const QString &server,
	    unsigned short int port )
{
    skipReadResponse = false;
    mSocket = new QSocket( this );
    connect ( mSocket, SIGNAL( readyRead() ),
	      this, SLOT( readyRead() ) );
    connect ( mSocket, SIGNAL( connected() ),
	      this, SLOT( connected() ) );
    connect ( mSocket, SIGNAL( error(int) ),
	      this, SLOT( socketError(int) ) );

    message = aMessage;

    this->from = from;
    rcpt = to;
    state = smtpInit;
    command = "";

    emit status( i18n( "Connecting to %1" ).arg( server ) );

    mSocket->connectToHost( server, port );
    t = new QTextStream( mSocket );
}

Smtp::~Smtp()
{
    if (t)
	delete t;
    if (mSocket)
	delete mSocket;
}

void Smtp::send( const QString &from, const QStringList &to,
	    const QString &aMessage )
{
    skipReadResponse = true;
    message = aMessage;
    this->from = from;
    rcpt = to;

    state = smtpMail;
    command = "";
    readyRead();
}

void Smtp::quit()
{
    skipReadResponse = true;
    state = smtpQuit;
    command = "";
    readyRead();
}

void Smtp::connected()
{
    emit status( i18n( "Connected to %1" ).arg( mSocket->peerName() ) );
}

void Smtp::socketError(int errorCode)
{
    command = "CONNECT";
    switch ( errorCode ) {
        case QSocket::ErrConnectionRefused:
	    responseLine = i18n( "Connection refused." );
	    break;
        case QSocket::ErrHostNotFound:
	    responseLine = i18n( "Host Not Found." );
	    break;
        case QSocket::ErrSocketRead:
	    responseLine = i18n( "Error reading socket." );
	    break;
        default:
	    responseLine = i18n( "Internal error, unrecognized error." );
    }
    QTimer::singleShot( 0, this, SLOT(emitError()) );
}

void Smtp::emitError() {
    error( command, responseLine );
}

void Smtp::readyRead()
{
    if (!skipReadResponse) {
	// SMTP is line-oriented
	if ( !mSocket->canReadLine() )
	    return;

	do {
	    responseLine = mSocket->readLine();
	    response += responseLine;
	} while( mSocket->canReadLine() && responseLine[3] != ' ' );
	responseLine.truncate( 3 );
    }
    skipReadResponse = false;

    if ( state == smtpInit && responseLine[0] == '2' ) {
	// banner was okay, let's go on
	command = "HELO there";
	*t << "HELO there\r\n";
	state = smtpMail;
    } else if ( state == smtpMail && responseLine[0] == '2' ) {
	// HELO response was okay (well, it has to be)
	command = "MAIL";
	*t << "MAIL FROM: <" << from << ">\r\n";
	state = smtpRcpt;
    } else if ( state == smtpRcpt && responseLine[0] == '2' && (rcpt.begin() != rcpt.end())) {
	command = "RCPT";
	*t << "RCPT TO: <" << *(rcpt.begin()) << ">\r\n";
	rcpt.remove( rcpt.begin() );
	if (rcpt.begin() == rcpt.end())
	    state = smtpData;
    } else if ( state == smtpData && responseLine[0] == '2' ) {
	command = "DATA";
	*t << "DATA\r\n";
	state = smtpBody;
    } else if ( state == smtpBody && responseLine[0] == '3' ) {
	command = "DATA";
	QString seperator = "";
	if (message[message.length() - 1] != '\n')
	    seperator = "\n";
	*t << message << seperator << ".\r\n";
	state = smtpSuccess;
    } else if ( state == smtpSuccess && responseLine[0] == '2' ) {
	QTimer::singleShot( 0, this, SIGNAL(success()) );
    } else if ( state == smtpQuit && responseLine[0] == '2' ) {
	command = "QUIT";
	*t << "QUIT\r\n";
	// here, we just close.
	state = smtpClose;
	emit status( tr( "Message sent" ) );
    } else if ( state == smtpClose ) {
	// we ignore it
    } else { // error occurred
	QTimer::singleShot( 0, this, SLOT(emitError()) );
	state = smtpClose;
    }

    response = "";

    if ( state == smtpClose ) {
	delete t;
	t = 0;
	delete mSocket;
	mSocket = 0;
	QTimer::singleShot( 0, this, SLOT(deleteMe()) );
    }
}

void Smtp::deleteMe()
{
    delete this;
}

<commit_msg>Fixed <CR><LF>.<CR><LF> issue which make qMail unhappy<commit_after>/****************************************************************************
**
** This file is a modified version of part of an example program for Qt.
** This file may be used, distributed and modified without limitation.
**
** Don Sanders <sanders@kde.org>
**
*****************************************************************************/

#include "smtp.h"

#include <qtextstream.h>
#include <qsocket.h>
#include <qtimer.h>
#include <kapp.h>
#include <kmessagebox.h>
#include <klocale.h>

Smtp::Smtp( const QString &from, const QStringList &to,
	    const QString &aMessage,
	    const QString &server,
	    unsigned short int port )
{
    skipReadResponse = false;
    mSocket = new QSocket( this );
    connect ( mSocket, SIGNAL( readyRead() ),
	      this, SLOT( readyRead() ) );
    connect ( mSocket, SIGNAL( connected() ),
	      this, SLOT( connected() ) );
    connect ( mSocket, SIGNAL( error(int) ),
	      this, SLOT( socketError(int) ) );

    message = aMessage;

    this->from = from;
    rcpt = to;
    state = smtpInit;
    command = "";

    emit status( i18n( "Connecting to %1" ).arg( server ) );

    mSocket->connectToHost( server, port );
    t = new QTextStream( mSocket );
}

Smtp::~Smtp()
{
    if (t)
	delete t;
    if (mSocket)
	delete mSocket;
}

void Smtp::send( const QString &from, const QStringList &to,
	    const QString &aMessage )
{
    skipReadResponse = true;
    message = aMessage;
    this->from = from;
    rcpt = to;

    state = smtpMail;
    command = "";
    readyRead();
}

void Smtp::quit()
{
    skipReadResponse = true;
    state = smtpQuit;
    command = "";
    readyRead();
}

void Smtp::connected()
{
    emit status( i18n( "Connected to %1" ).arg( mSocket->peerName() ) );
}

void Smtp::socketError(int errorCode)
{
    command = "CONNECT";
    switch ( errorCode ) {
        case QSocket::ErrConnectionRefused:
	    responseLine = i18n( "Connection refused." );
	    break;
        case QSocket::ErrHostNotFound:
	    responseLine = i18n( "Host Not Found." );
	    break;
        case QSocket::ErrSocketRead:
	    responseLine = i18n( "Error reading socket." );
	    break;
        default:
	    responseLine = i18n( "Internal error, unrecognized error." );
    }
    QTimer::singleShot( 0, this, SLOT(emitError()) );
}

void Smtp::emitError() {
    error( command, responseLine );
}

void Smtp::readyRead()
{
    if (!skipReadResponse) {
	// SMTP is line-oriented
	if ( !mSocket->canReadLine() )
	    return;

	do {
	    responseLine = mSocket->readLine();
	    response += responseLine;
	} while( mSocket->canReadLine() && responseLine[3] != ' ' );
	responseLine.truncate( 3 );
    }
    skipReadResponse = false;

    if ( state == smtpInit && responseLine[0] == '2' ) {
	// banner was okay, let's go on
	command = "HELO there";
	*t << "HELO there\r\n";
	state = smtpMail;
    } else if ( state == smtpMail && responseLine[0] == '2' ) {
	// HELO response was okay (well, it has to be)
	command = "MAIL";
	*t << "MAIL FROM: <" << from << ">\r\n";
	state = smtpRcpt;
    } else if ( state == smtpRcpt && responseLine[0] == '2' && (rcpt.begin() != rcpt.end())) {
	command = "RCPT";
	*t << "RCPT TO: <" << *(rcpt.begin()) << ">\r\n";
	rcpt.remove( rcpt.begin() );
	if (rcpt.begin() == rcpt.end())
	    state = smtpData;
    } else if ( state == smtpData && responseLine[0] == '2' ) {
	command = "DATA";
	*t << "DATA\r\n";
	state = smtpBody;
    } else if ( state == smtpBody && responseLine[0] == '3' ) {
	command = "DATA";
	QString seperator = "";
	if (message[message.length() - 1] != '\n')
	    seperator = "\r\n";
	*t << message << seperator << ".\r\n";
	state = smtpSuccess;
    } else if ( state == smtpSuccess && responseLine[0] == '2' ) {
	QTimer::singleShot( 0, this, SIGNAL(success()) );
    } else if ( state == smtpQuit && responseLine[0] == '2' ) {
	command = "QUIT";
	*t << "QUIT\r\n";
	// here, we just close.
	state = smtpClose;
	emit status( tr( "Message sent" ) );
    } else if ( state == smtpClose ) {
	// we ignore it
    } else { // error occurred
	QTimer::singleShot( 0, this, SLOT(emitError()) );
	state = smtpClose;
    }

    response = "";

    if ( state == smtpClose ) {
	delete t;
	t = 0;
	delete mSocket;
	mSocket = 0;
	QTimer::singleShot( 0, this, SLOT(deleteMe()) );
    }
}

void Smtp::deleteMe()
{
    delete this;
}

<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/external_registry_extension_provider_win.h"

#include "base/file_path.h"
#include "base/registry.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/version.h"

// The Registry hive where to look for external extensions.
const HKEY kRegRoot = HKEY_LOCAL_MACHINE;

// The Registry subkey that contains information about external extensions.
const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";

// Registry value of of that key that defines the path to the .crx file.
const wchar_t kRegistryExtensionPath[] = L"path";

// Registry value of that key that defines the current version of the .crx file.
const wchar_t kRegistryExtensionVersion[] = L"version";

ExternalRegistryExtensionProvider::ExternalRegistryExtensionProvider() {
}

ExternalRegistryExtensionProvider::~ExternalRegistryExtensionProvider() {
}

void ExternalRegistryExtensionProvider::VisitRegisteredExtension(
    Visitor* visitor, const std::set<std::string>& ids_to_ignore) const {
  RegistryKeyIterator iterator(kRegRoot,
                               ASCIIToWide(kRegistryExtensions).c_str());
  while (iterator.Valid()) {
    RegKey key;
    std::wstring key_path = ASCIIToWide(kRegistryExtensions);
    key_path.append(L"\\");
    key_path.append(iterator.Name());
    if (key.Open(kRegRoot, key_path.c_str(), KEY_READ)) {
      std::wstring extension_path;
      if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
        std::wstring extension_version;
        if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
          std::string id = WideToASCII(iterator.Name());
          StringToLowerASCII(&id);
          if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
            ++iterator;
            continue;
          }

          scoped_ptr<Version> version;
          version.reset(Version::GetVersionFromString(extension_version));
          FilePath path = FilePath::FromWStringHack(extension_path);
          visitor->OnExternalExtensionFileFound(id, version.get(), path,
                                                Extension::EXTERNAL_REGISTRY);
        } else {
          // TODO(erikkay): find a way to get this into about:extensions
          LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
                          " for key " << key_path;
        }
      } else {
        // TODO(erikkay): find a way to get this into about:extensions
        LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
                        " for key " << key_path;
      }
    }
    ++iterator;
  }
}

Version* ExternalRegistryExtensionProvider::RegisteredVersion(
    const std::string& id,
    Extension::Location* location) const  {
  RegKey key;
  std::wstring key_path = ASCIIToWide(kRegistryExtensions);
  key_path.append(L"\\");
  key_path.append(ASCIIToWide(id));

  if (!key.Open(kRegRoot, key_path.c_str(), KEY_READ))
    return NULL;

  std::wstring extension_version;
  if (!key.ReadValue(kRegistryExtensionVersion, &extension_version))
    return NULL;

  if (location)
    *location = Extension::EXTERNAL_REGISTRY;
  return Version::GetVersionFromString(extension_version);
}
<commit_msg>In windows registry external extensions provider, error out on invalid version.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/external_registry_extension_provider_win.h"

#include "base/file_path.h"
#include "base/registry.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/version.h"

// The Registry hive where to look for external extensions.
const HKEY kRegRoot = HKEY_LOCAL_MACHINE;

// The Registry subkey that contains information about external extensions.
const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";

// Registry value of of that key that defines the path to the .crx file.
const wchar_t kRegistryExtensionPath[] = L"path";

// Registry value of that key that defines the current version of the .crx file.
const wchar_t kRegistryExtensionVersion[] = L"version";

ExternalRegistryExtensionProvider::ExternalRegistryExtensionProvider() {
}

ExternalRegistryExtensionProvider::~ExternalRegistryExtensionProvider() {
}

void ExternalRegistryExtensionProvider::VisitRegisteredExtension(
    Visitor* visitor, const std::set<std::string>& ids_to_ignore) const {
  RegistryKeyIterator iterator(kRegRoot,
                               ASCIIToWide(kRegistryExtensions).c_str());
  while (iterator.Valid()) {
    RegKey key;
    std::wstring key_path = ASCIIToWide(kRegistryExtensions);
    key_path.append(L"\\");
    key_path.append(iterator.Name());
    if (key.Open(kRegRoot, key_path.c_str(), KEY_READ)) {
      std::wstring extension_path;
      if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
        std::wstring extension_version;
        if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
          std::string id = WideToASCII(iterator.Name());
          StringToLowerASCII(&id);
          if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
            ++iterator;
            continue;
          }

          scoped_ptr<Version> version;
          version.reset(Version::GetVersionFromString(extension_version));
          if (!version.get()) {
            LOG(ERROR) << "Invalid version value " << extension_version
                       << " for key " << key_path;
            continue;
          }

          FilePath path = FilePath::FromWStringHack(extension_path);
          visitor->OnExternalExtensionFileFound(id, version.get(), path,
                                                Extension::EXTERNAL_REGISTRY);
        } else {
          // TODO(erikkay): find a way to get this into about:extensions
          LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
                     << " for key " << key_path;
        }
      } else {
        // TODO(erikkay): find a way to get this into about:extensions
        LOG(ERROR) << "Missing value " << kRegistryExtensionPath
                   << " for key " << key_path;
      }
    }
    ++iterator;
  }
}

Version* ExternalRegistryExtensionProvider::RegisteredVersion(
    const std::string& id,
    Extension::Location* location) const  {
  RegKey key;
  std::wstring key_path = ASCIIToWide(kRegistryExtensions);
  key_path.append(L"\\");
  key_path.append(ASCIIToWide(id));

  if (!key.Open(kRegRoot, key_path.c_str(), KEY_READ))
    return NULL;

  std::wstring extension_version;
  if (!key.ReadValue(kRegistryExtensionVersion, &extension_version))
    return NULL;

  if (location)
    *location = Extension::EXTERNAL_REGISTRY;
  return Version::GetVersionFromString(extension_version);
}
<|endoftext|>


<commit_before>#ifndef ALEPH_PERSISTENT_HOMOLOGY_CONNECTED_COMPONENTS_HH__
#define ALEPH_PERSISTENT_HOMOLOGY_CONNECTED_COMPONENTS_HH__

#include "persistenceDiagrams/PersistenceDiagram.hh"

#include "topology/SimplicialComplex.hh"
#include "topology/UnionFind.hh"

#include <algorithm>
#include <vector>

namespace aleph
{

/**
  Calculates zero-dimensional persistent homology, i.e. tracking of connected
  components, for a given simplicial complex. This is highly-efficient, as it
  only requires a suitable 'Union--Find' data structure.

  As usual, the function assumes that the simplicial complex is in filtration
  order, meaning that faces are preceded by their cofaces. The function won't
  check this, though!
*/

template <class Simplex> PersistenceDiagram<typename Simplex::DataType> calculateZeroDimensionalPersistenceDiagram( const topology::SimplicialComplex<Simplex>& K )
{
  using DataType   = typename Simplex::DataType;
  using VertexType = typename Simplex::VertexType;

  using namespace topology;

  // Extract {0,1}-simplices -----------------------------------------
  //
  // Note that there is a range predicate for the simplicial complex class that
  // does essentially the same thing. However, the results of the query must be
  // consistent with respect to the filtration order.
  std::vector<Simplex> simplices;

  std::copy_if( K.begin(), K.end(),
                std::back_inserter( simplices ),
                [] ( const Simplex& s ) { return s.dimension() <= 1; } );

  SimplicialComplex<Simplex> S
    = SimplicialComplex<Simplex>( simplices.begin(), simplices.end() );

  std::vector<Simplex> vertices;

  std::copy_if( simplices.begin(), simplices.end() ,
                std::back_inserter( vertices ),
                [] ( const Simplex& s ) { return s.dimension() == 0; } );

  UnionFind<Simplex> uf( vertices.begin(), vertices.end() );
  PersistenceDiagram<DataType> pd;

  for( auto&& simplex : S )
  {
    // Only edges can destroy a component
    if( simplex.dimension() == 1 )
    {
      // Prepare component destruction -------------------------------

      VertexType u = *( simplex.begin() );
      VertexType v = *( simplex.begin() + 1 );

      auto youngerComponent = uf.find( u );
      auto olderComponent   = uf.find( v );

      // If the component has already been merged by some other edge, we are
      // not interested in it any longer.
      if( youngerComponent == olderComponent )
        continue;

      // Ensures that the younger component is always the first component. A
      // component is younger if it its parent vertex precedes the other one
      // in the current filtration.
      {
        auto uIndex = S.index( youngerComponent );
        auto vIndex = S.index( olderComponent );

        // The younger component must have the _larger_ index as it is born
        // _later_ in the filtration.
        if( uIndex < vIndex )
          std::swap( youngerComponent, olderComponent );
      }

      auto creation    = youngerComponent.data();
      auto destruction = simplex.data();

      uf.merge( youngerComponent, olderComponent );
      pd.add( creation, destruction );
    }
  }

  // Store information about unpaired simplices ----------------------
  //
  // All components in the Union--Find data structure now correspond to
  // essential 0-dimensional homology classes of the input complex.

  std::vector<Simplex> roots;
  uf.roots( std::back_inserter( roots ) );

  for( auto&& root : roots )
    pd.add( root.data() );

  // TODO: Return Union--Find data structure as well?
  return pd;
}

} // namespace aleph

#endif
<commit_msg>Rewrote calculation of connected components<commit_after>#ifndef ALEPH_PERSISTENT_HOMOLOGY_CONNECTED_COMPONENTS_HH__
#define ALEPH_PERSISTENT_HOMOLOGY_CONNECTED_COMPONENTS_HH__

#include "persistenceDiagrams/PersistenceDiagram.hh"

#include "topology/SimplicialComplex.hh"
#include "topology/UnionFind.hh"

#include <algorithm>
#include <vector>

namespace aleph
{

/**
  Calculates zero-dimensional persistent homology, i.e. tracking of connected
  components, for a given simplicial complex. This is highly-efficient, as it
  only requires a suitable 'Union--Find' data structure.

  As usual, the function assumes that the simplicial complex is in filtration
  order, meaning that faces are preceded by their cofaces. The function won't
  check this, though!
*/

template <class Simplex> PersistenceDiagram<typename Simplex::DataType> calculateZeroDimensionalPersistenceDiagram( const topology::SimplicialComplex<Simplex>& K )
{
  using DataType   = typename Simplex::DataType;
  using VertexType = typename Simplex::VertexType;

  using namespace topology;

  // Extract {0,1}-simplices -----------------------------------------
  //
  // Note that there is a range predicate for the simplicial complex class that
  // does essentially the same thing. However, the results of the query must be
  // consistent with respect to the filtration order.
  std::vector<Simplex> simplices;

  std::copy_if( K.begin(), K.end(),
                std::back_inserter( simplices ),
                [] ( const Simplex& s ) { return s.dimension() <= 1; } );

  SimplicialComplex<Simplex> S
    = SimplicialComplex<Simplex>( simplices.begin(), simplices.end() );

  std::vector<VertexType> vertices;

  for( auto&& s : simplices )
  {
    if( s.dimension() == 0 )
      vertices.push_back( *s.begin() );
  }

  UnionFind<VertexType> uf( vertices.begin(), vertices.end() );
  PersistenceDiagram<DataType> pd;

  for( auto&& simplex : S )
  {
    // Only edges can destroy a component
    if( simplex.dimension() == 1 )
    {
      // Prepare component destruction -------------------------------

      VertexType u = *( simplex.begin() );
      VertexType v = *( simplex.begin() + 1 );

      auto youngerComponent = uf.find( u );
      auto olderComponent   = uf.find( v );

      // If the component has already been merged by some other edge, we are
      // not interested in it any longer.
      if( youngerComponent == olderComponent )
        continue;

      // Ensures that the younger component is always the first component. A
      // component is younger if it its parent vertex precedes the other one
      // in the current filtration.
      auto uIndex = S.index( Simplex( youngerComponent ) );
      auto vIndex = S.index( Simplex( olderComponent ) );

      // The younger component must have the _larger_ index as it is born
      // _later_ in the filtration.
      if( uIndex < vIndex )
        std::swap( youngerComponent, olderComponent );

      auto creation    = S[uIndex].data();
      auto destruction = simplex.data();

      uf.merge( youngerComponent, olderComponent );
      pd.add( creation, destruction );
    }
  }

  // Store information about unpaired simplices ----------------------
  //
  // All components in the Union--Find data structure now correspond to
  // essential 0-dimensional homology classes of the input complex.

  std::vector<VertexType> roots;
  uf.roots( std::back_inserter( roots ) );

  for( auto&& root : roots )
  {
    auto creator = *S.find( Simplex( root ) );
    pd.add( creator.data() );
  }

  // TODO: Return Union--Find data structure as well?
  return pd;
}

} // namespace aleph

#endif
<|endoftext|>


<commit_before>#include "utils.hpp"
#include <cstring>
#include <boost/filesystem.hpp>

namespace bf = boost::filesystem;

static std::string makepath(bf::path, RdbAttrs);
// Get the key for the given path.
static bool getkey(const bf::path&, std::string&);
static bf::path keyfile(const std::string&);
static void touch(const bf::path&);

std::string pathfor(const char *root, RdbAttrs keys) {
	bool used[keys.size()];
	memset(used, 0, sizeof(*used) * keys.size());

	bf::path path(root);
	std::string curkey;
	while (keys.size() > 0) {
		if (bf::exists(path)) {
			if (!bf::is_directory(path))
				fatal("%s is not a directory\n", path.string().c_str());
			if (!getkey(path, curkey))
				return makepath(path.string().c_str(), keys);
			path /= keys[curkey];
			keys.erase(curkey);
		} else {
			return makepath(path.string().c_str(), keys);
		}
	}

	return path.string();
}

static std::string makepath(bf::path root, RdbAttrs keys) {
	if (!bf::exists(root) && keys.size() > 1)
		bf::create_directory(root);

	while (keys.size() > 0) {
		const std::string &key = keys.begin()->first;
		const std::string &vl = keys.begin()->second;
		touch(root / keyfile(key));
		keys.erase(key);

		root /= vl;
		if (keys.size() > 0)
			bf::create_directory(vl);
	}

	return root.string();
}

static bool getkey(const bf::path &p, std::string &key) {
	for (bf::directory_iterator it(p); it != bf::directory_iterator(); it++) {
		const char *fname = it->string().c_str();
		if (strstr(fname, "KEY=") == fname) {
			key = fname + strlen("KEY=");
			return true;
		}
	}
	return false;
}

static bf::path keyfile(const std::string &key) {
	return bf::path(std::string("KEY=") + key);
}

static void touch(const bf::path &p) {
	FILE *touch = fopen(p.string().c_str(), "w");
	if (!touch)
		fatalx(errno, "Failed to touch keyfile %s\n", p.string().c_str());
	fclose(touch);
}<commit_msg>rdb: handle unspecified keys.<commit_after>#include "utils.hpp"
#include <cstring>
#include <boost/filesystem.hpp>

namespace bf = boost::filesystem;

static std::string makepath(bf::path, RdbAttrs);
// Get the key for the given path.
static bool getkey(const bf::path&, std::string&);
static bf::path keyfile(const std::string&);
static void touch(const bf::path&);

std::string pathfor(const char *root, RdbAttrs keys) {
	bf::path path(root);
	std::string curkey;

	while (keys.size() > 0) {
		if (bf::exists(path)) {
			if (!bf::is_directory(path) && keys.size() > 0)
				fatal("%s is not a directory\n", path.string().c_str());
			if (!getkey(path, curkey))
				return makepath(path.string().c_str(), keys);
			RdbAttrs::iterator it = keys.find(curkey);
			if (it == keys.end()) {
				path /= "UNSPECIFIED";
			} else {
				path /= it->second;
				keys.erase(it);
			}
		} else {
			return makepath(path.string().c_str(), keys);
		}
	}

	return path.string();
}

static std::string makepath(bf::path root, RdbAttrs keys) {
	if (!bf::exists(root) && keys.size() > 1)
		bf::create_directory(root);

	while (keys.size() > 0) {
		const std::string &key = keys.begin()->first;
		const std::string &vl = keys.begin()->second;
		touch(root / keyfile(key));
		keys.erase(key);

		root /= vl;
		if (keys.size() > 0)
			bf::create_directory(vl);
	}

	return root.string();
}

static bool getkey(const bf::path &p, std::string &key) {
	for (bf::directory_iterator it(p); it != bf::directory_iterator(); it++) {
		const char *fname = it->string().c_str();
		if (strstr(fname, "KEY=") == fname) {
			key = fname + strlen("KEY=");
			return true;
		}
	}
	return false;
}

static bf::path keyfile(const std::string &key) {
	return bf::path(std::string("KEY=") + key);
}

static void touch(const bf::path &p) {
	FILE *touch = fopen(p.string().c_str(), "w");
	if (!touch)
		fatalx(errno, "Failed to touch keyfile %s\n", p.string().c_str());
	fclose(touch);
}<|endoftext|>


<commit_before>#include <ros/ros.h>

#include <stdint.h>
#include <math.h>

#include <cv_bridge/cv_bridge.h>
#include <nav_msgs/GetMap.h>
#include <nav_msgs/OccupancyGrid.h>
#include <quirkd/Alert.h>
#include <sensor_msgs/LaserScan.h>
#include <tf/transform_listener.h>
#include <tf/transform_datatypes.h>

class DataController {
    public:
        DataController() {
            alert_pub_ = n_.advertise<quirkd::Alert>("/quirkd/alert/notification", 1);
            laser_sub_ = n_.subscribe("/base_scan", 1, &DataController::laserScanCB, this);
            // ros::service::waitForService("static_map");
            static_map_client_ = n_.serviceClient<nav_msgs::GetMap>("static_map");
            // ros::service::waitForService("dynamic_map");
            dynamic_map_client_ = n_.serviceClient<nav_msgs::GetMap>("dynamic_map");
        }
        void laserScanCB(const sensor_msgs::LaserScan msg) {
            ROS_INFO("Laser Scan Callback");
            updated = true;
            last_data = msg;
            try {
                tf_.lookupTransform("/map", "/base_laser_link", ros::Time(0), last_tf);
                ROS_INFO("tf success for /map to /base_laser_link");
            } catch (tf::TransformException &ex) {
                ROS_WARN("tf fetch failed. %s", ex.what());
            }
        }
        void update() {
            ROS_INFO("Update Data Processor");
            nav_msgs::GetMap srv;
            cv_bridge::CvImagePtr static_image;
            cv_bridge::CvImagePtr dynamic_image;
            if (static_map_client_.call(srv)) {
                ROS_INFO("Successfull call static map");
                nav_msgs::OccupancyGrid og = srv.response.map;
                static_image = this -> gridToCvImage(&og);
            } else {
                ROS_WARN("Failed to get static map");
            }
            if (dynamic_map_client_.call(srv)) {
                ROS_INFO("Successfull call dynamic map");
                nav_msgs::OccupancyGrid og = srv.response.map;
                dynamic_image = this -> gridToCvImage(&og);
            } else {
                ROS_WARN("Failed to get dynamic map");
            }
            updated = false;
        }
        void pub_alert_status() {
            quirkd::Alert alert;
            alert.level = 0;
            alert.min_x = 0;
            alert.max_x = 1;
            alert.min_y = 0;
            alert.max_y = 1;
            updateAlertPerimeter(&alert, last_data, last_tf);
            alert_pub_.publish(alert);
        }
        bool updated;
        sensor_msgs::LaserScan last_data;
        tf::StampedTransform last_tf;
    private:
        ros::NodeHandle n_;
        ros::Publisher alert_pub_;
        ros::Subscriber laser_sub_;
        ros::ServiceClient dynamic_map_client_;
        ros::ServiceClient static_map_client_;
        tf::TransformListener tf_;
        void updateAlertPerimeter(quirkd::Alert* alert, const sensor_msgs::LaserScan scan, const tf::StampedTransform tf) {
            double base_x = tf.getOrigin().x();
            double base_y = tf.getOrigin().y();
            double r, p, base_heading;
            tf::Matrix3x3 m(tf.getRotation());
            m.getRPY(r, p, base_heading);

            double scan_min = base_heading + scan.angle_min;
            double scan_inc = scan.angle_increment;
            double heading = scan_min;

            alert->min_x = base_x;
            alert->max_x = base_x;
            alert->min_y = base_y;
            alert->max_y = base_y;

            double live_x, live_y;

            for (int i = 0; i < scan.ranges.size(); i++) {
                double dist = scan.ranges[i];

                live_x = dist * sin(heading);
                live_y = dist * cos(heading);
                alert->min_x = std::min(live_x, double(alert->min_x));
                alert->max_x = std::max(live_x, double(alert->max_x));
                alert->min_y = std::min(live_y, double(alert->min_y));
                alert->max_y = std::max(live_y, double(alert->max_y));

                heading += scan_inc;
            }
        }
        cv_bridge::CvImagePtr gridToCvImage(nav_msgs::OccupancyGrid* grid) {
            // Unpack the Occupancy Grid

            nav_msgs::MapMetaData info = grid -> info;
            float resolution = info.resolution;
            int cell_width = info.width;
            float map_width = cell_width * resolution;
            int cell_height = info.height;
            float map_height = cell_height * resolution;
            // I'm assuming the map isn't rotated right now because that could be really complex
            float origin_x = info.origin.position.x;
            float origin_y = info.origin.position.x;
            /*
             * From documentation:
             * The map data in row major order, starting at 0,0.
             * Valid values are in the range [0, 100]
             */
            std::vector<int8_t> data = grid -> data;
            std::vector<uint8_t> unsigned_data;
            unsigned_data.resize(data.size()); // allocate and fill vector to match num elements of data

            for (std::vector<int8_t>::size_type i = 0; i < data.size(); i++) {
                int8_t old_int = data[i];
                // assume unknown values (signed -1) are just 0
                // assumption is that unseen parts of the map have no obstacles until proven otherwise
                uint8_t new_int = (uint8_t)((old_int == -1) ? 0 : old_int);
                unsigned_data[(std::vector<uint8_t>::size_type) i] = new_int;
            }

            // Create the ROS Image Message

            sensor_msgs::Image converted_image;
            converted_image.width = cell_width;
            converted_image.height = cell_height;
            converted_image.encoding = sensor_msgs::image_encodings::MONO8;

            converted_image.step = cell_width;
            converted_image.data = unsigned_data;

            // TODO use CV Bridge to get the CvImagePtr

            cv_bridge::CvImagePtr cv_ptr;

            try {
                cv_ptr = cv_bridge::toCvCopy(converted_image, sensor_msgs::image_encodings::MONO8);
            } catch (cv_bridge::Exception& e) {
                ROS_ERROR("cv_bridge exception: %s", e.what());
            }

            return cv_ptr;
        }
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "DataController");
    ROS_INFO("1");
    DataController dp;
    ROS_INFO("2");
    dp.updated = false;
    ROS_INFO("3");
    ros::Rate r(30);
    ROS_INFO("4");
    dp.update();
    while(ros::ok()) {
        ros::spinOnce();
        if (dp.updated) {
            dp.update();
            ROS_INFO("Processed message data in loop");
        }
        dp.pub_alert_status();
        r.sleep();
    }
    ROS_INFO("Data Processor Exited.");
    return 0;
}
<commit_msg>The conversion to an image is working<commit_after>#include <ros/ros.h>

#include <math.h>
#include <stdint.h>
#include <stdbool.h>

#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <nav_msgs/GetMap.h>
#include <nav_msgs/OccupancyGrid.h>
#include <opencv2/highgui/highgui.hpp>
#include <quirkd/Alert.h>
#include <sensor_msgs/LaserScan.h>
#include <sensor_msgs/image_encodings.h>
#include <tf/transform_listener.h>
#include <tf/transform_datatypes.h>

class DataController {
    public:
        DataController()
            : it_(n_)
        {
            alert_pub_ = n_.advertise<quirkd::Alert>("/quirkd/alert/notification", 1);
            laser_sub_ = n_.subscribe("/base_scan", 1, &DataController::laserScanCB, this);
            // ros::service::waitForService("static_map");
            static_map_client_ = n_.serviceClient<nav_msgs::GetMap>("static_map");
            // ros::service::waitForService("dynamic_map");
            dynamic_map_client_ = n_.serviceClient<nav_msgs::GetMap>("dynamic_map");
            image_pub_ = it_.advertise("/quirkd/test/static_image", 1);
        }
        void laserScanCB(const sensor_msgs::LaserScan msg) {
            ROS_INFO("Laser Scan Callback");
            updated = true;
            last_data = msg;
            try {
                tf_.lookupTransform("/map", "/base_laser_link", ros::Time(0), last_tf);
                ROS_INFO("tf success for /map to /base_laser_link");
            } catch (tf::TransformException &ex) {
                ROS_WARN("tf fetch failed. %s", ex.what());
            }
        }
        void update() {
            ROS_INFO("Update Data Processor");
            nav_msgs::GetMap srv;
            cv_bridge::CvImagePtr static_image;
            cv_bridge::CvImagePtr dynamic_image;
            if (static_map_client_.call(srv)) {
                ROS_INFO("Successfull call static map");
                nav_msgs::OccupancyGrid og = srv.response.map;
                static_image = this -> gridToCvImage(&og);

                // For testing

                if (static_image -> image.rows > 60 && static_image -> image.cols>60) {
                    cv::circle(static_image -> image, cv::Point(50,50), 10, CV_RGB(100,100,100), -1);
                }
                ROS_INFO("Publish static image window");
                image_pub_.publish(static_image->toImageMsg());

            } else {
                ROS_WARN("Failed to get static map");
            }
            if (dynamic_map_client_.call(srv)) {
                ROS_INFO("Successfull call dynamic map");
                nav_msgs::OccupancyGrid og = srv.response.map;
                dynamic_image = this -> gridToCvImage(&og);
            } else {
                ROS_WARN("Failed to get dynamic map");
            }
            updated = false;
        }
        void pub_alert_status() {
            quirkd::Alert alert;
            alert.level = 0;
            alert.min_x = 0;
            alert.max_x = 1;
            alert.min_y = 0;
            alert.max_y = 1;
            updateAlertPerimeter(&alert, last_data, last_tf);
            alert_pub_.publish(alert);
        }
        bool updated;
        sensor_msgs::LaserScan last_data;
        tf::StampedTransform last_tf;
    private:
        ros::NodeHandle n_;
        image_transport::ImageTransport it_;
        image_transport::Publisher image_pub_;
        ros::Publisher alert_pub_;
        ros::Subscriber laser_sub_;
        ros::ServiceClient dynamic_map_client_;
        ros::ServiceClient static_map_client_;
        tf::TransformListener tf_;
        void updateAlertPerimeter(quirkd::Alert* alert, const sensor_msgs::LaserScan scan, const tf::StampedTransform tf) {
            double base_x = tf.getOrigin().x();
            double base_y = tf.getOrigin().y();
            double r, p, base_heading;
            tf::Matrix3x3 m(tf.getRotation());
            m.getRPY(r, p, base_heading);

            double scan_min = base_heading + scan.angle_min;
            double scan_inc = scan.angle_increment;
            double heading = scan_min;

            alert->min_x = base_x;
            alert->max_x = base_x;
            alert->min_y = base_y;
            alert->max_y = base_y;

            double live_x, live_y;

            for (int i = 0; i < scan.ranges.size(); i++) {
                double dist = scan.ranges[i];

                live_x = dist * sin(heading);
                live_y = dist * cos(heading);
                alert->min_x = std::min(live_x, double(alert->min_x));
                alert->max_x = std::max(live_x, double(alert->max_x));
                alert->min_y = std::min(live_y, double(alert->min_y));
                alert->max_y = std::max(live_y, double(alert->max_y));

                heading += scan_inc;
            }
        }
        cv_bridge::CvImagePtr gridToCvImage(nav_msgs::OccupancyGrid* grid) {
            // Unpack the Occupancy Grid

            nav_msgs::MapMetaData info = grid -> info;
            float resolution = info.resolution;
            int cell_width = info.width;
            float map_width = cell_width * resolution;
            int cell_height = info.height;
            float map_height = cell_height * resolution;
            // I'm assuming the map isn't rotated right now because that could be really complex
            float origin_x = info.origin.position.x;
            float origin_y = info.origin.position.x;
            /*
             * From documentation:
             * The map data in row major order, starting at 0,0.
             * Valid values are in the range [0, 100]
             */
            std::vector<int8_t> data = grid -> data;
            std::vector<uint8_t> unsigned_data;
            unsigned_data.resize(data.size()); // allocate and fill vector to match num elements of data

            for (std::vector<int8_t>::size_type i = 0; i < data.size(); i++) {
                int8_t old_int = data[i];
                // assume unknown values (signed -1) are just 0
                // assumption is that unseen parts of the map have no obstacles until proven otherwise
                uint8_t new_int = (uint8_t)((old_int == -1) ? 0 : old_int);
                unsigned_data[(std::vector<uint8_t>::size_type) i] = new_int;
            }

            // Create the ROS Image Message

            sensor_msgs::Image converted_image;
            converted_image.width = cell_width;
            converted_image.height = cell_height;
            converted_image.encoding = sensor_msgs::image_encodings::MONO8;

            converted_image.step = cell_width;
            converted_image.data = unsigned_data;

            // TODO use CV Bridge to get the CvImagePtr

            cv_bridge::CvImagePtr cv_ptr;

            try {
                cv_ptr = cv_bridge::toCvCopy(converted_image, sensor_msgs::image_encodings::MONO8);
            } catch (cv_bridge::Exception& e) {
                ROS_ERROR("cv_bridge exception: %s", e.what());
            }

            return cv_ptr;
        }
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "DataController");
    ROS_INFO("1");
    DataController dp;
    ROS_INFO("2");
    dp.updated = false;
    ROS_INFO("3");
    ros::Rate r(30);
    ROS_INFO("4");
    dp.update();
    while(ros::ok()) {
        ros::spinOnce();
        if (dp.updated || true) {
            dp.update();
            ROS_INFO("Processed message data in loop");
        }
        dp.pub_alert_status();
        r.sleep();
    }
    ROS_INFO("Data Processor Exited.");
    return 0;
}
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2014--2016
                           John Harvey 2014 (DS18B20 code)
                           Deniz Erbilgin 2015--2016
*/

/*
 * Header for common on-board and external sensors and actuators for V0p2 variants.
 */
#include <stdint.h>
#include <limits.h>
#include <util/atomic.h>

#include <Wire.h> // Arduino I2C library.
#include <OTV0p2Base.h>

#include "V0p2_Main.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.
#include "V0p2_Sensors.h" // I/O code access.
#include "Control.h"
#include "UI_Minimal.h"

// Sensor for supply (eg battery) voltage in millivolts.
// Singleton implementation/instance.
OTV0P2BASE::SupplyVoltageCentiVolts Supply_cV;

#ifdef TEMP_POT_AVAILABLE
// Singleton implementation/instance.
#if defined(TEMP_POT_REVERSE)
OTV0P2BASE::SensorTemperaturePot TempPot(OTV0P2BASE::SensorTemperaturePot::TEMP_POT_RAW_MAX, 0);
#else
OTV0P2BASE::SensorTemperaturePot TempPot(0, OTV0P2BASE::SensorTemperaturePot::TEMP_POT_RAW_MAX);
#endif // defined(TEMP_POT_REVERSE)
#endif

#ifdef ENABLE_AMBLIGHT_SENSOR
// Normal 2 bit shift between raw and externally-presented values.
static const uint8_t shiftRawScaleTo8Bit = 2;
#ifdef AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400
// This implementation expects a phototransitor TEPT4400 (50nA dark current, nominal 200uA@100lx@Vce=50V) from IO_POWER_UP to LDR_SENSOR_AIN and 220k to ground.
// Measurement should be taken wrt to internal fixed 1.1V bandgap reference, since light indication is current flow across a fixed resistor.
// Aiming for maximum reading at or above 100--300lx, ie decent domestic internal lighting.
// Note that phototransistor is likely far more directionally-sensitive than LDR and its response nearly linear.
// This extends the dynamic range and switches to measurement vs supply when full-scale against bandgap ref, then scales by Vss/Vbandgap and compresses to fit.
// http://home.wlv.ac.uk/~in6840/Lightinglevels.htm
// http://www.engineeringtoolbox.com/light-level-rooms-d_708.html
// http://www.pocklington-trust.org.uk/Resources/Thomas%20Pocklington/Documents/PDF/Research%20Publications/GPG5.pdf
// http://www.vishay.com/docs/84154/appnotesensors.pdf
#if (7 == V0p2_REV) // REV7 initial board run especially uses different phototransistor (not TEPT4400).
// Note that some REV7s from initial batch were fitted with wrong device entirely,
// an IR device, with very low effective sensitivity (FSD ~ 20 rather than 1023).
static const int LDR_THR_LOW = 180U;
static const int LDR_THR_HIGH = 250U;
#else // REV4 default values.
static const int LDR_THR_LOW = 270U;
static const int LDR_THR_HIGH = 400U;
#endif
#else // LDR (!defined(AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400))
// This implementation expects an LDR (1M dark resistance) from IO_POWER_UP to LDR_SENSOR_AIN and 100k to ground.
// Measurement should be taken wrt to supply voltage, since light indication is a fraction of that.
// Values below from PICAXE V0.09 impl approx multiplied by 4+ to allow for scale change.
#ifdef ENABLE_AMBLIGHT_EXTRA_SENSITIVE // Define if LDR not exposed to much light, eg for REV2 cut4 sideways-pointing LDR (TODO-209).
static const int LDR_THR_LOW = 50U;
static const int LDR_THR_HIGH = 70U;
#else // Normal settings.
static const int LDR_THR_LOW = 160U; // Was 30.
static const int LDR_THR_HIGH = 200U; // Was 35.
#endif // ENABLE_AMBLIGHT_EXTRA_SENSITIVE
#endif // AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400
// Singleton implementation/instance.
AmbientLight AmbLight(LDR_THR_HIGH >> shiftRawScaleTo8Bit);
#endif // ENABLE_AMBLIGHT_SENSOR

#if defined(ENABLE_MINIMAL_ONEWIRE_SUPPORT)
OTV0P2BASE::MinimalOneWire<> MinOW_DEFAULT;
#endif

#if defined(SENSOR_EXTERNAL_DS18B20_ENABLE_0) // Enable sensor zero.
OTV0P2BASE::TemperatureC16_DS18B20 extDS18B20_0(MinOW_DEFAULT, 0);
#endif

#if defined(ENABLE_PRIMARY_TEMP_SENSOR_SHT21)
// Singleton implementation/instance.
OTV0P2BASE::HumiditySensorSHT21 RelHumidity;
#else
OTV0P2BASE::DummyHumiditySensorSHT21 RelHumidity;
#endif

// Ambient/room temperature sensor, usually on main board.
#if defined(ENABLE_PRIMARY_TEMP_SENSOR_SHT21)
OTV0P2BASE::RoomTemperatureC16_SHT21 TemperatureC16; // SHT21 impl.
#elif defined(ENABLE_PRIMARY_TEMP_SENSOR_DS18B20)
#if defined(ENABLE_MINIMAL_ONEWIRE_SUPPORT)
// DSB18B20 temperature impl, with slightly reduced precision to improve speed.
OTV0P2BASE::TemperatureC16_DS18B20 TemperatureC16(MinOW_DEFAULT, 0, OTV0P2BASE::TemperatureC16_DS18B20::MAX_PRECISION - 1);
#endif
#else // Don't use TMP112 if SHT21 or DS18B20 are selected.
OTV0P2BASE::RoomTemperatureC16_TMP112 TemperatureC16;
#endif

#ifdef ENABLE_VOICE_SENSOR
// If count meets or exceeds this threshold in one poll period then
// the room is deemed to be occupied.
// Strictly positive.
// DHD20151119: even now it seems a threshold of >= 2 is needed to avoid false positives.
#define VOICE_DETECTION_THRESHOLD 4

// Force a read/poll of the voice level and return the value sensed.
// Thread-safe and ISR-safe.
uint8_t VoiceDetection::read()
{
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
  {
    isDetected = ((value = count) >= VOICE_DETECTION_THRESHOLD);
    // clear count and detection flag
    // isTriggered = false;
    count = 0;
  }
  return(value);
}

// Handle simple interrupt.
// Fast and ISR (Interrupt Service Routines) safe.
// Returns true if interrupt was successfully handled and cleared
// else another interrupt handler in the chain may be called
// to attempt to clear the interrupt.
bool VoiceDetection::handleInterruptSimple()
{
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
  {
    // Count of voice activations since last poll, avoiding overflow.
    if ((count < 255) && (++count >= VOICE_DETECTION_THRESHOLD))
    {
      // Act as soon as voice is detected.
      isDetected = true;
      // Don't regard this as a very strong indication,
      // as it could be a TV or radio on in the room.
      Occupancy.markAsPossiblyOccupied();
    }
  }

  //    // Flag that interrupt has occurred
  //    endOfLocking = OTV0P2BASE::getMinutesSinceMidnightLT() + lockingPeriod;
  //    isTriggered = true;
  // No further work to be done to 'clear' interrupt.
  return (true);
}

// Singleton implementation/instance.
VoiceDetection Voice;
#endif

////////////////////////// Actuators

// DORM1/REV7 direct drive actuator.
#ifdef HAS_DORM1_VALVE_DRIVE
//#ifdef DIRECT_MOTOR_DRIVE_V1
// Singleton implementation/instance.
#ifdef ENABLE_DORM1_MOTOR_REVERSED // Reversed vs sample 2015/12
OTRadValve::ValveMotorDirectV1<MOTOR_DRIVE_ML, MOTOR_DRIVE_MR, MOTOR_DRIVE_MI_AIN, MOTOR_DRIVE_MC_AIN> ValveDirect;
#else
OTRadValve::ValveMotorDirectV1<MOTOR_DRIVE_MR, MOTOR_DRIVE_ML, MOTOR_DRIVE_MI_AIN, MOTOR_DRIVE_MC_AIN> ValveDirect;
#endif // HAS_DORM1_MOTOR_REVERSED
#endif

// FHT8V radio-controlled actuator.
#ifdef ENABLE_FHT8VSIMPLE
// Function to append stats trailer (and 0xff) to FHT8V/FS20 TX buffer.
// Assume enough space in buffer for largest possible stats message.
#if defined(ALLOW_STATS_TX)
uint8_t *appendStatsToTXBufferWithFF(uint8_t *bptr, const uint8_t bufSize)
{
  OTV0P2BASE::FullStatsMessageCore_t trailer;
  populateCoreStats(&trailer);
  // Ensure that no ID is encoded in the message sent on the air since it would be a repeat from the FHT8V frame.
  trailer.containsID = false;

#if defined(ENABLE_MINIMAL_STATS_TXRX)
  // As bandwidth optimisation just write minimal trailer if only temp&power available.
  if (trailer.containsTempAndPower &&
      !trailer.containsID && !trailer.containsAmbL)
  {
    writeTrailingMinimalStatsPayload(bptr, &(trailer.tempAndPower));
    bptr += 3;
    *bptr = (uint8_t)0xff; // Terminate TX bytes.
  }
  else
#endif
  {
    // Assume enough space in buffer for largest possible stats message.
    bptr = encodeFullStatsMessageCore(bptr, bufSize, OTV0P2BASE::getStatsTXLevel(), false, &trailer);
  }
  return (bptr);
}
#else
#define appendStatsToTXBufferWithFF NULL // Do not append stats.
#endif
#endif // ENABLE_FHT8VSIMPLE

#ifdef ENABLE_FHT8VSIMPLE
OTRadValve::FHT8VRadValve<_FHT8V_MAX_EXTRA_TRAILER_BYTES, OTRadValve::FHT8VRadValveBase::RFM23_PREAMBLE_BYTES, OTRadValve::FHT8VRadValveBase::RFM23_PREAMBLE_BYTE> FHT8V(appendStatsToTXBufferWithFF);
#endif

// Clear both housecode parts (and thus disable local valve).
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
void FHT8VClearHC()
{
  FHT8V.clearHC();
  OTV0P2BASE::eeprom_smart_erase_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1);
  OTV0P2BASE::eeprom_smart_erase_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2);
}
#endif

// Set (non-volatile) HC1 and HC2 for single/primary FHT8V wireless valve under control.
// Also set value in FHT8V rad valve model.
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
void FHT8VSetHC1(uint8_t hc)
{
  FHT8V.setHC1(hc);
  OTV0P2BASE::eeprom_smart_update_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1, hc);
}
void FHT8VSetHC2(uint8_t hc)
{
  FHT8V.setHC2(hc);
  OTV0P2BASE::eeprom_smart_update_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2, hc);
}
#endif

// Get (non-volatile) HC1 and HC2 for single/primary FHT8V wireless valve under control (will be 0xff until set).
// FHT8V instance values are used as a cache.
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
uint8_t FHT8VGetHC1()
{
  const uint8_t vv = FHT8V.getHC1();
  // If cached value in FHT8V instance is valid, return it.
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(vv))
  {
    return (vv);
  }
  // Else if EEPROM value is valid, then cache it in the FHT8V instance and return it.
  const uint8_t ev = eeprom_read_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1);
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(ev))
  {
    FHT8V.setHC1(ev);
  }
  return (ev);
}
uint8_t FHT8VGetHC2()
{
  const uint8_t vv = FHT8V.getHC2();
  // If cached value in FHT8V instance is valid, return it.
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(vv))
  {
    return (vv);
  }
  // Else if EEPROM value is valid, then cache it in the FHT8V instance and return it.
  const uint8_t ev = eeprom_read_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2);
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(ev))
  {
    FHT8V.setHC2(ev);
  }
  return (ev);
}
#endif // ENABLE_FHT8VSIMPLE

#ifdef ENABLE_FHT8VSIMPLE
// Load EEPROM house codes into primary FHT8V instance at start-up or once cleared in FHT8V instance.
void FHT8VLoadHCFromEEPROM()
{
  // Uses side-effect to cache/save in FHT8V instance.
  FHT8VGetHC1();
  FHT8VGetHC2();
}
#endif // ENABLE_FHT8VSIMPLE
<commit_msg>lowered threshold to get something out of new QM-1s<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.

Author(s) / Copyright (s): Damon Hart-Davis 2014--2016
                           John Harvey 2014 (DS18B20 code)
                           Deniz Erbilgin 2015--2016
*/

/*
 * Header for common on-board and external sensors and actuators for V0p2 variants.
 */
#include <stdint.h>
#include <limits.h>
#include <util/atomic.h>

#include <Wire.h> // Arduino I2C library.
#include <OTV0p2Base.h>

#include "V0p2_Main.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.
#include "V0p2_Sensors.h" // I/O code access.
#include "Control.h"
#include "UI_Minimal.h"

// Sensor for supply (eg battery) voltage in millivolts.
// Singleton implementation/instance.
OTV0P2BASE::SupplyVoltageCentiVolts Supply_cV;

#ifdef TEMP_POT_AVAILABLE
// Singleton implementation/instance.
#if defined(TEMP_POT_REVERSE)
OTV0P2BASE::SensorTemperaturePot TempPot(OTV0P2BASE::SensorTemperaturePot::TEMP_POT_RAW_MAX, 0);
#else
OTV0P2BASE::SensorTemperaturePot TempPot(0, OTV0P2BASE::SensorTemperaturePot::TEMP_POT_RAW_MAX);
#endif // defined(TEMP_POT_REVERSE)
#endif

#ifdef ENABLE_AMBLIGHT_SENSOR
// Normal 2 bit shift between raw and externally-presented values.
static const uint8_t shiftRawScaleTo8Bit = 2;
#ifdef AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400
// This implementation expects a phototransitor TEPT4400 (50nA dark current, nominal 200uA@100lx@Vce=50V) from IO_POWER_UP to LDR_SENSOR_AIN and 220k to ground.
// Measurement should be taken wrt to internal fixed 1.1V bandgap reference, since light indication is current flow across a fixed resistor.
// Aiming for maximum reading at or above 100--300lx, ie decent domestic internal lighting.
// Note that phototransistor is likely far more directionally-sensitive than LDR and its response nearly linear.
// This extends the dynamic range and switches to measurement vs supply when full-scale against bandgap ref, then scales by Vss/Vbandgap and compresses to fit.
// http://home.wlv.ac.uk/~in6840/Lightinglevels.htm
// http://www.engineeringtoolbox.com/light-level-rooms-d_708.html
// http://www.pocklington-trust.org.uk/Resources/Thomas%20Pocklington/Documents/PDF/Research%20Publications/GPG5.pdf
// http://www.vishay.com/docs/84154/appnotesensors.pdf
#if (7 == V0p2_REV) // REV7 initial board run especially uses different phototransistor (not TEPT4400).
// Note that some REV7s from initial batch were fitted with wrong device entirely,
// an IR device, with very low effective sensitivity (FSD ~ 20 rather than 1023).
static const int LDR_THR_LOW = 180U;
static const int LDR_THR_HIGH = 250U;
#else // REV4 default values.
static const int LDR_THR_LOW = 270U;
static const int LDR_THR_HIGH = 400U;
#endif
#else // LDR (!defined(AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400))
// This implementation expects an LDR (1M dark resistance) from IO_POWER_UP to LDR_SENSOR_AIN and 100k to ground.
// Measurement should be taken wrt to supply voltage, since light indication is a fraction of that.
// Values below from PICAXE V0.09 impl approx multiplied by 4+ to allow for scale change.
#ifdef ENABLE_AMBLIGHT_EXTRA_SENSITIVE // Define if LDR not exposed to much light, eg for REV2 cut4 sideways-pointing LDR (TODO-209).
static const int LDR_THR_LOW = 50U;
static const int LDR_THR_HIGH = 70U;
#else // Normal settings.
static const int LDR_THR_LOW = 160U; // Was 30.
static const int LDR_THR_HIGH = 200U; // Was 35.
#endif // ENABLE_AMBLIGHT_EXTRA_SENSITIVE
#endif // AMBIENT_LIGHT_SENSOR_PHOTOTRANS_TEPT4400
// Singleton implementation/instance.
AmbientLight AmbLight(LDR_THR_HIGH >> shiftRawScaleTo8Bit);
#endif // ENABLE_AMBLIGHT_SENSOR

#if defined(ENABLE_MINIMAL_ONEWIRE_SUPPORT)
OTV0P2BASE::MinimalOneWire<> MinOW_DEFAULT;
#endif

#if defined(SENSOR_EXTERNAL_DS18B20_ENABLE_0) // Enable sensor zero.
OTV0P2BASE::TemperatureC16_DS18B20 extDS18B20_0(MinOW_DEFAULT, 0);
#endif

#if defined(ENABLE_PRIMARY_TEMP_SENSOR_SHT21)
// Singleton implementation/instance.
OTV0P2BASE::HumiditySensorSHT21 RelHumidity;
#else
OTV0P2BASE::DummyHumiditySensorSHT21 RelHumidity;
#endif

// Ambient/room temperature sensor, usually on main board.
#if defined(ENABLE_PRIMARY_TEMP_SENSOR_SHT21)
OTV0P2BASE::RoomTemperatureC16_SHT21 TemperatureC16; // SHT21 impl.
#elif defined(ENABLE_PRIMARY_TEMP_SENSOR_DS18B20)
#if defined(ENABLE_MINIMAL_ONEWIRE_SUPPORT)
// DSB18B20 temperature impl, with slightly reduced precision to improve speed.
OTV0P2BASE::TemperatureC16_DS18B20 TemperatureC16(MinOW_DEFAULT, 0, OTV0P2BASE::TemperatureC16_DS18B20::MAX_PRECISION - 1);
#endif
#else // Don't use TMP112 if SHT21 or DS18B20 are selected.
OTV0P2BASE::RoomTemperatureC16_TMP112 TemperatureC16;
#endif

#ifdef ENABLE_VOICE_SENSOR
// If count meets or exceeds this threshold in one poll period then
// the room is deemed to be occupied.
// Strictly positive.
// DHD20151119: even now it seems a threshold of >= 2 is needed to avoid false positives.
// DE20160101:  Lowered detection threshold as new boards have lower sensitivity
#define VOICE_DETECTION_THRESHOLD 2

// Force a read/poll of the voice level and return the value sensed.
// Thread-safe and ISR-safe.
uint8_t VoiceDetection::read()
{
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
  {
    isDetected = ((value = count) >= VOICE_DETECTION_THRESHOLD);
    // clear count and detection flag
    // isTriggered = false;
    count = 0;
  }
  return(value);
}

// Handle simple interrupt.
// Fast and ISR (Interrupt Service Routines) safe.
// Returns true if interrupt was successfully handled and cleared
// else another interrupt handler in the chain may be called
// to attempt to clear the interrupt.
bool VoiceDetection::handleInterruptSimple()
{
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
  {
    // Count of voice activations since last poll, avoiding overflow.
    if ((count < 255) && (++count >= VOICE_DETECTION_THRESHOLD))
    {
      // Act as soon as voice is detected.
      isDetected = true;
      // Don't regard this as a very strong indication,
      // as it could be a TV or radio on in the room.
      Occupancy.markAsPossiblyOccupied();
    }
  }

  //    // Flag that interrupt has occurred
  //    endOfLocking = OTV0P2BASE::getMinutesSinceMidnightLT() + lockingPeriod;
  //    isTriggered = true;
  // No further work to be done to 'clear' interrupt.
  return (true);
}

// Singleton implementation/instance.
VoiceDetection Voice;
#endif

////////////////////////// Actuators

// DORM1/REV7 direct drive actuator.
#ifdef HAS_DORM1_VALVE_DRIVE
//#ifdef DIRECT_MOTOR_DRIVE_V1
// Singleton implementation/instance.
#ifdef ENABLE_DORM1_MOTOR_REVERSED // Reversed vs sample 2015/12
OTRadValve::ValveMotorDirectV1<MOTOR_DRIVE_ML, MOTOR_DRIVE_MR, MOTOR_DRIVE_MI_AIN, MOTOR_DRIVE_MC_AIN> ValveDirect;
#else
OTRadValve::ValveMotorDirectV1<MOTOR_DRIVE_MR, MOTOR_DRIVE_ML, MOTOR_DRIVE_MI_AIN, MOTOR_DRIVE_MC_AIN> ValveDirect;
#endif // HAS_DORM1_MOTOR_REVERSED
#endif

// FHT8V radio-controlled actuator.
#ifdef ENABLE_FHT8VSIMPLE
// Function to append stats trailer (and 0xff) to FHT8V/FS20 TX buffer.
// Assume enough space in buffer for largest possible stats message.
#if defined(ALLOW_STATS_TX)
uint8_t *appendStatsToTXBufferWithFF(uint8_t *bptr, const uint8_t bufSize)
{
  OTV0P2BASE::FullStatsMessageCore_t trailer;
  populateCoreStats(&trailer);
  // Ensure that no ID is encoded in the message sent on the air since it would be a repeat from the FHT8V frame.
  trailer.containsID = false;

#if defined(ENABLE_MINIMAL_STATS_TXRX)
  // As bandwidth optimisation just write minimal trailer if only temp&power available.
  if (trailer.containsTempAndPower &&
      !trailer.containsID && !trailer.containsAmbL)
  {
    writeTrailingMinimalStatsPayload(bptr, &(trailer.tempAndPower));
    bptr += 3;
    *bptr = (uint8_t)0xff; // Terminate TX bytes.
  }
  else
#endif
  {
    // Assume enough space in buffer for largest possible stats message.
    bptr = encodeFullStatsMessageCore(bptr, bufSize, OTV0P2BASE::getStatsTXLevel(), false, &trailer);
  }
  return (bptr);
}
#else
#define appendStatsToTXBufferWithFF NULL // Do not append stats.
#endif
#endif // ENABLE_FHT8VSIMPLE

#ifdef ENABLE_FHT8VSIMPLE
OTRadValve::FHT8VRadValve<_FHT8V_MAX_EXTRA_TRAILER_BYTES, OTRadValve::FHT8VRadValveBase::RFM23_PREAMBLE_BYTES, OTRadValve::FHT8VRadValveBase::RFM23_PREAMBLE_BYTE> FHT8V(appendStatsToTXBufferWithFF);
#endif

// Clear both housecode parts (and thus disable local valve).
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
void FHT8VClearHC()
{
  FHT8V.clearHC();
  OTV0P2BASE::eeprom_smart_erase_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1);
  OTV0P2BASE::eeprom_smart_erase_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2);
}
#endif

// Set (non-volatile) HC1 and HC2 for single/primary FHT8V wireless valve under control.
// Also set value in FHT8V rad valve model.
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
void FHT8VSetHC1(uint8_t hc)
{
  FHT8V.setHC1(hc);
  OTV0P2BASE::eeprom_smart_update_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1, hc);
}
void FHT8VSetHC2(uint8_t hc)
{
  FHT8V.setHC2(hc);
  OTV0P2BASE::eeprom_smart_update_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2, hc);
}
#endif

// Get (non-volatile) HC1 and HC2 for single/primary FHT8V wireless valve under control (will be 0xff until set).
// FHT8V instance values are used as a cache.
// Does nothing if FHT8V not in use.
#ifdef ENABLE_FHT8VSIMPLE
uint8_t FHT8VGetHC1()
{
  const uint8_t vv = FHT8V.getHC1();
  // If cached value in FHT8V instance is valid, return it.
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(vv))
  {
    return (vv);
  }
  // Else if EEPROM value is valid, then cache it in the FHT8V instance and return it.
  const uint8_t ev = eeprom_read_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC1);
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(ev))
  {
    FHT8V.setHC1(ev);
  }
  return (ev);
}
uint8_t FHT8VGetHC2()
{
  const uint8_t vv = FHT8V.getHC2();
  // If cached value in FHT8V instance is valid, return it.
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(vv))
  {
    return (vv);
  }
  // Else if EEPROM value is valid, then cache it in the FHT8V instance and return it.
  const uint8_t ev = eeprom_read_byte((uint8_t*)V0P2BASE_EE_START_FHT8V_HC2);
  if (OTRadValve::FHT8VRadValveBase::isValidFHTV8HouseCode(ev))
  {
    FHT8V.setHC2(ev);
  }
  return (ev);
}
#endif // ENABLE_FHT8VSIMPLE

#ifdef ENABLE_FHT8VSIMPLE
// Load EEPROM house codes into primary FHT8V instance at start-up or once cleared in FHT8V instance.
void FHT8VLoadHCFromEEPROM()
{
  // Uses side-effect to cache/save in FHT8V instance.
  FHT8VGetHC1();
  FHT8VGetHC2();
}
#endif // ENABLE_FHT8VSIMPLE
<|endoftext|>