convert.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #ifndef convert_INCLUDED
  18. #define convert_INCLUDED
  19. #include <kopano/zcdefs.h>
  20. #include <map>
  21. #include <set>
  22. #include <list>
  23. #include <string>
  24. #include <stdexcept>
  25. #include <typeinfo>
  26. #include <iconv.h>
  27. #include <kopano/charset/traits.h>
  28. namespace KC {
  29. /**
  30. * @brief Exception class
  31. */
  32. class convert_exception : public std::runtime_error {
  33. public:
  34. enum exception_type {
  35. eUnknownCharset,
  36. eIllegalSequence
  37. };
  38. convert_exception(enum exception_type type, const std::string &message);
  39. enum exception_type type() const {
  40. return m_type;
  41. }
  42. private:
  43. enum exception_type m_type;
  44. };
  45. /**
  46. * @brief Unknown charset
  47. */
  48. class _kc_export_throw unknown_charset_exception _kc_final :
  49. public convert_exception {
  50. public:
  51. unknown_charset_exception(const std::string &message);
  52. };
  53. /**
  54. * @brief Illegal sequence
  55. */
  56. class _kc_export_throw illegal_sequence_exception _kc_final :
  57. public convert_exception {
  58. public:
  59. illegal_sequence_exception(const std::string &message);
  60. };
  61. namespace details {
  62. /**
  63. * @brief Performs the generic iconv processing.
  64. */
  65. class _kc_export iconv_context_base {
  66. public:
  67. /**
  68. * @brief Destructor.
  69. */
  70. virtual ~iconv_context_base();
  71. protected:
  72. /**
  73. * @brief Constructor.
  74. *
  75. * @param[in] tocode The destination charset.
  76. * @param[out] fromcode The source charset.
  77. */
  78. iconv_context_base(const char* tocode, const char* fromcode);
  79. /**
  80. * @brief Performs the actual conversion.
  81. *
  82. * Performs the conversion and stores the result in the output string
  83. * by calling append, which must be overridden by a derived class.
  84. * @param[in] lpFrom Pointer to the source data.
  85. * @param[in] cbFrom Size of the source data in bytes.
  86. */
  87. void doconvert(const char *lpFrom, size_t cbFrom);
  88. private:
  89. /**
  90. * @brief Appends converted data to the result.
  91. *
  92. * @param[in] lpBuf Pointer to the data to be appended.
  93. * @param[in] cbBuf Size of the data to be appended in bytes.
  94. */
  95. _kc_hidden virtual void append(const char *buf, size_t bufsize) = 0;
  96. iconv_t m_cd;
  97. bool m_bForce;
  98. bool m_bHTML;
  99. iconv_context_base(const iconv_context_base &) = delete;
  100. iconv_context_base &operator=(const iconv_context_base &) = delete;
  101. };
  102. /**
  103. * @brief Default converter from one charset to another with string types.
  104. */
  105. template <typename _To_Type, typename _From_Type>
  106. class _kc_export_dycast iconv_context _kc_final :
  107. public iconv_context_base {
  108. public:
  109. /**
  110. * @brief Contructor.
  111. *
  112. * Constructs a iconv_context_base with the right tocode and fromcode based
  113. * on the _To_Type and _From_Type template parameters.
  114. */
  115. iconv_context()
  116. : iconv_context_base(iconv_charset<_To_Type>::name(), iconv_charset<_From_Type>::name())
  117. {}
  118. /**
  119. * @brief Contructor.
  120. *
  121. * Constructs a iconv_context_base with the tocode based on the _To_Type
  122. * and the passed fromcode.
  123. */
  124. iconv_context(const char *fromcode)
  125. : iconv_context_base(iconv_charset<_To_Type>::name(), fromcode)
  126. {}
  127. /**
  128. * @brief Contructor.
  129. *
  130. * Constructs a iconv_context_base with the tocode based on the _To_Type
  131. * and the passed fromcode.
  132. */
  133. iconv_context(const char *tocode, const char *fromcode)
  134. : iconv_context_base(tocode, fromcode)
  135. {}
  136. /**
  137. * @brief Performs the conversion.
  138. *
  139. * The actual conversion in delegated to iconv_context_base.
  140. * @param[in] lpRaw Raw pointer to the data to be converted.
  141. * @param[in] cbRaw The size in bytes of the data to be converted.
  142. * @return The converted string.
  143. */
  144. _To_Type convert(const char *lpRaw, size_t cbRaw) {
  145. m_to.clear();
  146. doconvert(lpRaw, cbRaw);
  147. return m_to;
  148. }
  149. /**
  150. * @brief Performs the conversion.
  151. *
  152. * The actual conversion in delegated to iconv_context_base.
  153. * @param[in] _from The string to be converted.
  154. * @return The converted string.
  155. */
  156. _To_Type convert(const _From_Type &_from) {
  157. return convert(iconv_charset<_From_Type>::rawptr(_from), iconv_charset<_From_Type>::rawsize(_from));
  158. }
  159. private:
  160. _kc_hidden void append(const char *lpBuf, size_t cbBuf) _kc_override
  161. {
  162. m_to.append(reinterpret_cast<typename _To_Type::const_pointer>(lpBuf), cbBuf / sizeof(typename _To_Type::value_type));
  163. }
  164. _To_Type m_to;
  165. };
  166. /**
  167. * @brief Helper class for converting from one charset to another.
  168. *
  169. * The converter_helper class detects when the to and from charsets are identical. In
  170. * that case the string is merely copied.
  171. */
  172. template<typename _Type> class convert_helper _kc_final {
  173. public:
  174. /**
  175. * @brief Converts a string to a string with the same charset.
  176. *
  177. * Effectively this method does nothing but a copy.
  178. * @param[in] _from The string to be converted.
  179. * @return The converted string.
  180. */
  181. static _Type convert(const _Type &_from) { return _from; }
  182. /**
  183. * @brief Converts a string to a string with a different charset.
  184. *
  185. * The string with a charset linked to _Other_Type is converted to a
  186. * string with a charset linked to _Type.
  187. * @param[in] _from The string to be converted.
  188. * @return The converted string.
  189. */
  190. template <typename _Other_Type>
  191. static _Type convert(const _Other_Type &_from) {
  192. details::iconv_context<_Type, _Other_Type> context;
  193. return context.convert(_from);
  194. }
  195. };
  196. } // namespace details
  197. /**
  198. * @brief Converts a string to a string with a different charset.
  199. *
  200. * This is the function to call when a one of conversion from one charset to
  201. * another is required. The to- and from charsets are implicitly determined by
  202. * on one side the passed _To_Type and on the other side the _from argument.
  203. * @tparam _To_Type The type of the destination string.
  204. * @param[in] _from The string that is to be converted to another charset.
  205. * @return The converted string.
  206. *
  207. * @note Since this method needs to create an iconv object internally
  208. * it is better to use a convert_context when multiple conversions
  209. * need to be performed.
  210. */
  211. template <typename _To_Type, typename _From_Type>
  212. inline _To_Type convert_to(const _From_Type &_from)
  213. {
  214. return details::convert_helper<_To_Type>::convert(_from);
  215. }
  216. /**
  217. * @brief Converts a string to a string with a different charset.
  218. *
  219. * This is the function to call when a one of conversion from one charset to
  220. * another is required. The to charset is implicitly determined by
  221. * the passed _To_Type. The from charset is determined by fromcode.
  222. * @tparam _To_Type The type of the destination string.
  223. * @param[in] _from The string that is to be converted to another charset.
  224. * @param[in] cbBytes The size in bytes of the string to convert.
  225. * @param[in] fromcode The source charset.
  226. * @return The converted string.
  227. *
  228. * @note Since this method needs to create an iconv object internally
  229. * it is better to use a convert_context when multiple conversions
  230. * need to be performed.
  231. */
  232. template <typename _To_Type, typename _From_Type>
  233. inline _To_Type convert_to(const _From_Type &_from, size_t cbBytes, const char *fromcode)
  234. {
  235. details::iconv_context<_To_Type, _From_Type> context(fromcode);
  236. return context.convert(iconv_charset<_From_Type>::rawptr(_from), cbBytes);
  237. }
  238. /**
  239. * @brief Converts a string to a string with a different charset.
  240. *
  241. * This is the function to call when a one of conversion from one charset to
  242. * another is required. The to charset is determined by tocode.
  243. * The from charset is determined by fromcode.
  244. * @param[in] tocode The destination charset.
  245. * @param[in] _from The string that is to be converted to another charset.
  246. * @param[in] cbBytes The size in bytes of the string to convert.
  247. * @param[in] fromcode The source charset.
  248. * @return The converted string.
  249. *
  250. * @note Since this method needs to create an iconv object internally
  251. * it is better to use a convert_context when multiple conversions
  252. * need to be performed.
  253. */
  254. template <typename _To_Type, typename _From_Type>
  255. inline _To_Type convert_to(const char *tocode, const _From_Type &_from, size_t cbBytes, const char *fromcode)
  256. {
  257. details::iconv_context<_To_Type, _From_Type> context(tocode, fromcode);
  258. return context.convert(iconv_charset<_From_Type>::rawptr(_from), cbBytes);
  259. }
  260. /**
  261. * @brief Allows multiple conversions within the same context.
  262. *
  263. * The convert_context class is used to perform multiple conversions within the
  264. * same context. This basically means that the details::iconv_context classes can
  265. * be reused, removing the need to recreate them for each conversion.
  266. */
  267. class _kc_export convert_context _kc_final {
  268. public:
  269. /**
  270. * @brief Constructor.
  271. */
  272. convert_context(void) = default;
  273. /**
  274. * @brief Destructor.
  275. */
  276. ~convert_context();
  277. /**
  278. * @brief Converts a string to a string wirh a different charset.
  279. *
  280. * The to- and from charsets are implicitly determined by on one side the
  281. * passed _To_Type and on the other side the _from argument.
  282. * @tparam _To_Type The type of the destination string.
  283. * @param[in] _from The string that is to be converted to another charset.
  284. * @return The converted string.
  285. */
  286. template <typename _To_Type, typename _From_Type>
  287. _kc_hidden _To_Type convert_to(const _From_Type &_from)
  288. {
  289. return helper<_To_Type>(*this).convert(_from);
  290. }
  291. /**
  292. * @brief Converts a string to a string wirh a different charset.
  293. *
  294. * The to charset is implicitly determined by the passed _To_Type.
  295. * The from charset is passed in fromcode.
  296. * @tparam _To_Type The type of the destination string.
  297. * @param[in] _from The string that is to be converted to another charset.
  298. * @param[in] cbBytes The size in bytes of the string to convert.
  299. * @param[in] fromcode The source charset.
  300. * @return The converted string.
  301. */
  302. template <typename _To_Type, typename _From_Type>
  303. _kc_hidden _To_Type convert_to(const _From_Type &_from, size_t cbBytes,
  304. const char *fromcode)
  305. {
  306. return helper<_To_Type>(*this).convert(_from, cbBytes, fromcode);
  307. }
  308. /**
  309. * @brief Converts a string to a string wirh a different charset.
  310. *
  311. * The to charset is passed in tocode.
  312. * The from charset is passed in fromcode.
  313. * @param[in] tocode the destination charset.
  314. * @param[in] _from The string that is to be converted to another charset.
  315. * @param[in] cbBytes The size in bytes of the string to convert.
  316. * @param[in] fromcode The source charset.
  317. * @return The converted string.
  318. */
  319. template <typename _To_Type, typename _From_Type>
  320. _kc_hidden _To_Type convert_to(const char *tocode,
  321. const _From_Type &_from, size_t cbBytes, const char *fromcode)
  322. {
  323. return helper<_To_Type>(*this).convert(tocode, _from, cbBytes, fromcode);
  324. }
  325. private:
  326. /**
  327. * @brief Helper class for converting from one charset to another.
  328. *
  329. * The convert_context::helper class detects when the to and from charsets are
  330. * identical. In that case the string is merely copied.
  331. */
  332. template<typename _Type> class _kc_hidden helper _kc_final {
  333. public:
  334. /**
  335. * @brief Constructor.
  336. */
  337. helper(convert_context &context)
  338. : m_context(context)
  339. {}
  340. /**
  341. * @brief Converts a string to a string with the same charset.
  342. *
  343. * Effectively this method does nothing but a copy.
  344. * @param[in] _from The string to be converted.
  345. * @return The converted string.
  346. */
  347. _Type convert(const _Type &_from) {
  348. return _from;
  349. }
  350. /**
  351. * @brief Converts a string to a string with a different charset.
  352. *
  353. * The string with a charset linked to _Other_Type is converted to a
  354. * string with a charset linked to _Type. The actual conversion is
  355. * delegated to a iconv_context obtained through get_context().
  356. * @param[in] _from The string to be converted.
  357. * @return The converted string.
  358. */
  359. template <typename _Other_Type>
  360. _Type convert(const _Other_Type &_from) {
  361. return m_context.get_context<_Type, _Other_Type>()->convert(_from);
  362. }
  363. /**
  364. * @brief Converts a string to a string with a different charset.
  365. *
  366. * The string with a charset specified with fromcode is converted to a
  367. * string with a charset linked to _Type. The actual conversion is
  368. * delegated to a iconv_context obtained through get_context().
  369. * @param[in] _from The string to be converted.
  370. * @param[in] cbBytes The size in bytes of the string to convert.
  371. * @param[in] fromcode The source charset.
  372. * @return The converted string.
  373. */
  374. template <typename _Other_Type>
  375. _Type convert(const _Other_Type &_from, size_t cbBytes, const char *fromcode) {
  376. return m_context.get_context<_Type, _Other_Type>(fromcode)->convert(iconv_charset<_Other_Type>::rawptr(_from), cbBytes);
  377. }
  378. /**
  379. * @brief Converts a string to a string with a different charset.
  380. *
  381. * The string with a charset specified with fromcode is converted to a
  382. * string with a charset specified with tocode. The actual conversion is
  383. * delegated to a iconv_context obtained through get_context().
  384. * @param[in] tocode The destination charset.
  385. * @param[in] _from The string to be converted.
  386. * @param[in] cbBytes The size in bytes of the string to convert.
  387. * @param[in] fromcode The source charset.
  388. * @return The converted string.
  389. */
  390. template <typename _Other_Type>
  391. _Type convert(const char *tocode, const _Other_Type &_from, size_t cbBytes, const char *fromcode) {
  392. return m_context.get_context<_Type, _Other_Type>(tocode, fromcode)->convert(iconv_charset<_Other_Type>::rawptr(_from), cbBytes);
  393. }
  394. private:
  395. convert_context &m_context;
  396. };
  397. /**
  398. * @brief Helper class for converting from one charset to another.
  399. *
  400. * This specialization is used to convert to pointer types. In that case the
  401. * result needs to be stores to guarantee storage of the data. Without this
  402. * the caller will end up with a pointer to non-existing data.
  403. */
  404. template<typename _Type> class _kc_hidden helper<_Type *> _kc_final {
  405. public:
  406. typedef std::basic_string<_Type> string_type;
  407. /**
  408. * @brief Constructor.
  409. */
  410. helper(convert_context &context)
  411. : m_context(context)
  412. , m_helper(context)
  413. {}
  414. /**
  415. * @brief Converts a string to a string with a different charset.
  416. *
  417. * The string with a charset linked to _Other_Type is converted to a
  418. * string with a charset linked to _Type. The actual conversion is
  419. * delegated to a iconv_context obtained through get_context().
  420. * @param[in] _from The string to be converted.
  421. * @return The converted string.
  422. */
  423. template <typename _Other_Type>
  424. _Type* convert(const _Other_Type &_from) {
  425. string_type s = m_helper.convert(_from);
  426. return m_context.persist_string(s);
  427. }
  428. /**
  429. * @brief Converts a string to a string with a different charset.
  430. *
  431. * The string with a charset specified with fromcode is converted to a
  432. * string with a charset linked to _Type. The actual conversion is
  433. * delegated to a iconv_context obtained through get_context().
  434. * @param[in] _from The string to be converted.
  435. * @param[in] cbBytes The size in bytes of the string to convert.
  436. * @param[in] fromcode The source charset.
  437. * @return The converted string.
  438. */
  439. template <typename _Other_Type>
  440. _Type* convert(const _Other_Type &_from, size_t cbBytes, const char *fromcode) {
  441. string_type s = m_helper.convert(_from, cbBytes, fromcode);
  442. return m_context.persist_string(s);
  443. }
  444. /**
  445. * @brief Converts a string to a string with a different charset.
  446. *
  447. * The string with a charset specified with fromcode is converted to a
  448. * string with a charset specified with tocode. The actual conversion is
  449. * delegated to a iconv_context obtained through get_context().
  450. * @param[in] tocode The destination charset.
  451. * @param[in] _from The string to be converted.
  452. * @param[in] cbBytes The size in bytes of the string to convert.
  453. * @param[in] fromcode The source charset.
  454. * @return The converted string.
  455. */
  456. template <typename _Other_Type>
  457. _Type* convert(const char *tocode, const _Other_Type &_from, size_t cbBytes, const char *fromcode) {
  458. string_type s = m_helper.convert(tocode, _from, cbBytes, fromcode);
  459. return m_context.persist_string(s);
  460. }
  461. private:
  462. convert_context &m_context;
  463. helper<string_type> m_helper;
  464. };
  465. /**
  466. * @brief Key for the context_map;
  467. */
  468. struct context_key {
  469. const char *totype;
  470. const char *tocode;
  471. const char *fromtype;
  472. const char *fromcode;
  473. };
  474. /** Create a context_key based on the to- and from types and optionaly the to- and from codes.
  475. *
  476. * @tparam _To_Type
  477. * The destination type.
  478. * @tparam _From_Type
  479. * The source type.
  480. * @param[in] tocode
  481. * The destination encoding. NULL for autodetect (based on _To_Type).
  482. * @param[in] fromcode
  483. * The source encoding. NULL for autodetect (based on_From_Type).
  484. *
  485. * @return The new context_key
  486. */
  487. template <typename _To_Type, typename _From_Type>
  488. _kc_hidden context_key create_key(const char *tocode,
  489. const char *fromcode)
  490. {
  491. context_key key = {
  492. typeid(_To_Type).name(),
  493. (tocode ? tocode : iconv_charset<_To_Type>::name()),
  494. typeid(_From_Type).name(),
  495. (fromcode ? fromcode : iconv_charset<_From_Type>::name())
  496. };
  497. return key;
  498. }
  499. /**
  500. * @brief Sort predicate for the context_map;
  501. */
  502. class _kc_hidden context_predicate _kc_final {
  503. public:
  504. bool operator()(const context_key &lhs, const context_key &rhs) const {
  505. int r = strcmp(lhs.fromtype, rhs.fromtype);
  506. if (r != 0)
  507. return (r < 0);
  508. r = strcmp(lhs.totype, rhs.totype);
  509. if (r != 0)
  510. return (r < 0);
  511. r = strcmp(lhs.fromcode, rhs.fromcode);
  512. if (r != 0)
  513. return (r < 0);
  514. return (strcmp(lhs.tocode, rhs.tocode) < 0);
  515. }
  516. };
  517. /**
  518. * @brief Map containing contexts that can be reused.
  519. */
  520. typedef std::map<context_key, details::iconv_context_base*, context_predicate> context_map;
  521. /**
  522. * @brief Set containing dynamic allocated from- and to codes.
  523. */
  524. typedef std::set<const char*> code_set;
  525. /**
  526. * @brief Obtains an iconv_context object.
  527. *
  528. * The correct iconv_context is based on _To_Type and _From_Type and is
  529. * obtained from the context_map. If the correct iconv_context is not found a new
  530. * one is created and stored in the context_map;
  531. * @tparam _To_Type The type of the destination string.
  532. * @tparam _From_Type The type of the source string.
  533. * @return A pointer to a iconv_context.
  534. */
  535. template <typename _To_Type, typename _From_Type>
  536. _kc_hidden details::iconv_context<_To_Type, _From_Type> *get_context(void)
  537. {
  538. context_key key(create_key<_To_Type, _From_Type>(NULL, NULL));
  539. context_map::const_iterator iContext = m_contexts.find(key);
  540. if (iContext == m_contexts.cend()) {
  541. auto lpContext = new details::iconv_context<_To_Type, _From_Type>();
  542. iContext = m_contexts.insert(context_map::value_type(key, lpContext)).first;
  543. }
  544. return dynamic_cast<details::iconv_context<_To_Type, _From_Type>*>(iContext->second);
  545. }
  546. /**
  547. * @brief Obtains an iconv_context object.
  548. *
  549. * The correct iconv_context is based on _To_Type and fromcode and is
  550. * obtained from the context_map. If the correct iconv_context is not found a new
  551. * one is created and stored in the context_map;
  552. * @tparam _To_Type The type of the destination string.
  553. * @param[in] fromcode The source charset.
  554. * @return A pointer to a iconv_context.
  555. */
  556. template <typename _To_Type, typename _From_Type>
  557. _kc_hidden details::iconv_context<_To_Type, _From_Type> *
  558. get_context(const char *fromcode)
  559. {
  560. context_key key(create_key<_To_Type, _From_Type>(NULL, fromcode));
  561. context_map::const_iterator iContext = m_contexts.find(key);
  562. if (iContext == m_contexts.cend()) {
  563. auto lpContext = new details::iconv_context<_To_Type, _From_Type>(fromcode);
  564. // Before we store it, we need to copy the fromcode as we don't know what the
  565. // lifetime will be.
  566. persist_code(key, pfFromCode);
  567. iContext = m_contexts.insert(context_map::value_type(key, lpContext)).first;
  568. }
  569. return dynamic_cast<details::iconv_context<_To_Type, _From_Type>*>(iContext->second);
  570. }
  571. /**
  572. * @brief Obtains an iconv_context object.
  573. *
  574. * The correct iconv_context is based on tocode and fromcode and is
  575. * obtained from the context_map. If the correct iconv_context is not found a new
  576. * one is created and stored in the context_map;
  577. * @param[in] tocode The destination charset.
  578. * @param[in] fromcode The source charset.
  579. * @return A pointer to a iconv_context.
  580. */
  581. template <typename _To_Type, typename _From_Type>
  582. _kc_hidden details::iconv_context<_To_Type, _From_Type> *
  583. get_context(const char *tocode, const char *fromcode)
  584. {
  585. context_key key(create_key<_To_Type, _From_Type>(tocode, fromcode));
  586. context_map::const_iterator iContext = m_contexts.find(key);
  587. if (iContext == m_contexts.cend()) {
  588. auto lpContext = new details::iconv_context<_To_Type, _From_Type>(tocode, fromcode);
  589. // Before we store it, we need to copy the fromcode as we don't know what the
  590. // lifetime will be.
  591. persist_code(key, pfToCode|pfFromCode);
  592. iContext = m_contexts.insert(context_map::value_type(key, lpContext)).first;
  593. }
  594. return dynamic_cast<details::iconv_context<_To_Type, _From_Type>*>(iContext->second);
  595. }
  596. /**
  597. * @brief Flags that determine which code of a context_key is persisted
  598. */
  599. enum {
  600. pfToCode = 1,
  601. pfFromCode = 2
  602. };
  603. /**
  604. * @brief Persists the code for the fromcode when it's not certain it won't
  605. * be destroyed while in use.
  606. *
  607. * @param[in,out] key The key for which the second field will be persisted.
  608. */
  609. _kc_export void persist_code(context_key &key, unsigned flags);
  610. /**
  611. * Persist the string so a raw pointer to its content can be used.
  612. *
  613. * The pointer that can be used is returned by this function. Using the
  614. * pointer to the data of the original string will be a recipe to disaster.
  615. *
  616. * @param[in] string The string to persist.
  617. * @return The raw pointer that can be used as long as the convert_context exists.
  618. */
  619. char *persist_string(const std::string &);
  620. /**
  621. * Persist the string so a raw pointer to its content can be used.
  622. *
  623. * The pointer that can be used is returned by this function. Using the
  624. * pointer to the data of the original string will be a recipe to disaster.
  625. *
  626. * @param[in] string The string to persist.
  627. * @return The raw pointer that can be used as long as the convert_context exists.
  628. */
  629. wchar_t *persist_string(const std::wstring &wstrValue);
  630. code_set m_codes;
  631. context_map m_contexts;
  632. std::list<std::string> m_lstStrings;
  633. std::list<std::wstring> m_lstWstrings;
  634. // a convert_context is not supposed to be copyable.
  635. convert_context(const convert_context &) = delete;
  636. convert_context &operator=(const convert_context &) = delete;
  637. };
  638. /** Convert a string to UTF-8.
  639. *
  640. * @param[in] _context
  641. * The convert_context used for the conversion
  642. * @param[in] _ptr
  643. * Pointer to the string containing the data to be converted.
  644. * @param[in] _flags
  645. * If set to MAPI_UNICODE, the _ptr argument is interpreted as a wide character string. Otherwise
  646. * the _ptr argument is interpreted as a single byte string encoded in the current locale.
  647. *
  648. * @return The converted string.
  649. */
  650. #define TO_UTF8(_context, _ptr, _flags) \
  651. ((_ptr) ? \
  652. (_context).convert_to<char*>("UTF-8", (_ptr), \
  653. ((_flags) & MAPI_UNICODE) ? sizeof(wchar_t) * wcslen((wchar_t*)(_ptr)) : strlen((char*)(_ptr)), \
  654. ((_flags) & MAPI_UNICODE) ? CHARSET_WCHAR : CHARSET_CHAR) \
  655. : NULL )
  656. /**
  657. * Convert a string to UTF-8 with default arguments.
  658. *
  659. * This version requeres the convert_context to be named 'converter' and the flags argument 'ulFlags'.
  660. *
  661. * @param[in] _ptr
  662. * Pointer to the string containing the data to be converted.
  663. *
  664. * @return The converted string.
  665. */
  666. #define TO_UTF8_DEF(_ptr) \
  667. TO_UTF8(converter, (_ptr), ulFlags)
  668. namespace details {
  669. extern _kc_export HRESULT HrFromException(const convert_exception &);
  670. } // namespace details
  671. #ifdef MAPIDEFS_H
  672. /**
  673. * @brief Converts a string from one charset to another. Failure is indicated
  674. * through the return code instead of an exception.
  675. *
  676. * @param[in] _from The string to be converted.
  677. * @param[out] _to The converted string.
  678. * @return HRESULT.
  679. */
  680. template <typename _To_Type, typename _From_Type>
  681. HRESULT TryConvert(const _From_Type &_from, _To_Type &_to) {
  682. try {
  683. _to = convert_to<_To_Type>(_from);
  684. return hrSuccess;
  685. } catch (const convert_exception &ce) {
  686. return details::HrFromException(ce);
  687. }
  688. }
  689. /**
  690. * @brief Converts a string from one charset to another. Failure is indicated
  691. * through the return code instead of an exception.
  692. *
  693. * @param[in] _from The string to be converted.
  694. * @param[in] cbBytes The size in bytes of the string to convert.
  695. * @param[in] fromcode The source charset.
  696. * @param[out] _to The converted string.
  697. * @return HRESULT.
  698. */
  699. template <typename _To_Type, typename _From_Type>
  700. HRESULT TryConvert(const _From_Type &_from, size_t cbBytes, const char *fromcode, _To_Type &_to) {
  701. try {
  702. _to = convert_to<_To_Type>(_from, cbBytes, fromcode);
  703. return hrSuccess;
  704. } catch (const convert_exception &ce) {
  705. return details::HrFromException(ce);
  706. }
  707. }
  708. /**
  709. * @brief Converts a string from one charset to another. Failure is indicated
  710. * through the return code instead of an exception.
  711. *
  712. * @param[in] context A convert_context to perform the conversion on.
  713. * @param[in] _from The string to be converted.
  714. * @param[out] _to The converted string.
  715. * @return HRESULT.
  716. */
  717. template <typename _To_Type, typename _From_Type>
  718. HRESULT TryConvert(convert_context &context, const _From_Type &_from, _To_Type &_to) {
  719. try {
  720. _to = context.convert_to<_To_Type>(_from);
  721. return hrSuccess;
  722. } catch (const convert_exception &ce) {
  723. return details::HrFromException(ce);
  724. }
  725. }
  726. /**
  727. * @brief Converts a string from one charset to another. Failure is indicated
  728. * through the return code instead of an exception.
  729. *
  730. * @param[in] context A convert_context to perform the conversion on.
  731. * @param[in] _from The string to be converted.
  732. * @param[in] cbBytes The size in bytes of the string to convert.
  733. * @param[in] fromcode The source charset.
  734. * @param[out] _to The converted string.
  735. * @return HRESULT.
  736. */
  737. template <typename _To_Type, typename _From_Type>
  738. HRESULT TryConvert(convert_context &context, const _From_Type &_from, size_t cbBytes, const char *fromcode, _To_Type &_to) {
  739. try {
  740. _to = context.convert_to<_To_Type>(_from, cbBytes, fromcode);
  741. return hrSuccess;
  742. } catch (const convert_exception &ce) {
  743. return details::HrFromException(ce);
  744. }
  745. }
  746. #endif // MAPIDEFS_H
  747. } /* namespace */
  748. #endif // ndef convert_INCLUDED