<commit_before>/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp
**
** Copyright 2009, Google Inc.
**
** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkFontHost_fontconfig_direct.h"

#include <unistd.h>
#include <fcntl.h>

#include <fontconfig/fontconfig.h>

FontConfigDirect::FontConfigDirect()
    : next_file_id_(0) {
  FcInit();
}

// -----------------------------------------------------------------------------
// Normally we only return exactly the font asked for. In last-resort cases,
// the request is for one of the basic font names "Sans", "Serif" or
// "Monospace". This function tells you whether a given request is for such a
// fallback.
// -----------------------------------------------------------------------------
static bool IsFallbackFontAllowed(const std::string& family)
{
    const char* family_cstr = family.c_str();
    return strcasecmp(family_cstr, "sans") == 0 ||
           strcasecmp(family_cstr, "serif") == 0 ||
           strcasecmp(family_cstr, "monospace") == 0;
}

bool FontConfigDirect::Match(std::string* result_family,
                             unsigned* result_fileid,
                             bool fileid_valid, unsigned fileid,
                             const std::string& family, bool* is_bold,
                             bool* is_italic) {
    if (family.length() > kMaxFontFamilyLength)
        return false;

    SkAutoMutexAcquire ac(mutex_);
    FcPattern* pattern = FcPatternCreate();

    FcValue fcvalue;
    if (fileid_valid) {
        const std::map<unsigned, std::string>::const_iterator
            i = fileid_to_filename_.find(fileid);
        if (i == fileid_to_filename_.end()) {
            FcPatternDestroy(pattern);
            return false;
        }

        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) i->second.c_str();
        FcPatternAdd(pattern, FC_FILE, fcvalue, 0);
    }
    if (!family.empty()) {
        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) family.c_str();
        FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0);
    }

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0);

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    FcPatternAdd(pattern, FC_SLANT, fcvalue, 0);

    fcvalue.type = FcTypeBool;
    fcvalue.u.b = FcTrue;
    FcPatternAdd(pattern, FC_SCALABLE, fcvalue, 0);

    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    // Font matching:
    // CSS often specifies a fallback list of families:
    //    font-family: a, b, c, serif;
    // However, fontconfig will always do its best to find *a* font when asked
    // for something so we need a way to tell if the match which it has found is
    // "good enough" for us. Otherwise, we can return NULL which gets piped up
    // and lets WebKit know to try the next CSS family name. However, fontconfig
    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
    // wish to support that.
    //
    // Thus, if a specific family is requested we set @family_requested. Then we
    // record two strings: the family name after config processing and the
    // family name after resolving. If the two are equal, it's a good match.
    //
    // So consider the case where a user has mapped Arial to Helvetica in their
    // config.
    //    requested family: "Arial"
    //    post_config_family: "Helvetica"
    //    post_match_family: "Helvetica"
    //      -> good match
    //
    // and for a missing font:
    //    requested family: "Monaco"
    //    post_config_family: "Monaco"
    //    post_match_family: "Times New Roman"
    //      -> BAD match
    //
    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
    FcChar8* post_config_family;
    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);

    FcResult result;
    FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
    if (!font_set) {
        FcPatternDestroy(pattern);
        return false;
    }

    // Older versions of fontconfig have a bug where they cannot select
    // only scalable fonts so we have to manually filter the results.
    FcPattern* match = NULL;
    for (int i = 0; i < font_set->nfont; ++i) {
      FcPattern* current = font_set->fonts[i];
      FcBool is_scalable;

      if (FcPatternGetBool(current, FC_SCALABLE, 0,
                           &is_scalable) != FcResultMatch ||
          !is_scalable) {
        continue;
      }

      // fontconfig can also return fonts which are unreadable
      FcChar8* c_filename;
      if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != FcResultMatch)
        continue;

      if (access(reinterpret_cast<char*>(c_filename), R_OK) != 0)
        continue;

      match = current;
      break;
    }

    if (!match) {
      FcPatternDestroy(pattern);
      FcFontSetDestroy(font_set);
      return false;
    }

    if (!IsFallbackFontAllowed(family)) {
      bool family_names_match = false;
      for (int id = 0; id < 255; ++id) {
        FcChar8* post_match_family;
        if (FcPatternGetString(match, FC_FAMILY, id, &post_match_family) !=
            FcResultMatch)
          break;
        family_names_match =
          family.empty() ?
          true :
          (strcasecmp((char *)post_config_family,
                      (char *)post_match_family) == 0 ||
           // Workaround for Issue 12530:
           //   requested family: "Bitstream Vera Sans"
           //   post_config_family: "Arial"
           //   post_match_family: "Bitstream Vera Sans"
           // -> We should treat this case as a good match.
           strcasecmp(family.c_str(),
                      (char *)post_match_family) == 0);
        if (family_names_match)
          break;
      }
      if (!family.empty() && !family_names_match) {
        FcPatternDestroy(pattern);
        FcFontSetDestroy(font_set);
        return false;
      }
    }

    FcPatternDestroy(pattern);

    FcChar8* c_filename;
    if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
        FcFontSetDestroy(font_set);
        return false;
    }
    const std::string filename((char *) c_filename);

    unsigned out_fileid;
    if (fileid_valid) {
        out_fileid = fileid;
    } else {
        const std::map<std::string, unsigned>::const_iterator
            i = filename_to_fileid_.find(filename);
        if (i == filename_to_fileid_.end()) {
            out_fileid = next_file_id_++;
            filename_to_fileid_[filename] = out_fileid;
            fileid_to_filename_[out_fileid] = filename;
        } else {
            out_fileid = i->second;
        }
    }

    if (result_fileid)
        *result_fileid = out_fileid;

    FcChar8* c_family;
    if (FcPatternGetString(match, FC_FAMILY, 0, &c_family)) {
        FcFontSetDestroy(font_set);
        return false;
    }

    int resulting_bold;
    if (FcPatternGetInteger(match, FC_WEIGHT, 0, &resulting_bold))
      resulting_bold = FC_WEIGHT_NORMAL;

    int resulting_italic;
    if (FcPatternGetInteger(match, FC_SLANT, 0, &resulting_italic))
      resulting_italic = FC_SLANT_ROMAN;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // the undocumented property FC_MATRIX to a skew matrix. It'll then say
    // that the font is italic or oblique. So, if we see a matrix, we don't
    // believe that it's italic.
    FcValue matrix;
    const bool have_matrix = FcPatternGet(match, FC_MATRIX, 0, &matrix) == 0;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // FC_EMBOLDEN.
    FcValue embolden;
    const bool have_embolden =
        FcPatternGet(match, FC_EMBOLDEN, 0, &embolden) == 0;

    if (is_bold)
      *is_bold = resulting_bold >= FC_WEIGHT_BOLD && !have_embolden;
    if (is_italic)
      *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix;

    if (result_family)
        *result_family = (char *) c_family;

    FcFontSetDestroy(font_set);

    return true;
}

int FontConfigDirect::Open(unsigned fileid) {
    SkAutoMutexAcquire ac(mutex_);
    const std::map<unsigned, std::string>::const_iterator
        i = fileid_to_filename_.find(fileid);
    if (i == fileid_to_filename_.end())
        return -1;

    return open(i->second.c_str(), O_RDONLY);
}
<commit_msg>Fix for gratuitous fake emboldening of already-bold fonts on Linux.<commit_after>/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp
**
** Copyright 2009, Google Inc.
**
** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkFontHost_fontconfig_direct.h"

#include <unistd.h>
#include <fcntl.h>

#include <fontconfig/fontconfig.h>

FontConfigDirect::FontConfigDirect()
    : next_file_id_(0) {
  FcInit();
}

// -----------------------------------------------------------------------------
// Normally we only return exactly the font asked for. In last-resort cases,
// the request is for one of the basic font names "Sans", "Serif" or
// "Monospace". This function tells you whether a given request is for such a
// fallback.
// -----------------------------------------------------------------------------
static bool IsFallbackFontAllowed(const std::string& family)
{
    const char* family_cstr = family.c_str();
    return strcasecmp(family_cstr, "sans") == 0 ||
           strcasecmp(family_cstr, "serif") == 0 ||
           strcasecmp(family_cstr, "monospace") == 0;
}

bool FontConfigDirect::Match(std::string* result_family,
                             unsigned* result_fileid,
                             bool fileid_valid, unsigned fileid,
                             const std::string& family, bool* is_bold,
                             bool* is_italic) {
    if (family.length() > kMaxFontFamilyLength)
        return false;

    SkAutoMutexAcquire ac(mutex_);
    FcPattern* pattern = FcPatternCreate();

    FcValue fcvalue;
    if (fileid_valid) {
        const std::map<unsigned, std::string>::const_iterator
            i = fileid_to_filename_.find(fileid);
        if (i == fileid_to_filename_.end()) {
            FcPatternDestroy(pattern);
            return false;
        }

        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) i->second.c_str();
        FcPatternAdd(pattern, FC_FILE, fcvalue, 0);
    }
    if (!family.empty()) {
        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) family.c_str();
        FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0);
    }

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0);

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    FcPatternAdd(pattern, FC_SLANT, fcvalue, 0);

    fcvalue.type = FcTypeBool;
    fcvalue.u.b = FcTrue;
    FcPatternAdd(pattern, FC_SCALABLE, fcvalue, 0);

    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    // Font matching:
    // CSS often specifies a fallback list of families:
    //    font-family: a, b, c, serif;
    // However, fontconfig will always do its best to find *a* font when asked
    // for something so we need a way to tell if the match which it has found is
    // "good enough" for us. Otherwise, we can return NULL which gets piped up
    // and lets WebKit know to try the next CSS family name. However, fontconfig
    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
    // wish to support that.
    //
    // Thus, if a specific family is requested we set @family_requested. Then we
    // record two strings: the family name after config processing and the
    // family name after resolving. If the two are equal, it's a good match.
    //
    // So consider the case where a user has mapped Arial to Helvetica in their
    // config.
    //    requested family: "Arial"
    //    post_config_family: "Helvetica"
    //    post_match_family: "Helvetica"
    //      -> good match
    //
    // and for a missing font:
    //    requested family: "Monaco"
    //    post_config_family: "Monaco"
    //    post_match_family: "Times New Roman"
    //      -> BAD match
    //
    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
    FcChar8* post_config_family;
    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);

    FcResult result;
    FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
    if (!font_set) {
        FcPatternDestroy(pattern);
        return false;
    }

    // Older versions of fontconfig have a bug where they cannot select
    // only scalable fonts so we have to manually filter the results.
    FcPattern* match = NULL;
    for (int i = 0; i < font_set->nfont; ++i) {
      FcPattern* current = font_set->fonts[i];
      FcBool is_scalable;

      if (FcPatternGetBool(current, FC_SCALABLE, 0,
                           &is_scalable) != FcResultMatch ||
          !is_scalable) {
        continue;
      }

      // fontconfig can also return fonts which are unreadable
      FcChar8* c_filename;
      if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != FcResultMatch)
        continue;

      if (access(reinterpret_cast<char*>(c_filename), R_OK) != 0)
        continue;

      match = current;
      break;
    }

    if (!match) {
      FcPatternDestroy(pattern);
      FcFontSetDestroy(font_set);
      return false;
    }

    if (!IsFallbackFontAllowed(family)) {
      bool family_names_match = false;
      for (int id = 0; id < 255; ++id) {
        FcChar8* post_match_family;
        if (FcPatternGetString(match, FC_FAMILY, id, &post_match_family) !=
            FcResultMatch)
          break;
        family_names_match =
          family.empty() ?
          true :
          (strcasecmp((char *)post_config_family,
                      (char *)post_match_family) == 0 ||
           // Workaround for Issue 12530:
           //   requested family: "Bitstream Vera Sans"
           //   post_config_family: "Arial"
           //   post_match_family: "Bitstream Vera Sans"
           // -> We should treat this case as a good match.
           strcasecmp(family.c_str(),
                      (char *)post_match_family) == 0);
        if (family_names_match)
          break;
      }
      if (!family.empty() && !family_names_match) {
        FcPatternDestroy(pattern);
        FcFontSetDestroy(font_set);
        return false;
      }
    }

    FcPatternDestroy(pattern);

    FcChar8* c_filename;
    if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
        FcFontSetDestroy(font_set);
        return false;
    }
    const std::string filename((char *) c_filename);

    unsigned out_fileid;
    if (fileid_valid) {
        out_fileid = fileid;
    } else {
        const std::map<std::string, unsigned>::const_iterator
            i = filename_to_fileid_.find(filename);
        if (i == filename_to_fileid_.end()) {
            out_fileid = next_file_id_++;
            filename_to_fileid_[filename] = out_fileid;
            fileid_to_filename_[out_fileid] = filename;
        } else {
            out_fileid = i->second;
        }
    }

    if (result_fileid)
        *result_fileid = out_fileid;

    FcChar8* c_family;
    if (FcPatternGetString(match, FC_FAMILY, 0, &c_family)) {
        FcFontSetDestroy(font_set);
        return false;
    }

    int resulting_bold;
    if (FcPatternGetInteger(match, FC_WEIGHT, 0, &resulting_bold))
      resulting_bold = FC_WEIGHT_NORMAL;

    int resulting_italic;
    if (FcPatternGetInteger(match, FC_SLANT, 0, &resulting_italic))
      resulting_italic = FC_SLANT_ROMAN;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // the undocumented property FC_MATRIX to a skew matrix. It'll then say
    // that the font is italic or oblique. So, if we see a matrix, we don't
    // believe that it's italic.
    FcValue matrix;
    const bool have_matrix = FcPatternGet(match, FC_MATRIX, 0, &matrix) == 0;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // FC_EMBOLDEN.
    FcValue embolden;
    const bool have_embolden =
        FcPatternGet(match, FC_EMBOLDEN, 0, &embolden) == 0;

    if (is_bold)
      *is_bold = resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden;
    if (is_italic)
      *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix;

    if (result_family)
        *result_family = (char *) c_family;

    FcFontSetDestroy(font_set);

    return true;
}

int FontConfigDirect::Open(unsigned fileid) {
    SkAutoMutexAcquire ac(mutex_);
    const std::map<unsigned, std::string>::const_iterator
        i = fileid_to_filename_.find(fileid);
    if (i == fileid_to_filename_.end())
        return -1;

    return open(i->second.c_str(), O_RDONLY);
}
<|endoftext|>


<commit_before>/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp
**
** Copyright 2009, Google Inc.
**
** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkFontHost_fontconfig_direct.h"

#include <unistd.h>
#include <fcntl.h>

#include <fontconfig/fontconfig.h>

FontConfigDirect::FontConfigDirect()
    : next_file_id_(0) {
  FcInit();
}

// -----------------------------------------------------------------------------
// Normally we only return exactly the font asked for. In last-resort cases,
// the request is for one of the basic font names "Sans", "Serif" or
// "Monospace". This function tells you whether a given request is for such a
// fallback.
// -----------------------------------------------------------------------------
static bool IsFallbackFontAllowed(const std::string& family)
{
    return family == "Sans" ||
           family == "Serif" ||
           family == "Monospace";
}

bool FontConfigDirect::Match(std::string* result_family,
                             unsigned* result_fileid,
                             bool fileid_valid, unsigned fileid,
                             const std::string& family, bool* is_bold,
                             bool* is_italic) {
    SkAutoMutexAcquire ac(mutex_);
    FcPattern* pattern = FcPatternCreate();

    FcValue fcvalue;
    if (fileid_valid) {
        const std::map<unsigned, std::string>::const_iterator
            i = fileid_to_filename_.find(fileid);
        if (i == fileid_to_filename_.end()) {
            FcPatternDestroy(pattern);
            return false;
        }

        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) i->second.c_str();
        FcPatternAdd(pattern, FC_FILE, fcvalue, 0);
    }
    if (!family.empty()) {
        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) family.c_str();
        FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0);
    }

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0);

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    FcPatternAdd(pattern, FC_SLANT, fcvalue, 0);

    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    // Font matching:
    // CSS often specifies a fallback list of families:
    //    font-family: a, b, c, serif;
    // However, fontconfig will always do its best to find *a* font when asked
    // for something so we need a way to tell if the match which it has found is
    // "good enough" for us. Otherwise, we can return NULL which gets piped up
    // and lets WebKit know to try the next CSS family name. However, fontconfig
    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
    // wish to support that.
    //
    // Thus, if a specific family is requested we set @family_requested. Then we
    // record two strings: the family name after config processing and the
    // family name after resolving. If the two are equal, it's a good match.
    //
    // So consider the case where a user has mapped Arial to Helvetica in their
    // config.
    //    requested family: "Arial"
    //    post_config_family: "Helvetica"
    //    post_match_family: "Helvetica"
    //      -> good match
    //
    // and for a missing font:
    //    requested family: "Monaco"
    //    post_config_family: "Monaco"
    //    post_match_family: "Times New Roman"
    //      -> BAD match
    //
    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
    FcChar8* post_config_family;
    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);

    FcResult result;
    FcPattern* match = FcFontMatch(0, pattern, &result);
    if (!match) {
        FcPatternDestroy(pattern);
        return false;
    }

    // Skip non-scalable fonts. They don't work with FT_Set_Char_Size()
    // in SkFontHost_FreeType.cpp.
    FcBool scalable = FcFalse;
    FcPatternGetBool(match, FC_SCALABLE, 0, &scalable);
    if (scalable != FcTrue) {
        FcPatternDestroy(match);
        FcPatternDestroy(pattern);
        return false;
    }

    FcChar8* post_match_family;
    FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
    const bool family_names_match =
        family.empty() ?
        true :
        strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;

    FcPatternDestroy(pattern);

    if (!family_names_match && !IsFallbackFontAllowed(family)) {
        FcPatternDestroy(match);
        return false;
    }

    FcChar8* c_filename;
    if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
        FcPatternDestroy(match);
        return NULL;
    }
    const std::string filename((char *) c_filename);

    unsigned out_fileid;
    if (fileid_valid) {
        out_fileid = fileid;
    } else {
        const std::map<std::string, unsigned>::const_iterator
            i = filename_to_fileid_.find(filename);
        if (i == filename_to_fileid_.end()) {
            out_fileid = next_file_id_++;
            filename_to_fileid_[filename] = out_fileid;
            fileid_to_filename_[out_fileid] = filename;
        } else {
            out_fileid = i->second;
        }
    }

    if (result_fileid)
        *result_fileid = out_fileid;

    FcChar8* c_family;
    if (FcPatternGetString(match, FC_FAMILY, 0, &c_family)) {
        FcPatternDestroy(match);
        return NULL;
    }

    int resulting_bold;
    if (FcPatternGetInteger(match, FC_WEIGHT, 0, &resulting_bold))
      resulting_bold = FC_WEIGHT_NORMAL;

    int resulting_italic;
    if (FcPatternGetInteger(match, FC_SLANT, 0, &resulting_italic))
      resulting_italic = FC_SLANT_ROMAN;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // the undocumented property FC_MATRIX to a skew matrix. It'll then say
    // that the font is italic or oblique. So, if we see a matrix, we don't
    // believe that it's italic.
    FcValue matrix;
    const bool have_matrix = FcPatternGet(match, FC_MATRIX, 0, &matrix) == 0;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // FC_EMBOLDEN.
    FcValue embolden;
    const bool have_embolden =
        FcPatternGet(match, FC_EMBOLDEN, 0, &embolden) == 0;

    if (is_bold)
      *is_bold = resulting_bold >= FC_WEIGHT_BOLD && !have_embolden;
    if (is_italic)
      *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix;

    if (result_family)
        *result_family = (char *) c_family;

    FcPatternDestroy(match);

    return true;
}

int FontConfigDirect::Open(unsigned fileid) {
    SkAutoMutexAcquire ac(mutex_);
    const std::map<unsigned, std::string>::const_iterator
        i = fileid_to_filename_.find(fileid);
    if (i == fileid_to_filename_.end())
        return -1;

    return open(i->second.c_str(), O_RDONLY);
}
<commit_msg>Make font fallback case insensitive.<commit_after>/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp
**
** Copyright 2009, Google Inc.
**
** Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkFontHost_fontconfig_direct.h"

#include <unistd.h>
#include <fcntl.h>

#include <fontconfig/fontconfig.h>

FontConfigDirect::FontConfigDirect()
    : next_file_id_(0) {
  FcInit();
}

// -----------------------------------------------------------------------------
// Normally we only return exactly the font asked for. In last-resort cases,
// the request is for one of the basic font names "Sans", "Serif" or
// "Monospace". This function tells you whether a given request is for such a
// fallback.
// -----------------------------------------------------------------------------
static bool IsFallbackFontAllowed(const std::string& family)
{
    const char* family_cstr = family.c_str();
    return strcasecmp(family_cstr, "sans") == 0 ||
           strcasecmp(family_cstr, "serif") == 0 ||
           strcasecmp(family_cstr, "monospace") == 0;
}

bool FontConfigDirect::Match(std::string* result_family,
                             unsigned* result_fileid,
                             bool fileid_valid, unsigned fileid,
                             const std::string& family, bool* is_bold,
                             bool* is_italic) {
    SkAutoMutexAcquire ac(mutex_);
    FcPattern* pattern = FcPatternCreate();

    FcValue fcvalue;
    if (fileid_valid) {
        const std::map<unsigned, std::string>::const_iterator
            i = fileid_to_filename_.find(fileid);
        if (i == fileid_to_filename_.end()) {
            FcPatternDestroy(pattern);
            return false;
        }

        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) i->second.c_str();
        FcPatternAdd(pattern, FC_FILE, fcvalue, 0);
    }
    if (!family.empty()) {
        fcvalue.type = FcTypeString;
        fcvalue.u.s = (FcChar8*) family.c_str();
        FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0);
    }

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
    FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0);

    fcvalue.type = FcTypeInteger;
    fcvalue.u.i = is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
    FcPatternAdd(pattern, FC_SLANT, fcvalue, 0);

    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    // Font matching:
    // CSS often specifies a fallback list of families:
    //    font-family: a, b, c, serif;
    // However, fontconfig will always do its best to find *a* font when asked
    // for something so we need a way to tell if the match which it has found is
    // "good enough" for us. Otherwise, we can return NULL which gets piped up
    // and lets WebKit know to try the next CSS family name. However, fontconfig
    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
    // wish to support that.
    //
    // Thus, if a specific family is requested we set @family_requested. Then we
    // record two strings: the family name after config processing and the
    // family name after resolving. If the two are equal, it's a good match.
    //
    // So consider the case where a user has mapped Arial to Helvetica in their
    // config.
    //    requested family: "Arial"
    //    post_config_family: "Helvetica"
    //    post_match_family: "Helvetica"
    //      -> good match
    //
    // and for a missing font:
    //    requested family: "Monaco"
    //    post_config_family: "Monaco"
    //    post_match_family: "Times New Roman"
    //      -> BAD match
    //
    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
    FcChar8* post_config_family;
    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);

    FcResult result;
    FcPattern* match = FcFontMatch(0, pattern, &result);
    if (!match) {
        FcPatternDestroy(pattern);
        return false;
    }

    // Skip non-scalable fonts. They don't work with FT_Set_Char_Size()
    // in SkFontHost_FreeType.cpp.
    FcBool scalable = FcFalse;
    FcPatternGetBool(match, FC_SCALABLE, 0, &scalable);
    if (scalable != FcTrue) {
        FcPatternDestroy(match);
        FcPatternDestroy(pattern);
        return false;
    }

    FcChar8* post_match_family;
    FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
    const bool family_names_match =
        family.empty() ?
        true :
        strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;

    FcPatternDestroy(pattern);

    if (!family_names_match && !IsFallbackFontAllowed(family)) {
        FcPatternDestroy(match);
        return false;
    }

    FcChar8* c_filename;
    if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
        FcPatternDestroy(match);
        return NULL;
    }
    const std::string filename((char *) c_filename);

    unsigned out_fileid;
    if (fileid_valid) {
        out_fileid = fileid;
    } else {
        const std::map<std::string, unsigned>::const_iterator
            i = filename_to_fileid_.find(filename);
        if (i == filename_to_fileid_.end()) {
            out_fileid = next_file_id_++;
            filename_to_fileid_[filename] = out_fileid;
            fileid_to_filename_[out_fileid] = filename;
        } else {
            out_fileid = i->second;
        }
    }

    if (result_fileid)
        *result_fileid = out_fileid;

    FcChar8* c_family;
    if (FcPatternGetString(match, FC_FAMILY, 0, &c_family)) {
        FcPatternDestroy(match);
        return NULL;
    }

    int resulting_bold;
    if (FcPatternGetInteger(match, FC_WEIGHT, 0, &resulting_bold))
      resulting_bold = FC_WEIGHT_NORMAL;

    int resulting_italic;
    if (FcPatternGetInteger(match, FC_SLANT, 0, &resulting_italic))
      resulting_italic = FC_SLANT_ROMAN;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // the undocumented property FC_MATRIX to a skew matrix. It'll then say
    // that the font is italic or oblique. So, if we see a matrix, we don't
    // believe that it's italic.
    FcValue matrix;
    const bool have_matrix = FcPatternGet(match, FC_MATRIX, 0, &matrix) == 0;

    // If we ask for an italic font, fontconfig might take a roman font and set
    // FC_EMBOLDEN.
    FcValue embolden;
    const bool have_embolden =
        FcPatternGet(match, FC_EMBOLDEN, 0, &embolden) == 0;

    if (is_bold)
      *is_bold = resulting_bold >= FC_WEIGHT_BOLD && !have_embolden;
    if (is_italic)
      *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix;

    if (result_family)
        *result_family = (char *) c_family;

    FcPatternDestroy(match);

    return true;
}

int FontConfigDirect::Open(unsigned fileid) {
    SkAutoMutexAcquire ac(mutex_);
    const std::map<unsigned, std::string>::const_iterator
        i = fileid_to_filename_.find(fileid);
    if (i == fileid_to_filename_.end())
        return -1;

    return open(i->second.c_str(), O_RDONLY);
}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 2001, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.2  2001/07/24 21:52:27  peiyongz
 * XMLDouble: move fg...String to XMLUni
 *
 * Revision 1.1  2001/07/24 13:58:11  peiyongz
 * XMLDouble and related supporting methods from XMLBigInteger/XMLBigDecimal
 *
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <util/XMLDouble.hpp>
#include <util/PlatformUtils.hpp>
#include <util/XMLString.hpp>
#include <util/XMLUniDefs.hpp>
#include <util/NumberFormatException.hpp>
#include <util/TransService.hpp>
#include <util/Janitor.hpp>
#include <util/XMLUni.hpp>
#include <math.h>

//
// The special values positive and negative zero,
// positive and negative infinity and not-a-number
// have lexical representations 0, -0, INF, -INF and NaN, respectively
//

/***
 *   Algo:
 *
 *   . Check for special cases
 *   . construct fMantissa, fExponent (optional)
 *   . No checking on the value space
 *   .
***/
XMLDouble::XMLDouble(const XMLCh* const strValue)
:fMantissa(0)
,fExponent(0)
,fType(Normal)
,fValue(0)
{
    if ((!strValue) || (!*strValue))
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);

//    char *p1 = XMLString::transcode(strValue);
    XMLCh* tmpStrValue = XMLString::replicate(strValue);
    ArrayJanitor<XMLCh> janTmpName(tmpStrValue);
    XMLString::trim(tmpStrValue);
//    char *p2 = XMLString::transcode(tmpStrValue);

    if (XMLString::compareString(tmpStrValue, XMLUni::fgNegINFString) == 0)
    {
        fType = LiteralType::NegINF;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgNegZeroString) == 0)
    {
        fType = LiteralType::NegZero;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgPosZeroString) == 0)
    {
        fType = LiteralType::PosZero;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgPosINFString) == 0)
    {
        fType = LiteralType::PosINF;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgNaNString) == 0)
    {
        fType = LiteralType::NaN;
        return;
    }

    //
    // Normal case
    //
    int ePos = 0;
    int tmpStrLen = XMLString::stringLen(tmpStrValue);

    if ((( ePos = XMLString::indexOf(tmpStrValue, chLatin_E)) != -1 ) ||
        (( ePos = XMLString::indexOf(tmpStrValue, chLatin_e)) != -1 )  )
    {
        XMLCh* tmpMantissa = new XMLCh [ePos+1];
        XMLString::subString(tmpMantissa, tmpStrValue, 0, ePos);
        ArrayJanitor<XMLCh> janMantissa(tmpMantissa);
        fMantissa = new XMLBigDecimal(tmpMantissa);

        //
        // 1234.56E78
        //
        if ( ePos < tmpStrLen - 1)
        {
            XMLCh* tmpExponent = new XMLCh [tmpStrLen - ePos];
            XMLString::subString(tmpExponent, tmpStrValue, ePos + 1, tmpStrLen);
            ArrayJanitor<XMLCh> janExponent(tmpExponent);
            fExponent = new XMLBigInteger(tmpExponent);
        }
        //
        // 1234.56E
        //
        else
        {
            fExponent = new XMLBigInteger(XMLUni::fgZeroString);
        }

    }
    else
    {
        fMantissa = new XMLBigDecimal(tmpStrValue);
        fExponent = new XMLBigInteger(XMLUni::fgZeroString);
    }

    fValue = fMantissa->doubleValue() * pow(10.0, fExponent->intValue());
}

XMLDouble::XMLDouble(const XMLDouble& toCopy)
:fMantissa(0)
,fExponent(0)
,fType(Normal)
,fValue(0)
{
    fMantissa = new XMLBigDecimal(*(toCopy.fMantissa));
    fExponent = new XMLBigInteger(*(toCopy.fExponent));
    fType     = toCopy.fType;
    fValue    = toCopy.fValue;
}

//
// Add the 'E' as necessary
// The caller needs to de-allocate the memory allocated by this function
// Deallocate the memory allocated by XMLBigInteger
//
XMLCh*  XMLDouble::toString() const
{
    switch (fType)
    {
    case NegINF:
        return XMLString::replicate(XMLUni::fgNegINFString);

    case NegZero:
        return XMLString::replicate(XMLUni::fgNegZeroString);

    case PosZero:
        return XMLString::replicate(XMLUni::fgPosZeroString);

    case PosINF:
        return XMLString::replicate(XMLUni::fgPosINFString);

    case NaN:
        return XMLString::replicate(XMLUni::fgNaNString);

    case Normal:

        XMLCh *ret_mantissa;
        ret_mantissa = fMantissa->toString();

        if (fExponent)
        {
            XMLCh *ret_exponent = fExponent->toString();
            XMLCh *ret_val = new XMLCh [ XMLString::stringLen(ret_mantissa) +
                                         XMLString::stringLen(ret_exponent) + 2];
            *ret_val = 0;
            XMLString::catString(ret_val, ret_mantissa);
            XMLString::catString(ret_val, XMLUni::fgEString);
            XMLString::catString(ret_val, ret_exponent);

            delete [] ret_mantissa;
            delete [] ret_exponent;
            return ret_val;
        }
        else
            return ret_mantissa;

        break;
    default:
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);
        //internal error
    };

    return (XMLCh*) 0;
}

//
//
//
int XMLDouble::compareValues(const XMLDouble* const lValue
                           , const XMLDouble* const rValue)
{
    //
    // case#1: lValue normal
    //         rValue normal
    //
    if ((!lValue->isSpecialValue()) &&
        (!rValue->isSpecialValue())  )
    {
        //
        // if we use fValue to compare two
        // sequences "12.3456E4" and "1234.56E2",
        // they are _NOT_ the same. so we ask
        // BigDecimal to compare.
        //
        XMLBigDecimal ldv = XMLBigDecimal(*(lValue->fMantissa), lValue->fExponent->intValue());
        XMLBigDecimal rdv = XMLBigDecimal(*(rValue->fMantissa), rValue->fExponent->intValue());

        return XMLBigDecimal::compareValues(&ldv, &rdv);
    }
    //
    // case#2: lValue special
    //         rValue special
    //
    else
    if ((lValue->isSpecialValue()) &&
        (rValue->isSpecialValue())  )
    {
        if (lValue->fType == rValue->fType)
            return 0;
        else
            return (lValue->fType > rValue->fType ? 1 : -1);
    }
    //
    // case#3: lValue special
    //         rValue normal
    //
    else
    if ((lValue->isSpecialValue()) &&
        (!rValue->isSpecialValue())  )
    {
        return compareSpecial(lValue, rValue);
    }
    //
    // case#4: lValue normal
    //         rValue special
    //
    else
    {
        return (-1) * compareSpecial(rValue, lValue);
    }

    return 0;
}

int XMLDouble::compareSpecial(const XMLDouble* const specialValue
                            , const XMLDouble* const normalValue)
{
    switch (specialValue->fType)
    {
    case NegINF:
        return -1;

    case NegZero:
    case PosZero:
        return (normalValue->doubleValue() > 0 ? -1 : 1);

    case PosINF:
        return 1;

    case NaN:
        return 1;

    default:
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);
        //internal error
    }
}<commit_msg>Fix to Linux compilation error: XMLDouble::LiteralType' is not an aggregate type<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 2001, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.3  2001/07/25 19:06:56  peiyongz
 * Fix to Linux compilation error: XMLDouble::LiteralType' is not an aggregate type
 *
 * Revision 1.2  2001/07/24 21:52:27  peiyongz
 * XMLDouble: move fg...String to XMLUni
 *
 * Revision 1.1  2001/07/24 13:58:11  peiyongz
 * XMLDouble and related supporting methods from XMLBigInteger/XMLBigDecimal
 *
 */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <util/XMLDouble.hpp>
#include <util/PlatformUtils.hpp>
#include <util/XMLString.hpp>
#include <util/XMLUniDefs.hpp>
#include <util/NumberFormatException.hpp>
#include <util/TransService.hpp>
#include <util/Janitor.hpp>
#include <util/XMLUni.hpp>
#include <math.h>

//
// The special values positive and negative zero,
// positive and negative infinity and not-a-number
// have lexical representations 0, -0, INF, -INF and NaN, respectively
//

/***
 *   Algo:
 *
 *   . Check for special cases
 *   . construct fMantissa, fExponent (optional)
 *   . No checking on the value space
 *   .
***/
XMLDouble::XMLDouble(const XMLCh* const strValue)
:fMantissa(0)
,fExponent(0)
,fType(Normal)
,fValue(0)
{
    if ((!strValue) || (!*strValue))
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);

//    char *p1 = XMLString::transcode(strValue);
    XMLCh* tmpStrValue = XMLString::replicate(strValue);
    ArrayJanitor<XMLCh> janTmpName(tmpStrValue);
    XMLString::trim(tmpStrValue);
//    char *p2 = XMLString::transcode(tmpStrValue);

    if (XMLString::compareString(tmpStrValue, XMLUni::fgNegINFString) == 0)
    {
        fType = NegINF;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgNegZeroString) == 0)
    {
        fType = NegZero;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgPosZeroString) == 0)
    {
        fType = PosZero;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgPosINFString) == 0)
    {
        fType = PosINF;
        return;
    }
    else if (XMLString::compareString(tmpStrValue, XMLUni::fgNaNString) == 0)
    {
        fType = NaN;
        return;
    }

    //
    // Normal case
    //
    int ePos = 0;
    int tmpStrLen = XMLString::stringLen(tmpStrValue);

    if ((( ePos = XMLString::indexOf(tmpStrValue, chLatin_E)) != -1 ) ||
        (( ePos = XMLString::indexOf(tmpStrValue, chLatin_e)) != -1 )  )
    {
        XMLCh* tmpMantissa = new XMLCh [ePos+1];
        XMLString::subString(tmpMantissa, tmpStrValue, 0, ePos);
        ArrayJanitor<XMLCh> janMantissa(tmpMantissa);
        fMantissa = new XMLBigDecimal(tmpMantissa);

        //
        // 1234.56E78
        //
        if ( ePos < tmpStrLen - 1)
        {
            XMLCh* tmpExponent = new XMLCh [tmpStrLen - ePos];
            XMLString::subString(tmpExponent, tmpStrValue, ePos + 1, tmpStrLen);
            ArrayJanitor<XMLCh> janExponent(tmpExponent);
            fExponent = new XMLBigInteger(tmpExponent);
        }
        //
        // 1234.56E
        //
        else
        {
            fExponent = new XMLBigInteger(XMLUni::fgZeroString);
        }

    }
    else
    {
        fMantissa = new XMLBigDecimal(tmpStrValue);
        fExponent = new XMLBigInteger(XMLUni::fgZeroString);
    }

    fValue = fMantissa->doubleValue() * pow(10.0, fExponent->intValue());
}

XMLDouble::XMLDouble(const XMLDouble& toCopy)
:fMantissa(0)
,fExponent(0)
,fType(Normal)
,fValue(0)
{
    fMantissa = new XMLBigDecimal(*(toCopy.fMantissa));
    fExponent = new XMLBigInteger(*(toCopy.fExponent));
    fType     = toCopy.fType;
    fValue    = toCopy.fValue;
}

//
// Add the 'E' as necessary
// The caller needs to de-allocate the memory allocated by this function
// Deallocate the memory allocated by XMLBigInteger
//
XMLCh*  XMLDouble::toString() const
{
    switch (fType)
    {
    case NegINF:
        return XMLString::replicate(XMLUni::fgNegINFString);

    case NegZero:
        return XMLString::replicate(XMLUni::fgNegZeroString);

    case PosZero:
        return XMLString::replicate(XMLUni::fgPosZeroString);

    case PosINF:
        return XMLString::replicate(XMLUni::fgPosINFString);

    case NaN:
        return XMLString::replicate(XMLUni::fgNaNString);

    case Normal:

        XMLCh *ret_mantissa;
        ret_mantissa = fMantissa->toString();

        if (fExponent)
        {
            XMLCh *ret_exponent = fExponent->toString();
            XMLCh *ret_val = new XMLCh [ XMLString::stringLen(ret_mantissa) +
                                         XMLString::stringLen(ret_exponent) + 2];
            *ret_val = 0;
            XMLString::catString(ret_val, ret_mantissa);
            XMLString::catString(ret_val, XMLUni::fgEString);
            XMLString::catString(ret_val, ret_exponent);

            delete [] ret_mantissa;
            delete [] ret_exponent;
            return ret_val;
        }
        else
            return ret_mantissa;

        break;
    default:
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);
        //internal error
    };

    return (XMLCh*) 0;
}

//
//
//
int XMLDouble::compareValues(const XMLDouble* const lValue
                           , const XMLDouble* const rValue)
{
    //
    // case#1: lValue normal
    //         rValue normal
    //
    if ((!lValue->isSpecialValue()) &&
        (!rValue->isSpecialValue())  )
    {
        //
        // if we use fValue to compare two
        // sequences "12.3456E4" and "1234.56E2",
        // they are _NOT_ the same. so we ask
        // BigDecimal to compare.
        //
        XMLBigDecimal ldv = XMLBigDecimal(*(lValue->fMantissa), lValue->fExponent->intValue());
        XMLBigDecimal rdv = XMLBigDecimal(*(rValue->fMantissa), rValue->fExponent->intValue());

        return XMLBigDecimal::compareValues(&ldv, &rdv);
    }
    //
    // case#2: lValue special
    //         rValue special
    //
    else
    if ((lValue->isSpecialValue()) &&
        (rValue->isSpecialValue())  )
    {
        if (lValue->fType == rValue->fType)
            return 0;
        else
            return (lValue->fType > rValue->fType ? 1 : -1);
    }
    //
    // case#3: lValue special
    //         rValue normal
    //
    else
    if ((lValue->isSpecialValue()) &&
        (!rValue->isSpecialValue())  )
    {
        return compareSpecial(lValue, rValue);
    }
    //
    // case#4: lValue normal
    //         rValue special
    //
    else
    {
        return (-1) * compareSpecial(rValue, lValue);
    }

    return 0;
}

int XMLDouble::compareSpecial(const XMLDouble* const specialValue
                            , const XMLDouble* const normalValue)
{
    switch (specialValue->fType)
    {
    case NegINF:
        return -1;

    case NegZero:
    case PosZero:
        return (normalValue->doubleValue() > 0 ? -1 : 1);

    case PosINF:
        return 1;

    case NaN:
        return 1;

    default:
        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_emptyString);
        //internal error
    }
}<|endoftext|>


<commit_before>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (boost::asio::ip::tcp::socket * socket,
		const i2p::data::LeaseSet * leaseSet): m_Socket (socket)
	{
		m_Stream = i2p::stream::CreateStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		// TODO: remove from I2PTunnel
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred, 0);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port):
		m_Service (service), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		auto pos = m_Destination.find(".b32.i2p");
		if (pos != std::string::npos)
		{
			uint8_t hash[32];
			i2p::data::Base32ToByteStream (m_Destination.c_str(), pos, hash, 32);
			m_DestinationIdentHash = new i2p::data::IdentHash (hash);
		}
		else
		{
			pos = m_Destination.find (".i2p");
			if (pos != std::string::npos)
			{
				auto identHash = i2p::data::netdb.FindAddress (m_Destination);
				if (identHash)
					m_DestinationIdentHash = new i2p::data::IdentHash (*identHash);
			}
		}
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash);
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (m_Service);
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					auto identHash = i2p::data::netdb.FindAddress (m_Destination);
					if (identHash)
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (*identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash);
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (socket, m_RemoteLeaseSet);
				m_Connections.insert (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}
}
}
<commit_msg>close client socket if I2P gets closed<commit_after>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (boost::asio::ip::tcp::socket * socket,
		const i2p::data::LeaseSet * leaseSet): m_Socket (socket)
	{
		m_Stream = i2p::stream::CreateStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		m_Socket->close ();
		// TODO: remove from I2PTunnel
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred, 0);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port):
		m_Service (service), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		auto pos = m_Destination.find(".b32.i2p");
		if (pos != std::string::npos)
		{
			uint8_t hash[32];
			i2p::data::Base32ToByteStream (m_Destination.c_str(), pos, hash, 32);
			m_DestinationIdentHash = new i2p::data::IdentHash (hash);
		}
		else
		{
			pos = m_Destination.find (".i2p");
			if (pos != std::string::npos)
			{
				auto identHash = i2p::data::netdb.FindAddress (m_Destination);
				if (identHash)
					m_DestinationIdentHash = new i2p::data::IdentHash (*identHash);
			}
		}
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash);
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (m_Service);
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					auto identHash = i2p::data::netdb.FindAddress (m_Destination);
					if (identHash)
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (*identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash);
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (socket, m_RemoteLeaseSet);
				m_Connections.insert (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}
}
}
<|endoftext|>


<commit_before>#include "base64.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace client
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner), m_RemoteEndpoint (socket->remote_endpoint ()),
		m_IsQuiet (true)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
		m_Socket (socket), m_Stream (stream), m_Owner (owner),
		m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
	{
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
		m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet)
	{
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		delete m_Socket;
	}

	void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
	{
		if (msg)
			m_Stream->Send (msg, len); // connect and send
		else
			m_Stream->Send (m_Buffer, 0); // connect
		StreamReceive ();
		Receive ();
	}

	void I2PTunnelConnection::Connect ()
	{
		if (m_Socket)
			m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
				shared_from_this (), std::placeholders::_1));
	}

	void I2PTunnelConnection::Terminate ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			m_Stream.reset ();
		}
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (shared_from_this ());
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
			std::placeholders::_1, std::placeholders::_2));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
					std::placeholders::_1, std::placeholders::_2),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			if (m_IsQuiet)
				StreamReceive ();
			else
			{
				// send destination first like received from I2P
				std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
				dest += "\n";
				memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
				HandleStreamReceive (boost::system::error_code (), dest.size ());
			}
			Receive ();
		}
	}

	I2PTunnel::I2PTunnel (ClientDestination * localDestination) :
		m_LocalDestination (localDestination ? localDestination :
			i2p::client::context.CreateNewLocalDestination (false, I2P_TUNNEL_DEFAULT_KEY_TYPE))
	{
	}
	void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
		I2PTunnel (localDestination),
		m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		GetIdentHash();
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		m_Timer.cancel ();
		ClearConnections ();
		auto *originalIdentHash = m_DestinationIdentHash;
		m_DestinationIdentHash = nullptr;
		delete originalIdentHash;
	}

	/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
	const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
	{
		if (!m_DestinationIdentHash)
		{
			i2p::data::IdentHash identHash;
			if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
				m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
			else
				LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found");
		}
		return m_DestinationIdentHash;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, std::bind (&I2PClientTunnel::HandleAccept, this,
			std::placeholders::_1, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			const i2p::data::IdentHash *identHash = GetIdentHash();
			if (identHash)
				GetLocalDestination ()->CreateStream (
					std::bind (&I2PClientTunnel::HandleStreamRequestComplete,
					this, std::placeholders::_1, socket), *identHash);
			else
			{
				LogPrint (eLogError,"Closing socket");
				delete socket;
			}
			Accept ();
		}
		else
		{
			LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ());
			delete socket;
		}
	}

	void I2PClientTunnel::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket)
	{
		if (stream)
		{
			LogPrint (eLogInfo,"New I2PTunnel connection");
			auto connection = std::make_shared<I2PTunnelConnection>(this, socket, stream);
			AddConnection (connection);
			connection->I2PConnect ();
		}
		else
		{
			LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info.");
			delete socket;
		}
	}

	I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
		I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
	{
		if (stream)
		{
			auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
			AddConnection (conn);
			conn->Connect ();
		}
	}
}
}
<commit_msg>Detect null stream on I2PConnect<commit_after>#include <cassert>
#include "base64.h"
#include "Log.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace client
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner), m_RemoteEndpoint (socket->remote_endpoint ()),
		m_IsQuiet (true)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
		m_Socket (socket), m_Stream (stream), m_Owner (owner),
		m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
	{
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
		m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet)
	{
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		delete m_Socket;
	}

	void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
	{
		if (m_Stream)
		{
			if (msg)
				m_Stream->Send (msg, len); // connect and send
			else
				m_Stream->Send (m_Buffer, 0); // connect
		}
		StreamReceive ();
		Receive ();
	}

	void I2PTunnelConnection::Connect ()
	{
		if (m_Socket)
			m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
				shared_from_this (), std::placeholders::_1));
	}

	void I2PTunnelConnection::Terminate ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			m_Stream.reset ();
		}
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (shared_from_this ());
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
			std::placeholders::_1, std::placeholders::_2));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
					std::placeholders::_1, std::placeholders::_2),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			if (m_IsQuiet)
				StreamReceive ();
			else
			{
				// send destination first like received from I2P
				std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
				dest += "\n";
				memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
				HandleStreamReceive (boost::system::error_code (), dest.size ());
			}
			Receive ();
		}
	}

	I2PTunnel::I2PTunnel (ClientDestination * localDestination) :
		m_LocalDestination (localDestination ? localDestination :
			i2p::client::context.CreateNewLocalDestination (false, I2P_TUNNEL_DEFAULT_KEY_TYPE))
	{
	}
	void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
		I2PTunnel (localDestination),
		m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		GetIdentHash();
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		m_Timer.cancel ();
		ClearConnections ();
		auto *originalIdentHash = m_DestinationIdentHash;
		m_DestinationIdentHash = nullptr;
		delete originalIdentHash;
	}

	/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
	const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
	{
		if (!m_DestinationIdentHash)
		{
			i2p::data::IdentHash identHash;
			if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
				m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
			else
				LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found");
		}
		return m_DestinationIdentHash;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, std::bind (&I2PClientTunnel::HandleAccept, this,
			std::placeholders::_1, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			const i2p::data::IdentHash *identHash = GetIdentHash();
			if (identHash)
				GetLocalDestination ()->CreateStream (
					std::bind (&I2PClientTunnel::HandleStreamRequestComplete,
					this, std::placeholders::_1, socket), *identHash);
			else
			{
				LogPrint (eLogError,"Closing socket");
				delete socket;
			}
			Accept ();
		}
		else
		{
			LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ());
			delete socket;
		}
	}

	void I2PClientTunnel::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket)
	{
		if (stream)
		{
			LogPrint (eLogInfo,"New I2PTunnel connection");
			auto connection = std::make_shared<I2PTunnelConnection>(this, socket, stream);
			AddConnection (connection);
			connection->I2PConnect ();
		}
		else
		{
			LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info.");
			delete socket;
		}
	}

	I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
		I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
	{
		if (stream)
		{
			auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
			AddConnection (conn);
			conn->Connect ();
		}
	}
}
}
<|endoftext|>


<commit_before>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateNewOutgoingStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, Stream * stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
		m_Socket (socket), m_Stream (stream), m_Owner (owner)
	{
		if (m_Socket)
			m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
				this, boost::asio::placeholders::error));
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (this);
		// TODO: delete
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			if (m_Stream) m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			StreamReceive ();
			Receive ();
		}
	}

	void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
		int port, StreamingDestination * localDestination):
		I2PTunnel (service, localDestination ? localDestination : GetSharedLocalDestination ()),
		m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		i2p::data::IdentHash identHash;
		if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
			m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		ClearConnections ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					i2p::data::IdentHash identHash;
					if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
				AddConnection (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}

	I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
		StreamingDestination * localDestination): I2PTunnel (service, localDestination),
		m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (i2p::stream::Stream * stream)
	{
		if (stream)
			new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
	}
}
}
<commit_msg>delete closed I2P tunnel connection<commit_after>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "Destination.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateNewOutgoingStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, Stream * stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
		m_Socket (socket), m_Stream (stream), m_Owner (owner)
	{
		if (m_Socket)
			m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
				this, boost::asio::placeholders::error));
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (this);
		delete this;
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
				Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			if (ecode != boost::asio::error::operation_aborted)
			{
				if (m_Stream) m_Stream->Close ();
				DeleteStream (m_Stream);
				m_Stream = nullptr;
			}
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			StreamReceive ();
			Receive ();
		}
	}

	void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
		int port, StreamingDestination * localDestination):
		I2PTunnel (service, localDestination ? localDestination : GetSharedLocalDestination ()),
		m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		i2p::data::IdentHash identHash;
		if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
			m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		ClearConnections ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					i2p::data::IdentHash identHash;
					if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
				AddConnection (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}

	I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
		StreamingDestination * localDestination): I2PTunnel (service, localDestination),
		m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (i2p::stream::Stream * stream)
	{
		if (stream)
			new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
	}
}
}
<|endoftext|>