LCOV - code coverage report
Current view: top level - src/ctlib - ctutil.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 94 109 86.2 %
Date: 2025-02-21 09:36:06 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Brian Bruns
       3             :  * Copyright (C) 2011  Frediano Ziglio
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU Library General Public
       7             :  * License as published by the Free Software Foundation; either
       8             :  * version 2 of the License, or (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * Library General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Library General Public
      16             :  * License along with this library; if not, write to the
      17             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18             :  * Boston, MA 02111-1307, USA.
      19             :  */
      20             : 
      21             : #include <config.h>
      22             : 
      23             : #if HAVE_STRING_H
      24             : #include <string.h>
      25             : #endif /* HAVE_STRING_H */
      26             : 
      27             : #include <freetds/utils.h>
      28             : #include "cspublic.h"
      29             : #include "ctlib.h"
      30             : #include "syberror.h"
      31             : #include <freetds/tds.h>
      32             : #include <freetds/replacements.h>
      33             : /* #include "fortify.h" */
      34             : 
      35             : 
      36             : /*
      37             :  * test include consistency 
      38             :  * I don't think all compiler are able to compile this code... if not comment it
      39             :  */
      40             : #if ENABLE_EXTRA_CHECKS
      41             : 
      42             : #define TEST_EQUAL(t,a,b) TDS_COMPILE_CHECK(t,a==b)
      43             : 
      44             : TEST_EQUAL(t03,CS_NULLTERM,TDS_NULLTERM);
      45             : TEST_EQUAL(t04,CS_CMD_SUCCEED,TDS_CMD_SUCCEED);
      46             : TEST_EQUAL(t05,CS_CMD_FAIL,TDS_CMD_FAIL);
      47             : TEST_EQUAL(t06,CS_CMD_DONE,TDS_CMD_DONE);
      48             : TEST_EQUAL(t07,CS_NO_COUNT,TDS_NO_COUNT);
      49             : TEST_EQUAL(t08,CS_COMPUTE_RESULT,TDS_COMPUTE_RESULT);
      50             : TEST_EQUAL(t09,CS_PARAM_RESULT,TDS_PARAM_RESULT);
      51             : TEST_EQUAL(t10,CS_ROW_RESULT,TDS_ROW_RESULT);
      52             : TEST_EQUAL(t11,CS_STATUS_RESULT,TDS_STATUS_RESULT);
      53             : TEST_EQUAL(t12,CS_COMPUTEFMT_RESULT,TDS_COMPUTEFMT_RESULT);
      54             : TEST_EQUAL(t13,CS_ROWFMT_RESULT,TDS_ROWFMT_RESULT);
      55             : TEST_EQUAL(t14,CS_MSG_RESULT,TDS_MSG_RESULT);
      56             : TEST_EQUAL(t15,CS_DESCRIBE_RESULT,TDS_DESCRIBE_RESULT);
      57             : TEST_EQUAL(t16,CS_INT_CONTINUE,TDS_INT_CONTINUE);
      58             : TEST_EQUAL(t17,CS_INT_CANCEL,TDS_INT_CANCEL);
      59             : TEST_EQUAL(t18,CS_INT_TIMEOUT,TDS_INT_TIMEOUT);
      60             : 
      61             : #define TEST_ATTRIBUTE(t,sa,fa,sb,fb) \
      62             :         TDS_COMPILE_CHECK(t,sizeof(((sa*)0)->fa) == sizeof(((sb*)0)->fb) && TDS_OFFSET(sa,fa) == TDS_OFFSET(sb,fb))
      63             : 
      64             : TEST_ATTRIBUTE(t21,TDS_MONEY4,mny4,CS_MONEY4,mny4);
      65             : TEST_ATTRIBUTE(t22,TDS_OLD_MONEY,mnyhigh,CS_MONEY,mnyhigh);
      66             : TEST_ATTRIBUTE(t23,TDS_OLD_MONEY,mnylow,CS_MONEY,mnylow);
      67             : TEST_ATTRIBUTE(t24,TDS_DATETIME,dtdays,CS_DATETIME,dtdays);
      68             : TEST_ATTRIBUTE(t25,TDS_DATETIME,dttime,CS_DATETIME,dttime);
      69             : TEST_ATTRIBUTE(t26,TDS_DATETIME4,days,CS_DATETIME4,days);
      70             : TEST_ATTRIBUTE(t27,TDS_DATETIME4,minutes,CS_DATETIME4,minutes);
      71             : TEST_ATTRIBUTE(t28,TDS_NUMERIC,precision,CS_NUMERIC,precision);
      72             : TEST_ATTRIBUTE(t29,TDS_NUMERIC,scale,CS_NUMERIC,scale);
      73             : TEST_ATTRIBUTE(t30,TDS_NUMERIC,array,CS_NUMERIC,array);
      74             : TEST_ATTRIBUTE(t30,TDS_NUMERIC,precision,CS_DECIMAL,precision);
      75             : TEST_ATTRIBUTE(t31,TDS_NUMERIC,scale,CS_DECIMAL,scale);
      76             : TEST_ATTRIBUTE(t32,TDS_NUMERIC,array,CS_DECIMAL,array);
      77             : #endif
      78             : 
      79             : static int
      80             : _ct_translate_severity(int tds_severity)
      81             : {
      82          49 :         switch (tds_severity) {
      83             :         case EXINFO:
      84             :                 return CS_SV_INFORM; /* unused */
      85             :         case EXUSER:
      86             :                 return CS_SV_CONFIG_FAIL;
      87             :         case EXNONFATAL:
      88             :                 return CS_SV_INTERNAL_FAIL; /* unused */
      89             :         case EXCONVERSION:
      90             :                 return CS_SV_API_FAIL;
      91             :         case EXSERVER:
      92             :                 return CS_SV_INTERNAL_FAIL; /* unused */
      93             :         case EXTIME:
      94             :                 return CS_SV_RETRY_FAIL;
      95             :         case EXPROGRAM:
      96             :                 return CS_SV_API_FAIL;
      97             :         case EXRESOURCE:
      98             :                 return CS_SV_RESOURCE_FAIL; /* unused */
      99             :         case EXCOMM:
     100             :                 return CS_SV_COMM_FAIL;
     101             :         case EXFATAL:
     102             :                 return CS_SV_FATAL; /* unused */
     103             :         case EXCONSISTENCY:
     104             :         default:
     105             :                 return CS_SV_INTERNAL_FAIL;
     106             :         }
     107             : }
     108             : 
     109             : /* 
     110             :  * error handler 
     111             :  * This callback function should be invoked only from libtds through tds_ctx->err_handler.  
     112             :  */
     113             : int
     114          49 : _ct_handle_client_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     115             : {
     116             :         CS_CLIENTMSG errmsg;
     117          49 :         CS_CONNECTION *con = NULL;
     118          49 :         CS_CONTEXT *ctx = NULL;
     119          49 :         int ret = (int) CS_SUCCEED;
     120             : 
     121          49 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_client_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     122             : 
     123          49 :         if (tds && tds_get_parent(tds)) {
     124          49 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     125             :         }
     126             : 
     127          49 :         memset(&errmsg, '\0', sizeof(errmsg));
     128          49 :         errmsg.msgnumber = msg->msgno;
     129          98 :         errmsg.severity = _ct_translate_severity(msg->severity);
     130          49 :         strlcpy(errmsg.msgstring, msg->message, sizeof(errmsg.msgstring));
     131          49 :         errmsg.msgstringlen = strlen(errmsg.msgstring);
     132             : 
     133          49 :         if (msg->oserr) {
     134           2 :                 char *osstr = sock_strerror(msg->oserr);
     135             : 
     136           2 :                 errmsg.osstringlen = (CS_INT) strlen(osstr);
     137           2 :                 strlcpy(errmsg.osstring, osstr, sizeof(errmsg.osstring));
     138             :                 sock_strerror_free(osstr);
     139             :         } else {
     140          47 :                 errmsg.osstring[0] = '\0';
     141          47 :                 errmsg.osstringlen = 0;
     142             :         }
     143             : 
     144             :         /* if there is no connection, attempt to call the context handler */
     145          49 :         if (!con) {
     146           0 :                 ctx = (CS_CONTEXT *) ctx_tds->parent;
     147           0 :                 if (ctx->clientmsg_cb)
     148           0 :                         ret = ctx->clientmsg_cb(ctx, con, &errmsg);
     149          49 :         } else if (con->clientmsg_cb)
     150          35 :                 ret = con->clientmsg_cb(con->ctx, con, &errmsg);
     151          14 :         else if (con->ctx->clientmsg_cb)
     152           4 :                 ret = con->ctx->clientmsg_cb(con->ctx, con, &errmsg);
     153             :                 
     154             :         /*
     155             :          * The return code from the error handler is either CS_SUCCEED or CS_FAIL.
     156             :          * This function was called by libtds with some kind of communications failure, and there are
     157             :          * no cases in which "succeed" could mean anything: In most cases, the function is going to fail
     158             :          * no matter what.  
     159             :          *
     160             :          * Timeouts are a different matter; it's up to the client to decide whether to continue
     161             :          * waiting or to abort the operation and close the socket.  ct-lib applications do their 
     162             :          * own cancel processing -- they can call ct_cancel from within the error handler -- so 
     163             :          * they don't need to return TDS_INT_TIMEOUT.  They can, however, return TDS_INT_CONTINUE
     164             :          * or TDS_INT_CANCEL.  We map the client's return code to those. 
     165             :          *
     166             :          * Only for timeout errors does TDS_INT_CANCEL cause libtds to break the connection. 
     167             :          */
     168          49 :         if (msg->msgno == TDSETIME) {
     169          30 :                 switch (ret) {
     170             :                 case CS_SUCCEED:        return TDS_INT_CONTINUE;
     171          10 :                 case CS_FAIL:           return TDS_INT_CANCEL;
     172             :                 }
     173             :         }
     174          19 :         return TDS_INT_CANCEL;
     175             : }
     176             : 
     177             : /*
     178             :  * interrupt handler, installed on demand to avoid gratuitously interfering
     179             :  * with tds_select's optimization for the no-handler case */
     180             : int
     181          60 : _ct_handle_interrupt(void * ptr)
     182             : {
     183          60 :         CS_CONNECTION *con = (CS_CONNECTION *) ptr;
     184          60 :         if (con->interrupt_cb)
     185          60 :                 return (*con->interrupt_cb)(con);
     186           0 :         else if (con->ctx->interrupt_cb)
     187           0 :                 return (*con->ctx->interrupt_cb)(con);
     188             :         else
     189             :                 return TDS_INT_CONTINUE;
     190             : }
     191             : 
     192             : /* message handler */
     193             : TDSRET
     194        4116 : _ct_handle_server_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     195             : {
     196             :         CS_SERVERMSG_INTERNAL errmsg;
     197        4116 :         CS_CONNECTION *con = NULL;
     198             :         CS_CONTEXT *ctx;
     199             :         CS_SERVERMSG_COMMON2 *common2;
     200        4116 :         CS_RETCODE ret = CS_SUCCEED;
     201             : 
     202        4116 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_server_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     203             : 
     204        4116 :         if (tds && tds_get_parent(tds))
     205        4116 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     206             : 
     207        4116 :         ctx = con ? con->ctx : (CS_CONTEXT *) ctx_tds->parent;
     208             : 
     209        4116 :         memset(&errmsg, '\0', sizeof(errmsg));
     210        4116 :         errmsg.common.msgnumber = msg->msgno;
     211        4116 :         strlcpy(errmsg.common.text, msg->message, sizeof(errmsg.common.text));
     212        4116 :         errmsg.common.textlen = strlen(errmsg.common.text);
     213        4116 :         errmsg.common.state = msg->state;
     214        4116 :         errmsg.common.severity = msg->severity;
     215             : 
     216             : #define MIDDLE_PART(part) do { \
     217             :         common2 = (CS_SERVERMSG_COMMON2 *) &(errmsg.part.line); \
     218             :         if (msg->server) { \
     219             :                 errmsg.part.svrnlen = strlen(msg->server); \
     220             :                 strlcpy(errmsg.part.svrname, msg->server, sizeof(errmsg.part.svrname)); \
     221             :         } \
     222             :         if (msg->proc_name) { \
     223             :                 errmsg.part.proclen = strlen(msg->proc_name); \
     224             :                 strlcpy(errmsg.part.proc, msg->proc_name, sizeof(errmsg.part.proc)); \
     225             :         } \
     226             : } while(0)
     227             : 
     228        4116 :         if (ctx->use_large_identifiers)
     229        2058 :                 MIDDLE_PART(large);
     230             :         else
     231        2058 :                 MIDDLE_PART(small);
     232             : #undef MIDDLE_PART
     233             : 
     234        4116 :         common2->sqlstate[0] = 0;
     235        4116 :         if (msg->sql_state)
     236          76 :                 strlcpy((char *) common2->sqlstate, msg->sql_state, sizeof(common2->sqlstate));
     237        4116 :         common2->sqlstatelen = strlen((char *) common2->sqlstate);
     238        4116 :         common2->line = msg->line_number;
     239             : 
     240             :         /* if there is no connection, attempt to call the context handler */
     241        4116 :         if (!con) {
     242           0 :                 if (ctx->servermsg_cb)
     243           0 :                         ret = ctx->servermsg_cb(ctx, con, &errmsg.user);
     244        4116 :         } else if (con->servermsg_cb) {
     245          10 :                 ret = con->servermsg_cb(ctx, con, &errmsg.user);
     246        4106 :         } else if (ctx->servermsg_cb) {
     247        4098 :                 ret = ctx->servermsg_cb(ctx, con, &errmsg.user);
     248             :         }
     249        4108 :         return ret == CS_SUCCEED ? TDS_SUCCESS : TDS_FAIL;
     250             : }
     251             : 
     252             : /**
     253             :  * Check if a give version supports large identifiers.
     254             :  */
     255             : bool
     256        2760 : _ct_is_large_identifiers_version(CS_INT version)
     257             : {
     258        2760 :         switch (version) {
     259             :         case 112:
     260             :         case 1100:
     261             :         case 12500:
     262             :         case 15000:
     263             :                 return false;
     264             :         }
     265        1380 :         return true;
     266             : }
     267             : 
     268             : const CS_DATAFMT_COMMON *
     269        2832 : _ct_datafmt_common(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt)
     270             : {
     271        2832 :         if (!datafmt)
     272             :                 return NULL;
     273        2812 :         if (ctx->use_large_identifiers)
     274        1406 :                 return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_LARGE *) datafmt)->datatype);
     275        1406 :         return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_SMALL *) datafmt)->datatype);
     276             : }
     277             : 
     278             : /**
     279             :  * Converts CS_DATAFMT input parameter to CS_DATAFMT_LARGE.
     280             :  * @param ctx      CTLib context
     281             :  * @param datafmt  Input parameter
     282             :  * @param fmtbuf   Buffer to use in case conversion is required
     283             :  * @return parameter converted to large
     284             :  */
     285             : const CS_DATAFMT_LARGE *
     286         366 : _ct_datafmt_conv_in(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     287             : {
     288             :         const CS_DATAFMT_SMALL *small;
     289             : 
     290         366 :         if (!datafmt)
     291             :                 return NULL;
     292             : 
     293             :         /* read directly from input */
     294         366 :         if (ctx->use_large_identifiers)
     295             :                 return (CS_DATAFMT_LARGE *) datafmt;
     296             : 
     297             :         /* convert small format to large */
     298         183 :         small = (const CS_DATAFMT_SMALL *) datafmt;
     299             : 
     300         183 :         strlcpy(fmtbuf->name, small->name, sizeof(fmtbuf->name));
     301         183 :         fmtbuf->namelen = strlen(fmtbuf->name);
     302         183 :         *((CS_DATAFMT_COMMON *) &fmtbuf->datatype) = *((CS_DATAFMT_COMMON *) &small->datatype);
     303         183 :         return fmtbuf;
     304             : }
     305             : 
     306             : /**
     307             :  * Prepares to Convert CS_DATAFMT output parameter to CS_DATAFMT_LARGE.
     308             :  * @param ctx      CTLib context
     309             :  * @param datafmt  Input parameter
     310             :  * @param fmtbuf   Buffer to use in case conversion is required
     311             :  * @return parameter converted to large
     312             :  */
     313             : CS_DATAFMT_LARGE *
     314         888 : _ct_datafmt_conv_prepare(CS_CONTEXT * ctx, CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     315             : {
     316         888 :         if (!datafmt)
     317             :                 return NULL;
     318             : 
     319             :         /* write directly to output */
     320         888 :         if (ctx->use_large_identifiers)
     321             :                 return (CS_DATAFMT_LARGE *) datafmt;
     322             : 
     323         444 :         return fmtbuf;
     324             : }
     325             : 
     326             : /**
     327             :  * Converts CS_DATAFMT output parameter to CS_DATAFMT_LARGE after setting it.
     328             :  * @param datafmt  Input parameter
     329             :  * @param fmtbuf   Buffer to use in case conversion is required. You should pass
     330             :  *                 value returned by _ct_datafmt_conv_prepare().
     331             :  */
     332             : void
     333         888 : _ct_datafmt_conv_back(CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     334             : {
     335             :         CS_DATAFMT_SMALL *small;
     336             : 
     337             :         /* already right format */
     338         888 :         if ((void *) datafmt == (void*) fmtbuf)
     339             :                 return;
     340             : 
     341             :         /* convert large format to small */
     342         444 :         small = (CS_DATAFMT_SMALL *) datafmt;
     343             : 
     344         444 :         strlcpy(small->name, fmtbuf->name, sizeof(small->name));
     345         444 :         small->namelen = strlen(small->name);
     346         444 :         *((CS_DATAFMT_COMMON *) &small->datatype) = *((CS_DATAFMT_COMMON *) &fmtbuf->datatype);
     347             : }
     348             : 
     349             : /**
     350             :  * Get length of a string buffer
     351             :  *
     352             :  * @return length of string or original negative value if error.
     353             :  */
     354             : CS_INT
     355        6454 : _ct_get_string_length(const char *buf, CS_INT buflen)
     356             : {
     357        6554 :         if (buflen >= 0)
     358             :                 return buflen;
     359             : 
     360        6301 :         if (buflen == CS_NULLTERM)
     361        6161 :                 return (CS_INT) strlen(buf);
     362             : 
     363             :         return buflen;
     364             : }
     365             : 
     366             : CS_RETCODE
     367         100 : _ct_props_dstr(CS_CONNECTION * con TDS_UNUSED, DSTR *s, CS_INT action, CS_VOID * buffer, CS_INT buflen, CS_INT * out_len)
     368             : {
     369         100 :         if (action == CS_SET) {
     370         100 :                 buflen = _ct_get_string_length(buffer, buflen);
     371         100 :                 if (buflen < 0) {
     372             :                         /* TODO what error ?? */
     373             :                         /* _ctclient_msg(NULL, con, "ct_con_props(SET,APPNAME)", 1, 1, 1, 5, "%d, %s", buflen, "buflen"); */
     374             :                         return CS_FAIL;
     375             :                 }
     376         100 :                 if (tds_dstr_copyn(s, buffer, buflen) != NULL)
     377             :                         return CS_SUCCEED;
     378           0 :         } else if (action == CS_GET) {
     379           0 :                 if (out_len)
     380           0 :                         *out_len = tds_dstr_len(s);
     381           0 :                 strlcpy((char *) buffer, tds_dstr_cstr(s), buflen);
     382           0 :                 return CS_SUCCEED;
     383           0 :         } else if (action == CS_CLEAR) {
     384           0 :                 tds_dstr_empty(s);
     385           0 :                 return CS_SUCCEED;
     386             :         }
     387             :         return CS_FAIL;
     388             : }

Generated by: LCOV version 1.13