Added support for charset conversion with ICU (thanks to Mehmet Bozkurt).

This commit is contained in:
Vincent Richard 2013-03-25 12:32:48 +01:00
parent f381654277
commit 9d2703c376
13 changed files with 728 additions and 18 deletions

View File

@ -28,7 +28,7 @@ AUTHORS file.
- Zarafa <http://developer.zarafa.com/VmimePatches>
- Bartek Szurgot <vempirelord@wp.pl, http://baszerr.org>
- Achim Brandt <http://sourceforge.net/users/a-brandt/>
- Mehmet Bozkurt <mehmet.bozkurt78@gmail.com> (OpenSSL support)
- Mehmet Bozkurt <mehmet.bozkurt78@gmail.com> (OpenSSL support, ICU support)
Please apologize if I have forgotten someone here. ;) Send me an email
to <vincent@vmime.org> if you want your name to be listed.

View File

@ -12,7 +12,7 @@
# http://www.cmake.org
#
CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3 FATAL_ERROR)
INCLUDE(cmake/Utils.cmake)
@ -623,25 +623,74 @@ ENDIF(VMIME_HAVE_TLS_SUPPORT)
##############################################################################
# iconv
# Charset conversion library
INCLUDE(cmake/FindIconv.cmake)
INCLUDE(cmake/FindICU.cmake)
INCLUDE_DIRECTORIES(
${INCLUDE_DIRECTORIES}
${ICONV_INCLUDE_DIR}
)
FIND_PACKAGE(ICU QUIET)
IF(VMIME_BUILD_SHARED_LIBRARY)
TARGET_LINK_LIBRARIES(
${VMIME_LIBRARY_NAME}
${TARGET_LINK_LIBRARIES}
${ICONV_LIBRARIES}
)
IF(ICONV_FOUND)
SET(VMIME_CHARSETCONV_LIB_IS_ICONV_DEFAULT "ON")
SET(VMIME_CHARSETCONV_LIB_IS_ICU_DEFAULT "OFF")
ELSEIF(ICU_LIBRARIES)
SET(VMIME_CHARSETCONV_LIB_IS_ICONV_DEFAULT "OFF")
SET(VMIME_CHARSETCONV_LIB_IS_ICU_DEFAULT "ON")
ENDIF()
SET(VMIME_PKGCONFIG_LIBS "${VMIME_PKGCONFIG_LIBS} ${ICONV_LIBRARIES}")
SET(VMIME_PKGCONFIG_CFLAGS "${VMIME_PKGCONFIG_CFLAGS} -I${ICONV_INCLUDE_DIR}")
OPTION(
VMIME_CHARSETCONV_LIB_IS_ICONV
"Use iconv library for charset conversion"
${VMIME_CHARSETCONV_LIB_IS_ICONV_DEFAULT}
)
OPTION(
VMIME_CHARSETCONV_LIB_IS_ICU
"Use ICU library for charset conversion"
${VMIME_CHARSETCONV_LIB_IS_ICU_DEFAULT}
)
IF(VMIME_CHARSETCONV_LIB_IS_ICONV)
INCLUDE_DIRECTORIES(
${INCLUDE_DIRECTORIES}
${ICONV_INCLUDE_DIR}
)
IF(VMIME_BUILD_SHARED_LIBRARY)
TARGET_LINK_LIBRARIES(
${VMIME_LIBRARY_NAME}
${TARGET_LINK_LIBRARIES}
${ICONV_LIBRARIES}
)
ENDIF()
SET(VMIME_PKGCONFIG_LIBS "${VMIME_PKGCONFIG_LIBS} ${ICONV_LIBRARIES}")
SET(VMIME_PKGCONFIG_CFLAGS "${VMIME_PKGCONFIG_CFLAGS} -I${ICONV_INCLUDE_DIR}")
ELSEIF(VMIME_CHARSETCONV_LIB_IS_ICU)
INCLUDE_DIRECTORIES(
${INCLUDE_DIRECTORIES}
${ICU_INCLUDE_DIRS}
)
IF(VMIME_BUILD_SHARED_LIBRARY)
TARGET_LINK_LIBRARIES(
${VMIME_LIBRARY_NAME}
${TARGET_LINK_LIBRARIES}
${ICU_LIBRARIES}
)
ENDIF()
SET(VMIME_PKGCONFIG_LIBS "${VMIME_PKGCONFIG_LIBS} ${ICU_LIBRARIES}")
SET(VMIME_PKGCONFIG_CFLAGS "${VMIME_PKGCONFIG_CFLAGS} -I${ICU_INCLUDE_DIRS}")
ELSE()
MESSAGE(FATAL_ERROR "No charset conversion library was selected/found")
ENDIF()
##############################################################################

View File

@ -57,6 +57,7 @@ libvmime_sources = [
'charset.cpp', 'charset.hpp',
'charsetConverter.cpp', 'charsetConverter.hpp',
'charsetConverter_iconv.cpp', 'charsetConverter_iconv.hpp',
'charsetConverter_icu.cpp', 'charsetConverter_icu.hpp',
'charsetConverter_idna.cpp', 'charsetConverter_idna.hpp',
'charsetConverterOptions.cpp', 'charsetConverterOptions.hpp',
'component.cpp', 'component.hpp',
@ -673,6 +674,15 @@ if env['with_tls'] == 'yes':
env.Append(CXXFLAGS = ['-pthread'])
# Charset conversion library
# -- iconv
if sys.platform == "mac" or sys.platform == "darwin":
env.Append(LIBS = ['iconv', 'gcrypt'])
# -- ICU
env.Append(LIBS = ['icuuc', 'icudata', 'icui18n'])
# Generate help text for command line options
Help(opts.GenerateHelpText(env))
@ -836,6 +846,11 @@ config_hpp.write('\n')
config_hpp.write('#define VMIME_HAVE_SIZE_T 1\n')
config_hpp.write('\n')
config_hpp.write('// Charset conversion support\n')
config_hpp.write('#define VMIME_CHARSETCONV_LIB_IS_ICONV 1\n')
config_hpp.write('#define VMIME_CHARSETCONV_LIB_IS_ICU 0\n')
config_hpp.write('\n')
config_hpp.write('// Options\n')
config_hpp.write('// -- File-system support\n')

314
cmake/FindICU.cmake Normal file
View File

@ -0,0 +1,314 @@
# This module can find the International Components for Unicode (ICU) Library
#
# Requirements:
# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args)
#
# The following variables will be defined for your use:
# - ICU_FOUND : were all of your specified components found (include dependencies)?
# - ICU_INCLUDE_DIRS : ICU include directory
# - ICU_LIBRARIES : ICU libraries
# - ICU_VERSION : complete version of ICU (x.y.z)
# - ICU_MAJOR_VERSION : major version of ICU
# - ICU_MINOR_VERSION : minor version of ICU
# - ICU_PATCH_VERSION : patch version of ICU
# - ICU_<COMPONENT>_FOUND : were <COMPONENT> found? (FALSE for non specified component if it is not a dependency)
#
# For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways:
# - run cmake with -DICU_ROOT=<PATH>
# - define an environment variable with the same name before running cmake
# With cmake-gui, before pressing "Configure":
# 1) Press "Add Entry" button
# 2) Add a new entry defined as:
# - Name: ICU_ROOT
# - Type: choose PATH in the selection list
# - Press "..." button and select the root installation of ICU
#
# Example Usage:
#
# 1. Copy this file in the root of your project source directory
# 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt:
# set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
# 3. Finally call find_package() once, here are some examples to pick from
#
# Require ICU 4.4 or later
# find_package(ICU 4.4 REQUIRED)
#
# if(ICU_FOUND)
# include_directories(${ICU_INCLUDE_DIRS})
# add_executable(myapp myapp.c)
# target_link_libraries(myapp ${ICU_LIBRARIES})
# endif(ICU_FOUND)
#=============================================================================
# Copyright (c) 2011-2012, julp
# https://github.com/julp/FindICU.cmake
#
# Distributed under the OSI-approved BSD License
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#=============================================================================
find_package(PkgConfig QUIET)
########## Private ##########
set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables
set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables
set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables
function(icudebug _VARNAME)
if(${ICU_PUBLIC_VAR_NS}_DEBUG)
if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}")
else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = <UNDEFINED>")
endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
endif(${ICU_PUBLIC_VAR_NS}_DEBUG)
endfunction(icudebug)
set(${ICU_PRIVATE_VAR_NS}_ROOT "")
if(DEFINED ENV{ICU_ROOT})
set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}")
endif(DEFINED ENV{ICU_ROOT})
if (DEFINED ICU_ROOT)
set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}")
endif(DEFINED ICU_ROOT)
set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES )
set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES )
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64")
list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64")
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin")
list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib")
set(${ICU_PRIVATE_VAR_NS}_COMPONENTS )
# <icu component name> <library name 1> ... <library name N>
macro(icu_declare_component _NAME)
list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME})
set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN})
endmacro(icu_declare_component)
icu_declare_component(data icudata)
icu_declare_component(uc icuuc) # Common and Data libraries
icu_declare_component(i18n icui18n icuin) # Internationalization library
icu_declare_component(io icuio ustdio) # Stream and I/O Library
icu_declare_component(le icule) # Layout library
icu_declare_component(lx iculx) # Paragraph Layout library
########## Public ##########
set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE)
set(${ICU_PUBLIC_VAR_NS}_LIBRARIES )
set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS )
set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "")
set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "")
set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "")
set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "")
set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "")
set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "")
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS})
string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
# Check components
if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least
set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}")
endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
# Includes
find_path(
${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
NAMES unicode/utypes.h utypes.h
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
PATH_SUFFIXES "include"
DOC "Include directories for ICU"
)
if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
########## <part to keep synced with tests/version/CMakeLists.txt> ##########
if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3
file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
else()
message(FATAL_ERROR "ICU version header not found")
endif()
if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3
# [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0)
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1")
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}")
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0")
elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*")
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
#
# Since version 4.9.1, ICU release version numbering was totaly changed, see:
# - http://site.icu-project.org/download/49
# - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU
#
if(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION LESS 49)
string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
else(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION LESS 49)
string(REGEX MATCH [0-9]$ ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}")
string(REGEX REPLACE [0-9]$ "" ${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}")
string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
endif(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION LESS 49)
elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[
# [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0
set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if
if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$")
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}")
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0")
elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}")
set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}")
set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}")
endif()
else()
message(FATAL_ERROR "failed to detect ICU version")
endif()
set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}")
########## </part to keep synced with tests/version/CMakeLists.txt> ##########
# Check dependencies (implies pkg-config)
if(PKG_CONFIG_FOUND)
set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP})
pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET)
if(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES})
string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY})
list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY})
endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY)
endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
endif(PKG_CONFIG_FOUND)
# Check libraries
foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES )
set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES )
foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}})
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}")
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d")
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}")
list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d")
endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME)
find_library(
${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES}
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
PATH_SUFFIXES ${_ICU_LIB_SUFFIXES}
DOC "Release libraries for ICU"
)
find_library(
${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES}
HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}
PATH_SUFFIXES ${_ICU_LIB_SUFFIXES}
DOC "Debug libraries for ICU"
)
string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE)
set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE)
else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found
set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE)
if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug
set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}")
elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release
set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}")
else() # both found
set(
${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}
optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}
debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}
)
endif()
list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}})
endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
# Try to find out compiler flags
find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} icu-config)
if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
# Check find_package arguments
include(FindPackageHandleStandardArgs)
if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
find_package_handle_standard_args(
${ICU_PUBLIC_VAR_NS}
REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION
)
else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
message(FATAL_ERROR "Could not find ICU include directory")
endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS)
mark_as_advanced(
${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS
${ICU_PUBLIC_VAR_NS}_LIBRARIES
)
# IN (args)
icudebug("FIND_COMPONENTS")
icudebug("FIND_REQUIRED")
icudebug("FIND_QUIETLY")
icudebug("FIND_VERSION")
# OUT
# Found
icudebug("FOUND")
icudebug("UC_FOUND")
icudebug("I18N_FOUND")
icudebug("IO_FOUND")
icudebug("LE_FOUND")
icudebug("LX_FOUND")
icudebug("DATA_FOUND")
# Flags
icudebug("C_FLAGS")
icudebug("CPP_FLAGS")
icudebug("CXX_FLAGS")
icudebug("C_SHARED_FLAGS")
icudebug("CPP_SHARED_FLAGS")
icudebug("CXX_SHARED_FLAGS")
# Linking
icudebug("INCLUDE_DIRS")
icudebug("LIBRARIES")
# Version
icudebug("MAJOR_VERSION")
icudebug("MINOR_VERSION")
icudebug("PATCH_VERSION")
icudebug("VERSION")

View File

@ -37,6 +37,10 @@ typedef unsigned @VMIME_32BIT_TYPE@ vmime_uint32;
#cmakedefine01 VMIME_HAVE_SIZE_T
// Charset conversion support
#cmakedefine01 VMIME_CHARSETCONV_LIB_IS_ICONV
#cmakedefine01 VMIME_CHARSETCONV_LIB_IS_ICU
// Options
// -- File-system support
#cmakedefine01 VMIME_HAVE_FILESYSTEM_FEATURES

View File

@ -23,7 +23,6 @@
#include "vmime/charsetConverter.hpp"
#include "vmime/charsetConverter_iconv.hpp"
#include "vmime/charsetConverter_idna.hpp"
@ -39,7 +38,7 @@ ref <charsetConverter> charsetConverter::create
if (source == "idna" || dest == "idna")
return vmime::create <charsetConverter_idna>(source, dest, opts);
else
return vmime::create <charsetConverter_iconv>(source, dest, opts);
return createGenericConverter(source, dest, opts);
}

View File

@ -21,6 +21,12 @@
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_CHARSETCONV_LIB_IS_ICONV
#include "vmime/charsetConverter_iconv.hpp"
#include "vmime/exception.hpp"
@ -82,6 +88,15 @@ namespace vmime
{
// static
ref <charsetConverter> charsetConverter::createGenericConverter
(const charset& source, const charset& dest,
const charsetConverterOptions& opts)
{
return vmime::create <charsetConverter_iconv>(source, dest, opts);
}
charsetConverter_iconv::charsetConverter_iconv
(const charset& source, const charset& dest, const charsetConverterOptions& opts)
: m_desc(NULL), m_source(source), m_dest(dest), m_options(opts)
@ -433,3 +448,6 @@ void charsetFilteredOutputStream_iconv::flush()
} // vmime
#endif // VMIME_CHARSETCONV_LIB_IS_ICONV

202
src/charsetConverter_icu.cpp Executable file
View File

@ -0,0 +1,202 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_CHARSETCONV_LIB_IS_ICU
#include "vmime/charsetConverter_icu.hpp"
#include "vmime/exception.hpp"
#include "vmime/utility/inputStreamStringAdapter.hpp"
#include "vmime/utility/outputStreamStringAdapter.hpp"
extern "C"
{
#ifndef VMIME_BUILDING_DOC
#include <unicode/ucnv.h>
#include <unicode/ucnv_err.h>
#endif // VMIME_BUILDING_DOC
}
#include <unicode/unistr.h>
namespace vmime
{
// static
ref <charsetConverter> charsetConverter::createGenericConverter
(const charset& source, const charset& dest,
const charsetConverterOptions& opts)
{
return vmime::create <charsetConverter_icu>(source, dest, opts);
}
charsetConverter_icu::charsetConverter_icu
(const charset& source, const charset& dest, const charsetConverterOptions& opts)
: m_from(NULL), m_to(NULL), m_source(source), m_dest(dest), m_options(opts)
{
UErrorCode err = U_ZERO_ERROR;
m_from = ucnv_open(source.getName().c_str(), &err);
if (err != U_ZERO_ERROR)
throw exceptions::charset_conv_error("Cannot initialize ICU [from] converter.");
m_to = ucnv_open(dest.getName().c_str(), &err);
if (err != U_ZERO_ERROR)
throw exceptions::charset_conv_error("Cannot initialize ICU [to] converter.");
}
charsetConverter_icu::~charsetConverter_icu()
{
ucnv_close(m_from);
ucnv_close(m_to);
}
void charsetConverter_icu::convert(utility::inputStream& in, utility::outputStream& out)
{
UErrorCode err = U_ZERO_ERROR;
// From buffers
char cpInBuffer[16]; // stream data put here
size_t outSize = ucnv_getMinCharSize(m_from) * sizeof(cpInBuffer) * sizeof(UChar);
UChar* uOutBuffer = new UChar[outSize]; // Unicode chars end up here
// Auto delete Unicode char buffer
vmime::utility::auto_ptr<UChar> cleanup(uOutBuffer);
// To buffers
// converted (char) data end up here
size_t cpOutBufferSz = ucnv_getMaxCharSize(m_to) * outSize;
char* cpOutBuffer = new char[cpOutBufferSz];
vmime::utility::auto_ptr<char> cleanupOut(cpOutBuffer);
// Set replacement chars for when converting from Unicode to codepage
icu::UnicodeString substString(m_options.invalidSequence.c_str());
ucnv_setSubstString(m_to, substString.getTerminatedBuffer(), -1, &err);
if (U_FAILURE(err))
throw exceptions::charset_conv_error("[ICU] Error setting replacement char.");
// Input data available
while (!in.eof())
{
// Read input data into buffer
size_t inLength = static_cast<size_t>(in.read(cpInBuffer, sizeof(cpInBuffer)));
// Beginning of read data
const char* source = &cpInBuffer[0];
const char* sourceLimit = source + inLength; // end + 1
UBool flush = in.eof(); // is this last run?
UErrorCode toErr;
// Loop until all source has been processed
do
{
// Set up target pointers
UChar* target = uOutBuffer;
UChar* targetLimit = target + outSize;
toErr = U_ZERO_ERROR;
ucnv_toUnicode(m_from, &target, targetLimit,
&source, sourceLimit, NULL, flush, &toErr);
if (toErr != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(toErr))
throw exceptions::charset_conv_error("[ICU] Error converting to Unicode from " + m_source.getName());
// The Unicode source is the buffer just written and the limit
// is where the previous conversion stopped (target is moved in the conversion)
const UChar* uSource = uOutBuffer;
UChar* uSourceLimit = target;
UErrorCode fromErr;
// Loop until converted chars are fully written
do
{
char* cpTarget = &cpOutBuffer[0];
const char* cpTargetLimit = cpOutBuffer + cpOutBufferSz;
fromErr = U_ZERO_ERROR;
// Write converted bytes (Unicode) to destination codepage
ucnv_fromUnicode(m_to, &cpTarget, cpTargetLimit,
&uSource, uSourceLimit, NULL, flush, &fromErr);
if (fromErr != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(fromErr))
throw exceptions::charset_conv_error("[ICU] Error converting from Unicode to " + m_dest.getName());
// Write to destination stream
out.write(cpOutBuffer, (cpTarget - cpOutBuffer));
} while (fromErr == U_BUFFER_OVERFLOW_ERROR);
} while (toErr == U_BUFFER_OVERFLOW_ERROR);
}
}
void charsetConverter_icu::convert(const string& in, string& out)
{
if (m_source == m_dest)
{
// No conversion needed
out = in;
return;
}
out.clear();
utility::inputStreamStringAdapter is(in);
utility::outputStreamStringAdapter os(out);
convert(is, os);
os.flush();
}
ref <utility::charsetFilteredOutputStream> charsetConverter_icu::getFilteredOutputStream(utility::outputStream& os)
{
// TODO: implement charsetFilteredOutputStream for ICU
return NULL;
}
} // vmime
#endif // VMIME_CHARSETCONV_LIB_IS_ICU

View File

@ -49,6 +49,8 @@ VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest)
vmime::utility::outputStreamStringAdapter os(output);
vmime::ref <vmime::utility::filteredOutputStream> cfos = cc->getFilteredOutputStream(os);
VASSERT_NOT_NULL("filteredOutputStream availability", cfos);
// føo = 66 c3 b8 6f [UTF8]
// føo = 66 c3 b8 6f [latin1]
@ -79,6 +81,8 @@ VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest)
vmime::ref <vmime::utility::charsetFilteredOutputStream> os =
conv->getFilteredOutputStream(osa);
VASSERT_NOT_NULL("filteredOutputStream availability", os);
vmime::utility::inputStreamStringAdapter is(in);
vmime::utility::stream::value_type buffer[16];
@ -116,6 +120,8 @@ VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest)
vmime::ref <vmime::utility::charsetFilteredOutputStream> os =
conv->getFilteredOutputStream(osa);
VASSERT_NOT_NULL("filteredOutputStream availability", os);
vmime::utility::inputStreamStringAdapter is(in);
vmime::utility::bufferedStreamCopy(is, *os);
@ -151,6 +157,8 @@ VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest)
vmime::ref <vmime::utility::charsetFilteredOutputStream> os =
conv->getFilteredOutputStream(osa);
VASSERT_NOT_NULL("filteredOutputStream availability", os);
vmime::utility::inputStreamStringAdapter is(in);
vmime::utility::stream::value_type buffer[16];
@ -189,6 +197,8 @@ VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest)
vmime::ref <vmime::utility::charsetFilteredOutputStream> os =
conv->getFilteredOutputStream(osa);
VASSERT_NOT_NULL("filteredOutputStream availability", os);
vmime::utility::inputStreamStringAdapter is(in);
vmime::utility::stream::value_type buffer[16];

View File

@ -141,7 +141,8 @@ VMIME_TEST_SUITE_BEGIN(charsetTest)
void testUTF7Support()
{
// Ensure UTF-7 is supported, because it is used for IMAP
VASSERT_EQ("1", "VMime +ACY UTF-7 encoding", convertHelper("VMime & UTF-7 encoding", "utf-8", "utf-7"));
VASSERT_EQ("1", "VMime +- UTF-7 encoding", convertHelper("VMime + UTF-7 encoding", "utf-8", "utf-7"));
VASSERT_EQ("2", "f+APg-o", convertHelper("\x66\xc3\xb8\x6f", "utf-8", "utf-7"));
}
VMIME_TEST_SUITE_END

View File

@ -104,6 +104,12 @@ public:
* @return a filtered output stream, or NULL if not supported
*/
virtual ref <utility::charsetFilteredOutputStream> getFilteredOutputStream(utility::outputStream& os) = 0;
private:
static ref <charsetConverter> createGenericConverter
(const charset& source, const charset& dest,
const charsetConverterOptions& opts);
};

View File

@ -25,6 +25,12 @@
#define VMIME_CHARSETCONVERTER_ICONV_HPP_INCLUDED
#include "vmime/config.hpp"
#if VMIME_CHARSETCONV_LIB_IS_ICONV
#include "vmime/charsetConverter.hpp"
@ -121,4 +127,6 @@ private:
} // vmime
#endif // VMIME_CHARSETCONV_LIB_IS_ICONV
#endif // VMIME_CHARSETCONVERTER_ICONV_HPP_INCLUDED

84
vmime/charsetConverter_icu.hpp Executable file
View File

@ -0,0 +1,84 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#ifndef VMIME_CHARSETCONVERTER_ICU_HPP_INCLUDED
#define VMIME_CHARSETCONVERTER_ICU_HPP_INCLUDED
#include "vmime/config.hpp"
#if VMIME_CHARSETCONV_LIB_IS_ICU
#include "vmime/charsetConverter.hpp"
struct UConverter;
namespace vmime
{
/** A generic charset converter which uses ICU library.
*/
class charsetConverter_icu : public charsetConverter
{
public:
/** Construct and initialize an ICU charset converter.
*
* @param source input charset
* @param dest output charset
* @param opts conversion options
*/
charsetConverter_icu(const charset& source, const charset& dest,
const charsetConverterOptions& opts = charsetConverterOptions());
~charsetConverter_icu();
void convert(const string& in, string& out);
void convert(utility::inputStream& in, utility::outputStream& out);
ref <utility::charsetFilteredOutputStream> getFilteredOutputStream(utility::outputStream& os);
private:
UConverter* m_from;
UConverter* m_to;
charset m_source;
charset m_dest;
charsetConverterOptions m_options;
};
} // vmime
#endif // VMIME_CHARSETCONV_LIB_IS_ICU
#endif // VMIME_CHARSETCONVERTER_ICU_HPP_INCLUDED