<commit_before>/*  -*- mode: C++; c-file-style: "gnu" -*-

    This file is part of kdepim.
    Copyright (c) 2004 KDEPIM developers

    This library 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 library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "email.h"
#include <kdebug.h>
#include <klocale.h>
#include <qregexp.h>

//-----------------------------------------------------------------------------
QStringList KPIM::splitEmailAddrList(const QString& aStr)
{
  // Features:
  // - always ignores quoted characters
  // - ignores everything (including parentheses and commas)
  //   inside quoted strings
  // - supports nested comments
  // - ignores everything (including double quotes and commas)
  //   inside comments

  QStringList list;

  if (aStr.isEmpty())
    return list;

  QString addr;
  uint addrstart = 0;
  int commentlevel = 0;
  bool insidequote = false;

  for (uint index=0; index<aStr.length(); index++) {
    // the following conversion to latin1 is o.k. because
    // we can safely ignore all non-latin1 characters
    switch (aStr[index].latin1()) {
    case '"' : // start or end of quoted string
      if (commentlevel == 0)
        insidequote = !insidequote;
      break;
    case '(' : // start of comment
      if (!insidequote)
        commentlevel++;
      break;
    case ')' : // end of comment
      if (!insidequote) {
        if (commentlevel > 0)
          commentlevel--;
        else {
          kdDebug(5300) << "Error in address splitting: Unmatched ')'"
                        << endl;
          return list;
        }
      }
      break;
    case '\\' : // quoted character
      index++; // ignore the quoted character
      break;
    case ',' :
      if (!insidequote && (commentlevel == 0)) {
        addr = aStr.mid(addrstart, index-addrstart);
        if (!addr.isEmpty())
          list += addr.simplifyWhiteSpace();
        addrstart = index+1;
      }
      break;
    }
  }
  // append the last address to the list
  if (!insidequote && (commentlevel == 0)) {
    addr = aStr.mid(addrstart, aStr.length()-addrstart);
    if (!addr.isEmpty())
      list += addr.simplifyWhiteSpace();
  }
  else
    kdDebug(5300) << "Error in address splitting: "
                  << "Unexpected end of address list"
                  << endl;

  return list;
}

//-----------------------------------------------------------------------------
KPIM::EmailParseResult KPIM::isValidEmailAddress( const QString& aStr )
{
  // If we are passed an empty string bail right away no need to process further
  // and waste resources
  if ( aStr.isEmpty() ) {
    return AddressEmpty;
  }

  // count how many @'s are in the string that is passed to us
  // if 0 or > 1 take action
  // at this point to many @'s cannot bail out right away since
  // @ is allowed in qoutes, so we use a bool to keep track
  // and then make a judgement further down in the parser
  // FIXME count only @ not in double quotes

  bool tooManyAtsFlag = false;

  int atCount = aStr.contains('@');
  if ( atCount > 1 ) {
    tooManyAtsFlag = true;;
  } else if ( atCount == 0 ) {
	  return TooFewAts;
  }

  // The main parser, try and catch all weird and wonderful
  // mistakes users and/or machines can create

  enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
  bool inQuotedString = false;
  int commentLevel = 0;

  unsigned int strlen = aStr.length();

  for ( unsigned int index=0; index < strlen; index++ ) {
    switch ( context ) {
    case TopLevel : {
      switch ( aStr[index].latin1() ) {
        case '"' : inQuotedString = !inQuotedString;
          break;
        case '(' :
          if ( !inQuotedString ) {
            context = InComment;
            commentLevel = 1;
          }
          break;
        case '<' :
          if ( !inQuotedString ) {
            context = InAngleAddress;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
          break;
        case ',' :
          if ( !inQuotedString )
            return UnexpectedComma;
          break;
        case ')' :
          if ( !inQuotedString )
            return UnbalancedParens;
          break;
        case '>' :
          if ( !inQuotedString )
            return UnopenedAngleAddr;
          break;
        case '@' :
          if ( !inQuotedString ) {
            if ( index == 0 ) {  // Missing local part
              return MissingLocalPart;
            } else if( index == strlen-1 ) {
              return MissingDomainPart;
              break;
              }
            } else if ( inQuotedString ) {
              --atCount;
              if ( atCount == 1 ) {
                tooManyAtsFlag = false;
              }
            }
            break;
      }
      break;
    }
    case InComment : {
      switch ( aStr[index] ) {
        case '(' : ++commentLevel;
          break;
        case ')' : --commentLevel;
          if ( commentLevel == 0 ) {
            context = TopLevel;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
            break;
        }
        break;
    }

    case InAngleAddress : {
      switch ( aStr[index] ) {
        case '"' : inQuotedString = !inQuotedString;
            break;
        case '@' :
          if ( inQuotedString ) {
            --atCount;
            if ( atCount == 1 ) {
              tooManyAtsFlag = false;
            }
          }
          break;
        case '>' :
          if ( !inQuotedString ) {
            context = TopLevel;
            break;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
          break;
          }
        break;
      }
    }
  }
  if ( context == InComment )
    return UnbalancedParens;

  if ( context == TopLevel && !tooManyAtsFlag ) {
    return AddressOk;
  }

  if ( context == InAngleAddress )
    return UnclosedAngleAddr;

  if ( tooManyAtsFlag ) {
    return TooManyAts;
  }
  return AddressEmpty;
}

//-----------------------------------------------------------------------------
QString KPIM::emailParseResultToString( EmailParseResult errorCode )
{
  switch ( errorCode ) {
    case TooManyAts :
      return i18n("The email address you entered is not valid because it "
                "contains more than one @ "
                "you will not create valid messages if you do not "
                "change your address.");
    case TooFewAts :
      return i18n("The email address you entered is not valid because it "
                "does not contain a @ "
                "you will not create valid messages if you do not "
                "change your address.");
    case AddressEmpty :
      return i18n("You have to enter something in the email address field.");
    case MissingLocalPart :
      return i18n("The email address you entered is not valid because it "
                "does not contain a local part.");
    case MissingDomainPart :
      return i18n("The email address you entered is not valid because it "
                "does not contain a domain part.");
    case UnbalancedParens :
      return i18n("The email address you entered is not valid because it "
                "contains unclosed comments/brackets.");
    case AddressOk :
      return i18n("The email address you entered is valid.");
    case UnclosedAngleAddr :
      return i18n("The email address you entered is not valid because it "
                "contains an unclosed anglebracket.");
    case UnopenedAngleAddr :
      return i18n("The email address you entered is not valid because it "
                "contains an unopened anglebracket.");
    case UnexpectedComma :
      return i18n("The email address you have entered is not valid because it "
                "contains an unexpected comma.");
    case UnexpectedEnd :
      return i18n("The email address you entered is not valid because it ended "
                "unexpectadly, this probably means you have used an escaping type "
                "character like an \\  as the last character in your email "
                "address.");
  }
  return i18n("Unknown problem with email address");
}

//-----------------------------------------------------------------------------
bool KPIM::isValidSimpleEmailAddress( const QString& aStr )
{
  // If we are passed an empty string bail right away no need to process further
  // and waste resources
  if ( aStr.isEmpty() ) {
    return false;
  }

  int atChar = aStr.findRev( '@' );
  QString domainPart = aStr.mid( atChar + 1);
  QString localPart = aStr.left( atChar );
  bool tooManyAtsFlag = false;
  bool inQuotedString = false;
  int atCount = localPart.contains( '@' );

  unsigned int strlen = localPart.length();
  for ( unsigned int index=0; index < strlen; index++ ) {
    switch( localPart[ index ].latin1() ) {
      case '"' : inQuotedString = !inQuotedString;
        break;
      case '@' :
        if ( inQuotedString ) {
          --atCount;
          if ( atCount == 0 ) {
            tooManyAtsFlag = false;
          }
        }
        break;
      }
  }

  QString addrRx = "[a-zA-Z]*[\\w.-]*[a-zA-Z0-9]@";
  if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
    addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
  }
  if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
    addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
  } else {
    addrRx += "[\\w-]+(\\.[\\w-]+)";
  }
  QRegExp rx( addrRx );
  return  rx.exactMatch( aStr ) && !tooManyAtsFlag;
}

//-----------------------------------------------------------------------------
QCString KPIM::getEmailAddr(const QString& aStr)
{
  int a, i, j, len, found = 0;
  QChar c;
  // Find the '@' in the email address:
  a = aStr.find('@');
  if (a<0) return aStr.latin1();
  // Loop backwards until we find '<', '(', ' ', or beginning of string.
  for (i = a - 1; i >= 0; i--) {
    c = aStr[i];
    if (c == '<' || c == '(' || c == ' ') found = 1;
    if (found) break;
  }
  // Reset found for next loop.
  found = 0;
  // Loop forwards until we find '>', ')', ' ', or end of string.
  for (j = a + 1; j < (int)aStr.length(); j++) {
    c = aStr[j];
    if (c == '>' || c == ')' || c == ' ') found = 1;
    if (found) break;
  }
  // Calculate the length and return the result.
  len = j - (i + 1);
  return aStr.mid(i+1,len).latin1();
}

bool KPIM::getNameAndMail(const QString& aStr, QString& name, QString& mail)
{
  name = QString::null;
  mail = QString::null;

  const int len=aStr.length();
  const char cQuotes = '"';

  bool bInComment, bInQuotesOutsideOfEmail;
  int i=0, iAd=0, iMailStart=0, iMailEnd=0;
  QChar c;

  // Find the '@' of the email address
  // skipping all '@' inside "(...)" comments:
  bInComment = false;
  while( i < len ){
    c = aStr[i];
    if( !bInComment ){
      if( '(' == c ){
        bInComment = true;
      }else{
        if( '@' == c ){
          iAd = i;
          break; // found it
        }
      }
    }else{
      if( ')' == c ){
        bInComment = false;
      }
    }
    ++i;
  }

  if( !iAd ){
    // We suppose the user is typing the string manually and just
    // has not finished typing the mail address part.
    // So we take everything that's left of the '<' as name and the rest as mail
    for( i = 0; len > i; ++i ) {
      c = aStr[i];
      if( '<' != c )
        name.append( c );
      else
        break;
    }
    mail = aStr.mid( i+1 );
    if ( mail.endsWith( ">" ) )
      mail.truncate( mail.length() - 1 );

  }else{

    // Loop backwards until we find the start of the string
    // or a ',' that is outside of a comment
    //          and outside of quoted text before the leading '<'.
    bInComment = false;
    bInQuotesOutsideOfEmail = false;
    for( i = iAd-1; 0 <= i; --i ) {
      c = aStr[i];
      if( bInComment ){
        if( '(' == c ){
          if( !name.isEmpty() )
            name.prepend( ' ' );
          bInComment = false;
        }else{
          name.prepend( c ); // all comment stuff is part of the name
        }
      }else if( bInQuotesOutsideOfEmail ){
        if( cQuotes == c )
          bInQuotesOutsideOfEmail = false;
        else
          name.prepend( c );
      }else{
        // found the start of this addressee ?
        if( ',' == c )
          break;
        // stuff is before the leading '<' ?
        if( iMailStart ){
          if( cQuotes == c )
            bInQuotesOutsideOfEmail = true; // end of quoted text found
          else
            name.prepend( c );
        }else{
          switch( c ){
            case '<':
              iMailStart = i;
              break;
            case ')':
              if( !name.isEmpty() )
                name.prepend( ' ' );
              bInComment = true;
              break;
            default:
              if( ' ' != c )
                mail.prepend( c );
          }
        }
      }
    }

    name = name.simplifyWhiteSpace();
    mail = mail.simplifyWhiteSpace();

    if( mail.isEmpty() )
      return false;

    mail.append('@');

    // Loop forward until we find the end of the string
    // or a ',' that is outside of a comment
    //          and outside of quoted text behind the trailing '>'.
    bInComment = false;
    bInQuotesOutsideOfEmail = false;
    for( i = iAd+1; len > i; ++i ) {
      c = aStr[i];
      if( bInComment ){
        if( ')' == c ){
          if( !name.isEmpty() )
            name.append( ' ' );
          bInComment = false;
        }else{
          name.append( c ); // all comment stuff is part of the name
        }
      }else if( bInQuotesOutsideOfEmail ){
        if( cQuotes == c )
          bInQuotesOutsideOfEmail = false;
        else
          name.append( c );
      }else{
        // found the end of this addressee ?
        if( ',' == c )
          break;
        // stuff is behind the trailing '>' ?
        if( iMailEnd ){
          if( cQuotes == c )
            bInQuotesOutsideOfEmail = true; // start of quoted text found
          else
            name.append( c );
        }else{
          switch( c ){
            case '>':
              iMailEnd = i;
              break;
            case '(':
              if( !name.isEmpty() )
                name.append( ' ' );
              bInComment = true;
              break;
            default:
              if( ' ' != c )
                mail.append( c );
          }
        }
      }
    }
  }

  name = name.simplifyWhiteSpace();
  mail = mail.simplifyWhiteSpace();

  return ! (name.isEmpty() || mail.isEmpty());
}

bool KPIM::compareEmail( const QString& email1, const QString& email2,
                         bool matchName )
{
  QString e1Name, e1Email, e2Name, e2Email;

  getNameAndMail( email1, e1Name, e1Email );
  getNameAndMail( email2, e2Name, e2Email );

  return e1Email == e2Email &&
    ( !matchName || ( e1Name == e2Name ) );
}
<commit_msg>refactor code to make it readable and also correct and stop the compiler from whining<commit_after>/*  -*- mode: C++; c-file-style: "gnu" -*-

    This file is part of kdepim.
    Copyright (c) 2004 KDEPIM developers

    This library 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 library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "email.h"
#include <kdebug.h>
#include <klocale.h>
#include <qregexp.h>

//-----------------------------------------------------------------------------
QStringList KPIM::splitEmailAddrList(const QString& aStr)
{
  // Features:
  // - always ignores quoted characters
  // - ignores everything (including parentheses and commas)
  //   inside quoted strings
  // - supports nested comments
  // - ignores everything (including double quotes and commas)
  //   inside comments

  QStringList list;

  if (aStr.isEmpty())
    return list;

  QString addr;
  uint addrstart = 0;
  int commentlevel = 0;
  bool insidequote = false;

  for (uint index=0; index<aStr.length(); index++) {
    // the following conversion to latin1 is o.k. because
    // we can safely ignore all non-latin1 characters
    switch (aStr[index].latin1()) {
    case '"' : // start or end of quoted string
      if (commentlevel == 0)
        insidequote = !insidequote;
      break;
    case '(' : // start of comment
      if (!insidequote)
        commentlevel++;
      break;
    case ')' : // end of comment
      if (!insidequote) {
        if (commentlevel > 0)
          commentlevel--;
        else {
          kdDebug(5300) << "Error in address splitting: Unmatched ')'"
                        << endl;
          return list;
        }
      }
      break;
    case '\\' : // quoted character
      index++; // ignore the quoted character
      break;
    case ',' :
      if (!insidequote && (commentlevel == 0)) {
        addr = aStr.mid(addrstart, index-addrstart);
        if (!addr.isEmpty())
          list += addr.simplifyWhiteSpace();
        addrstart = index+1;
      }
      break;
    }
  }
  // append the last address to the list
  if (!insidequote && (commentlevel == 0)) {
    addr = aStr.mid(addrstart, aStr.length()-addrstart);
    if (!addr.isEmpty())
      list += addr.simplifyWhiteSpace();
  }
  else
    kdDebug(5300) << "Error in address splitting: "
                  << "Unexpected end of address list"
                  << endl;

  return list;
}

//-----------------------------------------------------------------------------
KPIM::EmailParseResult KPIM::isValidEmailAddress( const QString& aStr )
{
  // If we are passed an empty string bail right away no need to process further
  // and waste resources
  if ( aStr.isEmpty() ) {
    return AddressEmpty;
  }

  // count how many @'s are in the string that is passed to us
  // if 0 or > 1 take action
  // at this point to many @'s cannot bail out right away since
  // @ is allowed in qoutes, so we use a bool to keep track
  // and then make a judgement further down in the parser
  // FIXME count only @ not in double quotes

  bool tooManyAtsFlag = false;

  int atCount = aStr.contains('@');
  if ( atCount > 1 ) {
    tooManyAtsFlag = true;;
  } else if ( atCount == 0 ) {
	  return TooFewAts;
  }

  // The main parser, try and catch all weird and wonderful
  // mistakes users and/or machines can create

  enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
  bool inQuotedString = false;
  int commentLevel = 0;

  unsigned int strlen = aStr.length();

  for ( unsigned int index=0; index < strlen; index++ ) {
    switch ( context ) {
    case TopLevel : {
      switch ( aStr[index].latin1() ) {
        case '"' : inQuotedString = !inQuotedString;
          break;
        case '(' :
          if ( !inQuotedString ) {
            context = InComment;
            commentLevel = 1;
          }
          break;
        case '<' :
          if ( !inQuotedString ) {
            context = InAngleAddress;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
          break;
        case ',' :
          if ( !inQuotedString )
            return UnexpectedComma;
          break;
        case ')' :
          if ( !inQuotedString )
            return UnbalancedParens;
          break;
        case '>' :
          if ( !inQuotedString )
            return UnopenedAngleAddr;
          break;
        case '@' :
          if ( !inQuotedString ) {
            if ( index == 0 ) {  // Missing local part
              return MissingLocalPart;
            } else if( index == strlen-1 ) {
              return MissingDomainPart;
              break;
              }
            } else if ( inQuotedString ) {
              --atCount;
              if ( atCount == 1 ) {
                tooManyAtsFlag = false;
              }
            }
            break;
      }
      break;
    }
    case InComment : {
      switch ( aStr[index] ) {
        case '(' : ++commentLevel;
          break;
        case ')' : --commentLevel;
          if ( commentLevel == 0 ) {
            context = TopLevel;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
            break;
        }
        break;
    }

    case InAngleAddress : {
      switch ( aStr[index] ) {
        case '"' : inQuotedString = !inQuotedString;
            break;
        case '@' :
          if ( inQuotedString ) {
            --atCount;
            if ( atCount == 1 ) {
              tooManyAtsFlag = false;
            }
          }
          break;
        case '>' :
          if ( !inQuotedString ) {
            context = TopLevel;
            break;
          }
          break;
        case '\\' : // quoted character
          ++index; // skip the '\'
          if ( ++index > strlen )
            return UnexpectedEnd;
          break;
          }
        break;
      }
    }
  }
  if ( context == InComment )
    return UnbalancedParens;

  if ( context == InAngleAddress )
    return UnclosedAngleAddr;

  if ( tooManyAtsFlag ) {
    return TooManyAts;
  }
  return AddressOk;
}

//-----------------------------------------------------------------------------
QString KPIM::emailParseResultToString( EmailParseResult errorCode )
{
  switch ( errorCode ) {
    case TooManyAts :
      return i18n("The email address you entered is not valid because it "
                "contains more than one @ "
                "you will not create valid messages if you do not "
                "change your address.");
    case TooFewAts :
      return i18n("The email address you entered is not valid because it "
                "does not contain a @ "
                "you will not create valid messages if you do not "
                "change your address.");
    case AddressEmpty :
      return i18n("You have to enter something in the email address field.");
    case MissingLocalPart :
      return i18n("The email address you entered is not valid because it "
                "does not contain a local part.");
    case MissingDomainPart :
      return i18n("The email address you entered is not valid because it "
                "does not contain a domain part.");
    case UnbalancedParens :
      return i18n("The email address you entered is not valid because it "
                "contains unclosed comments/brackets.");
    case AddressOk :
      return i18n("The email address you entered is valid.");
    case UnclosedAngleAddr :
      return i18n("The email address you entered is not valid because it "
                "contains an unclosed anglebracket.");
    case UnopenedAngleAddr :
      return i18n("The email address you entered is not valid because it "
                "contains an unopened anglebracket.");
    case UnexpectedComma :
      return i18n("The email address you have entered is not valid because it "
                "contains an unexpected comma.");
    case UnexpectedEnd :
      return i18n("The email address you entered is not valid because it ended "
                "unexpectadly, this probably means you have used an escaping type "
                "character like an \\  as the last character in your email "
                "address.");
  }
  return i18n("Unknown problem with email address");
}

//-----------------------------------------------------------------------------
bool KPIM::isValidSimpleEmailAddress( const QString& aStr )
{
  // If we are passed an empty string bail right away no need to process further
  // and waste resources
  if ( aStr.isEmpty() ) {
    return false;
  }

  int atChar = aStr.findRev( '@' );
  QString domainPart = aStr.mid( atChar + 1);
  QString localPart = aStr.left( atChar );
  bool tooManyAtsFlag = false;
  bool inQuotedString = false;
  int atCount = localPart.contains( '@' );

  unsigned int strlen = localPart.length();
  for ( unsigned int index=0; index < strlen; index++ ) {
    switch( localPart[ index ].latin1() ) {
      case '"' : inQuotedString = !inQuotedString;
        break;
      case '@' :
        if ( inQuotedString ) {
          --atCount;
          if ( atCount == 0 ) {
            tooManyAtsFlag = false;
          }
        }
        break;
      }
  }

  QString addrRx = "[a-zA-Z]*[\\w.-]*[a-zA-Z0-9]@";
  if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
    addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
  }
  if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
    addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
  } else {
    addrRx += "[\\w-]+(\\.[\\w-]+)";
  }
  QRegExp rx( addrRx );
  return  rx.exactMatch( aStr ) && !tooManyAtsFlag;
}

//-----------------------------------------------------------------------------
QCString KPIM::getEmailAddr(const QString& aStr)
{
  int a, i, j, len, found = 0;
  QChar c;
  // Find the '@' in the email address:
  a = aStr.find('@');
  if (a<0) return aStr.latin1();
  // Loop backwards until we find '<', '(', ' ', or beginning of string.
  for (i = a - 1; i >= 0; i--) {
    c = aStr[i];
    if (c == '<' || c == '(' || c == ' ') found = 1;
    if (found) break;
  }
  // Reset found for next loop.
  found = 0;
  // Loop forwards until we find '>', ')', ' ', or end of string.
  for (j = a + 1; j < (int)aStr.length(); j++) {
    c = aStr[j];
    if (c == '>' || c == ')' || c == ' ') found = 1;
    if (found) break;
  }
  // Calculate the length and return the result.
  len = j - (i + 1);
  return aStr.mid(i+1,len).latin1();
}

bool KPIM::getNameAndMail(const QString& aStr, QString& name, QString& mail)
{
  name = QString::null;
  mail = QString::null;

  const int len=aStr.length();
  const char cQuotes = '"';

  bool bInComment, bInQuotesOutsideOfEmail;
  int i=0, iAd=0, iMailStart=0, iMailEnd=0;
  QChar c;

  // Find the '@' of the email address
  // skipping all '@' inside "(...)" comments:
  bInComment = false;
  while( i < len ){
    c = aStr[i];
    if( !bInComment ){
      if( '(' == c ){
        bInComment = true;
      }else{
        if( '@' == c ){
          iAd = i;
          break; // found it
        }
      }
    }else{
      if( ')' == c ){
        bInComment = false;
      }
    }
    ++i;
  }

  if( !iAd ){
    // We suppose the user is typing the string manually and just
    // has not finished typing the mail address part.
    // So we take everything that's left of the '<' as name and the rest as mail
    for( i = 0; len > i; ++i ) {
      c = aStr[i];
      if( '<' != c )
        name.append( c );
      else
        break;
    }
    mail = aStr.mid( i+1 );
    if ( mail.endsWith( ">" ) )
      mail.truncate( mail.length() - 1 );

  }else{

    // Loop backwards until we find the start of the string
    // or a ',' that is outside of a comment
    //          and outside of quoted text before the leading '<'.
    bInComment = false;
    bInQuotesOutsideOfEmail = false;
    for( i = iAd-1; 0 <= i; --i ) {
      c = aStr[i];
      if( bInComment ){
        if( '(' == c ){
          if( !name.isEmpty() )
            name.prepend( ' ' );
          bInComment = false;
        }else{
          name.prepend( c ); // all comment stuff is part of the name
        }
      }else if( bInQuotesOutsideOfEmail ){
        if( cQuotes == c )
          bInQuotesOutsideOfEmail = false;
        else
          name.prepend( c );
      }else{
        // found the start of this addressee ?
        if( ',' == c )
          break;
        // stuff is before the leading '<' ?
        if( iMailStart ){
          if( cQuotes == c )
            bInQuotesOutsideOfEmail = true; // end of quoted text found
          else
            name.prepend( c );
        }else{
          switch( c ){
            case '<':
              iMailStart = i;
              break;
            case ')':
              if( !name.isEmpty() )
                name.prepend( ' ' );
              bInComment = true;
              break;
            default:
              if( ' ' != c )
                mail.prepend( c );
          }
        }
      }
    }

    name = name.simplifyWhiteSpace();
    mail = mail.simplifyWhiteSpace();

    if( mail.isEmpty() )
      return false;

    mail.append('@');

    // Loop forward until we find the end of the string
    // or a ',' that is outside of a comment
    //          and outside of quoted text behind the trailing '>'.
    bInComment = false;
    bInQuotesOutsideOfEmail = false;
    for( i = iAd+1; len > i; ++i ) {
      c = aStr[i];
      if( bInComment ){
        if( ')' == c ){
          if( !name.isEmpty() )
            name.append( ' ' );
          bInComment = false;
        }else{
          name.append( c ); // all comment stuff is part of the name
        }
      }else if( bInQuotesOutsideOfEmail ){
        if( cQuotes == c )
          bInQuotesOutsideOfEmail = false;
        else
          name.append( c );
      }else{
        // found the end of this addressee ?
        if( ',' == c )
          break;
        // stuff is behind the trailing '>' ?
        if( iMailEnd ){
          if( cQuotes == c )
            bInQuotesOutsideOfEmail = true; // start of quoted text found
          else
            name.append( c );
        }else{
          switch( c ){
            case '>':
              iMailEnd = i;
              break;
            case '(':
              if( !name.isEmpty() )
                name.append( ' ' );
              bInComment = true;
              break;
            default:
              if( ' ' != c )
                mail.append( c );
          }
        }
      }
    }
  }

  name = name.simplifyWhiteSpace();
  mail = mail.simplifyWhiteSpace();

  return ! (name.isEmpty() || mail.isEmpty());
}

bool KPIM::compareEmail( const QString& email1, const QString& email2,
                         bool matchName )
{
  QString e1Name, e1Email, e2Name, e2Email;

  getNameAndMail( email1, e1Name, e1Email );
  getNameAndMail( email2, e2Name, e2Email );

  return e1Email == e2Email &&
    ( !matchName || ( e1Name == e2Name ) );
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: NavigatorChildWindow.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: obo $ $Date: 2004-01-20 10:39:36 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "NavigatorChildWindow.hxx"

#ifndef SD_NAVIGATOR_HXX
#include "navigatr.hxx"
#endif
#include "app.hrc"
#include "navigatr.hrc"
#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif

namespace sd {

SFX_IMPL_CHILDWINDOWCONTEXT(NavigatorChildWindow, SID_NAVIGATOR)

NavigatorChildWindow::NavigatorChildWindow (
    ::Window* pParent,
    USHORT nId,
    SfxBindings* pBindings,
    SfxChildWinInfo* pInfo )
    : SfxChildWindowContext( nId )
{
    SdNavigatorWin* pNavWin = new SdNavigatorWin( pParent, this,
                                        SdResId( FLT_NAVIGATOR ), pBindings );

    SetWindow( pNavWin );
}

} // end of namespace sd
<commit_msg>INTEGRATION: CWS ooo19126 (1.2.562); FILE MERGED 2005/09/05 13:21:36 rt 1.2.562.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: NavigatorChildWindow.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 03:49:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "NavigatorChildWindow.hxx"

#ifndef SD_NAVIGATOR_HXX
#include "navigatr.hxx"
#endif
#include "app.hrc"
#include "navigatr.hrc"
#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif

namespace sd {

SFX_IMPL_CHILDWINDOWCONTEXT(NavigatorChildWindow, SID_NAVIGATOR)

NavigatorChildWindow::NavigatorChildWindow (
    ::Window* pParent,
    USHORT nId,
    SfxBindings* pBindings,
    SfxChildWinInfo* pInfo )
    : SfxChildWindowContext( nId )
{
    SdNavigatorWin* pNavWin = new SdNavigatorWin( pParent, this,
                                        SdResId( FLT_NAVIGATOR ), pBindings );

    SetWindow( pNavWin );
}

} // end of namespace sd
<|endoftext|>


<commit_before>#include "UnitTest++/UnitTest++.h"
#include <monsoon/history/dir/io/replacement_map.h>
#include <vector>
#include <cstring>

using monsoon::history::io::replacement_map;

template<typename T>
auto read_all_at(const replacement_map& map, monsoon::io::fd::offset_type off, std::size_t len) -> std::vector<T> {
  static_assert(sizeof(T) == 1);

  auto buf = std::vector<T>(len, T());
  for (std::size_t total_rlen = 0; total_rlen < len; ) {
    std::size_t nbytes = len - total_rlen;
    const auto rlen = map.read_at(off + total_rlen, buf.data() + total_rlen, nbytes);
    if (rlen == 0u) {
      buf.resize(total_rlen);
      break;
    }

    CHECK_EQUAL(true, rlen > 0); // some data was read.
    CHECK_EQUAL(len - total_rlen, nbytes); // nbytes is not changed.

    total_rlen += rlen;
  }

  return buf;
}

TEST(empty_read) {
  replacement_map map;

  std::size_t nbytes = 17;
  auto buf = std::vector<std::uint8_t>(nbytes, std::uint8_t());
  auto rlen = map.read_at(5, buf.data(), nbytes);

  CHECK_EQUAL(17u, nbytes);
  CHECK_EQUAL(0u, rlen);
}

TEST(write_and_commit) {
  replacement_map map;
  const auto bytes = u8"foobar";

  map.write_at(17, &bytes[0], 6).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(std::string_view(bytes, 6), std::string_view(buf.data(), buf.size()));
}

TEST(write_uncommitted) {
  replacement_map map;
  const auto bytes = u8"foobar";

  map.write_at(17, &bytes[0], 6);
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(0u, buf.size());
}

TEST(write_replace) {
  replacement_map map;

  map.write_at(17, u8"foobar", 6).commit();
  map.write_at(18, u8"ffrr", 4).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(u8"fffrrr", std::string_view(buf.data(), buf.size()));
}

TEST(write_non_replace) {
  replacement_map map;

  map.write_at(17, u8"foo", 3).commit();
  map.write_at(19, u8"rbar", 4, false).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(u8"foobar", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_head) {
  replacement_map map;

  map.write_at(20, u8"xxxx", 4).commit();
  map.write_at(18, u8"yyyy", 4).commit();
  auto buf = read_all_at<char>(map, 18, 6);

  CHECK_EQUAL(u8"yyyyxx", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_tail) {
  replacement_map map;

  map.write_at(18, u8"xxxx", 4).commit();
  map.write_at(20, u8"yyyy", 4).commit();
  auto buf = read_all_at<char>(map, 18, 6);

  CHECK_EQUAL(u8"xxyyyy", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_many) {
  replacement_map map;

  map.write_at( 6, u8"x", 1).commit();
  map.write_at( 8, u8"x", 1).commit();
  map.write_at(10, u8"x", 1).commit();
  map.write_at( 6, u8"67890", 5).commit();
  auto buf = read_all_at<char>(map, 6, 5);

  CHECK_EQUAL(u8"67890", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_many_with_overlapping_head_and_tail) {
  replacement_map map;

  map.write_at( 5, u8"xx", 2).commit();
  map.write_at( 8, u8"y",  1).commit();
  map.write_at(10, u8"zz", 2).commit();
  map.write_at( 6, u8"67890", 5).commit();
  auto buf = read_all_at<char>(map, 5, 7);

  CHECK_EQUAL(u8"x67890z", std::string_view(buf.data(), buf.size()));
}

TEST(iterate) {
  replacement_map map;
  map.write_at( 0, u8"xx", 2).commit();
  map.write_at( 4, u8"y",  1).commit();
  map.write_at(10, u8"zz", 2).commit();

  replacement_map::const_iterator iter = map.cbegin();
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(0u, iter->begin_offset());
  CHECK_EQUAL(2u, iter->end_offset());
  CHECK_EQUAL(2u, iter->size());
  if (iter->size() >= 2u) CHECK(std::memcmp(u8"xx", iter->data(), 2u) == 0);

  ++iter;
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(4u, iter->begin_offset());
  CHECK_EQUAL(5u, iter->end_offset());
  CHECK_EQUAL(1u, iter->size());
  if (iter->size() >= 1u) CHECK(std::memcmp(u8"y", iter->data(), 1u) == 0);

  ++iter;
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(10u, iter->begin_offset());
  CHECK_EQUAL(12u, iter->end_offset());
  CHECK_EQUAL(2u, iter->size());
  if (iter->size() >= 2u) CHECK(std::memcmp(u8"zz", iter->data(), 2u) == 0);

  ++iter;
  CHECK(iter == map.end());
}

int main(int argc, char** argv) {
  return UnitTest::RunAllTests();
}
<commit_msg>Make test look slightly better.<commit_after>#include "UnitTest++/UnitTest++.h"
#include <monsoon/history/dir/io/replacement_map.h>
#include <vector>
#include <cstring>

using monsoon::history::io::replacement_map;

template<typename T>
auto read_all_at(const replacement_map& map, monsoon::io::fd::offset_type off, std::size_t len) -> std::vector<T> {
  static_assert(sizeof(T) == 1);

  auto buf = std::vector<T>(len, T());
  for (std::size_t total_rlen = 0; total_rlen < len; ) {
    std::size_t nbytes = len - total_rlen;
    const auto rlen = map.read_at(off + total_rlen, buf.data() + total_rlen, nbytes);
    if (rlen == 0u) {
      buf.resize(total_rlen);
      break;
    }

    CHECK(rlen > 0); // some data was read.
    CHECK_EQUAL(len - total_rlen, nbytes); // nbytes is not changed.

    total_rlen += rlen;
  }

  return buf;
}

TEST(empty_read) {
  replacement_map map;

  std::size_t nbytes = 17;
  auto buf = std::vector<std::uint8_t>(nbytes, std::uint8_t());
  auto rlen = map.read_at(5, buf.data(), nbytes);

  CHECK_EQUAL(17u, nbytes);
  CHECK_EQUAL(0u, rlen);
}

TEST(write_and_commit) {
  replacement_map map;
  const auto bytes = u8"foobar";

  map.write_at(17, &bytes[0], 6).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(std::string_view(bytes, 6), std::string_view(buf.data(), buf.size()));
}

TEST(write_uncommitted) {
  replacement_map map;
  const auto bytes = u8"foobar";

  map.write_at(17, &bytes[0], 6);
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(0u, buf.size());
}

TEST(write_replace) {
  replacement_map map;

  map.write_at(17, u8"foobar", 6).commit();
  map.write_at(18, u8"ffrr", 4).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(u8"fffrrr", std::string_view(buf.data(), buf.size()));
}

TEST(write_non_replace) {
  replacement_map map;

  map.write_at(17, u8"foo", 3).commit();
  map.write_at(19, u8"rbar", 4, false).commit();
  auto buf = read_all_at<char>(map, 17, 6);

  CHECK_EQUAL(u8"foobar", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_head) {
  replacement_map map;

  map.write_at(20, u8"xxxx", 4).commit();
  map.write_at(18, u8"yyyy", 4).commit();
  auto buf = read_all_at<char>(map, 18, 6);

  CHECK_EQUAL(u8"yyyyxx", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_tail) {
  replacement_map map;

  map.write_at(18, u8"xxxx", 4).commit();
  map.write_at(20, u8"yyyy", 4).commit();
  auto buf = read_all_at<char>(map, 18, 6);

  CHECK_EQUAL(u8"xxyyyy", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_many) {
  replacement_map map;

  map.write_at( 6, u8"x", 1).commit();
  map.write_at( 8, u8"x", 1).commit();
  map.write_at(10, u8"x", 1).commit();
  map.write_at( 6, u8"67890", 5).commit();
  auto buf = read_all_at<char>(map, 6, 5);

  CHECK_EQUAL(u8"67890", std::string_view(buf.data(), buf.size()));
}

TEST(write_replace_many_with_overlapping_head_and_tail) {
  replacement_map map;

  map.write_at( 5, u8"xx", 2).commit();
  map.write_at( 8, u8"y",  1).commit();
  map.write_at(10, u8"zz", 2).commit();
  map.write_at( 6, u8"67890", 5).commit();
  auto buf = read_all_at<char>(map, 5, 7);

  CHECK_EQUAL(u8"x67890z", std::string_view(buf.data(), buf.size()));
}

TEST(iterate) {
  replacement_map map;
  map.write_at( 0, u8"xx", 2).commit();
  map.write_at( 4, u8"y",  1).commit();
  map.write_at(10, u8"zz", 2).commit();

  replacement_map::const_iterator iter = map.cbegin();
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(0u, iter->begin_offset());
  CHECK_EQUAL(2u, iter->end_offset());
  CHECK_EQUAL(2u, iter->size());
  if (iter->size() >= 2u) CHECK(std::memcmp(u8"xx", iter->data(), 2u) == 0);

  ++iter;
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(4u, iter->begin_offset());
  CHECK_EQUAL(5u, iter->end_offset());
  CHECK_EQUAL(1u, iter->size());
  if (iter->size() >= 1u) CHECK(std::memcmp(u8"y", iter->data(), 1u) == 0);

  ++iter;
  REQUIRE CHECK(iter != map.end());
  CHECK_EQUAL(10u, iter->begin_offset());
  CHECK_EQUAL(12u, iter->end_offset());
  CHECK_EQUAL(2u, iter->size());
  if (iter->size() >= 2u) CHECK(std::memcmp(u8"zz", iter->data(), 2u) == 0);

  ++iter;
  CHECK(iter == map.end());
}

int main(int argc, char** argv) {
  return UnitTest::RunAllTests();
}
<|endoftext|>


<commit_before>#include <rleahylib/rleahylib.hpp>
#include <mod.hpp>
#include <server.hpp>
#include <cstring>
#include <fstream>

using namespace MCPP;

static const String name("Favicon Loader");
static const Word priority=1;
static const String filename("favicon.png");
static const String no_favicon("{0} could not be opened");
static const String invalid_format("{0} is not a valid PNG file");
static const String wrong_size("{0} must be {1}x{2}");
static const String favicon_key("favicon");
static const Byte png_header []={137,80,78,71,13,10,26,10};
static const Word height=64;
static const Word width=64;

class FaviconLoader : public Module {

	private:

		enum class Reason {

			Success,
			CouldNotOpen,
			Format,
			Size

		};

		static void cleanup () {

			Server::Get().Data().DeleteBinary(favicon_key);

		}

		static bool verify (const Byte * begin, const Byte * end) {

			for (auto b : png_header) {

				if (b!=*begin) return false;

				++begin;

			}

			return true;

		}

		static UInt32 get (const Byte * begin, const Byte *) {

			union {
				UInt32 retr;
				Byte buffer [sizeof(UInt32)];
			};

			if (Endianness::IsBigEndian<UInt32>()) {

				std::memcpy(buffer,begin,sizeof(UInt32));

			} else {

				for (Word i=0;i<sizeof(UInt32);++i) buffer[i]=begin[sizeof(UInt32)-i-1];

			}

			return retr;

		}

		//	Reloads favicon.png
		static Reason reload () {

			//	Get full path to favicon
			auto c_filename=Path::Combine(
				Path::GetPath(
					File::GetCurrentExecutableFileName()
				),
				filename
			).ToCString();

			//	Open a binary stream to read
			//	the favicon
			std::fstream stream(
				static_cast<char *>(c_filename),
				std::ios::in|std::ios::binary
			);

			auto & data=Server::Get().Data();

			//	If the favicon file could not
			//	be opened, fail out
			if (!stream) {

				cleanup();

				return Reason::CouldNotOpen;

			}

			//	This buffer holds the binary
			//	contents of the file
			//
			//	We read -- or try to read -- at
			//	least 20 bytes so we can verify:
			//
			//	1.	That it's a PNG file (from the
			//		first 8 bytes).
			//	2.	Its dimensions (which means we
			//		need to read the length header
			//		for the IHDR chunk, then the
			//		chunk type for the IHDR chunk,
			//		and then the first 8 bytes of
			//		the IHDR chunk).
			Vector<Byte> buffer(24);

			//	Read in enough information that we
			//	can determine the size of the image
			stream.read(
				reinterpret_cast<char *>(buffer.end()),
				24
			);

			//	If we couldn't read 20 bytes, there's
			//	no way it's a valid PNG file, fail out
			if (stream.gcount()!=24) {

				cleanup();

				return Reason::Format;

			}

			buffer.SetCount(24);

			//	Verify the header
			if (!verify(buffer.begin(),buffer.begin()+8)) {

				cleanup();

				return Reason::Format;

			}

			//	Verify sizes
			if (
				(get(
					buffer.begin()+16,
					buffer.begin()+20
				)!=width) ||
				(get(
					buffer.begin()+20,
					buffer.end()
				)!=height)
			) {

				cleanup();

				return Reason::Size;

			}

			//	Extract the entire file
			do {

				buffer.SetCapacity();

				stream.read(
					reinterpret_cast<char *>(buffer.end()),
					int(SafeWord(buffer.Capacity()-buffer.Count()))
				);

				buffer.SetCount(buffer.Count()+stream.gcount());

			} while (stream);

			//	Store the image in the backing
			//	store
			data.SaveBinary(
				favicon_key,
				buffer.begin(),
				buffer.Count()
			);

			//	Success
			return Reason::Success;

		}

	public:

		virtual const String & Name () const noexcept override {

			return name;

		}

		virtual Word Priority () const noexcept override {

			return priority;

		}

		virtual void Install () override {

			switch (reload()) {

				case Reason::Success:
				default:break;

				case Reason::CouldNotOpen:
					Server::Get().WriteLog(
						String::Format(
							no_favicon,
							filename
						),
						//	This is just informative, perhaps
						//	the user does not actually want
						//	a favicon
						Service::LogType::Information
					);
					break;

				case Reason::Format:
					Server::Get().WriteLog(
						String::Format(
							invalid_format,
							filename
						),
						//	This is an error.  The user
						//	clearly attempted to provide a
						//	favicon, but it's invalid
						Service::LogType::Error
					);
					break;

				case Reason::Size:
					Server::Get().WriteLog(
						String::Format(
							wrong_size,
							filename,
							width,
							height
						),
						//	This is an error.  The user
						//	clearly attempted to provide a
						//	favicon, but it's the wrong
						//	size
						Service::LogType::Error
					);
					break;

			}

		}

};

INSTALL_MODULE(FaviconLoader)
<commit_msg>Comment Fix<commit_after>#include <rleahylib/rleahylib.hpp>
#include <mod.hpp>
#include <server.hpp>
#include <cstring>
#include <fstream>

using namespace MCPP;

static const String name("Favicon Loader");
static const Word priority=1;
static const String filename("favicon.png");
static const String no_favicon("{0} could not be opened");
static const String invalid_format("{0} is not a valid PNG file");
static const String wrong_size("{0} must be {1}x{2}");
static const String favicon_key("favicon");
static const Byte png_header []={137,80,78,71,13,10,26,10};
static const Word height=64;
static const Word width=64;

class FaviconLoader : public Module {

	private:

		enum class Reason {

			Success,
			CouldNotOpen,
			Format,
			Size

		};

		static void cleanup () {

			Server::Get().Data().DeleteBinary(favicon_key);

		}

		static bool verify (const Byte * begin, const Byte * end) {

			for (auto b : png_header) {

				if (b!=*begin) return false;

				++begin;

			}

			return true;

		}

		static UInt32 get (const Byte * begin, const Byte *) {

			union {
				UInt32 retr;
				Byte buffer [sizeof(UInt32)];
			};

			if (Endianness::IsBigEndian<UInt32>()) {

				std::memcpy(buffer,begin,sizeof(UInt32));

			} else {

				for (Word i=0;i<sizeof(UInt32);++i) buffer[i]=begin[sizeof(UInt32)-i-1];

			}

			return retr;

		}

		//	Reloads favicon.png
		static Reason reload () {

			//	Get full path to favicon
			auto c_filename=Path::Combine(
				Path::GetPath(
					File::GetCurrentExecutableFileName()
				),
				filename
			).ToCString();

			//	Open a binary stream to read
			//	the favicon
			std::fstream stream(
				static_cast<char *>(c_filename),
				std::ios::in|std::ios::binary
			);

			auto & data=Server::Get().Data();

			//	If the favicon file could not
			//	be opened, fail out
			if (!stream) {

				cleanup();

				return Reason::CouldNotOpen;

			}

			//	This buffer holds the binary
			//	contents of the file
			//
			//	We read -- or try to read -- at
			//	least 24 bytes so we can verify:
			//
			//	1.	That it's a PNG file (from the
			//		first 8 bytes).
			//	2.	Its dimensions (which means we
			//		need to read the length header
			//		for the IHDR chunk, then the
			//		chunk type for the IHDR chunk,
			//		and then the first 8 bytes of
			//		the IHDR chunk).
			Vector<Byte> buffer(24);

			//	Read in enough information that we
			//	can determine the size of the image
			stream.read(
				reinterpret_cast<char *>(buffer.end()),
				24
			);

			//	If we couldn't read 24 bytes, there's
			//	no way it's a valid PNG file, fail out
			if (stream.gcount()!=24) {

				cleanup();

				return Reason::Format;

			}

			buffer.SetCount(24);

			//	Verify the header
			if (!verify(buffer.begin(),buffer.begin()+8)) {

				cleanup();

				return Reason::Format;

			}

			//	Verify sizes
			if (
				(get(
					buffer.begin()+16,
					buffer.begin()+20
				)!=width) ||
				(get(
					buffer.begin()+20,
					buffer.end()
				)!=height)
			) {

				cleanup();

				return Reason::Size;

			}

			//	Extract the entire file
			do {

				buffer.SetCapacity();

				stream.read(
					reinterpret_cast<char *>(buffer.end()),
					int(SafeWord(buffer.Capacity()-buffer.Count()))
				);

				buffer.SetCount(buffer.Count()+stream.gcount());

			} while (stream);

			//	Store the image in the backing
			//	store
			data.SaveBinary(
				favicon_key,
				buffer.begin(),
				buffer.Count()
			);

			//	Success
			return Reason::Success;

		}

	public:

		virtual const String & Name () const noexcept override {

			return name;

		}

		virtual Word Priority () const noexcept override {

			return priority;

		}

		virtual void Install () override {

			switch (reload()) {

				case Reason::Success:
				default:break;

				case Reason::CouldNotOpen:
					Server::Get().WriteLog(
						String::Format(
							no_favicon,
							filename
						),
						//	This is just informative, perhaps
						//	the user does not actually want
						//	a favicon
						Service::LogType::Information
					);
					break;

				case Reason::Format:
					Server::Get().WriteLog(
						String::Format(
							invalid_format,
							filename
						),
						//	This is an error.  The user
						//	clearly attempted to provide a
						//	favicon, but it's invalid
						Service::LogType::Error
					);
					break;

				case Reason::Size:
					Server::Get().WriteLog(
						String::Format(
							wrong_size,
							filename,
							width,
							height
						),
						//	This is an error.  The user
						//	clearly attempted to provide a
						//	favicon, but it's the wrong
						//	size
						Service::LogType::Error
					);
					break;

			}

		}

};

INSTALL_MODULE(FaviconLoader)
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Request.hxx"
#include "Stock.hxx"
#include "Client.hxx"
#include "HttpResponseHandler.hxx"
#include "lease.hxx"
#include "tcp_stock.hxx"
#include "access_log/ChildErrorLog.hxx"
#include "stock/Item.hxx"
#include "istream/UnusedPtr.hxx"
#include "pool/pool.hxx"
#include "AllocatorPtr.hxx"
#include "net/SocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/Cast.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cancellable.hxx"

#include <sys/socket.h>
#include <unistd.h>

class FcgiRequest final : Lease, Cancellable {
    struct pool &pool;

    StockItem *stock_item;

    ChildErrorLog log;

    CancellablePointer cancel_ptr;

public:
    FcgiRequest(struct pool &_pool, StockItem &_stock_item)
        :pool(_pool), stock_item(&_stock_item) {
    }

    void Start(EventLoop &event_loop, const char *site_name,
               const char *path,
               http_method_t method, const char *uri,
               const char *script_name, const char *path_info,
               const char *query_string,
               const char *document_root,
               const char *remote_addr,
               const StringMap &headers, UnusedIstreamPtr body,
               ConstBuffer<const char *> params,
               UniqueFileDescriptor &&stderr_fd,
               SocketDescriptor log_socket,
               HttpResponseHandler &handler,
               CancellablePointer &caller_cancel_ptr) {
        caller_cancel_ptr = *this;

        fcgi_stock_item_set_site(*stock_item, site_name);
        fcgi_stock_item_set_uri(*stock_item, uri);

        if (log_socket.IsDefined() && !stderr_fd.IsDefined())
            stderr_fd = log.EnableClient(event_loop, log_socket);

        const char *script_filename = fcgi_stock_translate_path(*stock_item, path,
                                                                pool);
        document_root = fcgi_stock_translate_path(*stock_item, document_root,
                                                  pool);

        fcgi_client_request(&pool, event_loop,
                            fcgi_stock_item_get(*stock_item),
                            fcgi_stock_item_get_domain(*stock_item) == AF_LOCAL
                            ? FdType::FD_SOCKET : FdType::FD_TCP,
                            *this,
                            method, uri,
                            script_filename,
                            script_name, path_info,
                            query_string,
                            document_root,
                            remote_addr,
                            headers, std::move(body),
                            params,
                            std::move(stderr_fd),
                            handler, cancel_ptr);
    }

private:
    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        fcgi_stock_aborted(*stock_item);

        cancel_ptr.Cancel();
    }

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) noexcept override {
        stock_item->Put(!reuse);
        stock_item = nullptr;
    }
};

void
fcgi_request(struct pool *pool, EventLoop &event_loop,
             FcgiStock *fcgi_stock,
             const char *site_name,
             const ChildOptions &options,
             const char *action,
             const char *path,
             ConstBuffer<const char *> args,
             http_method_t method, const char *uri,
             const char *script_name, const char *path_info,
             const char *query_string,
             const char *document_root,
             const char *remote_addr,
             const StringMap &headers, UnusedIstreamPtr body,
             ConstBuffer<const char *> params,
             UniqueFileDescriptor &&stderr_fd,
             HttpResponseHandler &handler,
             CancellablePointer &cancel_ptr)
{
    if (action == nullptr)
        action = path;

    StockItem *stock_item;
    try {
        stock_item = fcgi_stock_get(fcgi_stock, options,
                                    action,
                                    args);
    } catch (...) {
        body.Clear();
        handler.InvokeError(std::current_exception());
        return;
    }

    auto request = NewFromPool<FcgiRequest>(*pool, *pool, *stock_item);

    request->Start(event_loop, site_name, path, method, uri,
                   script_name, path_info,
                   query_string, document_root, remote_addr,
                   headers, std::move(body), params, std::move(stderr_fd),
                   fcgi_stock_get_log_socket(*fcgi_stock),
                   handler, cancel_ptr);
}
<commit_msg>fcgi/Request: call destructor<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Request.hxx"
#include "Stock.hxx"
#include "Client.hxx"
#include "HttpResponseHandler.hxx"
#include "lease.hxx"
#include "tcp_stock.hxx"
#include "access_log/ChildErrorLog.hxx"
#include "stock/Item.hxx"
#include "istream/UnusedPtr.hxx"
#include "pool/pool.hxx"
#include "AllocatorPtr.hxx"
#include "net/SocketDescriptor.hxx"
#include "io/UniqueFileDescriptor.hxx"
#include "util/Cast.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cancellable.hxx"
#include "util/LeakDetector.hxx"

#include <sys/socket.h>
#include <unistd.h>

class FcgiRequest final : Lease, Cancellable, LeakDetector {
    struct pool &pool;

    StockItem *stock_item;

    ChildErrorLog log;

    CancellablePointer cancel_ptr;

public:
    FcgiRequest(struct pool &_pool, StockItem &_stock_item)
        :pool(_pool), stock_item(&_stock_item) {
    }

    void Start(EventLoop &event_loop, const char *site_name,
               const char *path,
               http_method_t method, const char *uri,
               const char *script_name, const char *path_info,
               const char *query_string,
               const char *document_root,
               const char *remote_addr,
               const StringMap &headers, UnusedIstreamPtr body,
               ConstBuffer<const char *> params,
               UniqueFileDescriptor &&stderr_fd,
               SocketDescriptor log_socket,
               HttpResponseHandler &handler,
               CancellablePointer &caller_cancel_ptr) {
        caller_cancel_ptr = *this;

        fcgi_stock_item_set_site(*stock_item, site_name);
        fcgi_stock_item_set_uri(*stock_item, uri);

        if (log_socket.IsDefined() && !stderr_fd.IsDefined())
            stderr_fd = log.EnableClient(event_loop, log_socket);

        const char *script_filename = fcgi_stock_translate_path(*stock_item, path,
                                                                pool);
        document_root = fcgi_stock_translate_path(*stock_item, document_root,
                                                  pool);

        fcgi_client_request(&pool, event_loop,
                            fcgi_stock_item_get(*stock_item),
                            fcgi_stock_item_get_domain(*stock_item) == AF_LOCAL
                            ? FdType::FD_SOCKET : FdType::FD_TCP,
                            *this,
                            method, uri,
                            script_filename,
                            script_name, path_info,
                            query_string,
                            document_root,
                            remote_addr,
                            headers, std::move(body),
                            params,
                            std::move(stderr_fd),
                            handler, cancel_ptr);
    }

private:
    void Destroy() {
        DeleteFromPool(pool, this);
    }

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        fcgi_stock_aborted(*stock_item);

        cancel_ptr.Cancel();
    }

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) noexcept override {
        stock_item->Put(!reuse);
        stock_item = nullptr;

        Destroy();
    }
};

void
fcgi_request(struct pool *pool, EventLoop &event_loop,
             FcgiStock *fcgi_stock,
             const char *site_name,
             const ChildOptions &options,
             const char *action,
             const char *path,
             ConstBuffer<const char *> args,
             http_method_t method, const char *uri,
             const char *script_name, const char *path_info,
             const char *query_string,
             const char *document_root,
             const char *remote_addr,
             const StringMap &headers, UnusedIstreamPtr body,
             ConstBuffer<const char *> params,
             UniqueFileDescriptor &&stderr_fd,
             HttpResponseHandler &handler,
             CancellablePointer &cancel_ptr)
{
    if (action == nullptr)
        action = path;

    StockItem *stock_item;
    try {
        stock_item = fcgi_stock_get(fcgi_stock, options,
                                    action,
                                    args);
    } catch (...) {
        body.Clear();
        handler.InvokeError(std::current_exception());
        return;
    }

    auto request = NewFromPool<FcgiRequest>(*pool, *pool, *stock_item);

    request->Start(event_loop, site_name, path, method, uri,
                   script_name, path_info,
                   query_string, document_root, remote_addr,
                   headers, std::move(body), params, std::move(stderr_fd),
                   fcgi_stock_get_log_socket(*fcgi_stock),
                   handler, cancel_ptr);
}
<|endoftext|>


<commit_before>/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB

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

   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 */

#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif

#include "listener.h"
#include "priv.h"
#include <m_string.h>
#include <mysql.h>
#include <violite.h>
#ifndef __WIN__
#include <sys/un.h>
#endif
#include <sys/stat.h>

#include "thread_registry.h"
#include "options.h"
#include "instance_map.h"
#include "log.h"
#include "mysql_connection.h"
#include "portability.h"

/*
  Listener_thread - incapsulates listening functionality
*/

class Listener_thread: public Listener_thread_args
{
public:
  Listener_thread(const Listener_thread_args &args);
  ~Listener_thread();
  void run();
private:
  static const int LISTEN_BACK_LOG_SIZE= 5;     /* standard backlog size */
  ulong total_connection_count;
  Thread_info thread_info;

  int     sockets[2];
  int     num_sockets;
  fd_set  read_fds;
private:
  void handle_new_mysql_connection(Vio *vio);
  int   create_tcp_socket();
  int   create_unix_socket(struct sockaddr_un &unix_socket_address);
};

Listener_thread::Listener_thread(const Listener_thread_args &args) :
  Listener_thread_args(args.thread_registry, args.options, args.user_map,
                       args.instance_map)
  ,total_connection_count(0)
  ,thread_info(pthread_self())
  ,num_sockets(0)
{
}

Listener_thread::~Listener_thread()
{
}

/*
  Listener_thread::run() - listen all supported sockets and spawn a thread
  to handle incoming connection.
  Using 'die' in case of syscall failure is OK now - we don't hold any
  resources and 'die' kills the signal thread automatically. To be rewritten
  one day.
  See also comments in mysqlmanager.cc to picture general Instance Manager
  architecture.
*/

void Listener_thread::run()
{
  int n= 0;

#ifndef __WIN__
  /* we use this var to check whether we are running on LinuxThreads */
  pid_t thread_pid;

  thread_pid= getpid();

  struct sockaddr_un unix_socket_address;
  /* set global variable */
  linuxthreads= (thread_pid != manager_pid);
#endif

  thread_registry.register_thread(&thread_info);

  my_thread_init();

  FD_ZERO(&read_fds);

  /* I. prepare 'listen' sockets */
  if (create_tcp_socket())
    goto err;

#ifndef __WIN__
  if (create_unix_socket(unix_socket_address))
    goto err;
#endif

  /* II. Listen sockets and spawn childs */
  for (int i= 0; i < num_sockets; i++)
    n= max(n, sockets[i]);
  n++;

  timeval tv;
  while (!thread_registry.is_shutdown())
  {
    fd_set read_fds_arg= read_fds;
    /*
      We should reintialize timer as on linux it is modified
      to reflect amout of time not slept.
    */
    tv.tv_sec= 0;
    tv.tv_usec= 100000;

    /*
      When using valgrind 2.0 this syscall doesn't get kicked off by a
      signal during shutdown. This results in failing assert
      (Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
    */
    int rc= select(n, &read_fds_arg, 0, 0, &tv);

    if (rc == 0 || rc == -1)
    {
      if (rc == -1 && errno != EINTR)
        log_error("Listener_thread::run(): select() failed, %s",
                  strerror(errno));
      continue;
    }

    for (int socket_index= 0; socket_index < num_sockets; socket_index++)
    {
      /* Assuming that rc > 0 as we asked to wait forever */
      if (FD_ISSET(sockets[socket_index], &read_fds_arg))
      {
        int client_fd= accept(sockets[socket_index], 0, 0);
        /* accept may return -1 (failure or spurious wakeup) */
        if (client_fd >= 0)                    // connection established
        {
          Vio *vio= vio_new(client_fd, socket_index == 0 ?
                            VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
                            socket_index == 0 ? 1 : 0);
          if (vio != 0)
            handle_new_mysql_connection(vio);
          else
          {
            shutdown(client_fd, SHUT_RDWR);
            close(client_fd);
          }
        }
      }
    }
  }

  /* III. Release all resources and exit */

  log_info("Listener_thread::run(): shutdown requested, exiting...");

  for (int i= 0; i < num_sockets; i++)
    close(sockets[i]);

#ifndef __WIN__
  unlink(unix_socket_address.sun_path);
#endif

  thread_registry.unregister_thread(&thread_info);
  my_thread_end();
  return;

err:
  // we have to close the ip sockets in case of error
  for (int i= 0; i < num_sockets; i++)
    close(sockets[i]);

  thread_registry.unregister_thread(&thread_info);
  thread_registry.request_shutdown();
  my_thread_end();
  return;
}

void set_non_blocking(int socket)
{
#ifndef __WIN__
  int flags= fcntl(socket, F_GETFL, 0);
  fcntl(socket, F_SETFL, flags | O_NONBLOCK);
#else
  u_long arg= 1;
  ioctlsocket(socket, FIONBIO, &arg);
#endif
}

void set_no_inherit(int socket)
{
#ifndef __WIN__
  int flags= fcntl(socket, F_GETFD, 0);
  fcntl(socket, F_SETFD, flags | FD_CLOEXEC);
#endif
}

int Listener_thread::create_tcp_socket()
{
  /* value to be set by setsockopt */
  int arg= 1;

  int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
  if (ip_socket == INVALID_SOCKET)
  {
    log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
              strerror(errno));
    return -1;
  }

  struct sockaddr_in ip_socket_address;
  bzero(&ip_socket_address, sizeof(ip_socket_address));

  ulong im_bind_addr;
  if (options.bind_address != 0)
  {
    if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE)
      im_bind_addr= htonl(INADDR_ANY);
  }
  else
    im_bind_addr= htonl(INADDR_ANY);
  uint im_port= options.port_number;

  ip_socket_address.sin_family= AF_INET;
  ip_socket_address.sin_addr.s_addr= im_bind_addr;

  ip_socket_address.sin_port= (unsigned short)
    htons((unsigned short) im_port);

  setsockopt(ip_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &arg, sizeof(arg));
  if (bind(ip_socket, (struct sockaddr *) &ip_socket_address,
           sizeof(ip_socket_address)))
  {
    log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
              strerror(errno));
    close(ip_socket);
    return -1;
  }

  if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
  {
    log_error("Listener_thread::run(): listen(ip socket) failed, %s",
              strerror(errno));
    close(ip_socket);
    return -1;
  }

  /* set the socket nonblocking */
  set_non_blocking(ip_socket);

  /* make sure that instances won't be listening our sockets */
  set_no_inherit(ip_socket);

  FD_SET(ip_socket, &read_fds);
  sockets[num_sockets++]= ip_socket;
  log_info("accepting connections on ip socket");
  return 0;
}

#ifndef __WIN__
int Listener_thread::
create_unix_socket(struct sockaddr_un &unix_socket_address)
{
  int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
  if (unix_socket == INVALID_SOCKET)
  {
    log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
              strerror(errno));
    return -1;
  }

  bzero(&unix_socket_address, sizeof(unix_socket_address));

  unix_socket_address.sun_family= AF_UNIX;
  strmake(unix_socket_address.sun_path, options.socket_file_name,
          sizeof(unix_socket_address.sun_path));
  unlink(unix_socket_address.sun_path); // in case we have stale socket file

  /*
    POSIX specifies default permissions for a pathname created by bind
    to be 0777. We need everybody to have access to the socket.
  */
  mode_t old_mask= umask(0);
  if (bind(unix_socket, (struct sockaddr *) &unix_socket_address,
           sizeof(unix_socket_address)))
  {
    log_error("Listener_thread::run(): bind(unix socket) failed, "
              "socket file name is '%s', error '%s'",
              unix_socket_address.sun_path, strerror(errno));
    close(unix_socket);
    return -1;
  }

  umask(old_mask);

  if (listen(unix_socket, LISTEN_BACK_LOG_SIZE))
  {
    log_error("Listener_thread::run(): listen(unix socket) failed, %s",
              strerror(errno));
    close(unix_socket);
    return -1;
  }

  /* set the socket nonblocking */
  set_non_blocking(unix_socket);

  /* make sure that instances won't be listening our sockets */
  set_no_inherit(unix_socket);

  log_info("accepting connections on unix socket %s",
           unix_socket_address.sun_path);
  sockets[num_sockets++]= unix_socket;
  FD_SET(unix_socket, &read_fds);
  return 0;
}
#endif

/*
  Create new mysql connection. Created thread is responsible for deletion of
  the Mysql_connection_thread_args and Vio instances passed to it.
  SYNOPSYS
    handle_new_mysql_connection()
*/

void Listener_thread::handle_new_mysql_connection(Vio *vio)
{
  if (Mysql_connection_thread_args *mysql_thread_args=
      new Mysql_connection_thread_args(vio, thread_registry, user_map,
                                       ++total_connection_count,
                                       instance_map)
      )
  {
    /*
      Initialize thread attributes to create detached thread; it seems
      easier to do it ad-hoc than have a global variable for attributes.
    */
    pthread_t mysql_thd_id;
    pthread_attr_t mysql_thd_attr;
    pthread_attr_init(&mysql_thd_attr);
    pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED);
    if (pthread_create(&mysql_thd_id, &mysql_thd_attr, mysql_connection,
                       mysql_thread_args))
    {
      delete mysql_thread_args;
      vio_delete(vio);
      log_error("handle_one_mysql_connection(): pthread_create(mysql) failed");
    }
    pthread_attr_destroy(&mysql_thd_attr);
  }
  else
    vio_delete(vio);
}

pthread_handler_t listener(void *arg)
{
  Listener_thread_args *args= (Listener_thread_args *) arg;
  Listener_thread listener(*args);
  listener.run();
  /*
    args is a stack variable because listener thread lives as long as the
    manager process itself
  */
  return 0;
}

<commit_msg>fix a typo<commit_after>/* Copyright (C) 2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB

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

   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 */

#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION)
#pragma implementation
#endif

#include "listener.h"
#include "priv.h"
#include <m_string.h>
#include <mysql.h>
#include <violite.h>
#ifndef __WIN__
#include <sys/un.h>
#endif
#include <sys/stat.h>

#include "thread_registry.h"
#include "options.h"
#include "instance_map.h"
#include "log.h"
#include "mysql_connection.h"
#include "portability.h"

/*
  Listener_thread - incapsulates listening functionality
*/

class Listener_thread: public Listener_thread_args
{
public:
  Listener_thread(const Listener_thread_args &args);
  ~Listener_thread();
  void run();
private:
  static const int LISTEN_BACK_LOG_SIZE= 5;     /* standard backlog size */
  ulong total_connection_count;
  Thread_info thread_info;

  int     sockets[2];
  int     num_sockets;
  fd_set  read_fds;
private:
  void handle_new_mysql_connection(Vio *vio);
  int   create_tcp_socket();
  int   create_unix_socket(struct sockaddr_un &unix_socket_address);
};

Listener_thread::Listener_thread(const Listener_thread_args &args) :
  Listener_thread_args(args.thread_registry, args.options, args.user_map,
                       args.instance_map)
  ,total_connection_count(0)
  ,thread_info(pthread_self())
  ,num_sockets(0)
{
}

Listener_thread::~Listener_thread()
{
}

/*
  Listener_thread::run() - listen all supported sockets and spawn a thread
  to handle incoming connection.
  Using 'die' in case of syscall failure is OK now - we don't hold any
  resources and 'die' kills the signal thread automatically. To be rewritten
  one day.
  See also comments in mysqlmanager.cc to picture general Instance Manager
  architecture.
*/

void Listener_thread::run()
{
  int n= 0;

#ifndef __WIN__
  /* we use this var to check whether we are running on LinuxThreads */
  pid_t thread_pid;

  thread_pid= getpid();

  struct sockaddr_un unix_socket_address;
  /* set global variable */
  linuxthreads= (thread_pid != manager_pid);
#endif

  thread_registry.register_thread(&thread_info);

  my_thread_init();

  FD_ZERO(&read_fds);

  /* I. prepare 'listen' sockets */
  if (create_tcp_socket())
    goto err;

#ifndef __WIN__
  if (create_unix_socket(unix_socket_address))
    goto err;
#endif

  /* II. Listen sockets and spawn childs */
  for (int i= 0; i < num_sockets; i++)
    n= max(n, sockets[i]);
  n++;

  timeval tv;
  while (!thread_registry.is_shutdown())
  {
    fd_set read_fds_arg= read_fds;
    /*
      We should reintialize timer as on linux it is modified
      to reflect amount of time not slept.
    */
    tv.tv_sec= 0;
    tv.tv_usec= 100000;

    /*
      When using valgrind 2.0 this syscall doesn't get kicked off by a
      signal during shutdown. This results in failing assert
      (Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
    */
    int rc= select(n, &read_fds_arg, 0, 0, &tv);

    if (rc == 0 || rc == -1)
    {
      if (rc == -1 && errno != EINTR)
        log_error("Listener_thread::run(): select() failed, %s",
                  strerror(errno));
      continue;
    }

    for (int socket_index= 0; socket_index < num_sockets; socket_index++)
    {
      /* Assuming that rc > 0 as we asked to wait forever */
      if (FD_ISSET(sockets[socket_index], &read_fds_arg))
      {
        int client_fd= accept(sockets[socket_index], 0, 0);
        /* accept may return -1 (failure or spurious wakeup) */
        if (client_fd >= 0)                    // connection established
        {
          Vio *vio= vio_new(client_fd, socket_index == 0 ?
                            VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
                            socket_index == 0 ? 1 : 0);
          if (vio != 0)
            handle_new_mysql_connection(vio);
          else
          {
            shutdown(client_fd, SHUT_RDWR);
            close(client_fd);
          }
        }
      }
    }
  }

  /* III. Release all resources and exit */

  log_info("Listener_thread::run(): shutdown requested, exiting...");

  for (int i= 0; i < num_sockets; i++)
    close(sockets[i]);

#ifndef __WIN__
  unlink(unix_socket_address.sun_path);
#endif

  thread_registry.unregister_thread(&thread_info);
  my_thread_end();
  return;

err:
  // we have to close the ip sockets in case of error
  for (int i= 0; i < num_sockets; i++)
    close(sockets[i]);

  thread_registry.unregister_thread(&thread_info);
  thread_registry.request_shutdown();
  my_thread_end();
  return;
}

void set_non_blocking(int socket)
{
#ifndef __WIN__
  int flags= fcntl(socket, F_GETFL, 0);
  fcntl(socket, F_SETFL, flags | O_NONBLOCK);
#else
  u_long arg= 1;
  ioctlsocket(socket, FIONBIO, &arg);
#endif
}

void set_no_inherit(int socket)
{
#ifndef __WIN__
  int flags= fcntl(socket, F_GETFD, 0);
  fcntl(socket, F_SETFD, flags | FD_CLOEXEC);
#endif
}

int Listener_thread::create_tcp_socket()
{
  /* value to be set by setsockopt */
  int arg= 1;

  int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
  if (ip_socket == INVALID_SOCKET)
  {
    log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
              strerror(errno));
    return -1;
  }

  struct sockaddr_in ip_socket_address;
  bzero(&ip_socket_address, sizeof(ip_socket_address));

  ulong im_bind_addr;
  if (options.bind_address != 0)
  {
    if ((im_bind_addr= (ulong) inet_addr(options.bind_address)) == INADDR_NONE)
      im_bind_addr= htonl(INADDR_ANY);
  }
  else
    im_bind_addr= htonl(INADDR_ANY);
  uint im_port= options.port_number;

  ip_socket_address.sin_family= AF_INET;
  ip_socket_address.sin_addr.s_addr= im_bind_addr;

  ip_socket_address.sin_port= (unsigned short)
    htons((unsigned short) im_port);

  setsockopt(ip_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &arg, sizeof(arg));
  if (bind(ip_socket, (struct sockaddr *) &ip_socket_address,
           sizeof(ip_socket_address)))
  {
    log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
              strerror(errno));
    close(ip_socket);
    return -1;
  }

  if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
  {
    log_error("Listener_thread::run(): listen(ip socket) failed, %s",
              strerror(errno));
    close(ip_socket);
    return -1;
  }

  /* set the socket nonblocking */
  set_non_blocking(ip_socket);

  /* make sure that instances won't be listening our sockets */
  set_no_inherit(ip_socket);

  FD_SET(ip_socket, &read_fds);
  sockets[num_sockets++]= ip_socket;
  log_info("accepting connections on ip socket");
  return 0;
}

#ifndef __WIN__
int Listener_thread::
create_unix_socket(struct sockaddr_un &unix_socket_address)
{
  int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
  if (unix_socket == INVALID_SOCKET)
  {
    log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
              strerror(errno));
    return -1;
  }

  bzero(&unix_socket_address, sizeof(unix_socket_address));

  unix_socket_address.sun_family= AF_UNIX;
  strmake(unix_socket_address.sun_path, options.socket_file_name,
          sizeof(unix_socket_address.sun_path));
  unlink(unix_socket_address.sun_path); // in case we have stale socket file

  /*
    POSIX specifies default permissions for a pathname created by bind
    to be 0777. We need everybody to have access to the socket.
  */
  mode_t old_mask= umask(0);
  if (bind(unix_socket, (struct sockaddr *) &unix_socket_address,
           sizeof(unix_socket_address)))
  {
    log_error("Listener_thread::run(): bind(unix socket) failed, "
              "socket file name is '%s', error '%s'",
              unix_socket_address.sun_path, strerror(errno));
    close(unix_socket);
    return -1;
  }

  umask(old_mask);

  if (listen(unix_socket, LISTEN_BACK_LOG_SIZE))
  {
    log_error("Listener_thread::run(): listen(unix socket) failed, %s",
              strerror(errno));
    close(unix_socket);
    return -1;
  }

  /* set the socket nonblocking */
  set_non_blocking(unix_socket);

  /* make sure that instances won't be listening our sockets */
  set_no_inherit(unix_socket);

  log_info("accepting connections on unix socket %s",
           unix_socket_address.sun_path);
  sockets[num_sockets++]= unix_socket;
  FD_SET(unix_socket, &read_fds);
  return 0;
}
#endif

/*
  Create new mysql connection. Created thread is responsible for deletion of
  the Mysql_connection_thread_args and Vio instances passed to it.
  SYNOPSYS
    handle_new_mysql_connection()
*/

void Listener_thread::handle_new_mysql_connection(Vio *vio)
{
  if (Mysql_connection_thread_args *mysql_thread_args=
      new Mysql_connection_thread_args(vio, thread_registry, user_map,
                                       ++total_connection_count,
                                       instance_map)
      )
  {
    /*
      Initialize thread attributes to create detached thread; it seems
      easier to do it ad-hoc than have a global variable for attributes.
    */
    pthread_t mysql_thd_id;
    pthread_attr_t mysql_thd_attr;
    pthread_attr_init(&mysql_thd_attr);
    pthread_attr_setdetachstate(&mysql_thd_attr, PTHREAD_CREATE_DETACHED);
    if (pthread_create(&mysql_thd_id, &mysql_thd_attr, mysql_connection,
                       mysql_thread_args))
    {
      delete mysql_thread_args;
      vio_delete(vio);
      log_error("handle_one_mysql_connection(): pthread_create(mysql) failed");
    }
    pthread_attr_destroy(&mysql_thd_attr);
  }
  else
    vio_delete(vio);
}

pthread_handler_t listener(void *arg)
{
  Listener_thread_args *args= (Listener_thread_args *) arg;
  Listener_thread listener(*args);
  listener.run();
  /*
    args is a stack variable because listener thread lives as long as the
    manager process itself
  */
  return 0;
}

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "filter.hh"

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>

#include <boost/ptr_container/ptr_unordered_map.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "lo.pb.h"

#include "bc/index.h"
#include "db/read-only-driver.hh"
#include "db/statement-driver.hh"
#include "filter/spec_loader.h"
#include "lo/layout_loader.h"

using std::cerr;
using std::endl;
using std::memcpy;
using std::set;
using std::string;

namespace flint {
namespace filter {

namespace {

class TimeUnitLoader : db::StatementDriver {
public:
	// Note that db is for read only.
	TimeUnitLoader(sqlite3 *db)
		: db::StatementDriver(db, "SELECT * from time_unit")
	{
	}

	bool Load(string *time_unit) {
		int e = sqlite3_step(stmt());
		if (e == SQLITE_DONE) {
			// nothing to do
			sqlite3_reset(stmt());
			return true;
		}
		if (e == SQLITE_ROW) {
			const unsigned char *name = sqlite3_column_text(stmt(), 0);
			*time_unit = string((const char *)name);
			sqlite3_reset(stmt());
			return true;
		}
		cerr << "failed to step statement: " << e << endl;
		return false;
	}
};

class Spec {
public:
	Spec(const Spec &) = delete;
	Spec &operator=(const Spec &) = delete;

	Spec() {}

	void AddSpec(const boost::uuids::uuid &u, const char *name) {
		m_[u].insert(name);
	}

	bool Contains(const boost::uuids::uuid &id, const string &name) const {
		boost::ptr_unordered_map<boost::uuids::uuid, std::unordered_set<string> >::const_iterator it = m_.find(id);
		if (it == m_.end()) return false;
		return it->second->find(name) != it->second->end();
	}

private:
	boost::ptr_unordered_map<boost::uuids::uuid, std::unordered_set<string> > m_;
};

class Layout {
public:
	Layout(const Layout &) = delete;
	Layout &operator=(const Layout &) = delete;

	Layout() {}

	void AddTrack(std::unique_ptr<lo::Track> &&track) {
		tv_.push_back(std::move(track));
	}

	void AddSector(std::unique_ptr<lo::Sector> &&sector) {
		sv_.push_back(std::move(sector));
	}

	void AddData(std::unique_ptr<lo::Data> &&data) {
		dv_.push_back(std::move(data));
	}

	int Fill(const string &time_unit, const Spec *spec, std::vector<std::unique_ptr<lo::Column> > *columns) const {
		std::unique_ptr<lo::Column> c(new lo::Column);

		char us0[boost::uuids::uuid::static_size()] = {0};
		c->set_position(0);
		c->set_size(1);
		c->set_uuid(us0, boost::uuids::uuid::static_size());
		c->set_id(0); // no such value, let's use 0 as a fallback
		c->set_name("time");
		c->set_type(lo::T);
		c->set_unit(time_unit);
		columns->push_back(std::move(c));

		int si = 0;
		int di = 0;
		int pos = kOffsetBase;
		for (const auto &tp : tv_) {
			int nos = tp->nos();
			int nod = tp->nod();
			int sie = si + nos;
			int dib = di;
			int die = di + nod;

			while (si < sie) {
				const auto &sp = sv_.at(si++);
				boost::uuids::uuid su;
				memcpy(&su, sp->id().data(), su.size());
				di = dib;
				while (di < die) {
					const auto &dp = dv_.at(di++);
					if (spec->Contains(su, dp->name())) {
						c.reset(new lo::Column);
						c->set_position(pos);
						c->set_size(dp->size());
						c->set_uuid(sp->id().data(), boost::uuids::uuid::static_size());
						c->set_id(dp->id());
						c->set_name(dp->name());
						c->set_type(dp->type());
						c->set_unit(dp->unit());
						if (sp->has_label()) c->set_label(sp->label());
						columns->push_back(std::move(c));
					}
					pos += dp->size();
				}
			}
		}
		return pos;
	}

private:
	typedef std::vector<std::unique_ptr<lo::Track> > TrackVector;
	typedef std::vector<std::unique_ptr<lo::Sector> > SectorVector;
	typedef std::vector<std::unique_ptr<lo::Data> > DataVector;

	TrackVector tv_;
	SectorVector sv_;
	DataVector dv_;
};

}

bool Create(sqlite3 *db, const char *spec_file, const char *layout_file, const char *output_file)
{
	string time_unit;
	{
		TimeUnitLoader loader(db);
		if (!loader.Load(&time_unit)) return false;
	}

	std::unique_ptr<Spec> spec(new Spec);
	// load spec
	{
		std::unique_ptr<SpecLoader> loader(new SpecLoader(spec_file));
		if (!loader->Load(spec.get())) return false;
	}

	// load layout
	std::unique_ptr<Layout> layout(new Layout);
	{
		std::unique_ptr<LayoutLoader> loader(new LayoutLoader(layout_file));
		if (!loader->Load(layout.get())) return false;
	}

	std::vector<std::unique_ptr<lo::Column> > columns;
	int size = layout->Fill(time_unit, spec.get(), &columns);
	std::unique_ptr<lo::Header> header(new lo::Header);
	header->set_size(size);

	std::ofstream ofs(output_file, std::ios::out|std::ios::binary);
	if (!ofs.is_open()) {
		cerr << "could not open output file: " << output_file << endl;
		return false;
	}
	if (!PackToOstream(*header, &ofs)) {
		cerr << "failed to pack Header" << endl;
		return false;
	}
	for (const auto &cp : columns) {
		if (!PackToOstream(*cp, &ofs)) {
			cerr << "failed to pack Column" << endl;
			return false;
		}
	}
	ofs.close();
	return true;
}

}
}
<commit_msg>Replace boost::ptr_unordered_map with std::unordered_map<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "filter.hh"

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <boost/functional/hash.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "lo.pb.h"

#include "bc/index.h"
#include "db/read-only-driver.hh"
#include "db/statement-driver.hh"
#include "filter/spec_loader.h"
#include "lo/layout_loader.h"

using std::cerr;
using std::endl;
using std::memcpy;
using std::set;
using std::string;

namespace flint {
namespace filter {

namespace {

class TimeUnitLoader : db::StatementDriver {
public:
	// Note that db is for read only.
	TimeUnitLoader(sqlite3 *db)
		: db::StatementDriver(db, "SELECT * from time_unit")
	{
	}

	bool Load(string *time_unit) {
		int e = sqlite3_step(stmt());
		if (e == SQLITE_DONE) {
			// nothing to do
			sqlite3_reset(stmt());
			return true;
		}
		if (e == SQLITE_ROW) {
			const unsigned char *name = sqlite3_column_text(stmt(), 0);
			*time_unit = string((const char *)name);
			sqlite3_reset(stmt());
			return true;
		}
		cerr << "failed to step statement: " << e << endl;
		return false;
	}
};

class Spec {
public:
	Spec(const Spec &) = delete;
	Spec &operator=(const Spec &) = delete;

	Spec() {}

	void AddSpec(const boost::uuids::uuid &u, const char *name) {
		m_[u].insert(name);
	}

	bool Contains(const boost::uuids::uuid &id, const string &name) const {
		auto it = m_.find(id);
		if (it == m_.end()) return false;
		return it->second.find(name) != it->second.end();
	}

private:
	std::unordered_map<boost::uuids::uuid,
					   std::unordered_set<string>,
					   boost::hash<boost::uuids::uuid> > m_;
};

class Layout {
public:
	Layout(const Layout &) = delete;
	Layout &operator=(const Layout &) = delete;

	Layout() {}

	void AddTrack(std::unique_ptr<lo::Track> &&track) {
		tv_.push_back(std::move(track));
	}

	void AddSector(std::unique_ptr<lo::Sector> &&sector) {
		sv_.push_back(std::move(sector));
	}

	void AddData(std::unique_ptr<lo::Data> &&data) {
		dv_.push_back(std::move(data));
	}

	int Fill(const string &time_unit, const Spec *spec, std::vector<std::unique_ptr<lo::Column> > *columns) const {
		std::unique_ptr<lo::Column> c(new lo::Column);

		char us0[boost::uuids::uuid::static_size()] = {0};
		c->set_position(0);
		c->set_size(1);
		c->set_uuid(us0, boost::uuids::uuid::static_size());
		c->set_id(0); // no such value, let's use 0 as a fallback
		c->set_name("time");
		c->set_type(lo::T);
		c->set_unit(time_unit);
		columns->push_back(std::move(c));

		int si = 0;
		int di = 0;
		int pos = kOffsetBase;
		for (const auto &tp : tv_) {
			int nos = tp->nos();
			int nod = tp->nod();
			int sie = si + nos;
			int dib = di;
			int die = di + nod;

			while (si < sie) {
				const auto &sp = sv_.at(si++);
				boost::uuids::uuid su;
				memcpy(&su, sp->id().data(), su.size());
				di = dib;
				while (di < die) {
					const auto &dp = dv_.at(di++);
					if (spec->Contains(su, dp->name())) {
						c.reset(new lo::Column);
						c->set_position(pos);
						c->set_size(dp->size());
						c->set_uuid(sp->id().data(), boost::uuids::uuid::static_size());
						c->set_id(dp->id());
						c->set_name(dp->name());
						c->set_type(dp->type());
						c->set_unit(dp->unit());
						if (sp->has_label()) c->set_label(sp->label());
						columns->push_back(std::move(c));
					}
					pos += dp->size();
				}
			}
		}
		return pos;
	}

private:
	typedef std::vector<std::unique_ptr<lo::Track> > TrackVector;
	typedef std::vector<std::unique_ptr<lo::Sector> > SectorVector;
	typedef std::vector<std::unique_ptr<lo::Data> > DataVector;

	TrackVector tv_;
	SectorVector sv_;
	DataVector dv_;
};

}

bool Create(sqlite3 *db, const char *spec_file, const char *layout_file, const char *output_file)
{
	string time_unit;
	{
		TimeUnitLoader loader(db);
		if (!loader.Load(&time_unit)) return false;
	}

	std::unique_ptr<Spec> spec(new Spec);
	// load spec
	{
		std::unique_ptr<SpecLoader> loader(new SpecLoader(spec_file));
		if (!loader->Load(spec.get())) return false;
	}

	// load layout
	std::unique_ptr<Layout> layout(new Layout);
	{
		std::unique_ptr<LayoutLoader> loader(new LayoutLoader(layout_file));
		if (!loader->Load(layout.get())) return false;
	}

	std::vector<std::unique_ptr<lo::Column> > columns;
	int size = layout->Fill(time_unit, spec.get(), &columns);
	std::unique_ptr<lo::Header> header(new lo::Header);
	header->set_size(size);

	std::ofstream ofs(output_file, std::ios::out|std::ios::binary);
	if (!ofs.is_open()) {
		cerr << "could not open output file: " << output_file << endl;
		return false;
	}
	if (!PackToOstream(*header, &ofs)) {
		cerr << "failed to pack Header" << endl;
		return false;
	}
	for (const auto &cp : columns) {
		if (!PackToOstream(*cp, &ofs)) {
			cerr << "failed to pack Column" << endl;
			return false;
		}
	}
	ofs.close();
	return true;
}

}
}
<|endoftext|>