PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/mysqlnd - mysqlnd_wireprotocol.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 813 927 87.7 %
Date: 2014-07-27 Functions: 45 47 95.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   +----------------------------------------------------------------------+
       3             :   | PHP Version 5                                                        |
       4             :   +----------------------------------------------------------------------+
       5             :   | Copyright (c) 2006-2013 The PHP Group                                |
       6             :   +----------------------------------------------------------------------+
       7             :   | This source file is subject to version 3.01 of the PHP license,      |
       8             :   | that is bundled with this package in the file LICENSE, and is        |
       9             :   | available through the world-wide-web at the following url:           |
      10             :   | http://www.php.net/license/3_01.txt                                  |
      11             :   | If you did not receive a copy of the PHP license and are unable to   |
      12             :   | obtain it through the world-wide-web, please send a note to          |
      13             :   | license@php.net so we can mail you a copy immediately.               |
      14             :   +----------------------------------------------------------------------+
      15             :   | Authors: Georg Richter <georg@mysql.com>                             |
      16             :   |          Andrey Hristov <andrey@mysql.com>                           |
      17             :   |          Ulf Wendel <uwendel@mysql.com>                              |
      18             :   +----------------------------------------------------------------------+
      19             : */
      20             : #include "php.h"
      21             : #include "php_globals.h"
      22             : #include "mysqlnd.h"
      23             : #include "mysqlnd_priv.h"
      24             : #include "mysqlnd_wireprotocol.h"
      25             : #include "mysqlnd_statistics.h"
      26             : #include "mysqlnd_debug.h"
      27             : #include "ext/standard/sha1.h"
      28             : #include "zend_ini.h"
      29             : 
      30             : #define MYSQLND_SILENT 1
      31             : 
      32             : #define MYSQLND_DUMP_HEADER_N_BODY
      33             : 
      34             : #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
      35             :         { \
      36             :                 DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
      37             :                 if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
      38             :                         CONN_SET_STATE(conn, CONN_QUIT_SENT); \
      39             :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
      40             :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
      41             :                         DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
      42             :                         DBG_RETURN(FAIL);\
      43             :                 }\
      44             :                 if ((buf_size) < (packet)->header.size) { \
      45             :                         DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
      46             :                                                 (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
      47             :                                                 DBG_RETURN(FAIL); \
      48             :                 }\
      49             :                 if (FAIL == conn->net->m.receive((conn), (buf), (packet)->header.size TSRMLS_CC)) { \
      50             :                         CONN_SET_STATE(conn, CONN_QUIT_SENT); \
      51             :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
      52             :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
      53             :                         DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
      54             :                         DBG_RETURN(FAIL);\
      55             :                 } \
      56             :                 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
      57             :                                                                                         MYSQLND_HEADER_SIZE + (packet)->header.size, \
      58             :                                                                                         packet_type_to_statistic_packet_count[packet_type], \
      59             :                                                                                         1); \
      60             :         }
      61             : 
      62             : 
      63             : #define BAIL_IF_NO_MORE_DATA \
      64             :         if ((size_t)(p - begin) > packet->header.size) { \
      65             :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
      66             :                 goto premature_end; \
      67             :         } \
      68             : 
      69             : 
      70             : static const char *unknown_sqlstate= "HY000";
      71             : 
      72             : const char * const mysqlnd_empty_string = "";
      73             : 
      74             : /* Used in mysqlnd_debug.c */
      75             : const char mysqlnd_read_header_name[]   = "mysqlnd_read_header";
      76             : const char mysqlnd_read_body_name[]             = "mysqlnd_read_body";
      77             : 
      78             : 
      79             : #define ERROR_MARKER 0xFF
      80             : #define EODATA_MARKER 0xFE
      81             : 
      82             : /* {{{ mysqlnd_command_to_text
      83             :  */
      84             : const char * const mysqlnd_command_to_text[COM_END] =
      85             : {
      86             :   "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
      87             :   "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
      88             :   "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
      89             :   "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
      90             :   "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
      91             :   "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
      92             :   "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
      93             : };
      94             : /* }}} */
      95             : 
      96             : 
      97             : 
      98             : static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
      99             : {
     100             :         STAT_LAST,
     101             :         STAT_LAST,
     102             :         STAT_BYTES_RECEIVED_OK,
     103             :         STAT_BYTES_RECEIVED_EOF,
     104             :         STAT_LAST,
     105             :         STAT_BYTES_RECEIVED_RSET_HEADER,
     106             :         STAT_BYTES_RECEIVED_RSET_FIELD_META,
     107             :         STAT_BYTES_RECEIVED_RSET_ROW,
     108             :         STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
     109             :         STAT_BYTES_RECEIVED_CHANGE_USER,
     110             : };
     111             : 
     112             : static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
     113             : {
     114             :         STAT_LAST,
     115             :         STAT_LAST,
     116             :         STAT_PACKETS_RECEIVED_OK,
     117             :         STAT_PACKETS_RECEIVED_EOF,
     118             :         STAT_LAST,
     119             :         STAT_PACKETS_RECEIVED_RSET_HEADER,
     120             :         STAT_PACKETS_RECEIVED_RSET_FIELD_META,
     121             :         STAT_PACKETS_RECEIVED_RSET_ROW,
     122             :         STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
     123             :         STAT_PACKETS_RECEIVED_CHANGE_USER,
     124             : };
     125             : 
     126             : 
     127             : /* {{{ php_mysqlnd_net_field_length
     128             :    Get next field's length */
     129             : unsigned long
     130      300456 : php_mysqlnd_net_field_length(zend_uchar **packet)
     131             : {
     132      300456 :         register zend_uchar *p= (zend_uchar *)*packet;
     133             : 
     134      300456 :         if (*p < 251) {
     135      267380 :                 (*packet)++;
     136      267380 :                 return (unsigned long) *p;
     137             :         }
     138             : 
     139       33076 :         switch (*p) {
     140             :                 case 251:
     141         748 :                         (*packet)++;
     142         748 :                         return MYSQLND_NULL_LENGTH;
     143             :                 case 252:
     144       32309 :                         (*packet) += 3;
     145       32309 :                         return (unsigned long) uint2korr(p+1);
     146             :                 case 253:
     147          18 :                         (*packet) += 4;
     148          18 :                         return (unsigned long) uint3korr(p+1);
     149             :                 default:
     150           1 :                         (*packet) += 9;
     151           1 :                         return (unsigned long) uint4korr(p+1);
     152             :         }
     153             : }
     154             : /* }}} */
     155             : 
     156             : 
     157             : /* {{{ php_mysqlnd_net_field_length_ll
     158             :    Get next field's length */
     159             : uint64_t
     160       42684 : php_mysqlnd_net_field_length_ll(zend_uchar **packet)
     161             : {
     162       42684 :         register zend_uchar *p= (zend_uchar *)*packet;
     163             : 
     164       42684 :         if (*p < 251) {
     165       42675 :                 (*packet)++;
     166       42675 :                 return (uint64_t) *p;
     167             :         }
     168             : 
     169           9 :         switch (*p) {
     170             :                 case 251:
     171           0 :                         (*packet)++;
     172           0 :                         return (uint64_t) MYSQLND_NULL_LENGTH;
     173             :                 case 252:
     174           9 :                         (*packet) += 3;
     175           9 :                         return (uint64_t) uint2korr(p + 1);
     176             :                 case 253:
     177           0 :                         (*packet) += 4;
     178           0 :                         return (uint64_t) uint3korr(p + 1);
     179             :                 default:
     180           0 :                         (*packet) += 9;
     181           0 :                         return (uint64_t) uint8korr(p + 1);
     182             :         }
     183             : }
     184             : /* }}} */
     185             : 
     186             : 
     187             : /* {{{ php_mysqlnd_net_store_length */
     188             : zend_uchar *
     189         656 : php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
     190             : {
     191         656 :         if (length < (uint64_t) L64(251)) {
     192         579 :                 *packet = (zend_uchar) length;
     193         579 :                 return packet + 1;
     194             :         }
     195             : 
     196          77 :         if (length < (uint64_t) L64(65536)) {
     197          73 :                 *packet++ = 252;
     198          73 :                 int2store(packet,(unsigned int) length);
     199          73 :                 return packet + 2;
     200             :         }
     201             : 
     202           4 :         if (length < (uint64_t) L64(16777216)) {
     203           4 :                 *packet++ = 253;
     204           4 :                 int3store(packet,(ulong) length);
     205           4 :                 return packet + 3;
     206             :         }
     207           0 :         *packet++ = 254;
     208           0 :         int8store(packet, length);
     209           0 :         return packet + 8;
     210             : }
     211             : /* }}} */
     212             : 
     213             : 
     214             : /* {{{ php_mysqlnd_read_error_from_line */
     215             : static enum_func_status
     216         862 : php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
     217             :                                                                 char *error, int error_buf_len,
     218             :                                                                 unsigned int *error_no, char *sqlstate TSRMLS_DC)
     219             : {
     220         862 :         zend_uchar *p = buf;
     221         862 :         int error_msg_len= 0;
     222             : 
     223         862 :         DBG_ENTER("php_mysqlnd_read_error_from_line");
     224             : 
     225         862 :         *error_no = CR_UNKNOWN_ERROR;
     226         862 :         memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
     227             : 
     228         862 :         if (buf_len > 2) {
     229         862 :                 *error_no = uint2korr(p);
     230         862 :                 p+= 2;
     231             :                 /*
     232             :                   sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
     233             :                   if it was >=2 then we would need a check
     234             :                 */
     235         862 :                 if (*p == '#') {
     236         862 :                         ++p;
     237         862 :                         if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
     238         862 :                                 memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
     239         862 :                                 p+= MYSQLND_SQLSTATE_LENGTH;
     240             :                         } else {
     241           0 :                                 goto end;
     242             :                         }
     243             :                 }
     244         862 :                 if ((buf_len - (p - buf)) > 0) {
     245         862 :                         error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
     246         862 :                         memcpy(error, p, error_msg_len);
     247             :                 }
     248             :         }
     249             : end:
     250         862 :         sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
     251         862 :         error[error_msg_len]= '\0';
     252             : 
     253         862 :         DBG_RETURN(FAIL);
     254             : }
     255             : /* }}} */
     256             : 
     257             : 
     258             : /* {{{ mysqlnd_read_header */
     259             : static enum_func_status
     260      278395 : mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC)
     261             : {
     262      278395 :         MYSQLND_NET * net = conn->net;
     263             :         zend_uchar buffer[MYSQLND_HEADER_SIZE];
     264             : 
     265      278395 :         DBG_ENTER("mysqlnd_read_header_name");
     266      278395 :         DBG_INF_FMT("compressed=%u conn_id=%u", net->compressed, conn->thread_id);
     267      278395 :         if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
     268           3 :                 DBG_RETURN(FAIL);
     269             :         }
     270             : 
     271      278392 :         header->size = uint3korr(buffer);
     272      278392 :         header->packet_no = uint1korr(buffer + 3);
     273             : 
     274             : #ifdef MYSQLND_DUMP_HEADER_N_BODY
     275      278392 :         DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
     276             : #endif
     277      278392 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats,
     278             :                                                         STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
     279             :                                                         STAT_PACKETS_RECEIVED, 1);
     280             : 
     281      278392 :         if (net->compressed || net->packet_no == header->packet_no) {
     282             :                 /*
     283             :                   Have to increase the number, so we can send correct number back. It will
     284             :                   round at 255 as this is unsigned char. The server needs this for simple
     285             :                   flow control checking.
     286             :                 */
     287      278391 :                 net->packet_no++;
     288      278391 :                 DBG_RETURN(PASS);
     289             :         }
     290             : 
     291           3 :         DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
     292           2 :                                 net->packet_no, header->packet_no, header->size);
     293             : 
     294           3 :         php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
     295           2 :                           net->packet_no, header->packet_no, header->size);
     296           1 :         DBG_RETURN(FAIL);
     297             : }
     298             : /* }}} */
     299             : 
     300             : 
     301             : /* {{{ php_mysqlnd_greet_read */
     302             : static enum_func_status
     303        1815 : php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     304             : {
     305             :         zend_uchar buf[2048];
     306        1815 :         zend_uchar *p = buf;
     307        1815 :         zend_uchar *begin = buf;
     308        1815 :         MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
     309             : 
     310        1815 :         DBG_ENTER("php_mysqlnd_greet_read");
     311             : 
     312        1815 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
     313        1815 :         BAIL_IF_NO_MORE_DATA;
     314             : 
     315        1815 :         packet->protocol_version = uint1korr(p);
     316        1815 :         p++;
     317        1815 :         BAIL_IF_NO_MORE_DATA;
     318             : 
     319        1815 :         if (ERROR_MARKER == packet->protocol_version) {
     320           0 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     321             :                                                                                  packet->error, sizeof(packet->error),
     322             :                                                                                  &packet->error_no, packet->sqlstate
     323             :                                                                                  TSRMLS_CC);
     324             :                 /*
     325             :                   The server doesn't send sqlstate in the greet packet.
     326             :                   It's a bug#26426 , so we have to set it correctly ourselves.
     327             :                   It's probably "Too many connections, which has SQL state 08004".
     328             :                 */
     329           0 :                 if (packet->error_no == 1040) {
     330           0 :                         memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
     331             :                 }
     332           0 :                 DBG_RETURN(PASS);
     333             :         }
     334             : 
     335        1815 :         packet->server_version = estrdup((char *)p);
     336        1815 :         p+= strlen(packet->server_version) + 1; /* eat the '\0' */
     337        1815 :         BAIL_IF_NO_MORE_DATA;
     338             : 
     339        1815 :         packet->thread_id = uint4korr(p);
     340        1815 :         p+=4;
     341        1815 :         BAIL_IF_NO_MORE_DATA;
     342             : 
     343        1815 :         memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
     344        1815 :         p+= 8;
     345        1815 :         BAIL_IF_NO_MORE_DATA;
     346             : 
     347             :         /* pad1 */
     348        1815 :         p++;
     349        1815 :         BAIL_IF_NO_MORE_DATA;
     350             : 
     351        1815 :         packet->server_capabilities = uint2korr(p);
     352        1815 :         p+= 2;
     353        1815 :         BAIL_IF_NO_MORE_DATA;
     354             : 
     355        1815 :         packet->charset_no = uint1korr(p);
     356        1815 :         p++;
     357        1815 :         BAIL_IF_NO_MORE_DATA;
     358             : 
     359        1815 :         packet->server_status = uint2korr(p);
     360        1815 :         p+= 2;
     361        1815 :         BAIL_IF_NO_MORE_DATA;
     362             : 
     363             :         /* pad2 */
     364        1815 :         p+= 13;
     365        1815 :         BAIL_IF_NO_MORE_DATA;
     366             : 
     367        1815 :         if ((size_t) (p - buf) < packet->header.size) {
     368             :                 /* scramble_buf is split into two parts */
     369        1815 :                 memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
     370             :                                 p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
     371             :         } else {
     372           0 :                 packet->pre41 = TRUE;
     373             :         }
     374             : 
     375        3630 :         DBG_INF_FMT("proto=%u server=%s thread_id=%u",
     376        1815 :                                 packet->protocol_version, packet->server_version, packet->thread_id);
     377             : 
     378        5445 :         DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i",
     379        5445 :                                 packet->server_capabilities, packet->charset_no, packet->server_status);
     380             : 
     381        1815 :         DBG_RETURN(PASS);
     382             : premature_end:
     383           0 :         DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
     384           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
     385           0 :                                          p - begin - packet->header.size);
     386           0 :         DBG_RETURN(FAIL);
     387             : }
     388             : /* }}} */
     389             : 
     390             : 
     391             : /* {{{ php_mysqlnd_greet_free_mem */
     392             : static
     393        1829 : void php_mysqlnd_greet_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     394             : {
     395        1829 :         MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
     396        1829 :         if (p->server_version) {
     397        1815 :                 efree(p->server_version);
     398        1815 :                 p->server_version = NULL;
     399             :         }
     400        1829 :         if (!stack_allocation) {
     401        1829 :                 mnd_pefree(p, p->header.persistent);
     402             :         }
     403        1829 : }
     404             : /* }}} */
     405             : 
     406             : 
     407             : /* {{{ php_mysqlnd_crypt */
     408             : static void
     409        1865 : php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
     410             : {
     411        1865 :         const zend_uchar *s1_end = s1 + len;
     412       41030 :         while (s1 < s1_end) {
     413       37300 :                 *buffer++= *s1++ ^ *s2++;
     414             :         }
     415        1865 : }
     416             : /* }}} */
     417             : 
     418             : 
     419             : /* {{{ php_mysqlnd_scramble */
     420        1865 : void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password)
     421             : {
     422             :         PHP_SHA1_CTX context;
     423             :         zend_uchar sha1[SHA1_MAX_LENGTH];
     424             :         zend_uchar sha2[SHA1_MAX_LENGTH];
     425             : 
     426             : 
     427             :         /* Phase 1: hash password */
     428        1865 :         PHP_SHA1Init(&context);
     429        1865 :         PHP_SHA1Update(&context, password, strlen((char *)password));
     430        1865 :         PHP_SHA1Final(sha1, &context);
     431             : 
     432             :         /* Phase 2: hash sha1 */
     433        1865 :         PHP_SHA1Init(&context);
     434        1865 :         PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
     435        1865 :         PHP_SHA1Final(sha2, &context);
     436             : 
     437             :         /* Phase 3: hash scramble + sha2 */
     438        1865 :         PHP_SHA1Init(&context);
     439        1865 :         PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
     440        1865 :         PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
     441        1865 :         PHP_SHA1Final(buffer, &context);
     442             : 
     443             :         /* let's crypt buffer now */
     444        1865 :         php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
     445        1865 : }
     446             : /* }}} */
     447             : 
     448             : 
     449             : #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SHA1_MAX_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 128)
     450             : 
     451             : /* {{{ php_mysqlnd_auth_write */
     452             : static
     453        1815 : size_t php_mysqlnd_auth_write(void *_packet, MYSQLND * conn TSRMLS_DC)
     454             : {
     455             :         char buffer[AUTH_WRITE_BUFFER_LEN];
     456        1815 :         register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
     457             :         int len;
     458        1815 :         register MYSQLND_PACKET_AUTH *packet= (MYSQLND_PACKET_AUTH *) _packet;
     459             : 
     460        1815 :         DBG_ENTER("php_mysqlnd_auth_write");
     461             : 
     462        1815 :         int4store(p, packet->client_flags);
     463        1815 :         p+= 4;
     464             : 
     465        1815 :         int4store(p, packet->max_packet_size);
     466        1815 :         p+= 4;
     467             : 
     468        1815 :         int1store(p, packet->charset_no);
     469        1815 :         p++;
     470             : 
     471        1815 :         memset(p, 0, 23); /* filler */
     472        1815 :         p+= 23;
     473             : 
     474        1815 :         if (!packet->send_half_packet) {
     475        1812 :                 len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
     476        1812 :                 memcpy(p, packet->user, len);
     477        1812 :                 p+= len;
     478        1812 :                 *p++ = '\0';
     479             : 
     480             :                 /* copy scrambled pass*/
     481        3608 :                 if (packet->password && packet->password[0]) {
     482             :                         /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
     483        1796 :                         int1store(p, SHA1_MAX_LENGTH);
     484        1796 :                         p++;
     485        1796 :                         php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
     486        1796 :                         p+= SHA1_MAX_LENGTH;
     487             :                 } else {
     488             :                         /* Zero length */
     489          16 :                         int1store(p, 0);
     490          16 :                         p++;
     491             :                 }
     492             : 
     493        1812 :                 if (packet->db) {
     494        1812 :                         size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
     495        1812 :                         memcpy(p, packet->db, real_db_len);
     496        1812 :                         p+= real_db_len;
     497        1812 :                         *p++= '\0';
     498             :                 }
     499             :                 /* Handle CLIENT_CONNECT_WITH_DB */
     500             :                 /* no \0 for no DB */
     501             :         }
     502             : 
     503        1815 :         DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
     504             : }
     505             : /* }}} */
     506             : 
     507             : 
     508             : /* {{{ php_mysqlnd_auth_free_mem */
     509             : static
     510        1815 : void php_mysqlnd_auth_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     511             : {
     512        1815 :         if (!stack_allocation) {
     513        1815 :                 MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet;
     514        1815 :                 mnd_pefree(p, p->header.persistent);
     515             :         }
     516        1815 : }
     517             : /* }}} */
     518             : 
     519             : 
     520             : #define OK_BUFFER_SIZE 2048
     521             : 
     522             : /* {{{ php_mysqlnd_ok_read */
     523             : static enum_func_status
     524        2112 : php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     525             : {
     526             :         zend_uchar local_buf[OK_BUFFER_SIZE];
     527        2112 :         size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
     528        2112 :         zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
     529        2112 :         zend_uchar *p = buf;
     530        2112 :         zend_uchar *begin = buf;
     531             :         unsigned long i;
     532        2112 :         register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
     533             : 
     534        2112 :         DBG_ENTER("php_mysqlnd_ok_read");
     535             : 
     536        2112 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
     537        2112 :         BAIL_IF_NO_MORE_DATA;
     538             : 
     539             :         /* Should be always 0x0 or ERROR_MARKER for error */
     540        2112 :         packet->field_count = uint1korr(p);
     541        2112 :         p++;
     542        2112 :         BAIL_IF_NO_MORE_DATA;
     543             : 
     544        2112 :         if (ERROR_MARKER == packet->field_count) {
     545          44 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     546             :                                                                                  packet->error, sizeof(packet->error),
     547             :                                                                                  &packet->error_no, packet->sqlstate
     548             :                                                                                  TSRMLS_CC);
     549          44 :                 DBG_RETURN(PASS);
     550             :         }
     551             :         /* Everything was fine! */
     552        2068 :         packet->affected_rows  = php_mysqlnd_net_field_length_ll(&p);
     553        2068 :         BAIL_IF_NO_MORE_DATA;
     554             : 
     555        2068 :         packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
     556        2068 :         BAIL_IF_NO_MORE_DATA;
     557             : 
     558        2068 :         packet->server_status = uint2korr(p);
     559        2068 :         p+= 2;
     560        2068 :         BAIL_IF_NO_MORE_DATA;
     561             : 
     562        2068 :         packet->warning_count = uint2korr(p);
     563        2068 :         p+= 2;
     564        2068 :         BAIL_IF_NO_MORE_DATA;
     565             : 
     566             :         /* There is a message */
     567        2080 :         if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
     568          12 :                 packet->message_len = MIN(i, buf_len - (p - begin));
     569          12 :                 packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
     570             :         } else {
     571        2056 :                 packet->message = NULL;
     572        2056 :                 packet->message_len = 0;
     573             :         }
     574             : 
     575        4136 :         DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
     576        2068 :                                 packet->affected_rows, packet->last_insert_id, packet->server_status,
     577        2068 :                                 packet->warning_count);
     578             : 
     579        2068 :         BAIL_IF_NO_MORE_DATA;
     580             : 
     581        2068 :         DBG_RETURN(PASS);
     582             : premature_end:
     583           0 :         DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
     584           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
     585           0 :                                          p - begin - packet->header.size);
     586           0 :         DBG_RETURN(FAIL);
     587             : }
     588             : /* }}} */
     589             : 
     590             : 
     591             : /* {{{ php_mysqlnd_ok_free_mem */
     592             : static void
     593        2115 : php_mysqlnd_ok_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     594             : {
     595        2115 :         MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
     596        2115 :         if (p->message) {
     597          12 :                 mnd_efree(p->message);
     598          12 :                 p->message = NULL;
     599             :         }
     600        2115 :         if (!stack_allocation) {
     601        2115 :                 mnd_pefree(p, p->header.persistent);
     602             :         }
     603        2115 : }
     604             : /* }}} */
     605             : 
     606             : 
     607             : /* {{{ php_mysqlnd_eof_read */
     608             : static enum_func_status
     609        7988 : php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     610             : {
     611             :         /*
     612             :           EOF packet is since 4.1 five bytes long,
     613             :           but we can get also an error, make it bigger.
     614             : 
     615             :           Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
     616             :         */
     617        7988 :         MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
     618        7988 :         size_t buf_len = conn->net->cmd_buffer.length;
     619        7988 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
     620        7988 :         zend_uchar *p = buf;
     621        7988 :         zend_uchar *begin = buf;
     622             : 
     623        7988 :         DBG_ENTER("php_mysqlnd_eof_read");
     624             : 
     625        7988 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
     626        7988 :         BAIL_IF_NO_MORE_DATA;
     627             : 
     628             :         /* Should be always EODATA_MARKER */
     629        7988 :         packet->field_count = uint1korr(p);
     630        7988 :         p++;
     631        7988 :         BAIL_IF_NO_MORE_DATA;
     632             : 
     633        7988 :         if (ERROR_MARKER == packet->field_count) {
     634           2 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     635             :                                                                                  packet->error, sizeof(packet->error),
     636             :                                                                                  &packet->error_no, packet->sqlstate
     637             :                                                                                  TSRMLS_CC);
     638           2 :                 DBG_RETURN(PASS);
     639             :         }
     640             : 
     641             :         /*
     642             :                 4.1 sends 1 byte EOF packet after metadata of
     643             :                 PREPARE/EXECUTE but 5 bytes after the result. This is not
     644             :                 according to the Docs@Forge!!!
     645             :         */
     646        7986 :         if (packet->header.size > 1) {
     647        7986 :                 packet->warning_count = uint2korr(p);
     648        7986 :                 p+= 2;
     649        7986 :                 BAIL_IF_NO_MORE_DATA;
     650             : 
     651        7986 :                 packet->server_status = uint2korr(p);
     652        7986 :                 p+= 2;
     653        7986 :                 BAIL_IF_NO_MORE_DATA;
     654             :         } else {
     655           0 :                 packet->warning_count = 0;
     656           0 :                 packet->server_status = 0;
     657             :         }
     658             : 
     659        7986 :         BAIL_IF_NO_MORE_DATA;
     660             : 
     661       23958 :         DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
     662       23958 :                                 packet->field_count, packet->server_status, packet->warning_count);
     663             : 
     664        7986 :         DBG_RETURN(PASS);
     665             : premature_end:
     666           0 :         DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
     667           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
     668           0 :                                          p - begin - packet->header.size);
     669           0 :         DBG_RETURN(FAIL);
     670             : }
     671             : /* }}} */
     672             : 
     673             : 
     674             : /* {{{ php_mysqlnd_eof_free_mem */
     675             : static
     676        7988 : void php_mysqlnd_eof_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     677             : {
     678        7988 :         if (!stack_allocation) {
     679        7988 :                 mnd_pefree(_packet, ((MYSQLND_PACKET_EOF *)_packet)->header.persistent);
     680             :         }
     681        7988 : }
     682             : /* }}} */
     683             : 
     684             : 
     685             : /* {{{ php_mysqlnd_cmd_write */
     686       35330 : size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
     687             : {
     688             :         /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
     689       35330 :         MYSQLND_PACKET_COMMAND *packet= (MYSQLND_PACKET_COMMAND *) _packet;
     690       35330 :         MYSQLND_NET *net = conn->net;
     691       35330 :         unsigned int error_reporting = EG(error_reporting);
     692       35330 :         size_t written = 0;
     693             : 
     694       35330 :         DBG_ENTER("php_mysqlnd_cmd_write");
     695             :         /*
     696             :           Reset packet_no, or we will get bad handshake!
     697             :           Every command starts a new TX and packet numbers are reset to 0.
     698             :         */
     699       35330 :         net->packet_no = 0;
     700       35330 :         net->compressed_envelope_packet_no = 0; /* this is for the response */
     701             : 
     702       35330 :         if (error_reporting) {
     703       34752 :                 EG(error_reporting) = 0;
     704             :         }
     705             : 
     706       35330 :         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
     707             : 
     708             : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
     709             :         net->m.consume_uneaten_data(net, packet->command TSRMLS_CC);
     710             : #endif
     711             : 
     712       37144 :         if (!packet->argument || !packet->arg_len) {
     713             :                 char buffer[MYSQLND_HEADER_SIZE + 1];
     714             : 
     715        1814 :                 int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
     716        1814 :                 written = conn->net->m.send(conn, buffer, 1 TSRMLS_CC);
     717             :         } else {
     718       33516 :                 size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
     719             :                 zend_uchar *tmp, *p;
     720       33516 :                 tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
     721       33516 :                 if (!tmp) {
     722           0 :                         goto end;
     723             :                 }
     724       33516 :                 p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
     725             : 
     726       33516 :                 int1store(p, packet->command);
     727       33516 :                 p++;
     728             : 
     729       33516 :                 memcpy(p, packet->argument, packet->arg_len);
     730             : 
     731       33516 :                 ret = conn->net->m.send(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
     732       33516 :                 if (tmp != net->cmd_buffer.buffer) {
     733          38 :                         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
     734          38 :                         mnd_efree(tmp);
     735             :                 }
     736       33516 :                 written = ret;
     737             :         }
     738             : end:
     739       35330 :         if (error_reporting) {
     740             :                 /* restore error reporting */
     741       34752 :                 EG(error_reporting) = error_reporting;
     742             :         }
     743       35330 :         DBG_RETURN(written);
     744             : }
     745             : /* }}} */
     746             : 
     747             : 
     748             : /* {{{ php_mysqlnd_cmd_free_mem */
     749             : static
     750       35330 : void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     751             : {
     752       35330 :         if (!stack_allocation) {
     753       35330 :                 MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet;
     754       35330 :                 mnd_pefree(p, p->header.persistent);
     755             :         }
     756       35330 : }
     757             : /* }}} */
     758             : 
     759             : 
     760             : /* {{{ php_mysqlnd_rset_header_read */
     761             : static enum_func_status
     762       24671 : php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     763             : {
     764       24671 :         enum_func_status ret = PASS;
     765       24671 :         size_t buf_len = conn->net->cmd_buffer.length;
     766       24671 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
     767       24671 :         zend_uchar *p = buf;
     768       24671 :         zend_uchar *begin = buf;
     769             :         size_t len;
     770       24671 :         MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
     771             : 
     772       24671 :         DBG_ENTER("php_mysqlnd_rset_header_read");
     773             : 
     774       24671 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
     775       24670 :         BAIL_IF_NO_MORE_DATA;
     776             : 
     777             :         /*
     778             :           Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
     779             :           of encoded sequence for length.
     780             :         */
     781       24670 :         if (ERROR_MARKER == *p) {
     782             :                 /* Error */
     783         777 :                 p++;
     784         777 :                 BAIL_IF_NO_MORE_DATA;
     785         777 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     786             :                                                                                  packet->error_info.error, sizeof(packet->error_info.error),
     787             :                                                                                  &packet->error_info.error_no, packet->error_info.sqlstate
     788             :                                                                                  TSRMLS_CC);
     789         777 :                 DBG_RETURN(PASS);
     790             :         }
     791             : 
     792       23893 :         packet->field_count = php_mysqlnd_net_field_length(&p);
     793       23893 :         BAIL_IF_NO_MORE_DATA;
     794             : 
     795       23892 :         switch (packet->field_count) {
     796             :                 case MYSQLND_NULL_LENGTH:
     797          12 :                         DBG_INF("LOAD LOCAL");
     798             :                         /*
     799             :                           First byte in the packet is the field count.
     800             :                           Thus, the name is size - 1. And we add 1 for a trailing \0.
     801             :                           Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
     802             :                           that packet->header.size is > 0. Which means that len can't underflow, that
     803             :                           would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
     804             :                         */
     805          12 :                         len = packet->header.size - 1;
     806          12 :                         packet->info_or_local_file = mnd_emalloc(len + 1);
     807          12 :                         if (packet->info_or_local_file) {
     808          12 :                                 memcpy(packet->info_or_local_file, p, len);
     809          12 :                                 packet->info_or_local_file[len] = '\0';
     810          12 :                                 packet->info_or_local_file_len = len;
     811             :                         } else {
     812           0 :                                 SET_OOM_ERROR(conn->error_info);
     813           0 :                                 ret = FAIL;
     814             :                         }
     815          12 :                         break;
     816             :                 case 0x00:
     817       19274 :                         DBG_INF("UPSERT");
     818       19274 :                         packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
     819       19274 :                         BAIL_IF_NO_MORE_DATA;
     820             : 
     821       19274 :                         packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
     822       19274 :                         BAIL_IF_NO_MORE_DATA;
     823             : 
     824       19274 :                         packet->server_status = uint2korr(p);
     825       19274 :                         p+=2;
     826       19274 :                         BAIL_IF_NO_MORE_DATA;
     827             : 
     828       19274 :                         packet->warning_count = uint2korr(p);
     829       19274 :                         p+=2;
     830       19274 :                         BAIL_IF_NO_MORE_DATA;
     831             :                         /* Check for additional textual data */
     832       19274 :                         if (packet->header.size  > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
     833         393 :                                 packet->info_or_local_file = mnd_emalloc(len + 1);
     834         393 :                                 if (packet->info_or_local_file) {
     835         393 :                                         memcpy(packet->info_or_local_file, p, len);
     836         393 :                                         packet->info_or_local_file[len] = '\0';
     837         393 :                                         packet->info_or_local_file_len = len;
     838             :                                 } else {
     839           0 :                                         SET_OOM_ERROR(conn->error_info);
     840           0 :                                         ret = FAIL;
     841             :                                 }
     842             :                         }
     843       38548 :                         DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u",
     844             :                                                 packet->affected_rows, packet->last_insert_id,
     845       38548 :                                                 packet->server_status, packet->warning_count);
     846       19274 :                         break;
     847             :                 default:
     848        4606 :                         DBG_INF("SELECT");
     849             :                         /* Result set */
     850             :                         break;
     851             :         }
     852       23892 :         BAIL_IF_NO_MORE_DATA;
     853             : 
     854       23892 :         DBG_RETURN(ret);
     855             : premature_end:
     856           1 :         DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
     857           1 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
     858           1 :                                          p - begin - packet->header.size);
     859           1 :         DBG_RETURN(FAIL);
     860             : }
     861             : /* }}} */
     862             : 
     863             : 
     864             : /* {{{ php_mysqlnd_rset_header_free_mem */
     865             : static
     866       24671 : void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
     867             : {
     868       24671 :         MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
     869       24671 :         DBG_ENTER("php_mysqlnd_rset_header_free_mem");
     870       24671 :         if (p->info_or_local_file) {
     871         405 :                 mnd_efree(p->info_or_local_file);
     872         405 :                 p->info_or_local_file = NULL;
     873             :         }
     874       24671 :         if (!stack_allocation) {
     875       24671 :                 mnd_pefree(p, p->header.persistent);
     876             :         }
     877       24671 :         DBG_VOID_RETURN;
     878             : }
     879             : /* }}} */
     880             : 
     881             : static size_t rset_field_offsets[] =
     882             : {
     883             :         STRUCT_OFFSET(MYSQLND_FIELD, catalog),
     884             :         STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
     885             :         STRUCT_OFFSET(MYSQLND_FIELD, db),
     886             :         STRUCT_OFFSET(MYSQLND_FIELD, db_length),
     887             :         STRUCT_OFFSET(MYSQLND_FIELD, table),
     888             :         STRUCT_OFFSET(MYSQLND_FIELD, table_length),
     889             :         STRUCT_OFFSET(MYSQLND_FIELD, org_table),
     890             :         STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
     891             :         STRUCT_OFFSET(MYSQLND_FIELD, name),
     892             :         STRUCT_OFFSET(MYSQLND_FIELD, name_length),
     893             :         STRUCT_OFFSET(MYSQLND_FIELD, org_name),
     894             :         STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
     895             : };
     896             : 
     897             : 
     898             : /* {{{ php_mysqlnd_rset_field_read */
     899             : static enum_func_status
     900      156154 : php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     901             : {
     902             :         /* Should be enough for the metadata of a single row */
     903      156154 :         MYSQLND_PACKET_RES_FIELD *packet= (MYSQLND_PACKET_RES_FIELD *) _packet;
     904      156154 :         size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
     905      156154 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
     906      156154 :         zend_uchar *p = buf;
     907      156154 :         zend_uchar *begin = buf;
     908             :         char *root_ptr;
     909             :         unsigned long len;
     910             :         MYSQLND_FIELD *meta;
     911      156154 :         unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
     912             : 
     913      156154 :         DBG_ENTER("php_mysqlnd_rset_field_read");
     914             : 
     915      156154 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
     916             : 
     917      156154 :         if (packet->skip_parsing) {
     918      132404 :                 DBG_RETURN(PASS);
     919             :         }
     920             : 
     921       23750 :         BAIL_IF_NO_MORE_DATA;
     922       23750 :         if (ERROR_MARKER == *p) {
     923             :                 /* Error */
     924           1 :                 p++;
     925           1 :                 BAIL_IF_NO_MORE_DATA;
     926           1 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     927             :                                                                                  packet->error_info.error, sizeof(packet->error_info.error),
     928             :                                                                                  &packet->error_info.error_no, packet->error_info.sqlstate
     929             :                                                                                  TSRMLS_CC);
     930           1 :                 DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
     931           1 :                 DBG_RETURN(PASS);
     932       23749 :         } else if (EODATA_MARKER == *p && packet->header.size < 8) {
     933             :                 /* Premature EOF. That should be COM_FIELD_LIST */
     934           3 :                 DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
     935           3 :                 packet->stupid_list_fields_eof = TRUE;
     936           3 :                 DBG_RETURN(PASS);
     937             :         }
     938             : 
     939       23746 :         meta = packet->metadata;
     940             : 
     941      166222 :         for (i = 0; i < field_count; i += 2) {
     942      142476 :                 len = php_mysqlnd_net_field_length(&p);
     943      142476 :                 BAIL_IF_NO_MORE_DATA;
     944      142476 :                 switch ((len)) {
     945             :                         case 0:
     946       23689 :                                 *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
     947       23689 :                                 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
     948       23689 :                                 break;
     949             :                         case MYSQLND_NULL_LENGTH:
     950           0 :                                 goto faulty_or_fake;
     951             :                         default:
     952      118787 :                                 *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p;
     953      118787 :                                 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
     954      118787 :                                 p += len;
     955      118787 :                                 total_len += len + 1;
     956             :                                 break;
     957             :                 }
     958      142476 :                 BAIL_IF_NO_MORE_DATA;
     959             :         }
     960             : 
     961             :         /* 1 byte length */
     962       23746 :         if (12 != *p) {
     963           0 :                 DBG_ERR_FMT("Protocol error. Server sent false length. Expected 12 got %d", (int) *p);
     964           0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent false length. Expected 12");
     965             :         }
     966       23746 :         p++;
     967       23746 :         BAIL_IF_NO_MORE_DATA;
     968             : 
     969       23746 :         meta->charsetnr = uint2korr(p);
     970       23746 :         p += 2;
     971       23746 :         BAIL_IF_NO_MORE_DATA;
     972             : 
     973       23746 :         meta->length = uint4korr(p);
     974       23746 :         p += 4;
     975       23746 :         BAIL_IF_NO_MORE_DATA;
     976             : 
     977       23746 :         meta->type = uint1korr(p);
     978       23746 :         p += 1;
     979       23746 :         BAIL_IF_NO_MORE_DATA;
     980             : 
     981       23746 :         meta->flags = uint2korr(p);
     982       23746 :         p += 2;
     983       23746 :         BAIL_IF_NO_MORE_DATA;
     984             : 
     985       23746 :         meta->decimals = uint1korr(p);
     986       23746 :         p += 1;
     987       23746 :         BAIL_IF_NO_MORE_DATA;
     988             : 
     989             :         /* 2 byte filler */
     990       23746 :         p +=2;
     991       23746 :         BAIL_IF_NO_MORE_DATA;
     992             : 
     993             :         /* Should we set NUM_FLAG (libmysql does it) ? */
     994       47678 :         if (
     995       23746 :                 (meta->type <= MYSQL_TYPE_INT24 &&
     996       10818 :                         (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
     997       13114 :                 ) || meta->type == MYSQL_TYPE_YEAR)
     998             :         {
     999       10679 :                 meta->flags |= NUM_FLAG;
    1000             :         }
    1001             : 
    1002             : 
    1003             :         /*
    1004             :           def could be empty, thus don't allocate on the root.
    1005             :           NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
    1006             :           Otherwise the string is length encoded.
    1007             :         */
    1008       23751 :         if (packet->header.size > (size_t) (p - buf) &&
    1009           5 :                 (len = php_mysqlnd_net_field_length(&p)) &&
    1010             :                 len != MYSQLND_NULL_LENGTH)
    1011             :         {
    1012           3 :                 BAIL_IF_NO_MORE_DATA;
    1013           3 :                 DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
    1014           3 :                 meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
    1015           3 :                 if (!meta->def) {
    1016           0 :                         SET_OOM_ERROR(conn->error_info);
    1017           0 :                         DBG_RETURN(FAIL);
    1018             :                 }
    1019           3 :                 memcpy(meta->def, p, len);
    1020           3 :                 meta->def[len] = '\0';
    1021           3 :                 meta->def_length = len;
    1022           3 :                 p += len;
    1023             :         }
    1024             : 
    1025       23746 :         DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc);
    1026       23746 :         root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
    1027       23746 :         if (!root_ptr) {
    1028           0 :                 SET_OOM_ERROR(conn->error_info);
    1029           0 :                 DBG_RETURN(FAIL);
    1030             :         }
    1031             : 
    1032       23746 :         meta->root_len = total_len;
    1033             :         /* Now do allocs */
    1034       23746 :         if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
    1035       23746 :                 len = meta->catalog_length;
    1036       23746 :                 meta->catalog = memcpy(root_ptr, meta->catalog, len);
    1037       23746 :                 *(root_ptr +=len) = '\0';
    1038       23746 :                 root_ptr++;
    1039             :         }
    1040             : 
    1041       23746 :         if (meta->db && meta->db != mysqlnd_empty_string) {
    1042       17609 :                 len = meta->db_length;
    1043       17609 :                 meta->db = memcpy(root_ptr, meta->db, len);
    1044       17609 :                 *(root_ptr +=len) = '\0';
    1045       17609 :                 root_ptr++;
    1046             :         }
    1047             : 
    1048       23746 :         if (meta->table && meta->table != mysqlnd_empty_string) {
    1049       18024 :                 len = meta->table_length;
    1050       18024 :                 meta->table = memcpy(root_ptr, meta->table, len);
    1051       18024 :                 *(root_ptr +=len) = '\0';
    1052       18024 :                 root_ptr++;
    1053             :         }
    1054             : 
    1055       23746 :         if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
    1056       17609 :                 len = meta->org_table_length;
    1057       17609 :                 meta->org_table = memcpy(root_ptr, meta->org_table, len);
    1058       17609 :                 *(root_ptr +=len) = '\0';
    1059       17609 :                 root_ptr++;
    1060             :         }
    1061             : 
    1062       23746 :         if (meta->name && meta->name != mysqlnd_empty_string) {
    1063       23737 :                 len = meta->name_length;
    1064       23737 :                 meta->name = memcpy(root_ptr, meta->name, len);
    1065       23737 :                 *(root_ptr +=len) = '\0';
    1066       23737 :                 root_ptr++;
    1067             :         }
    1068             : 
    1069       23746 :         if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
    1070       18062 :                 len = meta->org_name_length;
    1071       18062 :                 meta->org_name = memcpy(root_ptr, meta->org_name, len);
    1072       18062 :                 *(root_ptr +=len) = '\0';
    1073       18062 :                 root_ptr++;
    1074             :         }
    1075             : 
    1076       23746 :         DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
    1077       23746 :                                 meta->name? meta->name:"*NA*");
    1078             : 
    1079       23746 :         DBG_RETURN(PASS);
    1080             : 
    1081             : faulty_or_fake:
    1082           0 :         DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
    1083           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
    1084             :                                          " The server is faulty");
    1085           0 :         DBG_RETURN(FAIL);
    1086             : premature_end:
    1087           0 :         DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
    1088           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
    1089           0 :                                         "shorter than expected", p - begin - packet->header.size);
    1090           0 :         DBG_RETURN(FAIL);
    1091             : }
    1092             : /* }}} */
    1093             : 
    1094             : 
    1095             : /* {{{ php_mysqlnd_rset_field_free_mem */
    1096             : static
    1097        7858 : void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
    1098             : {
    1099        7858 :         MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet;
    1100             :         /* p->metadata was passed to us as temporal buffer */
    1101        7858 :         if (!stack_allocation) {
    1102        7858 :                 mnd_pefree(p, p->header.persistent);
    1103             :         }
    1104        7858 : }
    1105             : /* }}} */
    1106             : 
    1107             : 
    1108             : /* {{{ php_mysqlnd_read_row_ex */
    1109             : static enum_func_status
    1110       81337 : php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
    1111             :                                                 MYSQLND_MEMORY_POOL_CHUNK **buffer,
    1112             :                                                 size_t *data_size, zend_bool persistent_alloc,
    1113             :                                                 unsigned int prealloc_more_bytes TSRMLS_DC)
    1114             : {
    1115       81337 :         enum_func_status ret = PASS;
    1116             :         MYSQLND_PACKET_HEADER header;
    1117       81337 :         zend_uchar *p = NULL;
    1118       81337 :         zend_bool first_iteration = TRUE;
    1119             : 
    1120       81337 :         DBG_ENTER("php_mysqlnd_read_row_ex");
    1121             : 
    1122             :         /*
    1123             :           To ease the process the server splits everything in packets up to 2^24 - 1.
    1124             :           Even in the case the payload is evenly divisible by this value, the last
    1125             :           packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
    1126             :           for next one if they have 2^24 - 1 sizes. But just read the header of a
    1127             :           zero-length byte, don't read the body, there is no such.
    1128             :         */
    1129             : 
    1130       81337 :         *data_size = prealloc_more_bytes;
    1131             :         while (1) {
    1132       81337 :                 if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
    1133           2 :                         ret = FAIL;
    1134           2 :                         break;
    1135             :                 }
    1136             : 
    1137       81335 :                 *data_size += header.size;
    1138             : 
    1139       81335 :                 if (first_iteration) {
    1140       81335 :                         first_iteration = FALSE;
    1141             :                         /*
    1142             :                           We need a trailing \0 for the last string, in case of text-mode,
    1143             :                           to be able to implement read-only variables. Thus, we add + 1.
    1144             :                         */
    1145       81335 :                         *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
    1146       81335 :                         if (!*buffer) {
    1147           0 :                                 ret = FAIL;
    1148           0 :                                 break;
    1149             :                         }
    1150       81335 :                         p = (*buffer)->ptr;
    1151           0 :                 } else if (!first_iteration) {
    1152             :                         /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
    1153           0 :                         if (!header.size) {
    1154           0 :                                 break;
    1155             :                         }
    1156             : 
    1157             :                         /*
    1158             :                           We have to realloc the buffer.
    1159             : 
    1160             :                           We need a trailing \0 for the last string, in case of text-mode,
    1161             :                           to be able to implement read-only variables.
    1162             :                         */
    1163           0 :                         if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) {
    1164           0 :                                 SET_OOM_ERROR(conn->error_info);
    1165           0 :                                 ret = FAIL;
    1166           0 :                                 break;
    1167             :                         }
    1168             :                         /* The position could have changed, recalculate */
    1169           0 :                         p = (*buffer)->ptr + (*data_size - header.size);
    1170             :                 }
    1171             : 
    1172       81335 :                 if (PASS != (ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) {
    1173           0 :                         DBG_ERR("Empty row packet body");
    1174           0 :                         php_error(E_WARNING, "Empty row packet body");
    1175           0 :                         break;
    1176             :                 }
    1177             : 
    1178       81335 :                 if (header.size < MYSQLND_MAX_PACKET_SIZE) {
    1179       81335 :                         break;
    1180             :                 }
    1181           0 :         }
    1182       81337 :         if (ret == FAIL && *buffer) {
    1183           0 :                 (*buffer)->free_chunk((*buffer) TSRMLS_CC);
    1184           0 :                 *buffer = NULL;
    1185             :         }
    1186       81337 :         *data_size -= prealloc_more_bytes;
    1187       81337 :         DBG_RETURN(ret);
    1188             : }
    1189             : /* }}} */
    1190             : 
    1191             : 
    1192             : /* {{{ php_mysqlnd_rowp_read_binary_protocol */
    1193             : enum_func_status
    1194       20077 : php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
    1195             :                                                                           unsigned int field_count, MYSQLND_FIELD *fields_metadata,
    1196             :                                                                           zend_bool persistent,
    1197             :                                                                           zend_bool as_unicode, zend_bool as_int_or_float,
    1198             :                                                                           MYSQLND_STATS * stats TSRMLS_DC)
    1199             : {
    1200             :         unsigned int i;
    1201       20077 :         zend_uchar *p = row_buffer->ptr;
    1202             :         zend_uchar *null_ptr, bit;
    1203             :         zval **current_field, **end_field, **start_field;
    1204             : 
    1205       20077 :         DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
    1206             : 
    1207       20077 :         if (!fields) {
    1208           0 :                 DBG_RETURN(FAIL);
    1209             :         }
    1210             : 
    1211       20077 :         end_field = (start_field = fields) + field_count;
    1212             : 
    1213             :         /* skip the first byte, not EODATA_MARKER -> 0x0, status */
    1214       20077 :         p++;
    1215       20077 :         null_ptr= p;
    1216       20077 :         p += (field_count + 9)/8;       /* skip null bits */
    1217       20077 :         bit     = 4;                                    /* first 2 bits are reserved */
    1218             : 
    1219       64242 :         for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
    1220       44165 :                 DBG_INF("Directly creating zval");
    1221       44165 :                 MAKE_STD_ZVAL(*current_field);
    1222       44165 :                 if (!*current_field) {
    1223           0 :                         DBG_RETURN(FAIL);
    1224             :                 }
    1225             :         }
    1226             : 
    1227       64242 :         for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
    1228             :                 enum_mysqlnd_collected_stats statistic;
    1229       44165 :                 zend_uchar * orig_p = p;
    1230             : 
    1231      309155 :                 DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u",
    1232             :                         *current_field, i,
    1233      132495 :                         fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
    1234      132495 :                         fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
    1235       44165 :                 if (*null_ptr & bit) {
    1236        1153 :                         DBG_INF("It's null");
    1237        1153 :                         ZVAL_NULL(*current_field);
    1238        1153 :                         statistic = STAT_BINARY_TYPE_FETCHED_NULL;
    1239             :                 } else {
    1240       43012 :                         enum_mysqlnd_field_types type = fields_metadata[i].type;
    1241       43012 :                         mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC);
    1242             : 
    1243       43012 :                         if (MYSQLND_G(collect_statistics)) {
    1244       43012 :                                 switch (fields_metadata[i].type) {
    1245           0 :                                         case MYSQL_TYPE_DECIMAL:        statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
    1246          60 :                                         case MYSQL_TYPE_TINY:           statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
    1247          43 :                                         case MYSQL_TYPE_SHORT:          statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
    1248       19051 :                                         case MYSQL_TYPE_LONG:           statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
    1249         141 :                                         case MYSQL_TYPE_FLOAT:          statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
    1250          60 :                                         case MYSQL_TYPE_DOUBLE:         statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
    1251           0 :                                         case MYSQL_TYPE_NULL:           statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
    1252          17 :                                         case MYSQL_TYPE_TIMESTAMP:      statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
    1253        2048 :                                         case MYSQL_TYPE_LONGLONG:       statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
    1254          28 :                                         case MYSQL_TYPE_INT24:          statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
    1255          19 :                                         case MYSQL_TYPE_DATE:           statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
    1256          19 :                                         case MYSQL_TYPE_TIME:           statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
    1257          19 :                                         case MYSQL_TYPE_DATETIME:       statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
    1258          19 :                                         case MYSQL_TYPE_YEAR:           statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
    1259           1 :                                         case MYSQL_TYPE_NEWDATE:        statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
    1260           0 :                                         case MYSQL_TYPE_VARCHAR:        statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1261        1487 :                                         case MYSQL_TYPE_BIT:            statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
    1262          61 :                                         case MYSQL_TYPE_NEWDECIMAL:     statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
    1263           0 :                                         case MYSQL_TYPE_ENUM:           statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
    1264           0 :                                         case MYSQL_TYPE_SET:            statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
    1265           0 :                                         case MYSQL_TYPE_TINY_BLOB:      statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1266           9 :                                         case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1267           0 :                                         case MYSQL_TYPE_LONG_BLOB:      statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1268        1164 :                                         case MYSQL_TYPE_BLOB:           statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1269       18438 :                                         case MYSQL_TYPE_VAR_STRING:     statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1270         280 :                                         case MYSQL_TYPE_STRING:         statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1271          48 :                                         case MYSQL_TYPE_GEOMETRY:       statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
    1272           0 :                                         default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
    1273             :                                 }
    1274             :                         }
    1275             :                 }
    1276       44165 :                 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
    1277             :                                                                                 STAT_BYTES_RECEIVED_PURE_DATA_PS,
    1278             :                                                                                 (Z_TYPE_PP(current_field) == IS_STRING)?
    1279             :                                                                                         Z_STRLEN_PP(current_field) : (p - orig_p));
    1280             : 
    1281       44165 :                 if (!((bit<<=1) & 255)) {
    1282         162 :                         bit = 1;        /* to the following byte */
    1283         162 :                         null_ptr++;
    1284             :                 }
    1285             :         }
    1286             : 
    1287       20077 :         DBG_RETURN(PASS);
    1288             : }
    1289             : /* }}} */
    1290             : 
    1291             : 
    1292             : /* {{{ php_mysqlnd_rowp_read_text_protocol */
    1293             : enum_func_status
    1294       55001 : php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
    1295             :                                                                         unsigned int field_count, MYSQLND_FIELD *fields_metadata,
    1296             :                                                                         zend_bool persistent,
    1297             :                                                                         zend_bool as_unicode, zend_bool as_int_or_float,
    1298             :                                                                         MYSQLND_STATS * stats TSRMLS_DC)
    1299             : {
    1300             :         unsigned int i;
    1301       55001 :         zend_bool last_field_was_string = FALSE;
    1302             :         zval **current_field, **end_field, **start_field;
    1303       55001 :         zend_uchar *p = row_buffer->ptr;
    1304       55001 :         size_t data_size = row_buffer->app;
    1305       55001 :         zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
    1306             : 
    1307       55001 :         DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
    1308             : 
    1309       55001 :         if (!fields) {
    1310           0 :                 DBG_RETURN(FAIL);
    1311             :         }
    1312             : 
    1313       55001 :         end_field = (start_field = fields) + field_count;
    1314             : 
    1315      167116 :         for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
    1316      112115 :                 DBG_INF("Directly creating zval");
    1317      112115 :                 MAKE_STD_ZVAL(*current_field);
    1318      112115 :                 if (!*current_field) {
    1319           0 :                         DBG_RETURN(FAIL);
    1320             :                 }
    1321             :         }
    1322             : 
    1323      167116 :         for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
    1324             :                 /* Don't reverse the order. It is significant!*/
    1325      112115 :                 zend_uchar *this_field_len_pos = p;
    1326             :                 /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
    1327      112115 :                 unsigned long len = php_mysqlnd_net_field_length(&p);
    1328             : 
    1329      112115 :                 if (current_field > start_field && last_field_was_string) {
    1330             :                         /*
    1331             :                           Normal queries:
    1332             :                           We have to put \0 now to the end of the previous field, if it was
    1333             :                           a string. IS_NULL doesn't matter. Because we have already read our
    1334             :                           length, then we can overwrite it in the row buffer.
    1335             :                           This statement terminates the previous field, not the current one.
    1336             : 
    1337             :                           NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
    1338             :                           Any string's length is encoded in at least one byte, so we can stick
    1339             :                           a \0 there.
    1340             :                         */
    1341             : 
    1342       57062 :                         *this_field_len_pos = '\0';
    1343             :                 }
    1344             : 
    1345             :                 /* NULL or NOT NULL, this is the question! */
    1346      112115 :                 if (len == MYSQLND_NULL_LENGTH) {
    1347         734 :                         ZVAL_NULL(*current_field);
    1348         734 :                         last_field_was_string = FALSE;
    1349             :                 } else {
    1350             : #if MYSQLND_UNICODE || defined(MYSQLND_STRING_TO_INT_CONVERSION)
    1351             :                         struct st_mysqlnd_perm_bind perm_bind =
    1352      111381 :                                         mysqlnd_ps_fetch_functions[fields_metadata[i].type];
    1353             : #endif
    1354      111381 :                         if (MYSQLND_G(collect_statistics)) {
    1355             :                                 enum_mysqlnd_collected_stats statistic;
    1356      111357 :                                 switch (fields_metadata[i].type) {
    1357           0 :                                         case MYSQL_TYPE_DECIMAL:        statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
    1358          41 :                                         case MYSQL_TYPE_TINY:           statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
    1359          40 :                                         case MYSQL_TYPE_SHORT:          statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
    1360       54141 :                                         case MYSQL_TYPE_LONG:           statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
    1361          35 :                                         case MYSQL_TYPE_FLOAT:          statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
    1362          35 :                                         case MYSQL_TYPE_DOUBLE:         statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
    1363           0 :                                         case MYSQL_TYPE_NULL:           statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
    1364          30 :                                         case MYSQL_TYPE_TIMESTAMP:      statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
    1365        1342 :                                         case MYSQL_TYPE_LONGLONG:       statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
    1366          27 :                                         case MYSQL_TYPE_INT24:          statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
    1367          16 :                                         case MYSQL_TYPE_DATE:           statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
    1368          16 :                                         case MYSQL_TYPE_TIME:           statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
    1369          18 :                                         case MYSQL_TYPE_DATETIME:       statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
    1370          16 :                                         case MYSQL_TYPE_YEAR:           statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
    1371           1 :                                         case MYSQL_TYPE_NEWDATE:        statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
    1372           0 :                                         case MYSQL_TYPE_VARCHAR:        statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1373         464 :                                         case MYSQL_TYPE_BIT:            statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
    1374          35 :                                         case MYSQL_TYPE_NEWDECIMAL:     statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
    1375           0 :                                         case MYSQL_TYPE_ENUM:           statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
    1376           0 :                                         case MYSQL_TYPE_SET:            statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
    1377           0 :                                         case MYSQL_TYPE_TINY_BLOB:      statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1378          20 :                                         case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1379           0 :                                         case MYSQL_TYPE_LONG_BLOB:      statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1380         169 :                                         case MYSQL_TYPE_BLOB:           statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1381       18440 :                                         case MYSQL_TYPE_VAR_STRING:     statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1382       36423 :                                         case MYSQL_TYPE_STRING:         statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1383          48 :                                         case MYSQL_TYPE_GEOMETRY:       statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
    1384           0 :                                         default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
    1385             :                                 }
    1386      111357 :                                 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
    1387             :                         }
    1388             : #ifdef MYSQLND_STRING_TO_INT_CONVERSION
    1389      111389 :                         if (as_int_or_float && perm_bind.php_type == IS_LONG) {
    1390           8 :                                 zend_uchar save = *(p + len);
    1391             :                                 /* We have to make it ASCIIZ temporarily */
    1392           8 :                                 *(p + len) = '\0';
    1393           8 :                                 if (perm_bind.pack_len < SIZEOF_LONG) {
    1394             :                                         /* direct conversion */
    1395             :                                         int64_t v =
    1396             : #ifndef PHP_WIN32
    1397           6 :                                                 atoll((char *) p);
    1398             : #else
    1399             :                                                 _atoi64((char *) p);
    1400             : #endif
    1401           6 :                                         ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
    1402             :                                 } else {
    1403             :                                         uint64_t v =
    1404             : #ifndef PHP_WIN32
    1405           2 :                                                 (uint64_t) atoll((char *) p);
    1406             : #else
    1407             :                                                 (uint64_t) _atoi64((char *) p);
    1408             : #endif
    1409           2 :                                         zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
    1410             :                                         /* We have to make it ASCIIZ temporarily */
    1411             : #if SIZEOF_LONG==8
    1412           2 :                                         if (uns == TRUE && v > 9223372036854775807L)
    1413             : #elif SIZEOF_LONG==4
    1414             :                                         if ((uns == TRUE && v > L64(2147483647)) ||
    1415             :                                                 (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
    1416             :                                                 (L64(-2147483648) > (int64_t) v))))
    1417             : #else
    1418             : #error Need fix for this architecture
    1419             : #endif /* SIZEOF */
    1420             :                                         {
    1421           0 :                                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1422             :                                         } else {
    1423           2 :                                                 ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
    1424             :                                         }
    1425             :                                 }
    1426           8 :                                 *(p + len) = save;
    1427      111375 :                         } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
    1428           2 :                                 zend_uchar save = *(p + len);
    1429             :                                 /* We have to make it ASCIIZ temporarily */
    1430           2 :                                 *(p + len) = '\0';
    1431           2 :                                 ZVAL_DOUBLE(*current_field, atof((char *) p));
    1432           2 :                                 *(p + len) = save;
    1433             :                         } else
    1434             : #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
    1435      111371 :                         if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
    1436             :                                 /*
    1437             :                                   BIT fields are specially handled. As they come as bit mask, we have
    1438             :                                   to convert it to human-readable representation. As the bits take
    1439             :                                   less space in the protocol than the numbers they represent, we don't
    1440             :                                   have enough space in the packet buffer to overwrite inside.
    1441             :                                   Thus, a bit more space is pre-allocated at the end of the buffer,
    1442             :                                   see php_mysqlnd_rowp_read(). And we add the strings at the end.
    1443             :                                   Definitely not nice, _hackish_ :(, but works.
    1444             :                                 */
    1445         463 :                                 zend_uchar *start = bit_area;
    1446         463 :                                 ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC);
    1447             :                                 /*
    1448             :                                   We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
    1449             :                                   later in this function there will be an advancement.
    1450             :                                 */
    1451         463 :                                 p -= len;
    1452         463 :                                 if (Z_TYPE_PP(current_field) == IS_LONG) {
    1453         463 :                                         bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
    1454             : #if MYSQLND_UNICODE
    1455             :                                         if (as_unicode) {
    1456             :                                                 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
    1457             :                                         } else
    1458             : #endif
    1459             :                                         {
    1460         463 :                                                 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
    1461             :                                         }
    1462           0 :                                 } else if (Z_TYPE_PP(current_field) == IS_STRING){
    1463           0 :                                         memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
    1464           0 :                                         bit_area += Z_STRLEN_PP(current_field);
    1465           0 :                                         *bit_area++ = '\0';
    1466           0 :                                         zval_dtor(*current_field);
    1467             : #if MYSQLND_UNICODE
    1468             :                                         if (as_unicode) {
    1469             :                                                 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
    1470             :                                         } else
    1471             : #endif
    1472             :                                         {
    1473           0 :                                                 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
    1474             :                                         }
    1475             :                                 }
    1476             :                                 /*
    1477             :                                   IS_UNICODE should not be specially handled. In unicode mode
    1478             :                                   the buffers are not referenced - everything is copied.
    1479             :                                 */
    1480             :                         } else
    1481             : #if MYSQLND_UNICODE == 0
    1482             :                         {
    1483      110908 :                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1484             :                         }
    1485             : #else
    1486             :                         /*
    1487             :                           Here we have to convert to UTF16, which means not reusing the buffer.
    1488             :                           Which in turn means that we can free the buffers once we have
    1489             :                           stored the result set, if we use store_result().
    1490             : 
    1491             :                           Also the destruction of the zvals should not call zval_copy_ctor()
    1492             :                           because then we will leak.
    1493             : 
    1494             :                           XXX: Keep in mind that up there there is an open `else` in
    1495             :                           #ifdef MYSQLND_STRING_TO_INT_CONVERSION
    1496             :                           which will make with this `if` an `else if`.
    1497             :                         */
    1498             :                         if ((perm_bind.is_possibly_blob == TRUE &&
    1499             :                                  fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
    1500             :                                 (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
    1501             :                         {
    1502             :                                 /* BLOB - no conversion please */
    1503             :                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1504             :                         } else {
    1505             :                                 ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
    1506             :                         }
    1507             : #endif
    1508      111381 :                         p += len;
    1509      111381 :                         last_field_was_string = TRUE;
    1510             :                 }
    1511             :         }
    1512       55001 :         if (last_field_was_string) {
    1513             :                 /* Normal queries: The buffer has one more byte at the end, because we need it */
    1514       54319 :                 row_buffer->ptr[data_size] = '\0';
    1515             :         }
    1516             : 
    1517       55001 :         DBG_RETURN(PASS);
    1518             : }
    1519             : /* }}} */
    1520             : 
    1521             : 
    1522             : /* {{{ php_mysqlnd_rowp_read */
    1523             : /*
    1524             :   if normal statements => packet->fields is created by this function,
    1525             :   if PS => packet->fields is passed from outside
    1526             : */
    1527             : static enum_func_status
    1528       81337 : php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1529             : {
    1530       81337 :         MYSQLND_NET *net = conn->net;
    1531             :         zend_uchar *p;
    1532       81337 :         enum_func_status ret = PASS;
    1533       81337 :         size_t old_chunk_size = net->stream->chunk_size;
    1534       81337 :         MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
    1535       81337 :         size_t post_alloc_for_bit_fields = 0;
    1536       81337 :         size_t data_size = 0;
    1537             : 
    1538       81337 :         DBG_ENTER("php_mysqlnd_rowp_read");
    1539             : 
    1540       81337 :         if (!packet->binary_protocol && packet->bit_fields_count) {
    1541             :                 /* For every field we need terminating \0 */
    1542         969 :                 post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
    1543             :         }
    1544             : 
    1545      162674 :         ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
    1546       81337 :                                                                   packet->persistent_alloc, post_alloc_for_bit_fields
    1547             :                                                                   TSRMLS_CC);
    1548       81337 :         if (FAIL == ret) {
    1549           2 :                 goto end;
    1550             :         }
    1551       81335 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
    1552             :                                                                                 MYSQLND_HEADER_SIZE + packet->header.size,
    1553             :                                                                                 packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
    1554             :                                                                                 1);
    1555             : 
    1556             :         /* packet->row_buffer->ptr is of size 'data_size + 1' */
    1557       81335 :         packet->header.size = data_size;
    1558       81335 :         packet->row_buffer->app = data_size;
    1559             : 
    1560       81335 :         if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
    1561             :                 /*
    1562             :                    Error message as part of the result set,
    1563             :                    not good but we should not hang. See:
    1564             :                    Bug #27876 : SF with cyrillic variable name fails during execution
    1565             :                 */
    1566           0 :                 ret = FAIL;
    1567           0 :                 php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
    1568             :                                                                                  packet->error_info.error,
    1569             :                                                                                  sizeof(packet->error_info.error),
    1570             :                                                                                  &packet->error_info.error_no,
    1571             :                                                                                  packet->error_info.sqlstate
    1572             :                                                                                  TSRMLS_CC);
    1573       85948 :         } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
    1574        4613 :                 packet->eof = TRUE;
    1575        4613 :                 p++;
    1576        4613 :                 if (data_size > 1) {
    1577        4613 :                         packet->warning_count = uint2korr(p);
    1578        4613 :                         p += 2;
    1579        4613 :                         packet->server_status = uint2korr(p);
    1580             :                         /* Seems we have 3 bytes reserved for future use */
    1581        4613 :                         DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
    1582             :                 }
    1583             :         } else {
    1584       76722 :                 MYSQLND_INC_CONN_STATISTIC(conn->stats,
    1585             :                                                                         packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
    1586             :                                                                                                                          STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
    1587             : 
    1588       76722 :                 packet->eof = FALSE;
    1589             :                 /* packet->field_count is set by the user of the packet */
    1590             : 
    1591       76722 :                 if (!packet->skip_extraction) {
    1592       17685 :                         if (!packet->fields) {
    1593       17685 :                                 DBG_INF("Allocating packet->fields");
    1594             :                                 /*
    1595             :                                   old-API will probably set packet->fields to NULL every time, though for
    1596             :                                   unbuffered sets it makes not much sense as the zvals in this buffer matter,
    1597             :                                   not the buffer. Constantly allocating and deallocating brings nothing.
    1598             : 
    1599             :                                   For PS - if stmt_store() is performed, thus we don't have a cursor, it will
    1600             :                                   behave just like old-API buffered. Cursors will behave like a bit different,
    1601             :                                   but mostly like old-API unbuffered and thus will populate this array with
    1602             :                                   value.
    1603             :                                 */
    1604       17685 :                                 packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
    1605             :                                                                                                                 packet->persistent_alloc);
    1606             :                         }
    1607             :                 } else {
    1608       59037 :                         MYSQLND_INC_CONN_STATISTIC(conn->stats,
    1609             :                                                                                 packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
    1610             :                                                                                                                                  STAT_ROWS_SKIPPED_NORMAL);
    1611             :                 }
    1612             :         }
    1613             : 
    1614             : end:
    1615       81337 :         net->stream->chunk_size = old_chunk_size;
    1616       81337 :         DBG_RETURN(ret);
    1617             : }
    1618             : /* }}} */
    1619             : 
    1620             : 
    1621             : /* {{{ php_mysqlnd_rowp_free_mem */
    1622             : static void
    1623        4598 : php_mysqlnd_rowp_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
    1624             : {
    1625             :         MYSQLND_PACKET_ROW *p;
    1626             : 
    1627        4598 :         DBG_ENTER("php_mysqlnd_rowp_free_mem");
    1628        4598 :         p = (MYSQLND_PACKET_ROW *) _packet;
    1629        4598 :         if (p->row_buffer) {
    1630        4589 :                 p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC);
    1631        4589 :                 p->row_buffer = NULL;
    1632             :         }
    1633        4598 :         DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
    1634             :         /*
    1635             :           Don't free packet->fields :
    1636             :           - normal queries -> store_result() | fetch_row_unbuffered() will transfer
    1637             :             the ownership and NULL it.
    1638             :           - PS will pass in it the bound variables, we have to use them! and of course
    1639             :             not free the array. As it is passed to us, we should not clean it ourselves.
    1640             :         */
    1641        4598 :         if (!stack_allocation) {
    1642        4598 :                 mnd_pefree(p, p->header.persistent);
    1643             :         }
    1644        4598 :         DBG_VOID_RETURN;
    1645             : }
    1646             : /* }}} */
    1647             : 
    1648             : 
    1649             : /* {{{ php_mysqlnd_stats_read */
    1650             : static enum_func_status
    1651          16 : php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1652             : {
    1653          16 :         MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
    1654          16 :         size_t buf_len = conn->net->cmd_buffer.length;
    1655          16 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
    1656             : 
    1657          16 :         DBG_ENTER("php_mysqlnd_stats_read");
    1658             : 
    1659          16 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
    1660             : 
    1661          16 :         packet->message = mnd_emalloc(packet->header.size + 1);
    1662          16 :         memcpy(packet->message, buf, packet->header.size);
    1663          16 :         packet->message[packet->header.size] = '\0';
    1664          16 :         packet->message_len = packet->header.size;
    1665             : 
    1666          16 :         DBG_RETURN(PASS);
    1667             : }
    1668             : /* }}} */
    1669             : 
    1670             : 
    1671             : /* {{{ php_mysqlnd_stats_free_mem */
    1672             : static
    1673          16 : void php_mysqlnd_stats_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
    1674             : {
    1675          16 :         MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
    1676          16 :         if (p->message) {
    1677          16 :                 mnd_efree(p->message);
    1678          16 :                 p->message = NULL;
    1679             :         }
    1680          16 :         if (!stack_allocation) {
    1681          16 :                 mnd_pefree(p, p->header.persistent);
    1682             :         }
    1683          16 : }
    1684             : /* }}} */
    1685             : 
    1686             : 
    1687             : /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
    1688             : #define PREPARE_RESPONSE_SIZE_41 9
    1689             : #define PREPARE_RESPONSE_SIZE_50 12
    1690             : 
    1691             : /* {{{ php_mysqlnd_prepare_read */
    1692             : static enum_func_status
    1693        4234 : php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1694             : {
    1695             :         /* In case of an error, we should have place to put it */
    1696        4234 :         size_t buf_len = conn->net->cmd_buffer.length;
    1697        4234 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
    1698        4234 :         zend_uchar *p = buf;
    1699        4234 :         zend_uchar *begin = buf;
    1700             :         unsigned int data_size;
    1701        4234 :         MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
    1702             : 
    1703        4234 :         DBG_ENTER("php_mysqlnd_prepare_read");
    1704             : 
    1705        4234 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
    1706        4233 :         BAIL_IF_NO_MORE_DATA;
    1707             : 
    1708        4233 :         data_size = packet->header.size;
    1709        4233 :         packet->error_code = uint1korr(p);
    1710        4233 :         p++;
    1711        4233 :         BAIL_IF_NO_MORE_DATA;
    1712             : 
    1713        4233 :         if (ERROR_MARKER == packet->error_code) {
    1714          26 :                 php_mysqlnd_read_error_from_line(p, data_size - 1,
    1715             :                                                                                  packet->error_info.error,
    1716             :                                                                                  sizeof(packet->error_info.error),
    1717             :                                                                                  &packet->error_info.error_no,
    1718             :                                                                                  packet->error_info.sqlstate
    1719             :                                                                                  TSRMLS_CC);
    1720          26 :                 DBG_RETURN(PASS);
    1721             :         }
    1722             : 
    1723        4207 :         if (data_size != PREPARE_RESPONSE_SIZE_41 &&
    1724             :                 data_size != PREPARE_RESPONSE_SIZE_50 &&
    1725             :                 !(data_size > PREPARE_RESPONSE_SIZE_50)) {
    1726           0 :                 DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
    1727           0 :                 php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
    1728           0 :                 DBG_RETURN(FAIL);
    1729             :         }
    1730             : 
    1731        4207 :         packet->stmt_id = uint4korr(p);
    1732        4207 :         p += 4;
    1733        4207 :         BAIL_IF_NO_MORE_DATA;
    1734             : 
    1735             :         /* Number of columns in result set */
    1736        4207 :         packet->field_count = uint2korr(p);
    1737        4207 :         p += 2;
    1738        4207 :         BAIL_IF_NO_MORE_DATA;
    1739             : 
    1740        4207 :         packet->param_count = uint2korr(p);
    1741        4207 :         p += 2;
    1742        4207 :         BAIL_IF_NO_MORE_DATA;
    1743             : 
    1744        4207 :         if (data_size > 9) {
    1745             :                 /* 0x0 filler sent by the server for 5.0+ clients */
    1746        4207 :                 p++;
    1747        4207 :                 BAIL_IF_NO_MORE_DATA;
    1748             : 
    1749        4207 :                 packet->warning_count = uint2korr(p);
    1750             :         }
    1751             : 
    1752        4207 :         DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u",
    1753             :                                 packet->stmt_id, packet->field_count, packet->param_count);
    1754             : 
    1755        4207 :         BAIL_IF_NO_MORE_DATA;
    1756             : 
    1757        4207 :         DBG_RETURN(PASS);
    1758             : premature_end:
    1759           0 :         DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
    1760           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    1761           0 :                                          p - begin - packet->header.size);
    1762           0 :         DBG_RETURN(FAIL);
    1763             : }
    1764             : /* }}} */
    1765             : 
    1766             : 
    1767             : /* {{{ php_mysqlnd_prepare_free_mem */
    1768             : static void
    1769        4234 : php_mysqlnd_prepare_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
    1770             : {
    1771        4234 :         MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
    1772        4234 :         if (!stack_allocation) {
    1773        4234 :                 mnd_pefree(p, p->header.persistent);
    1774             :         }
    1775        4234 : }
    1776             : /* }}} */
    1777             : 
    1778             : 
    1779             : /* {{{ php_mysqlnd_chg_user_read */
    1780             : static enum_func_status
    1781          68 : php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1782             : {
    1783             :         /* There could be an error message */
    1784          68 :         size_t buf_len = conn->net->cmd_buffer.length;
    1785          68 :         zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
    1786          68 :         zend_uchar *p = buf;
    1787          68 :         zend_uchar *begin = buf;
    1788          68 :         MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
    1789             : 
    1790          68 :         DBG_ENTER("php_mysqlnd_chg_user_read");
    1791             : 
    1792          68 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
    1793          68 :         BAIL_IF_NO_MORE_DATA;
    1794             : 
    1795             :         /*
    1796             :           Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
    1797             :           of encoded sequence for length.
    1798             :         */
    1799             : 
    1800             :         /* Should be always 0x0 or ERROR_MARKER for error */
    1801          68 :         packet->field_count= uint1korr(p);
    1802          68 :         p++;
    1803             : 
    1804          68 :         if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
    1805             :                 /* We don't handle 3.23 authentication */
    1806           0 :                 packet->server_asked_323_auth = TRUE;
    1807           0 :                 DBG_RETURN(FAIL);
    1808             :         }
    1809             : 
    1810          68 :         if (ERROR_MARKER == packet->field_count) {
    1811          12 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    1812             :                                                                                  packet->error_info.error,
    1813             :                                                                                  sizeof(packet->error_info.error),
    1814             :                                                                                  &packet->error_info.error_no,
    1815             :                                                                                  packet->error_info.sqlstate
    1816             :                                                                                  TSRMLS_CC);
    1817             :         }
    1818          68 :         BAIL_IF_NO_MORE_DATA;
    1819             : 
    1820          68 :         DBG_RETURN(PASS);
    1821             : premature_end:
    1822           0 :         DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
    1823           0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    1824           0 :                                                  p - begin - packet->header.size);
    1825           0 :         DBG_RETURN(FAIL);
    1826             : }
    1827             : /* }}} */
    1828             : 
    1829             : 
    1830             : /* {{{ php_mysqlnd_chg_user_free_mem */
    1831             : static void
    1832          68 : php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
    1833             : {
    1834          68 :         if (!stack_allocation) {
    1835          68 :                 mnd_pefree(_packet, ((MYSQLND_PACKET_CHG_USER_RESPONSE *)_packet)->header.persistent);
    1836             :         }
    1837          68 : }
    1838             : /* }}} */
    1839             : 
    1840             : 
    1841             : /* {{{ packet_methods */
    1842             : static
    1843             : mysqlnd_packet_methods packet_methods[PROT_LAST] =
    1844             : {
    1845             :         {
    1846             :                 sizeof(MYSQLND_PACKET_GREET),
    1847             :                 php_mysqlnd_greet_read,
    1848             :                 NULL, /* write */
    1849             :                 php_mysqlnd_greet_free_mem,
    1850             :         }, /* PROT_GREET_PACKET */
    1851             :         {
    1852             :                 sizeof(MYSQLND_PACKET_AUTH),
    1853             :                 NULL, /* read */
    1854             :                 php_mysqlnd_auth_write,
    1855             :                 php_mysqlnd_auth_free_mem,
    1856             :         }, /* PROT_AUTH_PACKET */
    1857             :         {
    1858             :                 sizeof(MYSQLND_PACKET_OK),
    1859             :                 php_mysqlnd_ok_read, /* read */
    1860             :                 NULL, /* write */
    1861             :                 php_mysqlnd_ok_free_mem,
    1862             :         }, /* PROT_OK_PACKET */
    1863             :         {
    1864             :                 sizeof(MYSQLND_PACKET_EOF),
    1865             :                 php_mysqlnd_eof_read, /* read */
    1866             :                 NULL, /* write */
    1867             :                 php_mysqlnd_eof_free_mem,
    1868             :         }, /* PROT_EOF_PACKET */
    1869             :         {
    1870             :                 sizeof(MYSQLND_PACKET_COMMAND),
    1871             :                 NULL, /* read */
    1872             :                 php_mysqlnd_cmd_write, /* write */
    1873             :                 php_mysqlnd_cmd_free_mem,
    1874             :         }, /* PROT_CMD_PACKET */
    1875             :         {
    1876             :                 sizeof(MYSQLND_PACKET_RSET_HEADER),
    1877             :                 php_mysqlnd_rset_header_read, /* read */
    1878             :                 NULL, /* write */
    1879             :                 php_mysqlnd_rset_header_free_mem,
    1880             :         }, /* PROT_RSET_HEADER_PACKET */
    1881             :         {
    1882             :                 sizeof(MYSQLND_PACKET_RES_FIELD),
    1883             :                 php_mysqlnd_rset_field_read, /* read */
    1884             :                 NULL, /* write */
    1885             :                 php_mysqlnd_rset_field_free_mem,
    1886             :         }, /* PROT_RSET_FLD_PACKET */
    1887             :         {
    1888             :                 sizeof(MYSQLND_PACKET_ROW),
    1889             :                 php_mysqlnd_rowp_read, /* read */
    1890             :                 NULL, /* write */
    1891             :                 php_mysqlnd_rowp_free_mem,
    1892             :         }, /* PROT_ROW_PACKET */
    1893             :         {
    1894             :                 sizeof(MYSQLND_PACKET_STATS),
    1895             :                 php_mysqlnd_stats_read, /* read */
    1896             :                 NULL, /* write */
    1897             :                 php_mysqlnd_stats_free_mem,
    1898             :         }, /* PROT_STATS_PACKET */
    1899             :         {
    1900             :                 sizeof(MYSQLND_PACKET_PREPARE_RESPONSE),
    1901             :                 php_mysqlnd_prepare_read, /* read */
    1902             :                 NULL, /* write */
    1903             :                 php_mysqlnd_prepare_free_mem,
    1904             :         }, /* PROT_PREPARE_RESP_PACKET */
    1905             :         {
    1906             :                 sizeof(MYSQLND_PACKET_CHG_USER_RESPONSE),
    1907             :                 php_mysqlnd_chg_user_read, /* read */
    1908             :                 NULL, /* write */
    1909             :                 php_mysqlnd_chg_user_free_mem,
    1910             :         } /* PROT_CHG_USER_RESP_PACKET */
    1911             : };
    1912             : /* }}} */
    1913             : 
    1914             : 
    1915             : /* {{{ mysqlnd_protocol::get_greet_packet */
    1916             : static struct st_mysqlnd_packet_greet *
    1917        1829 : MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1918             : {
    1919        1829 :         struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
    1920        1829 :         DBG_ENTER("mysqlnd_protocol::get_greet_packet");
    1921        1829 :         if (packet) {
    1922        1829 :                 packet->header.m = &packet_methods[PROT_GREET_PACKET];
    1923        1829 :                 packet->header.persistent = persistent;
    1924             :         }
    1925        1829 :         DBG_RETURN(packet);
    1926             : }
    1927             : /* }}} */
    1928             : 
    1929             : 
    1930             : /* {{{ mysqlnd_protocol::get_auth_packet */
    1931             : static struct st_mysqlnd_packet_auth *
    1932        1815 : MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1933             : {
    1934        1815 :         struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
    1935        1815 :         DBG_ENTER("mysqlnd_protocol::get_auth_packet");
    1936        1815 :         if (packet) {
    1937        1815 :                 packet->header.m = &packet_methods[PROT_AUTH_PACKET];
    1938        1815 :                 packet->header.persistent = persistent;
    1939             :         }
    1940        1815 :         DBG_RETURN(packet);
    1941             : }
    1942             : /* }}} */
    1943             : 
    1944             : 
    1945             : /* {{{ mysqlnd_protocol::get_ok_packet */
    1946             : static struct st_mysqlnd_packet_ok *
    1947        2115 : MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1948             : {
    1949        2115 :         struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
    1950        2115 :         DBG_ENTER("mysqlnd_protocol::get_ok_packet");
    1951        2115 :         if (packet) {
    1952        2115 :                 packet->header.m = &packet_methods[PROT_OK_PACKET];
    1953        2115 :                 packet->header.persistent = persistent;
    1954             :         }
    1955        2115 :         DBG_RETURN(packet);
    1956             : }
    1957             : /* }}} */
    1958             : 
    1959             : 
    1960             : /* {{{ mysqlnd_protocol::get_eof_packet */
    1961             : static struct st_mysqlnd_packet_eof *
    1962        7988 : MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1963             : {
    1964        7988 :         struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
    1965        7988 :         DBG_ENTER("mysqlnd_protocol::get_eof_packet");
    1966        7988 :         if (packet) {
    1967        7988 :                 packet->header.m = &packet_methods[PROT_EOF_PACKET];
    1968        7988 :                 packet->header.persistent = persistent;
    1969             :         }
    1970        7988 :         DBG_RETURN(packet);
    1971             : }
    1972             : /* }}} */
    1973             : 
    1974             : 
    1975             : /* {{{ mysqlnd_protocol::get_command_packet */
    1976             : static struct st_mysqlnd_packet_command *
    1977       35330 : MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1978             : {
    1979       35330 :         struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
    1980       35330 :         DBG_ENTER("mysqlnd_protocol::get_command_packet");
    1981       35330 :         if (packet) {
    1982       35330 :                 packet->header.m = &packet_methods[PROT_CMD_PACKET];
    1983       35330 :                 packet->header.persistent = persistent;
    1984             :         }
    1985       35330 :         DBG_RETURN(packet);
    1986             : }
    1987             : /* }}} */
    1988             : 
    1989             : 
    1990             : /* {{{ mysqlnd_protocol::get_rset_packet */
    1991             : static struct st_mysqlnd_packet_rset_header *
    1992       24671 : MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    1993             : {
    1994       24671 :         struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
    1995       24671 :         DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
    1996       24671 :         if (packet) {
    1997       24671 :                 packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
    1998       24671 :                 packet->header.persistent = persistent;
    1999             :         }
    2000       24671 :         DBG_RETURN(packet);
    2001             : }
    2002             : /* }}} */
    2003             : 
    2004             : 
    2005             : /* {{{ mysqlnd_protocol::get_result_field_packet */
    2006             : static struct st_mysqlnd_packet_res_field *
    2007        7858 : MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    2008             : {
    2009        7858 :         struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
    2010        7858 :         DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
    2011        7858 :         if (packet) {
    2012        7858 :                 packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
    2013        7858 :                 packet->header.persistent = persistent;
    2014             :         }
    2015        7858 :         DBG_RETURN(packet);
    2016             : }
    2017             : /* }}} */
    2018             : 
    2019             : 
    2020             : /* {{{ mysqlnd_protocol::get_row_packet */
    2021             : static struct st_mysqlnd_packet_row *
    2022        4597 : MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    2023             : {
    2024        4597 :         struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
    2025        4597 :         DBG_ENTER("mysqlnd_protocol::get_row_packet");
    2026        4597 :         if (packet) {
    2027        4597 :                 packet->header.m = &packet_methods[PROT_ROW_PACKET];
    2028        4597 :                 packet->header.persistent = persistent;
    2029             :         }
    2030        4597 :         DBG_RETURN(packet);
    2031             : }
    2032             : /* }}} */
    2033             : 
    2034             : 
    2035             : /* {{{ mysqlnd_protocol::get_stats_packet */
    2036             : static struct st_mysqlnd_packet_stats *
    2037          16 : MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    2038             : {
    2039          16 :         struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
    2040          16 :         DBG_ENTER("mysqlnd_protocol::get_stats_packet");
    2041          16 :         if (packet) {
    2042          16 :                 packet->header.m = &packet_methods[PROT_STATS_PACKET];
    2043          16 :                 packet->header.persistent = persistent;
    2044             :         }
    2045          16 :         DBG_RETURN(packet);
    2046             : }
    2047             : /* }}} */
    2048             : 
    2049             : 
    2050             : /* {{{ mysqlnd_protocol::get_prepare_response_packet */
    2051             : static struct st_mysqlnd_packet_prepare_response *
    2052        4234 : MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    2053             : {
    2054        4234 :         struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
    2055        4234 :         DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
    2056        4234 :         if (packet) {
    2057        4234 :                 packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
    2058        4234 :                 packet->header.persistent = persistent;
    2059             :         }
    2060        4234 :         DBG_RETURN(packet);
    2061             : }
    2062             : /* }}} */
    2063             : 
    2064             : 
    2065             : /* {{{ mysqlnd_protocol::get_change_user_response_packet */
    2066             : static struct st_mysqlnd_packet_chg_user_resp*
    2067          68 : MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
    2068             : {
    2069          68 :         struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
    2070          68 :         DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
    2071          68 :         if (packet) {
    2072          68 :                 packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
    2073          68 :                 packet->header.persistent = persistent;
    2074             :         }
    2075          68 :         DBG_RETURN(packet);
    2076             : }
    2077             : /* }}} */
    2078             : 
    2079             : 
    2080             : static
    2081             : MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
    2082             :         MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
    2083             :         MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
    2084             :         MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet),
    2085             :         MYSQLND_METHOD(mysqlnd_protocol, get_command_packet),
    2086             :         MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet),
    2087             :         MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet),
    2088             :         MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet),
    2089             :         MYSQLND_METHOD(mysqlnd_protocol, get_row_packet),
    2090             :         MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet),
    2091             :         MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
    2092             :         MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)
    2093             : MYSQLND_CLASS_METHODS_END;
    2094             : 
    2095             : 
    2096             : /* {{{ mysqlnd_protocol_init */
    2097             : PHPAPI MYSQLND_PROTOCOL *
    2098        1894 : mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)
    2099             : {
    2100        1894 :         size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
    2101        1894 :         MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
    2102             : 
    2103        1894 :         DBG_ENTER("mysqlnd_protocol_init");
    2104        1894 :         DBG_INF_FMT("persistent=%u", persistent);
    2105        1894 :         if (ret) {
    2106        1894 :                 ret->persistent = persistent;
    2107        1894 :                 ret->m = mysqlnd_mysqlnd_protocol_methods;
    2108             :         }
    2109             : 
    2110        1894 :         DBG_RETURN(ret);
    2111             : }
    2112             : /* }}} */
    2113             : 
    2114             : 
    2115             : /* {{{ mysqlnd_protocol_free */
    2116             : PHPAPI void
    2117        1853 : mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)
    2118             : {
    2119        1853 :         DBG_ENTER("mysqlnd_protocol_free");
    2120             : 
    2121        1853 :         if (protocol) {
    2122        1853 :                 zend_bool pers = protocol->persistent;
    2123        1853 :                 mnd_pefree(protocol, pers);
    2124             :         }
    2125        1853 :         DBG_VOID_RETURN;
    2126             : }
    2127             : /* }}} */
    2128             : 
    2129             : 
    2130             : /* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
    2131             : PHPAPI void **
    2132           0 : _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC)
    2133             : {
    2134           0 :         DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
    2135           0 :         DBG_INF_FMT("plugin_id=%u", plugin_id);
    2136           0 :         if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
    2137           0 :                 return NULL;
    2138             :         }
    2139           0 :         DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
    2140             : }
    2141             : /* }}} */
    2142             : 
    2143             : 
    2144             : /* {{{ mysqlnd_protocol_get_methods */
    2145             : PHPAPI struct st_mysqlnd_protocol_methods *
    2146           0 : mysqlnd_protocol_get_methods()
    2147             : {
    2148           0 :         return &mysqlnd_mysqlnd_protocol_methods;
    2149             : }
    2150             : /* }}} */
    2151             : 
    2152             : 
    2153             : /*
    2154             :  * Local variables:
    2155             :  * tab-width: 4
    2156             :  * c-basic-offset: 4
    2157             :  * End:
    2158             :  * vim600: noet sw=4 ts=4 fdm=marker
    2159             :  * vim<600: noet sw=4 ts=4
    2160             :  */

Generated by: LCOV version 1.10

Generated at Sun, 27 Jul 2014 12:58:31 +0000 (5 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.