iconv.c 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp> |
  14. | Stig Bakken <ssb@php.net> |
  15. | Moriyoshi Koizumi <moriyoshi@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include "config.h"
  20. #endif
  21. #include "php.h"
  22. #include "php_globals.h"
  23. #include "ext/standard/info.h"
  24. #include "main/php_output.h"
  25. #include "SAPI.h"
  26. #include "php_ini.h"
  27. #include <stdlib.h>
  28. #include <errno.h>
  29. #include "php_iconv.h"
  30. #ifdef HAVE_ICONV
  31. #include <iconv.h>
  32. #ifdef HAVE_GLIBC_ICONV
  33. #include <gnu/libc-version.h>
  34. #endif
  35. #ifdef HAVE_LIBICONV
  36. #undef iconv
  37. #endif
  38. #include "zend_smart_str.h"
  39. #include "ext/standard/base64.h"
  40. #include "ext/standard/quot_print.h"
  41. #include "iconv_arginfo.h"
  42. #define _php_iconv_memequal(a, b, c) \
  43. (memcmp(a, b, c) == 0)
  44. ZEND_DECLARE_MODULE_GLOBALS(iconv)
  45. static PHP_GINIT_FUNCTION(iconv);
  46. /* {{{ iconv_module_entry */
  47. zend_module_entry iconv_module_entry = {
  48. STANDARD_MODULE_HEADER,
  49. "iconv",
  50. ext_functions,
  51. PHP_MINIT(miconv),
  52. PHP_MSHUTDOWN(miconv),
  53. NULL,
  54. NULL,
  55. PHP_MINFO(miconv),
  56. PHP_ICONV_VERSION,
  57. PHP_MODULE_GLOBALS(iconv),
  58. PHP_GINIT(iconv),
  59. NULL,
  60. NULL,
  61. STANDARD_MODULE_PROPERTIES_EX
  62. };
  63. /* }}} */
  64. #ifdef COMPILE_DL_ICONV
  65. #ifdef ZTS
  66. ZEND_TSRMLS_CACHE_DEFINE()
  67. #endif
  68. ZEND_GET_MODULE(iconv)
  69. #endif
  70. /* {{{ PHP_GINIT_FUNCTION */
  71. static PHP_GINIT_FUNCTION(iconv)
  72. {
  73. #if defined(COMPILE_DL_ICONV) && defined(ZTS)
  74. ZEND_TSRMLS_CACHE_UPDATE();
  75. #endif
  76. iconv_globals->input_encoding = NULL;
  77. iconv_globals->output_encoding = NULL;
  78. iconv_globals->internal_encoding = NULL;
  79. }
  80. /* }}} */
  81. #if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV)
  82. #define iconv libiconv
  83. #endif
  84. /* {{{ typedef enum php_iconv_enc_scheme_t */
  85. typedef enum _php_iconv_enc_scheme_t {
  86. PHP_ICONV_ENC_SCHEME_BASE64,
  87. PHP_ICONV_ENC_SCHEME_QPRINT
  88. } php_iconv_enc_scheme_t;
  89. /* }}} */
  90. #define PHP_ICONV_MIME_DECODE_STRICT (1<<0)
  91. #define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1)
  92. /* {{{ prototypes */
  93. static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd);
  94. static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd);
  95. static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset);
  96. static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_t nbytes, const char *enc);
  97. static php_iconv_err_t _php_iconv_substr(smart_str *pretval, const char *str, size_t nbytes, zend_long offset, zend_long len, const char *enc);
  98. static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, size_t max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc);
  99. static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode);
  100. static php_iconv_err_t php_iconv_stream_filter_register_factory(void);
  101. static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void);
  102. static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len);
  103. static php_output_handler *php_iconv_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags);
  104. static int php_iconv_output_handler(void **nothing, php_output_context *output_context);
  105. /* }}} */
  106. /* {{{ static globals */
  107. static const char _generic_superset_name[] = ICONV_UCS4_ENCODING;
  108. #define GENERIC_SUPERSET_NAME _generic_superset_name
  109. #define GENERIC_SUPERSET_NBYTES 4
  110. /* }}} */
  111. static PHP_INI_MH(OnUpdateInputEncoding)
  112. {
  113. if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
  114. return FAILURE;
  115. }
  116. if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
  117. php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.input_encoding is deprecated");
  118. }
  119. OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
  120. return SUCCESS;
  121. }
  122. static PHP_INI_MH(OnUpdateOutputEncoding)
  123. {
  124. if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
  125. return FAILURE;
  126. }
  127. if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
  128. php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.output_encoding is deprecated");
  129. }
  130. OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
  131. return SUCCESS;
  132. }
  133. static PHP_INI_MH(OnUpdateInternalEncoding)
  134. {
  135. if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
  136. return FAILURE;
  137. }
  138. if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
  139. php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.internal_encoding is deprecated");
  140. }
  141. OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
  142. return SUCCESS;
  143. }
  144. /* {{{ PHP_INI */
  145. PHP_INI_BEGIN()
  146. STD_PHP_INI_ENTRY("iconv.input_encoding", "", PHP_INI_ALL, OnUpdateInputEncoding, input_encoding, zend_iconv_globals, iconv_globals)
  147. STD_PHP_INI_ENTRY("iconv.output_encoding", "", PHP_INI_ALL, OnUpdateOutputEncoding, output_encoding, zend_iconv_globals, iconv_globals)
  148. STD_PHP_INI_ENTRY("iconv.internal_encoding", "", PHP_INI_ALL, OnUpdateInternalEncoding, internal_encoding, zend_iconv_globals, iconv_globals)
  149. PHP_INI_END()
  150. /* }}} */
  151. /* {{{ PHP_MINIT_FUNCTION */
  152. PHP_MINIT_FUNCTION(miconv)
  153. {
  154. char *version = "unknown";
  155. REGISTER_INI_ENTRIES();
  156. #ifdef HAVE_LIBICONV
  157. {
  158. static char buf[16];
  159. snprintf(buf, sizeof(buf), "%d.%d",
  160. _libiconv_version >> 8, _libiconv_version & 0xff);
  161. version = buf;
  162. }
  163. #elif HAVE_GLIBC_ICONV
  164. version = (char *)gnu_get_libc_version();
  165. #endif
  166. #ifdef PHP_ICONV_IMPL
  167. REGISTER_STRING_CONSTANT("ICONV_IMPL", PHP_ICONV_IMPL, CONST_CS | CONST_PERSISTENT);
  168. #elif HAVE_LIBICONV
  169. REGISTER_STRING_CONSTANT("ICONV_IMPL", "libiconv", CONST_CS | CONST_PERSISTENT);
  170. #else
  171. REGISTER_STRING_CONSTANT("ICONV_IMPL", "unknown", CONST_CS | CONST_PERSISTENT);
  172. #endif
  173. REGISTER_STRING_CONSTANT("ICONV_VERSION", version, CONST_CS | CONST_PERSISTENT);
  174. REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT);
  175. REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT);
  176. if (php_iconv_stream_filter_register_factory() != PHP_ICONV_ERR_SUCCESS) {
  177. return FAILURE;
  178. }
  179. php_output_handler_alias_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_handler_init);
  180. php_output_handler_conflict_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_conflict);
  181. return SUCCESS;
  182. }
  183. /* }}} */
  184. /* {{{ PHP_MSHUTDOWN_FUNCTION */
  185. PHP_MSHUTDOWN_FUNCTION(miconv)
  186. {
  187. php_iconv_stream_filter_unregister_factory();
  188. UNREGISTER_INI_ENTRIES();
  189. return SUCCESS;
  190. }
  191. /* }}} */
  192. /* {{{ PHP_MINFO_FUNCTION */
  193. PHP_MINFO_FUNCTION(miconv)
  194. {
  195. zval *iconv_impl, *iconv_ver;
  196. iconv_impl = zend_get_constant_str("ICONV_IMPL", sizeof("ICONV_IMPL")-1);
  197. iconv_ver = zend_get_constant_str("ICONV_VERSION", sizeof("ICONV_VERSION")-1);
  198. php_info_print_table_start();
  199. php_info_print_table_row(2, "iconv support", "enabled");
  200. php_info_print_table_row(2, "iconv implementation", Z_STRVAL_P(iconv_impl));
  201. php_info_print_table_row(2, "iconv library version", Z_STRVAL_P(iconv_ver));
  202. php_info_print_table_end();
  203. DISPLAY_INI_ENTRIES();
  204. }
  205. /* }}} */
  206. static const char *get_internal_encoding(void) {
  207. if (ICONVG(internal_encoding) && ICONVG(internal_encoding)[0]) {
  208. return ICONVG(internal_encoding);
  209. }
  210. return php_get_internal_encoding();
  211. }
  212. static const char *get_input_encoding(void) {
  213. if (ICONVG(input_encoding) && ICONVG(input_encoding)[0]) {
  214. return ICONVG(input_encoding);
  215. }
  216. return php_get_input_encoding();
  217. }
  218. static const char *get_output_encoding(void) {
  219. if (ICONVG(output_encoding) && ICONVG(output_encoding)[0]) {
  220. return ICONVG(output_encoding);
  221. }
  222. return php_get_output_encoding();
  223. }
  224. static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len)
  225. {
  226. if (php_output_get_level()) {
  227. if (php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("ob_iconv_handler"))
  228. || php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("mb_output_handler"))) {
  229. return FAILURE;
  230. }
  231. }
  232. return SUCCESS;
  233. }
  234. static php_output_handler *php_iconv_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags)
  235. {
  236. return php_output_handler_create_internal(handler_name, handler_name_len, php_iconv_output_handler, chunk_size, flags);
  237. }
  238. static int php_iconv_output_handler(void **nothing, php_output_context *output_context)
  239. {
  240. char *s, *content_type, *mimetype = NULL;
  241. int output_status, mimetype_len = 0;
  242. if (output_context->op & PHP_OUTPUT_HANDLER_START) {
  243. output_status = php_output_get_status();
  244. if (output_status & PHP_OUTPUT_SENT) {
  245. return FAILURE;
  246. }
  247. if (SG(sapi_headers).mimetype && !strncasecmp(SG(sapi_headers).mimetype, "text/", 5)) {
  248. if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
  249. mimetype = SG(sapi_headers).mimetype;
  250. } else {
  251. mimetype = SG(sapi_headers).mimetype;
  252. mimetype_len = (int)(s - SG(sapi_headers).mimetype);
  253. }
  254. } else if (SG(sapi_headers).send_default_content_type) {
  255. mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE;
  256. }
  257. if (mimetype != NULL && (!(output_context->op & PHP_OUTPUT_HANDLER_CLEAN) || ((output_context->op & PHP_OUTPUT_HANDLER_START) && !(output_context->op & PHP_OUTPUT_HANDLER_FINAL)))) {
  258. size_t len;
  259. char *p = strstr(get_output_encoding(), "//");
  260. if (p) {
  261. len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%.*s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, (int) (p - get_output_encoding()), get_output_encoding());
  262. } else {
  263. len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, get_output_encoding());
  264. }
  265. if (content_type && SUCCESS == sapi_add_header(content_type, len, 0)) {
  266. SG(sapi_headers).send_default_content_type = 0;
  267. php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
  268. }
  269. }
  270. }
  271. if (output_context->in.used) {
  272. zend_string *out;
  273. output_context->out.free = 1;
  274. _php_iconv_show_error(php_iconv_string(output_context->in.data, output_context->in.used, &out, get_output_encoding(), get_internal_encoding()), get_output_encoding(), get_internal_encoding());
  275. if (out) {
  276. output_context->out.data = estrndup(ZSTR_VAL(out), ZSTR_LEN(out));
  277. output_context->out.used = ZSTR_LEN(out);
  278. zend_string_efree(out);
  279. } else {
  280. output_context->out.data = NULL;
  281. output_context->out.used = 0;
  282. }
  283. }
  284. return SUCCESS;
  285. }
  286. /* {{{ _php_iconv_appendl() */
  287. static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd)
  288. {
  289. const char *in_p = s;
  290. size_t in_left = l;
  291. char *out_p;
  292. size_t out_left = 0;
  293. size_t buf_growth = 128;
  294. if (in_p != NULL) {
  295. while (in_left > 0) {
  296. out_left = buf_growth;
  297. smart_str_alloc(d, out_left, 0);
  298. out_p = ZSTR_VAL((d)->s) + ZSTR_LEN((d)->s);
  299. if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
  300. switch (errno) {
  301. case EINVAL:
  302. return PHP_ICONV_ERR_ILLEGAL_CHAR;
  303. case EILSEQ:
  304. return PHP_ICONV_ERR_ILLEGAL_SEQ;
  305. case E2BIG:
  306. break;
  307. default:
  308. return PHP_ICONV_ERR_UNKNOWN;
  309. }
  310. }
  311. ZSTR_LEN((d)->s) += (buf_growth - out_left);
  312. buf_growth <<= 1;
  313. }
  314. } else {
  315. for (;;) {
  316. out_left = buf_growth;
  317. smart_str_alloc(d, out_left, 0);
  318. out_p = ZSTR_VAL((d)->s) + ZSTR_LEN((d)->s);
  319. if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)0) {
  320. ZSTR_LEN((d)->s) += (buf_growth - out_left);
  321. break;
  322. } else {
  323. if (errno != E2BIG) {
  324. return PHP_ICONV_ERR_UNKNOWN;
  325. }
  326. }
  327. ZSTR_LEN((d)->s) += (buf_growth - out_left);
  328. buf_growth <<= 1;
  329. }
  330. }
  331. return PHP_ICONV_ERR_SUCCESS;
  332. }
  333. /* }}} */
  334. /* {{{ _php_iconv_appendc() */
  335. static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd)
  336. {
  337. return _php_iconv_appendl(d, &c, 1, cd);
  338. }
  339. /* }}} */
  340. /* {{{ */
  341. #ifdef ICONV_BROKEN_IGNORE
  342. static int _php_check_ignore(const char *charset)
  343. {
  344. size_t clen = strlen(charset);
  345. if (clen >= 9 && strcmp("//IGNORE", charset+clen-8) == 0) {
  346. return 1;
  347. }
  348. if (clen >= 19 && strcmp("//IGNORE//TRANSLIT", charset+clen-18) == 0) {
  349. return 1;
  350. }
  351. return 0;
  352. }
  353. #else
  354. #define _php_check_ignore(x) (0)
  355. #endif
  356. /* }}} */
  357. /* {{{ php_iconv_string() */
  358. PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len, zend_string **out, const char *out_charset, const char *in_charset)
  359. {
  360. iconv_t cd;
  361. size_t in_left, out_size, out_left;
  362. char *out_p;
  363. size_t bsz, result = 0;
  364. php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
  365. zend_string *out_buf;
  366. int ignore_ilseq = _php_check_ignore(out_charset);
  367. *out = NULL;
  368. cd = iconv_open(out_charset, in_charset);
  369. if (cd == (iconv_t)(-1)) {
  370. if (errno == EINVAL) {
  371. return PHP_ICONV_ERR_WRONG_CHARSET;
  372. } else {
  373. return PHP_ICONV_ERR_CONVERTER;
  374. }
  375. }
  376. in_left= in_len;
  377. out_left = in_len + 32; /* Avoid realloc() most cases */
  378. out_size = 0;
  379. bsz = out_left;
  380. out_buf = zend_string_alloc(bsz, 0);
  381. out_p = ZSTR_VAL(out_buf);
  382. while (in_left > 0) {
  383. result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
  384. out_size = bsz - out_left;
  385. if (result == (size_t)(-1)) {
  386. if (ignore_ilseq && errno == EILSEQ) {
  387. if (in_left <= 1) {
  388. result = 0;
  389. } else {
  390. errno = 0;
  391. in_p++;
  392. in_left--;
  393. continue;
  394. }
  395. }
  396. if (errno == E2BIG && in_left > 0) {
  397. /* converted string is longer than out buffer */
  398. bsz += in_len;
  399. out_buf = zend_string_extend(out_buf, bsz, 0);
  400. out_p = ZSTR_VAL(out_buf);
  401. out_p += out_size;
  402. out_left = bsz - out_size;
  403. continue;
  404. }
  405. }
  406. break;
  407. }
  408. if (result != (size_t)(-1)) {
  409. /* flush the shift-out sequences */
  410. for (;;) {
  411. result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
  412. out_size = bsz - out_left;
  413. if (result != (size_t)(-1)) {
  414. break;
  415. }
  416. if (errno == E2BIG) {
  417. bsz += 16;
  418. out_buf = zend_string_extend(out_buf, bsz, 0);
  419. out_p = ZSTR_VAL(out_buf);
  420. out_p += out_size;
  421. out_left = bsz - out_size;
  422. } else {
  423. break;
  424. }
  425. }
  426. }
  427. iconv_close(cd);
  428. if (result == (size_t)(-1)) {
  429. switch (errno) {
  430. case EINVAL:
  431. retval = PHP_ICONV_ERR_ILLEGAL_CHAR;
  432. break;
  433. case EILSEQ:
  434. retval = PHP_ICONV_ERR_ILLEGAL_SEQ;
  435. break;
  436. case E2BIG:
  437. /* should not happen */
  438. retval = PHP_ICONV_ERR_TOO_BIG;
  439. break;
  440. default:
  441. /* other error */
  442. zend_string_efree(out_buf);
  443. return PHP_ICONV_ERR_UNKNOWN;
  444. }
  445. }
  446. *out_p = '\0';
  447. ZSTR_LEN(out_buf) = out_size;
  448. *out = out_buf;
  449. return retval;
  450. }
  451. /* }}} */
  452. /* {{{ _php_iconv_strlen() */
  453. static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_t nbytes, const char *enc)
  454. {
  455. char buf[GENERIC_SUPERSET_NBYTES*2];
  456. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  457. iconv_t cd;
  458. const char *in_p;
  459. size_t in_left;
  460. char *out_p;
  461. size_t out_left;
  462. size_t cnt;
  463. int more;
  464. *pretval = (size_t)-1;
  465. cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
  466. if (cd == (iconv_t)(-1)) {
  467. if (errno == EINVAL) {
  468. return PHP_ICONV_ERR_WRONG_CHARSET;
  469. } else {
  470. return PHP_ICONV_ERR_CONVERTER;
  471. }
  472. }
  473. errno = 0;
  474. out_left = 0;
  475. more = nbytes > 0;
  476. for (in_p = str, in_left = nbytes, cnt = 0; more;) {
  477. out_p = buf;
  478. out_left = sizeof(buf);
  479. more = in_left > 0;
  480. iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
  481. if (out_left == sizeof(buf)) {
  482. break;
  483. } else {
  484. ZEND_ASSERT((sizeof(buf) - out_left) % GENERIC_SUPERSET_NBYTES == 0);
  485. cnt += (sizeof(buf) - out_left) / GENERIC_SUPERSET_NBYTES;
  486. }
  487. }
  488. switch (errno) {
  489. case EINVAL:
  490. err = PHP_ICONV_ERR_ILLEGAL_CHAR;
  491. break;
  492. case EILSEQ:
  493. err = PHP_ICONV_ERR_ILLEGAL_SEQ;
  494. break;
  495. case E2BIG:
  496. case 0:
  497. *pretval = cnt;
  498. break;
  499. default:
  500. err = PHP_ICONV_ERR_UNKNOWN;
  501. break;
  502. }
  503. iconv_close(cd);
  504. return err;
  505. }
  506. /* }}} */
  507. /* {{{ _php_iconv_substr() */
  508. static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
  509. const char *str, size_t nbytes, zend_long offset, zend_long len, const char *enc)
  510. {
  511. char buf[GENERIC_SUPERSET_NBYTES];
  512. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  513. iconv_t cd1, cd2;
  514. const char *in_p;
  515. size_t in_left;
  516. char *out_p;
  517. size_t out_left;
  518. size_t cnt;
  519. size_t total_len;
  520. int more;
  521. err = _php_iconv_strlen(&total_len, str, nbytes, enc);
  522. if (err != PHP_ICONV_ERR_SUCCESS) {
  523. return err;
  524. }
  525. if (offset < 0) {
  526. if ((offset += total_len) < 0) {
  527. offset = 0;
  528. }
  529. } else if ((size_t)offset > total_len) {
  530. offset = total_len;
  531. }
  532. if (len < 0) {
  533. if ((len += (total_len - offset)) < 0) {
  534. len = 0;
  535. }
  536. } else if ((size_t)len > total_len) {
  537. len = total_len;
  538. }
  539. if ((size_t)(offset + len) > total_len ) {
  540. /* trying to compute the length */
  541. len = total_len - offset;
  542. }
  543. if (len == 0) {
  544. smart_str_appendl(pretval, "", 0);
  545. smart_str_0(pretval);
  546. return PHP_ICONV_ERR_SUCCESS;
  547. }
  548. cd1 = iconv_open(GENERIC_SUPERSET_NAME, enc);
  549. if (cd1 == (iconv_t)(-1)) {
  550. if (errno == EINVAL) {
  551. return PHP_ICONV_ERR_WRONG_CHARSET;
  552. } else {
  553. return PHP_ICONV_ERR_CONVERTER;
  554. }
  555. }
  556. cd2 = (iconv_t)NULL;
  557. errno = 0;
  558. more = nbytes > 0 && len > 0;
  559. for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
  560. out_p = buf;
  561. out_left = sizeof(buf);
  562. more = in_left > 0 && len > 0;
  563. iconv(cd1, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
  564. if (out_left == sizeof(buf)) {
  565. break;
  566. }
  567. if ((zend_long)cnt >= offset) {
  568. if (cd2 == (iconv_t)NULL) {
  569. cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
  570. if (cd2 == (iconv_t)(-1)) {
  571. cd2 = (iconv_t)NULL;
  572. if (errno == EINVAL) {
  573. err = PHP_ICONV_ERR_WRONG_CHARSET;
  574. } else {
  575. err = PHP_ICONV_ERR_CONVERTER;
  576. }
  577. break;
  578. }
  579. }
  580. if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
  581. break;
  582. }
  583. --len;
  584. }
  585. }
  586. switch (errno) {
  587. case EINVAL:
  588. err = PHP_ICONV_ERR_ILLEGAL_CHAR;
  589. break;
  590. case EILSEQ:
  591. err = PHP_ICONV_ERR_ILLEGAL_SEQ;
  592. break;
  593. case E2BIG:
  594. break;
  595. }
  596. if (err == PHP_ICONV_ERR_SUCCESS) {
  597. if (cd2 != (iconv_t)NULL) {
  598. _php_iconv_appendl(pretval, NULL, 0, cd2);
  599. }
  600. smart_str_0(pretval);
  601. }
  602. if (cd1 != (iconv_t)NULL) {
  603. iconv_close(cd1);
  604. }
  605. if (cd2 != (iconv_t)NULL) {
  606. iconv_close(cd2);
  607. }
  608. return err;
  609. }
  610. /* }}} */
  611. /* {{{ _php_iconv_strpos() */
  612. static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
  613. const char *haystk, size_t haystk_nbytes,
  614. const char *ndl, size_t ndl_nbytes,
  615. size_t offset, const char *enc, bool reverse)
  616. {
  617. char buf[GENERIC_SUPERSET_NBYTES];
  618. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  619. iconv_t cd;
  620. const char *in_p;
  621. size_t in_left;
  622. char *out_p;
  623. size_t out_left;
  624. size_t cnt;
  625. zend_string *ndl_buf;
  626. const char *ndl_buf_p;
  627. size_t ndl_buf_left;
  628. size_t match_ofs;
  629. int more;
  630. size_t iconv_ret;
  631. *pretval = (size_t)-1;
  632. err = php_iconv_string(ndl, ndl_nbytes, &ndl_buf, GENERIC_SUPERSET_NAME, enc);
  633. if (err != PHP_ICONV_ERR_SUCCESS) {
  634. if (ndl_buf != NULL) {
  635. zend_string_efree(ndl_buf);
  636. }
  637. return err;
  638. }
  639. cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
  640. if (cd == (iconv_t)(-1)) {
  641. if (ndl_buf != NULL) {
  642. zend_string_efree(ndl_buf);
  643. }
  644. if (errno == EINVAL) {
  645. return PHP_ICONV_ERR_WRONG_CHARSET;
  646. } else {
  647. return PHP_ICONV_ERR_CONVERTER;
  648. }
  649. }
  650. ndl_buf_p = ZSTR_VAL(ndl_buf);
  651. ndl_buf_left = ZSTR_LEN(ndl_buf);
  652. match_ofs = (size_t)-1;
  653. more = haystk_nbytes > 0;
  654. for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; more; ++cnt) {
  655. out_p = buf;
  656. out_left = sizeof(buf);
  657. more = in_left > 0;
  658. iconv_ret = iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
  659. if (out_left == sizeof(buf)) {
  660. break;
  661. }
  662. if (iconv_ret == (size_t)-1) {
  663. switch (errno) {
  664. case EINVAL:
  665. err = PHP_ICONV_ERR_ILLEGAL_CHAR;
  666. break;
  667. case EILSEQ:
  668. err = PHP_ICONV_ERR_ILLEGAL_SEQ;
  669. break;
  670. case E2BIG:
  671. break;
  672. default:
  673. err = PHP_ICONV_ERR_UNKNOWN;
  674. break;
  675. }
  676. }
  677. if (cnt >= offset) {
  678. if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
  679. if (match_ofs == (size_t)-1) {
  680. match_ofs = cnt;
  681. }
  682. ndl_buf_p += GENERIC_SUPERSET_NBYTES;
  683. ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
  684. if (ndl_buf_left == 0) {
  685. *pretval = match_ofs;
  686. if (reverse) {
  687. /* If searching backward, continue trying to find a later match. */
  688. ndl_buf_p = ZSTR_VAL(ndl_buf);
  689. ndl_buf_left = ZSTR_LEN(ndl_buf);
  690. match_ofs = -1;
  691. } else {
  692. /* If searching forward, stop at first match. */
  693. break;
  694. }
  695. }
  696. } else {
  697. size_t i, j, lim;
  698. i = 0;
  699. j = GENERIC_SUPERSET_NBYTES;
  700. lim = (size_t)(ndl_buf_p - ZSTR_VAL(ndl_buf));
  701. while (j < lim) {
  702. if (_php_iconv_memequal(&ZSTR_VAL(ndl_buf)[j], &ZSTR_VAL(ndl_buf)[i],
  703. GENERIC_SUPERSET_NBYTES)) {
  704. i += GENERIC_SUPERSET_NBYTES;
  705. } else {
  706. j -= i;
  707. i = 0;
  708. }
  709. j += GENERIC_SUPERSET_NBYTES;
  710. }
  711. if (_php_iconv_memequal(buf, &ZSTR_VAL(ndl_buf)[i], sizeof(buf))) {
  712. match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
  713. i += GENERIC_SUPERSET_NBYTES;
  714. ndl_buf_p = &ZSTR_VAL(ndl_buf)[i];
  715. ndl_buf_left = ZSTR_LEN(ndl_buf) - i;
  716. } else {
  717. match_ofs = (size_t)-1;
  718. ndl_buf_p = ZSTR_VAL(ndl_buf);
  719. ndl_buf_left = ZSTR_LEN(ndl_buf);
  720. }
  721. }
  722. }
  723. }
  724. if (ndl_buf) {
  725. zend_string_efree(ndl_buf);
  726. }
  727. iconv_close(cd);
  728. if (err == PHP_ICONV_ERR_SUCCESS && offset > cnt) {
  729. return PHP_ICONV_ERR_OUT_BY_BOUNDS;
  730. }
  731. return err;
  732. }
  733. /* }}} */
  734. /* {{{ _php_iconv_mime_encode() */
  735. static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, size_t max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc)
  736. {
  737. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  738. iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
  739. size_t char_cnt = 0;
  740. size_t out_charset_len;
  741. size_t lfchars_len;
  742. char *buf = NULL;
  743. const char *in_p;
  744. size_t in_left;
  745. char *out_p;
  746. size_t out_left;
  747. zend_string *encoded = NULL;
  748. static int qp_table[256] = {
  749. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */
  750. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
  751. 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */
  752. 1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */
  753. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
  754. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */
  755. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */
  756. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */
  757. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
  758. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
  759. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */
  760. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */
  761. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */
  762. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */
  763. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */
  764. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 /* 0xF0 */
  765. };
  766. out_charset_len = strlen(out_charset);
  767. lfchars_len = strlen(lfchars);
  768. if ((fname_nbytes + 2) >= max_line_len
  769. || (out_charset_len + 12) >= max_line_len) {
  770. /* field name is too long */
  771. err = PHP_ICONV_ERR_TOO_BIG;
  772. goto out;
  773. }
  774. cd_pl = iconv_open(ICONV_ASCII_ENCODING, enc);
  775. if (cd_pl == (iconv_t)(-1)) {
  776. if (errno == EINVAL) {
  777. err = PHP_ICONV_ERR_WRONG_CHARSET;
  778. } else {
  779. err = PHP_ICONV_ERR_CONVERTER;
  780. }
  781. goto out;
  782. }
  783. cd = iconv_open(out_charset, enc);
  784. if (cd == (iconv_t)(-1)) {
  785. if (errno == EINVAL) {
  786. err = PHP_ICONV_ERR_WRONG_CHARSET;
  787. } else {
  788. err = PHP_ICONV_ERR_CONVERTER;
  789. }
  790. goto out;
  791. }
  792. buf = safe_emalloc(1, max_line_len, 5);
  793. char_cnt = max_line_len;
  794. _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
  795. char_cnt -= fname_nbytes;
  796. smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
  797. char_cnt -= 2;
  798. in_p = fval;
  799. in_left = fval_nbytes;
  800. do {
  801. size_t prev_in_left;
  802. size_t out_size;
  803. size_t encoded_word_min_len = sizeof("=\?\?X\?\?=")-1 + out_charset_len + (enc_scheme == PHP_ICONV_ENC_SCHEME_BASE64 ? 4 : 3);
  804. if (char_cnt < encoded_word_min_len + lfchars_len + 1) {
  805. /* lfchars must be encoded in ASCII here*/
  806. smart_str_appendl(pretval, lfchars, lfchars_len);
  807. smart_str_appendc(pretval, ' ');
  808. char_cnt = max_line_len - 1;
  809. }
  810. smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
  811. char_cnt -= 2;
  812. smart_str_appendl(pretval, out_charset, out_charset_len);
  813. char_cnt -= out_charset_len;
  814. smart_str_appendc(pretval, '?');
  815. char_cnt --;
  816. switch (enc_scheme) {
  817. case PHP_ICONV_ENC_SCHEME_BASE64: {
  818. size_t ini_in_left;
  819. const char *ini_in_p;
  820. size_t out_reserved = 4;
  821. smart_str_appendc(pretval, 'B');
  822. char_cnt--;
  823. smart_str_appendc(pretval, '?');
  824. char_cnt--;
  825. prev_in_left = ini_in_left = in_left;
  826. ini_in_p = in_p;
  827. out_size = (char_cnt - 2) / 4 * 3;
  828. for (;;) {
  829. out_p = buf;
  830. if (out_size <= out_reserved) {
  831. err = PHP_ICONV_ERR_TOO_BIG;
  832. goto out;
  833. }
  834. out_left = out_size - out_reserved;
  835. if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
  836. switch (errno) {
  837. case EINVAL:
  838. err = PHP_ICONV_ERR_ILLEGAL_CHAR;
  839. goto out;
  840. case EILSEQ:
  841. err = PHP_ICONV_ERR_ILLEGAL_SEQ;
  842. goto out;
  843. case E2BIG:
  844. if (prev_in_left == in_left) {
  845. err = PHP_ICONV_ERR_TOO_BIG;
  846. goto out;
  847. }
  848. break;
  849. default:
  850. err = PHP_ICONV_ERR_UNKNOWN;
  851. goto out;
  852. }
  853. }
  854. out_left += out_reserved;
  855. if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
  856. if (errno != E2BIG) {
  857. err = PHP_ICONV_ERR_UNKNOWN;
  858. goto out;
  859. }
  860. } else {
  861. break;
  862. }
  863. if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
  864. err = PHP_ICONV_ERR_UNKNOWN;
  865. goto out;
  866. }
  867. out_reserved += 4;
  868. in_left = ini_in_left;
  869. in_p = ini_in_p;
  870. }
  871. prev_in_left = in_left;
  872. encoded = php_base64_encode((unsigned char *) buf, (out_size - out_left));
  873. if (char_cnt < ZSTR_LEN(encoded)) {
  874. /* something went wrong! */
  875. err = PHP_ICONV_ERR_UNKNOWN;
  876. goto out;
  877. }
  878. smart_str_appendl(pretval, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
  879. char_cnt -= ZSTR_LEN(encoded);
  880. smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
  881. char_cnt -= 2;
  882. zend_string_release_ex(encoded, 0);
  883. encoded = NULL;
  884. } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
  885. case PHP_ICONV_ENC_SCHEME_QPRINT: {
  886. size_t ini_in_left;
  887. const char *ini_in_p;
  888. const unsigned char *p;
  889. size_t nbytes_required;
  890. smart_str_appendc(pretval, 'Q');
  891. char_cnt--;
  892. smart_str_appendc(pretval, '?');
  893. char_cnt--;
  894. prev_in_left = ini_in_left = in_left;
  895. ini_in_p = in_p;
  896. for (out_size = (char_cnt - 2); out_size > 0;) {
  897. nbytes_required = 0;
  898. out_p = buf;
  899. out_left = out_size;
  900. if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
  901. switch (errno) {
  902. case EINVAL:
  903. err = PHP_ICONV_ERR_ILLEGAL_CHAR;
  904. goto out;
  905. case EILSEQ:
  906. err = PHP_ICONV_ERR_ILLEGAL_SEQ;
  907. goto out;
  908. case E2BIG:
  909. if (prev_in_left == in_left) {
  910. err = PHP_ICONV_ERR_UNKNOWN;
  911. goto out;
  912. }
  913. break;
  914. default:
  915. err = PHP_ICONV_ERR_UNKNOWN;
  916. goto out;
  917. }
  918. }
  919. if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
  920. if (errno != E2BIG) {
  921. err = PHP_ICONV_ERR_UNKNOWN;
  922. goto out;
  923. }
  924. }
  925. for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
  926. nbytes_required += qp_table[*p];
  927. }
  928. if (nbytes_required <= char_cnt - 2) {
  929. break;
  930. }
  931. out_size -= ((nbytes_required - (char_cnt - 2)) + 2) / 3;
  932. in_left = ini_in_left;
  933. in_p = ini_in_p;
  934. }
  935. for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
  936. if (qp_table[*p] == 1) {
  937. smart_str_appendc(pretval, *(char *)p);
  938. char_cnt--;
  939. } else {
  940. static const char qp_digits[] = "0123456789ABCDEF";
  941. smart_str_appendc(pretval, '=');
  942. smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
  943. smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
  944. char_cnt -= 3;
  945. }
  946. }
  947. smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
  948. char_cnt -= 2;
  949. if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
  950. err = PHP_ICONV_ERR_UNKNOWN;
  951. goto out;
  952. }
  953. } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
  954. }
  955. } while (in_left > 0);
  956. smart_str_0(pretval);
  957. out:
  958. if (cd != (iconv_t)(-1)) {
  959. iconv_close(cd);
  960. }
  961. if (cd_pl != (iconv_t)(-1)) {
  962. iconv_close(cd_pl);
  963. }
  964. if (encoded != NULL) {
  965. zend_string_release_ex(encoded, 0);
  966. }
  967. if (buf != NULL) {
  968. efree(buf);
  969. }
  970. return err;
  971. }
  972. /* }}} */
  973. /* {{{ _php_iconv_mime_decode() */
  974. static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode)
  975. {
  976. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  977. iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
  978. const char *p1;
  979. size_t str_left;
  980. unsigned int scan_stat = 0;
  981. const char *csname = NULL;
  982. size_t csname_len;
  983. const char *encoded_text = NULL;
  984. size_t encoded_text_len = 0;
  985. const char *encoded_word = NULL;
  986. const char *spaces = NULL;
  987. php_iconv_enc_scheme_t enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
  988. if (next_pos != NULL) {
  989. *next_pos = NULL;
  990. }
  991. cd_pl = iconv_open(enc, ICONV_ASCII_ENCODING);
  992. if (cd_pl == (iconv_t)(-1)) {
  993. if (errno == EINVAL) {
  994. err = PHP_ICONV_ERR_WRONG_CHARSET;
  995. } else {
  996. err = PHP_ICONV_ERR_CONVERTER;
  997. }
  998. goto out;
  999. }
  1000. p1 = str;
  1001. for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
  1002. int eos = 0;
  1003. switch (scan_stat) {
  1004. case 0: /* expecting any character */
  1005. switch (*p1) {
  1006. case '\r': /* part of an EOL sequence? */
  1007. scan_stat = 7;
  1008. break;
  1009. case '\n':
  1010. scan_stat = 8;
  1011. break;
  1012. case '=': /* first letter of an encoded chunk */
  1013. encoded_word = p1;
  1014. scan_stat = 1;
  1015. break;
  1016. case ' ': case '\t': /* a chunk of whitespaces */
  1017. spaces = p1;
  1018. scan_stat = 11;
  1019. break;
  1020. default: /* first letter of a non-encoded word */
  1021. err = _php_iconv_appendc(pretval, *p1, cd_pl);
  1022. if (err != PHP_ICONV_ERR_SUCCESS) {
  1023. if (mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR) {
  1024. err = PHP_ICONV_ERR_SUCCESS;
  1025. } else {
  1026. goto out;
  1027. }
  1028. }
  1029. encoded_word = NULL;
  1030. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1031. scan_stat = 12;
  1032. }
  1033. break;
  1034. }
  1035. break;
  1036. case 1: /* expecting a delimiter */
  1037. if (*p1 != '?') {
  1038. if (*p1 == '\r' || *p1 == '\n') {
  1039. --p1;
  1040. }
  1041. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1042. if (err != PHP_ICONV_ERR_SUCCESS) {
  1043. goto out;
  1044. }
  1045. encoded_word = NULL;
  1046. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1047. scan_stat = 12;
  1048. } else {
  1049. scan_stat = 0;
  1050. }
  1051. break;
  1052. }
  1053. csname = p1 + 1;
  1054. scan_stat = 2;
  1055. break;
  1056. case 2: /* expecting a charset name */
  1057. switch (*p1) {
  1058. case '?': /* normal delimiter: encoding scheme follows */
  1059. scan_stat = 3;
  1060. break;
  1061. case '*': /* new style delimiter: locale id follows */
  1062. scan_stat = 10;
  1063. break;
  1064. case '\r': case '\n': /* not an encoded-word */
  1065. --p1;
  1066. _php_iconv_appendc(pretval, '=', cd_pl);
  1067. _php_iconv_appendc(pretval, '?', cd_pl);
  1068. err = _php_iconv_appendl(pretval, csname, (size_t)((p1 + 1) - csname), cd_pl);
  1069. if (err != PHP_ICONV_ERR_SUCCESS) {
  1070. goto out;
  1071. }
  1072. csname = NULL;
  1073. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1074. scan_stat = 12;
  1075. }
  1076. else {
  1077. scan_stat = 0;
  1078. }
  1079. continue;
  1080. }
  1081. if (scan_stat != 2) {
  1082. char tmpbuf[80];
  1083. if (csname == NULL) {
  1084. err = PHP_ICONV_ERR_MALFORMED;
  1085. goto out;
  1086. }
  1087. csname_len = (size_t)(p1 - csname);
  1088. if (csname_len > sizeof(tmpbuf) - 1) {
  1089. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1090. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1091. if (err != PHP_ICONV_ERR_SUCCESS) {
  1092. goto out;
  1093. }
  1094. encoded_word = NULL;
  1095. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1096. scan_stat = 12;
  1097. } else {
  1098. scan_stat = 0;
  1099. }
  1100. break;
  1101. } else {
  1102. err = PHP_ICONV_ERR_MALFORMED;
  1103. goto out;
  1104. }
  1105. }
  1106. memcpy(tmpbuf, csname, csname_len);
  1107. tmpbuf[csname_len] = '\0';
  1108. if (cd != (iconv_t)(-1)) {
  1109. iconv_close(cd);
  1110. }
  1111. cd = iconv_open(enc, tmpbuf);
  1112. if (cd == (iconv_t)(-1)) {
  1113. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1114. /* Bad character set, but the user wants us to
  1115. * press on. In this case, we'll just insert the
  1116. * undecoded encoded word, since there isn't really
  1117. * a more sensible behaviour available; the only
  1118. * other options are to swallow the encoded word
  1119. * entirely or decode it with an arbitrarily chosen
  1120. * single byte encoding, both of which seem to have
  1121. * a higher WTF factor than leaving it undecoded.
  1122. *
  1123. * Given this approach, we need to skip ahead to
  1124. * the end of the encoded word. */
  1125. int qmarks = 2;
  1126. while (qmarks > 0 && str_left > 1) {
  1127. if (*(++p1) == '?') {
  1128. --qmarks;
  1129. }
  1130. --str_left;
  1131. }
  1132. /* Look ahead to check for the terminating = that
  1133. * should be there as well; if it's there, we'll
  1134. * also include that. If it's not, there isn't much
  1135. * we can do at this point. */
  1136. if (*(p1 + 1) == '=') {
  1137. ++p1;
  1138. if (str_left > 1) {
  1139. --str_left;
  1140. }
  1141. }
  1142. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1143. if (err != PHP_ICONV_ERR_SUCCESS) {
  1144. goto out;
  1145. }
  1146. /* Let's go back and see if there are further
  1147. * encoded words or bare content, and hope they
  1148. * might actually have a valid character set. */
  1149. scan_stat = 12;
  1150. break;
  1151. } else {
  1152. if (errno == EINVAL) {
  1153. err = PHP_ICONV_ERR_WRONG_CHARSET;
  1154. } else {
  1155. err = PHP_ICONV_ERR_CONVERTER;
  1156. }
  1157. goto out;
  1158. }
  1159. }
  1160. }
  1161. break;
  1162. case 3: /* expecting a encoding scheme specifier */
  1163. switch (*p1) {
  1164. case 'b':
  1165. case 'B':
  1166. enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
  1167. scan_stat = 4;
  1168. break;
  1169. case 'q':
  1170. case 'Q':
  1171. enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
  1172. scan_stat = 4;
  1173. break;
  1174. default:
  1175. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1176. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1177. if (err != PHP_ICONV_ERR_SUCCESS) {
  1178. goto out;
  1179. }
  1180. encoded_word = NULL;
  1181. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1182. scan_stat = 12;
  1183. } else {
  1184. scan_stat = 0;
  1185. }
  1186. break;
  1187. } else {
  1188. err = PHP_ICONV_ERR_MALFORMED;
  1189. goto out;
  1190. }
  1191. }
  1192. break;
  1193. case 4: /* expecting a delimiter */
  1194. if (*p1 != '?') {
  1195. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1196. /* pass the entire chunk through the converter */
  1197. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1198. if (err != PHP_ICONV_ERR_SUCCESS) {
  1199. goto out;
  1200. }
  1201. encoded_word = NULL;
  1202. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1203. scan_stat = 12;
  1204. } else {
  1205. scan_stat = 0;
  1206. }
  1207. break;
  1208. } else {
  1209. err = PHP_ICONV_ERR_MALFORMED;
  1210. goto out;
  1211. }
  1212. }
  1213. encoded_text = p1 + 1;
  1214. scan_stat = 5;
  1215. break;
  1216. case 5: /* expecting an encoded portion */
  1217. if (*p1 == '?') {
  1218. encoded_text_len = (size_t)(p1 - encoded_text);
  1219. scan_stat = 6;
  1220. }
  1221. break;
  1222. case 7: /* expecting a "\n" character */
  1223. if (*p1 == '\n') {
  1224. scan_stat = 8;
  1225. } else {
  1226. /* bare CR */
  1227. _php_iconv_appendc(pretval, '\r', cd_pl);
  1228. _php_iconv_appendc(pretval, *p1, cd_pl);
  1229. scan_stat = 0;
  1230. }
  1231. break;
  1232. case 8: /* checking whether the following line is part of a
  1233. folded header */
  1234. if (*p1 != ' ' && *p1 != '\t') {
  1235. --p1;
  1236. str_left = 1; /* quit_loop */
  1237. break;
  1238. }
  1239. if (encoded_word == NULL) {
  1240. _php_iconv_appendc(pretval, ' ', cd_pl);
  1241. }
  1242. spaces = NULL;
  1243. scan_stat = 11;
  1244. break;
  1245. case 6: /* expecting a End-Of-Chunk character "=" */
  1246. if (*p1 != '=') {
  1247. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1248. /* pass the entire chunk through the converter */
  1249. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1250. if (err != PHP_ICONV_ERR_SUCCESS) {
  1251. goto out;
  1252. }
  1253. encoded_word = NULL;
  1254. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1255. scan_stat = 12;
  1256. } else {
  1257. scan_stat = 0;
  1258. }
  1259. break;
  1260. } else {
  1261. err = PHP_ICONV_ERR_MALFORMED;
  1262. goto out;
  1263. }
  1264. }
  1265. scan_stat = 9;
  1266. if (str_left == 1) {
  1267. eos = 1;
  1268. } else {
  1269. break;
  1270. }
  1271. /* TODO might want to rearrange logic so this is more obvious */
  1272. ZEND_FALLTHROUGH;
  1273. case 9: /* choice point, seeing what to do next.*/
  1274. switch (*p1) {
  1275. default:
  1276. /* Handle non-RFC-compliant formats
  1277. *
  1278. * RFC2047 requires the character that comes right
  1279. * after an encoded word (chunk) to be a whitespace,
  1280. * while there are lots of broken implementations that
  1281. * generate such malformed headers that don't fulfill
  1282. * that requirement.
  1283. */
  1284. if (!eos) {
  1285. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1286. /* pass the entire chunk through the converter */
  1287. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1288. if (err != PHP_ICONV_ERR_SUCCESS) {
  1289. goto out;
  1290. }
  1291. scan_stat = 12;
  1292. break;
  1293. }
  1294. }
  1295. ZEND_FALLTHROUGH;
  1296. case '\r': case '\n': case ' ': case '\t': {
  1297. zend_string *decoded_text;
  1298. switch (enc_scheme) {
  1299. case PHP_ICONV_ENC_SCHEME_BASE64:
  1300. decoded_text = php_base64_decode((unsigned char*)encoded_text, encoded_text_len);
  1301. break;
  1302. case PHP_ICONV_ENC_SCHEME_QPRINT:
  1303. decoded_text = php_quot_print_decode((unsigned char*)encoded_text, encoded_text_len, 1);
  1304. break;
  1305. default:
  1306. decoded_text = NULL;
  1307. break;
  1308. }
  1309. if (decoded_text == NULL) {
  1310. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1311. /* pass the entire chunk through the converter */
  1312. err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
  1313. if (err != PHP_ICONV_ERR_SUCCESS) {
  1314. goto out;
  1315. }
  1316. encoded_word = NULL;
  1317. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1318. scan_stat = 12;
  1319. } else {
  1320. scan_stat = 0;
  1321. }
  1322. break;
  1323. } else {
  1324. err = PHP_ICONV_ERR_UNKNOWN;
  1325. goto out;
  1326. }
  1327. }
  1328. err = _php_iconv_appendl(pretval, ZSTR_VAL(decoded_text), ZSTR_LEN(decoded_text), cd);
  1329. if (err == PHP_ICONV_ERR_SUCCESS) {
  1330. err = _php_iconv_appendl(pretval, NULL, 0, cd);
  1331. }
  1332. zend_string_release_ex(decoded_text, 0);
  1333. if (err != PHP_ICONV_ERR_SUCCESS) {
  1334. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1335. /* pass the entire chunk through the converter */
  1336. err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl);
  1337. encoded_word = NULL;
  1338. if (err != PHP_ICONV_ERR_SUCCESS) {
  1339. break;
  1340. }
  1341. } else {
  1342. goto out;
  1343. }
  1344. }
  1345. if (eos) { /* reached end-of-string. done. */
  1346. scan_stat = 0;
  1347. break;
  1348. }
  1349. switch (*p1) {
  1350. case '\r': /* part of an EOL sequence? */
  1351. scan_stat = 7;
  1352. break;
  1353. case '\n':
  1354. scan_stat = 8;
  1355. break;
  1356. case '=': /* first letter of an encoded chunk */
  1357. scan_stat = 1;
  1358. break;
  1359. case ' ': case '\t': /* medial whitespaces */
  1360. spaces = p1;
  1361. scan_stat = 11;
  1362. break;
  1363. default: /* first letter of a non-encoded word */
  1364. _php_iconv_appendc(pretval, *p1, cd_pl);
  1365. scan_stat = 12;
  1366. break;
  1367. }
  1368. } break;
  1369. }
  1370. break;
  1371. case 10: /* expects a language specifier. dismiss it for now */
  1372. if (*p1 == '?') {
  1373. scan_stat = 3;
  1374. }
  1375. break;
  1376. case 11: /* expecting a chunk of whitespaces */
  1377. switch (*p1) {
  1378. case '\r': /* part of an EOL sequence? */
  1379. scan_stat = 7;
  1380. break;
  1381. case '\n':
  1382. scan_stat = 8;
  1383. break;
  1384. case '=': /* first letter of an encoded chunk */
  1385. if (spaces != NULL && encoded_word == NULL) {
  1386. _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
  1387. spaces = NULL;
  1388. }
  1389. encoded_word = p1;
  1390. scan_stat = 1;
  1391. break;
  1392. case ' ': case '\t':
  1393. break;
  1394. default: /* first letter of a non-encoded word */
  1395. if (spaces != NULL) {
  1396. _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
  1397. spaces = NULL;
  1398. }
  1399. _php_iconv_appendc(pretval, *p1, cd_pl);
  1400. encoded_word = NULL;
  1401. if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1402. scan_stat = 12;
  1403. } else {
  1404. scan_stat = 0;
  1405. }
  1406. break;
  1407. }
  1408. break;
  1409. case 12: /* expecting a non-encoded word */
  1410. switch (*p1) {
  1411. case '\r': /* part of an EOL sequence? */
  1412. scan_stat = 7;
  1413. break;
  1414. case '\n':
  1415. scan_stat = 8;
  1416. break;
  1417. case ' ': case '\t':
  1418. spaces = p1;
  1419. scan_stat = 11;
  1420. break;
  1421. case '=': /* first letter of an encoded chunk */
  1422. if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
  1423. encoded_word = p1;
  1424. scan_stat = 1;
  1425. break;
  1426. }
  1427. ZEND_FALLTHROUGH;
  1428. default:
  1429. _php_iconv_appendc(pretval, *p1, cd_pl);
  1430. break;
  1431. }
  1432. break;
  1433. }
  1434. }
  1435. switch (scan_stat) {
  1436. case 0: case 8: case 11: case 12:
  1437. break;
  1438. default:
  1439. if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
  1440. if (scan_stat == 1) {
  1441. _php_iconv_appendc(pretval, '=', cd_pl);
  1442. }
  1443. err = PHP_ICONV_ERR_SUCCESS;
  1444. } else {
  1445. err = PHP_ICONV_ERR_MALFORMED;
  1446. goto out;
  1447. }
  1448. }
  1449. if (next_pos != NULL) {
  1450. *next_pos = p1;
  1451. }
  1452. smart_str_0(pretval);
  1453. out:
  1454. if (cd != (iconv_t)(-1)) {
  1455. iconv_close(cd);
  1456. }
  1457. if (cd_pl != (iconv_t)(-1)) {
  1458. iconv_close(cd_pl);
  1459. }
  1460. return err;
  1461. }
  1462. /* }}} */
  1463. /* {{{ php_iconv_show_error() */
  1464. static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset)
  1465. {
  1466. switch (err) {
  1467. case PHP_ICONV_ERR_SUCCESS:
  1468. break;
  1469. case PHP_ICONV_ERR_CONVERTER:
  1470. php_error_docref(NULL, E_WARNING, "Cannot open converter");
  1471. break;
  1472. case PHP_ICONV_ERR_WRONG_CHARSET:
  1473. php_error_docref(NULL, E_WARNING, "Wrong encoding, conversion from \"%s\" to \"%s\" is not allowed",
  1474. in_charset, out_charset);
  1475. break;
  1476. case PHP_ICONV_ERR_ILLEGAL_CHAR:
  1477. php_error_docref(NULL, E_NOTICE, "Detected an incomplete multibyte character in input string");
  1478. break;
  1479. case PHP_ICONV_ERR_ILLEGAL_SEQ:
  1480. php_error_docref(NULL, E_NOTICE, "Detected an illegal character in input string");
  1481. break;
  1482. case PHP_ICONV_ERR_TOO_BIG:
  1483. /* should not happen */
  1484. php_error_docref(NULL, E_WARNING, "Buffer length exceeded");
  1485. break;
  1486. case PHP_ICONV_ERR_MALFORMED:
  1487. php_error_docref(NULL, E_WARNING, "Malformed string");
  1488. break;
  1489. case PHP_ICONV_ERR_OUT_BY_BOUNDS:
  1490. zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
  1491. break;
  1492. default:
  1493. /* other error */
  1494. php_error_docref(NULL, E_NOTICE, "Unknown error (%d)", errno);
  1495. break;
  1496. }
  1497. }
  1498. /* }}} */
  1499. /* {{{ Returns the character count of str */
  1500. PHP_FUNCTION(iconv_strlen)
  1501. {
  1502. const char *charset = NULL;
  1503. size_t charset_len;
  1504. zend_string *str;
  1505. php_iconv_err_t err;
  1506. size_t retval;
  1507. if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|s!",
  1508. &str, &charset, &charset_len) == FAILURE) {
  1509. RETURN_THROWS();
  1510. }
  1511. if (charset == NULL) {
  1512. charset = get_internal_encoding();
  1513. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1514. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1515. RETURN_FALSE;
  1516. }
  1517. err = _php_iconv_strlen(&retval, ZSTR_VAL(str), ZSTR_LEN(str), charset);
  1518. _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
  1519. if (err == PHP_ICONV_ERR_SUCCESS) {
  1520. RETVAL_LONG(retval);
  1521. } else {
  1522. RETVAL_FALSE;
  1523. }
  1524. }
  1525. /* }}} */
  1526. /* {{{ Returns specified part of a string */
  1527. PHP_FUNCTION(iconv_substr)
  1528. {
  1529. const char *charset = NULL;
  1530. size_t charset_len;
  1531. zend_string *str;
  1532. zend_long offset, length = 0;
  1533. bool len_is_null = 1;
  1534. php_iconv_err_t err;
  1535. smart_str retval = {0};
  1536. if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l!s!",
  1537. &str, &offset, &length, &len_is_null,
  1538. &charset, &charset_len) == FAILURE) {
  1539. RETURN_THROWS();
  1540. }
  1541. if (charset == NULL) {
  1542. charset = get_internal_encoding();
  1543. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1544. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1545. RETURN_FALSE;
  1546. }
  1547. if (len_is_null) {
  1548. length = ZSTR_LEN(str);
  1549. }
  1550. err = _php_iconv_substr(&retval, ZSTR_VAL(str), ZSTR_LEN(str), offset, length, charset);
  1551. _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
  1552. if (err == PHP_ICONV_ERR_SUCCESS && retval.s != NULL) {
  1553. RETURN_NEW_STR(retval.s);
  1554. }
  1555. smart_str_free(&retval);
  1556. RETURN_FALSE;
  1557. }
  1558. /* }}} */
  1559. /* {{{ Finds position of first occurrence of needle within part of haystack beginning with offset */
  1560. PHP_FUNCTION(iconv_strpos)
  1561. {
  1562. const char *charset = NULL;
  1563. size_t charset_len, haystk_len;
  1564. zend_string *haystk;
  1565. zend_string *ndl;
  1566. zend_long offset = 0;
  1567. php_iconv_err_t err;
  1568. size_t retval;
  1569. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|ls!",
  1570. &haystk, &ndl,
  1571. &offset, &charset, &charset_len) == FAILURE) {
  1572. RETURN_THROWS();
  1573. }
  1574. if (charset == NULL) {
  1575. charset = get_internal_encoding();
  1576. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1577. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1578. RETURN_FALSE;
  1579. }
  1580. if (offset < 0) {
  1581. /* Convert negative offset (counted from the end of string) */
  1582. err = _php_iconv_strlen(&haystk_len, ZSTR_VAL(haystk), ZSTR_LEN(haystk), charset);
  1583. if (err != PHP_ICONV_ERR_SUCCESS) {
  1584. _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
  1585. RETURN_FALSE;
  1586. }
  1587. offset += haystk_len;
  1588. if (offset < 0) { /* If offset before start */
  1589. zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
  1590. RETURN_THROWS();
  1591. }
  1592. }
  1593. if (ZSTR_LEN(ndl) < 1) {
  1594. // TODO: Support empty needles!
  1595. RETURN_FALSE;
  1596. }
  1597. err = _php_iconv_strpos(
  1598. &retval, ZSTR_VAL(haystk), ZSTR_LEN(haystk), ZSTR_VAL(ndl), ZSTR_LEN(ndl),
  1599. offset, charset, /* reverse */ false);
  1600. _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
  1601. if (err == PHP_ICONV_ERR_SUCCESS && retval != (size_t)-1) {
  1602. RETVAL_LONG((zend_long)retval);
  1603. } else {
  1604. RETVAL_FALSE;
  1605. }
  1606. }
  1607. /* }}} */
  1608. /* {{{ Finds position of last occurrence of needle within part of haystack beginning with offset */
  1609. PHP_FUNCTION(iconv_strrpos)
  1610. {
  1611. const char *charset = NULL;
  1612. size_t charset_len;
  1613. zend_string *haystk;
  1614. zend_string *ndl;
  1615. php_iconv_err_t err;
  1616. size_t retval;
  1617. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|s!",
  1618. &haystk, &ndl,
  1619. &charset, &charset_len) == FAILURE) {
  1620. RETURN_THROWS();
  1621. }
  1622. if (ZSTR_LEN(ndl) < 1) {
  1623. RETURN_FALSE;
  1624. }
  1625. if (charset == NULL) {
  1626. charset = get_internal_encoding();
  1627. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1628. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1629. RETURN_FALSE;
  1630. }
  1631. err = _php_iconv_strpos(
  1632. &retval, ZSTR_VAL(haystk), ZSTR_LEN(haystk), ZSTR_VAL(ndl), ZSTR_LEN(ndl),
  1633. /* offset */ 0, charset, /* reserve */ true);
  1634. _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
  1635. if (err == PHP_ICONV_ERR_SUCCESS && retval != (size_t)-1) {
  1636. RETVAL_LONG((zend_long)retval);
  1637. } else {
  1638. RETVAL_FALSE;
  1639. }
  1640. }
  1641. /* }}} */
  1642. /* {{{ Composes a mime header field with field_name and field_value in a specified scheme */
  1643. PHP_FUNCTION(iconv_mime_encode)
  1644. {
  1645. zend_string *field_name = NULL;
  1646. zend_string *field_value = NULL;
  1647. zend_string *tmp_str = NULL;
  1648. zval *pref = NULL;
  1649. smart_str retval = {0};
  1650. php_iconv_err_t err;
  1651. const char *in_charset = get_internal_encoding();
  1652. const char *out_charset = in_charset;
  1653. zend_long line_len = 76;
  1654. const char *lfchars = "\r\n";
  1655. php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
  1656. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|a",
  1657. &field_name, &field_value,
  1658. &pref) == FAILURE) {
  1659. RETURN_THROWS();
  1660. }
  1661. if (pref != NULL) {
  1662. zval *pzval;
  1663. if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "scheme", sizeof("scheme") - 1)) != NULL) {
  1664. if (Z_TYPE_P(pzval) == IS_STRING && Z_STRLEN_P(pzval) > 0) {
  1665. switch (Z_STRVAL_P(pzval)[0]) {
  1666. case 'B': case 'b':
  1667. scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
  1668. break;
  1669. case 'Q': case 'q':
  1670. scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT;
  1671. break;
  1672. }
  1673. }
  1674. }
  1675. if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) {
  1676. if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) {
  1677. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1678. RETURN_FALSE;
  1679. }
  1680. if (Z_STRLEN_P(pzval) > 0) {
  1681. in_charset = Z_STRVAL_P(pzval);
  1682. }
  1683. }
  1684. if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) {
  1685. if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) {
  1686. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1687. RETURN_FALSE;
  1688. }
  1689. if (Z_STRLEN_P(pzval) > 0) {
  1690. out_charset = Z_STRVAL_P(pzval);
  1691. }
  1692. }
  1693. if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-length", sizeof("line-length") - 1)) != NULL) {
  1694. line_len = zval_get_long(pzval);
  1695. }
  1696. if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars") - 1)) != NULL) {
  1697. if (Z_TYPE_P(pzval) != IS_STRING) {
  1698. tmp_str = zval_try_get_string_func(pzval);
  1699. if (UNEXPECTED(!tmp_str)) {
  1700. return;
  1701. }
  1702. lfchars = ZSTR_VAL(tmp_str);
  1703. } else {
  1704. lfchars = Z_STRVAL_P(pzval);
  1705. }
  1706. }
  1707. }
  1708. err = _php_iconv_mime_encode(&retval, ZSTR_VAL(field_name), ZSTR_LEN(field_name),
  1709. ZSTR_VAL(field_value), ZSTR_LEN(field_value), line_len, lfchars, scheme_id,
  1710. out_charset, in_charset);
  1711. _php_iconv_show_error(err, out_charset, in_charset);
  1712. if (err == PHP_ICONV_ERR_SUCCESS) {
  1713. if (retval.s != NULL) {
  1714. RETVAL_STR(retval.s);
  1715. } else {
  1716. RETVAL_EMPTY_STRING();
  1717. }
  1718. } else {
  1719. smart_str_free(&retval);
  1720. RETVAL_FALSE;
  1721. }
  1722. if (tmp_str) {
  1723. zend_string_release_ex(tmp_str, 0);
  1724. }
  1725. }
  1726. /* }}} */
  1727. /* {{{ Decodes a mime header field */
  1728. PHP_FUNCTION(iconv_mime_decode)
  1729. {
  1730. zend_string *encoded_str;
  1731. const char *charset = NULL;
  1732. size_t charset_len;
  1733. zend_long mode = 0;
  1734. smart_str retval = {0};
  1735. php_iconv_err_t err;
  1736. if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
  1737. &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
  1738. RETURN_THROWS();
  1739. }
  1740. if (charset == NULL) {
  1741. charset = get_internal_encoding();
  1742. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1743. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1744. RETURN_FALSE;
  1745. }
  1746. err = _php_iconv_mime_decode(&retval, ZSTR_VAL(encoded_str), ZSTR_LEN(encoded_str), charset, NULL, (int)mode);
  1747. _php_iconv_show_error(err, charset, "???");
  1748. if (err == PHP_ICONV_ERR_SUCCESS) {
  1749. if (retval.s != NULL) {
  1750. RETVAL_STR(retval.s);
  1751. } else {
  1752. RETVAL_EMPTY_STRING();
  1753. }
  1754. } else {
  1755. smart_str_free(&retval);
  1756. RETVAL_FALSE;
  1757. }
  1758. }
  1759. /* }}} */
  1760. /* {{{ Decodes multiple mime header fields */
  1761. PHP_FUNCTION(iconv_mime_decode_headers)
  1762. {
  1763. zend_string *encoded_str;
  1764. const char *charset = NULL;
  1765. size_t charset_len;
  1766. zend_long mode = 0;
  1767. char *enc_str_tmp;
  1768. size_t enc_str_len_tmp;
  1769. php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
  1770. if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
  1771. &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
  1772. RETURN_THROWS();
  1773. }
  1774. if (charset == NULL) {
  1775. charset = get_internal_encoding();
  1776. } else if (charset_len >= ICONV_CSNMAXLEN) {
  1777. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1778. RETURN_FALSE;
  1779. }
  1780. array_init(return_value);
  1781. enc_str_tmp = ZSTR_VAL(encoded_str);
  1782. enc_str_len_tmp = ZSTR_LEN(encoded_str);
  1783. while (enc_str_len_tmp > 0) {
  1784. smart_str decoded_header = {0};
  1785. char *header_name = NULL;
  1786. size_t header_name_len = 0;
  1787. char *header_value = NULL;
  1788. size_t header_value_len = 0;
  1789. char *p, *limit;
  1790. const char *next_pos;
  1791. if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, enc_str_tmp, enc_str_len_tmp, charset, &next_pos, (int)mode))) {
  1792. smart_str_free(&decoded_header);
  1793. break;
  1794. }
  1795. if (decoded_header.s == NULL) {
  1796. break;
  1797. }
  1798. limit = ZSTR_VAL(decoded_header.s) + ZSTR_LEN(decoded_header.s);
  1799. for (p = ZSTR_VAL(decoded_header.s); p < limit; p++) {
  1800. if (*p == ':') {
  1801. *p = '\0';
  1802. header_name = ZSTR_VAL(decoded_header.s);
  1803. header_name_len = p - ZSTR_VAL(decoded_header.s);
  1804. while (++p < limit) {
  1805. if (*p != ' ' && *p != '\t') {
  1806. break;
  1807. }
  1808. }
  1809. header_value = p;
  1810. header_value_len = limit - p;
  1811. break;
  1812. }
  1813. }
  1814. if (header_name != NULL) {
  1815. zval *elem;
  1816. if ((elem = zend_hash_str_find(Z_ARRVAL_P(return_value), header_name, header_name_len)) != NULL) {
  1817. if (Z_TYPE_P(elem) != IS_ARRAY) {
  1818. zval new_elem;
  1819. array_init(&new_elem);
  1820. Z_ADDREF_P(elem);
  1821. add_next_index_zval(&new_elem, elem);
  1822. elem = zend_hash_str_update(Z_ARRVAL_P(return_value), header_name, header_name_len, &new_elem);
  1823. }
  1824. add_next_index_stringl(elem, header_value, header_value_len);
  1825. } else {
  1826. add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len);
  1827. }
  1828. }
  1829. enc_str_len_tmp -= next_pos - enc_str_tmp;
  1830. enc_str_tmp = (char *)next_pos;
  1831. smart_str_free(&decoded_header);
  1832. }
  1833. if (err != PHP_ICONV_ERR_SUCCESS) {
  1834. _php_iconv_show_error(err, charset, "???");
  1835. zend_array_destroy(Z_ARR_P(return_value));
  1836. RETVAL_FALSE;
  1837. }
  1838. }
  1839. /* }}} */
  1840. /* {{{ Returns str converted to the out_charset character set */
  1841. PHP_FUNCTION(iconv)
  1842. {
  1843. char *in_charset, *out_charset;
  1844. zend_string *in_buffer;
  1845. size_t in_charset_len = 0, out_charset_len = 0;
  1846. php_iconv_err_t err;
  1847. zend_string *out_buffer;
  1848. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssS",
  1849. &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer) == FAILURE) {
  1850. RETURN_THROWS();
  1851. }
  1852. if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) {
  1853. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1854. RETURN_FALSE;
  1855. }
  1856. err = php_iconv_string(ZSTR_VAL(in_buffer), (size_t)ZSTR_LEN(in_buffer), &out_buffer, out_charset, in_charset);
  1857. _php_iconv_show_error(err, out_charset, in_charset);
  1858. if (err == PHP_ICONV_ERR_SUCCESS && out_buffer != NULL) {
  1859. RETVAL_NEW_STR(out_buffer);
  1860. } else {
  1861. if (out_buffer != NULL) {
  1862. zend_string_efree(out_buffer);
  1863. }
  1864. RETURN_FALSE;
  1865. }
  1866. }
  1867. /* }}} */
  1868. /* {{{ Sets internal encoding and output encoding for ob_iconv_handler() */
  1869. PHP_FUNCTION(iconv_set_encoding)
  1870. {
  1871. zend_string *type;
  1872. zend_string *charset;
  1873. zend_result retval;
  1874. zend_string *name;
  1875. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &type, &charset) == FAILURE) {
  1876. RETURN_THROWS();
  1877. }
  1878. if (ZSTR_LEN(charset) >= ICONV_CSNMAXLEN) {
  1879. php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
  1880. RETURN_FALSE;
  1881. }
  1882. if(zend_string_equals_literal_ci(type, "input_encoding")) {
  1883. name = zend_string_init("iconv.input_encoding", sizeof("iconv.input_encoding") - 1, 0);
  1884. } else if(zend_string_equals_literal_ci(type, "output_encoding")) {
  1885. name = zend_string_init("iconv.output_encoding", sizeof("iconv.output_encoding") - 1, 0);
  1886. } else if(zend_string_equals_literal_ci(type, "internal_encoding")) {
  1887. name = zend_string_init("iconv.internal_encoding", sizeof("iconv.internal_encoding") - 1, 0);
  1888. } else {
  1889. RETURN_FALSE;
  1890. }
  1891. retval = zend_alter_ini_entry(name, charset, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
  1892. zend_string_release_ex(name, 0);
  1893. if (retval == SUCCESS) {
  1894. RETURN_TRUE;
  1895. } else {
  1896. RETURN_FALSE;
  1897. }
  1898. }
  1899. /* }}} */
  1900. /* {{{ Get internal encoding and output encoding for ob_iconv_handler() */
  1901. PHP_FUNCTION(iconv_get_encoding)
  1902. {
  1903. zend_string *type = NULL;
  1904. if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &type) == FAILURE) {
  1905. RETURN_THROWS();
  1906. }
  1907. if (!type || zend_string_equals_literal_ci(type, "all")) {
  1908. array_init(return_value);
  1909. add_assoc_string(return_value, "input_encoding", get_input_encoding());
  1910. add_assoc_string(return_value, "output_encoding", get_output_encoding());
  1911. add_assoc_string(return_value, "internal_encoding", get_internal_encoding());
  1912. } else if (zend_string_equals_literal_ci(type, "input_encoding")) {
  1913. RETVAL_STRING(get_input_encoding());
  1914. } else if (zend_string_equals_literal_ci(type, "output_encoding")) {
  1915. RETVAL_STRING(get_output_encoding());
  1916. } else if (zend_string_equals_literal_ci(type, "internal_encoding")) {
  1917. RETVAL_STRING(get_internal_encoding());
  1918. } else {
  1919. /* TODO Warning/ValueError? */
  1920. RETURN_FALSE;
  1921. }
  1922. }
  1923. /* }}} */
  1924. /* {{{ iconv stream filter */
  1925. typedef struct _php_iconv_stream_filter {
  1926. iconv_t cd;
  1927. int persistent;
  1928. char *to_charset;
  1929. size_t to_charset_len;
  1930. char *from_charset;
  1931. size_t from_charset_len;
  1932. char stub[128];
  1933. size_t stub_len;
  1934. } php_iconv_stream_filter;
  1935. /* }}} iconv stream filter */
  1936. /* {{{ php_iconv_stream_filter_dtor */
  1937. static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self)
  1938. {
  1939. iconv_close(self->cd);
  1940. pefree(self->to_charset, self->persistent);
  1941. pefree(self->from_charset, self->persistent);
  1942. }
  1943. /* }}} */
  1944. /* {{{ php_iconv_stream_filter_ctor() */
  1945. static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self,
  1946. const char *to_charset, size_t to_charset_len,
  1947. const char *from_charset, size_t from_charset_len, int persistent)
  1948. {
  1949. self->to_charset = pemalloc(to_charset_len + 1, persistent);
  1950. self->to_charset_len = to_charset_len;
  1951. self->from_charset = pemalloc(from_charset_len + 1, persistent);
  1952. self->from_charset_len = from_charset_len;
  1953. memcpy(self->to_charset, to_charset, to_charset_len);
  1954. self->to_charset[to_charset_len] = '\0';
  1955. memcpy(self->from_charset, from_charset, from_charset_len);
  1956. self->from_charset[from_charset_len] = '\0';
  1957. if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) {
  1958. pefree(self->from_charset, persistent);
  1959. pefree(self->to_charset, persistent);
  1960. return PHP_ICONV_ERR_UNKNOWN;
  1961. }
  1962. self->persistent = persistent;
  1963. self->stub_len = 0;
  1964. return PHP_ICONV_ERR_SUCCESS;
  1965. }
  1966. /* }}} */
  1967. /* {{{ php_iconv_stream_filter_append_bucket */
  1968. static int php_iconv_stream_filter_append_bucket(
  1969. php_iconv_stream_filter *self,
  1970. php_stream *stream, php_stream_filter *filter,
  1971. php_stream_bucket_brigade *buckets_out,
  1972. const char *ps, size_t buf_len, size_t *consumed,
  1973. int persistent)
  1974. {
  1975. php_stream_bucket *new_bucket;
  1976. char *out_buf = NULL;
  1977. size_t out_buf_size;
  1978. char *pd, *pt;
  1979. size_t ocnt, prev_ocnt, icnt, tcnt;
  1980. size_t initial_out_buf_size;
  1981. if (ps == NULL) {
  1982. initial_out_buf_size = 64;
  1983. icnt = 1;
  1984. } else {
  1985. initial_out_buf_size = buf_len;
  1986. icnt = buf_len;
  1987. }
  1988. out_buf_size = ocnt = prev_ocnt = initial_out_buf_size;
  1989. out_buf = pemalloc(out_buf_size, persistent);
  1990. pd = out_buf;
  1991. if (self->stub_len > 0) {
  1992. pt = self->stub;
  1993. tcnt = self->stub_len;
  1994. while (tcnt > 0) {
  1995. if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
  1996. switch (errno) {
  1997. case EILSEQ:
  1998. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
  1999. goto out_failure;
  2000. case EINVAL:
  2001. if (ps != NULL) {
  2002. if (icnt > 0) {
  2003. if (self->stub_len >= sizeof(self->stub)) {
  2004. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
  2005. goto out_failure;
  2006. }
  2007. self->stub[self->stub_len++] = *(ps++);
  2008. icnt--;
  2009. pt = self->stub;
  2010. tcnt = self->stub_len;
  2011. } else {
  2012. tcnt = 0;
  2013. break;
  2014. }
  2015. } else {
  2016. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
  2017. goto out_failure;
  2018. }
  2019. break;
  2020. case E2BIG: {
  2021. char *new_out_buf;
  2022. size_t new_out_buf_size;
  2023. new_out_buf_size = out_buf_size << 1;
  2024. if (new_out_buf_size < out_buf_size) {
  2025. /* whoa! no bigger buckets are sold anywhere... */
  2026. if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
  2027. goto out_failure;
  2028. }
  2029. php_stream_bucket_append(buckets_out, new_bucket);
  2030. out_buf_size = ocnt = initial_out_buf_size;
  2031. out_buf = pemalloc(out_buf_size, persistent);
  2032. pd = out_buf;
  2033. } else {
  2034. new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
  2035. pd = new_out_buf + (pd - out_buf);
  2036. ocnt += (new_out_buf_size - out_buf_size);
  2037. out_buf = new_out_buf;
  2038. out_buf_size = new_out_buf_size;
  2039. }
  2040. } break;
  2041. default:
  2042. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
  2043. goto out_failure;
  2044. }
  2045. }
  2046. prev_ocnt = ocnt;
  2047. }
  2048. memmove(self->stub, pt, tcnt);
  2049. self->stub_len = tcnt;
  2050. }
  2051. while (icnt > 0) {
  2052. if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
  2053. iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
  2054. switch (errno) {
  2055. case EILSEQ:
  2056. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
  2057. goto out_failure;
  2058. case EINVAL:
  2059. if (ps != NULL) {
  2060. if (icnt > sizeof(self->stub)) {
  2061. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
  2062. goto out_failure;
  2063. }
  2064. memcpy(self->stub, ps, icnt);
  2065. self->stub_len = icnt;
  2066. ps += icnt;
  2067. icnt = 0;
  2068. } else {
  2069. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
  2070. goto out_failure;
  2071. }
  2072. break;
  2073. case E2BIG: {
  2074. char *new_out_buf;
  2075. size_t new_out_buf_size;
  2076. new_out_buf_size = out_buf_size << 1;
  2077. if (new_out_buf_size < out_buf_size) {
  2078. /* whoa! no bigger buckets are sold anywhere... */
  2079. if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
  2080. goto out_failure;
  2081. }
  2082. php_stream_bucket_append(buckets_out, new_bucket);
  2083. out_buf_size = ocnt = initial_out_buf_size;
  2084. out_buf = pemalloc(out_buf_size, persistent);
  2085. pd = out_buf;
  2086. } else {
  2087. new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
  2088. pd = new_out_buf + (pd - out_buf);
  2089. ocnt += (new_out_buf_size - out_buf_size);
  2090. out_buf = new_out_buf;
  2091. out_buf_size = new_out_buf_size;
  2092. }
  2093. } break;
  2094. default:
  2095. php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
  2096. goto out_failure;
  2097. }
  2098. } else {
  2099. if (ps == NULL) {
  2100. break;
  2101. }
  2102. }
  2103. prev_ocnt = ocnt;
  2104. }
  2105. if (out_buf_size > ocnt) {
  2106. if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
  2107. goto out_failure;
  2108. }
  2109. php_stream_bucket_append(buckets_out, new_bucket);
  2110. } else {
  2111. pefree(out_buf, persistent);
  2112. }
  2113. *consumed += buf_len - icnt;
  2114. return SUCCESS;
  2115. out_failure:
  2116. pefree(out_buf, persistent);
  2117. return FAILURE;
  2118. }
  2119. /* }}} php_iconv_stream_filter_append_bucket */
  2120. /* {{{ php_iconv_stream_filter_do_filter */
  2121. static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
  2122. php_stream *stream, php_stream_filter *filter,
  2123. php_stream_bucket_brigade *buckets_in,
  2124. php_stream_bucket_brigade *buckets_out,
  2125. size_t *bytes_consumed, int flags)
  2126. {
  2127. php_stream_bucket *bucket = NULL;
  2128. size_t consumed = 0;
  2129. php_iconv_stream_filter *self = (php_iconv_stream_filter *)Z_PTR(filter->abstract);
  2130. while (buckets_in->head != NULL) {
  2131. bucket = buckets_in->head;
  2132. php_stream_bucket_unlink(bucket);
  2133. if (php_iconv_stream_filter_append_bucket(self, stream, filter,
  2134. buckets_out, bucket->buf, bucket->buflen, &consumed,
  2135. php_stream_is_persistent(stream)) != SUCCESS) {
  2136. goto out_failure;
  2137. }
  2138. php_stream_bucket_delref(bucket);
  2139. }
  2140. if (flags != PSFS_FLAG_NORMAL) {
  2141. if (php_iconv_stream_filter_append_bucket(self, stream, filter,
  2142. buckets_out, NULL, 0, &consumed,
  2143. php_stream_is_persistent(stream)) != SUCCESS) {
  2144. goto out_failure;
  2145. }
  2146. }
  2147. if (bytes_consumed != NULL) {
  2148. *bytes_consumed = consumed;
  2149. }
  2150. return PSFS_PASS_ON;
  2151. out_failure:
  2152. if (bucket != NULL) {
  2153. php_stream_bucket_delref(bucket);
  2154. }
  2155. return PSFS_ERR_FATAL;
  2156. }
  2157. /* }}} */
  2158. /* {{{ php_iconv_stream_filter_cleanup */
  2159. static void php_iconv_stream_filter_cleanup(php_stream_filter *filter)
  2160. {
  2161. php_iconv_stream_filter_dtor((php_iconv_stream_filter *)Z_PTR(filter->abstract));
  2162. pefree(Z_PTR(filter->abstract), ((php_iconv_stream_filter *)Z_PTR(filter->abstract))->persistent);
  2163. }
  2164. /* }}} */
  2165. static const php_stream_filter_ops php_iconv_stream_filter_ops = {
  2166. php_iconv_stream_filter_do_filter,
  2167. php_iconv_stream_filter_cleanup,
  2168. "convert.iconv.*"
  2169. };
  2170. /* {{{ php_iconv_stream_filter_create */
  2171. static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, uint8_t persistent)
  2172. {
  2173. php_stream_filter *retval = NULL;
  2174. php_iconv_stream_filter *inst;
  2175. char *from_charset = NULL, *to_charset = NULL;
  2176. size_t from_charset_len, to_charset_len;
  2177. if ((from_charset = strchr(name, '.')) == NULL) {
  2178. return NULL;
  2179. }
  2180. ++from_charset;
  2181. if ((from_charset = strchr(from_charset, '.')) == NULL) {
  2182. return NULL;
  2183. }
  2184. ++from_charset;
  2185. if ((to_charset = strpbrk(from_charset, "/.")) == NULL) {
  2186. return NULL;
  2187. }
  2188. from_charset_len = to_charset - from_charset;
  2189. ++to_charset;
  2190. to_charset_len = strlen(to_charset);
  2191. if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) {
  2192. return NULL;
  2193. }
  2194. inst = pemalloc(sizeof(php_iconv_stream_filter), persistent);
  2195. if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) {
  2196. pefree(inst, persistent);
  2197. return NULL;
  2198. }
  2199. if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) {
  2200. php_iconv_stream_filter_dtor(inst);
  2201. pefree(inst, persistent);
  2202. }
  2203. return retval;
  2204. }
  2205. /* }}} */
  2206. /* {{{ php_iconv_stream_register_factory */
  2207. static php_iconv_err_t php_iconv_stream_filter_register_factory(void)
  2208. {
  2209. static const php_stream_filter_factory filter_factory = {
  2210. php_iconv_stream_filter_factory_create
  2211. };
  2212. if (FAILURE == php_stream_filter_register_factory(
  2213. php_iconv_stream_filter_ops.label,
  2214. &filter_factory)) {
  2215. return PHP_ICONV_ERR_UNKNOWN;
  2216. }
  2217. return PHP_ICONV_ERR_SUCCESS;
  2218. }
  2219. /* }}} */
  2220. /* {{{ php_iconv_stream_unregister_factory */
  2221. static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void)
  2222. {
  2223. if (FAILURE == php_stream_filter_unregister_factory(
  2224. php_iconv_stream_filter_ops.label)) {
  2225. return PHP_ICONV_ERR_UNKNOWN;
  2226. }
  2227. return PHP_ICONV_ERR_SUCCESS;
  2228. }
  2229. /* }}} */
  2230. /* }}} */
  2231. #endif