LCOV - code coverage report
Current view: top level - src/ctlib - ctutil.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 74 98 75.5 %
Date: 2025-01-18 12:13:41 Functions: 7 8 87.5 %

          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             : 
      58             : #define TEST_ATTRIBUTE(t,sa,fa,sb,fb) \
      59             :         TDS_COMPILE_CHECK(t,sizeof(((sa*)0)->fa) == sizeof(((sb*)0)->fb) && TDS_OFFSET(sa,fa) == TDS_OFFSET(sb,fb))
      60             : 
      61             : TEST_ATTRIBUTE(t21,TDS_MONEY4,mny4,CS_MONEY4,mny4);
      62             : TEST_ATTRIBUTE(t22,TDS_OLD_MONEY,mnyhigh,CS_MONEY,mnyhigh);
      63             : TEST_ATTRIBUTE(t23,TDS_OLD_MONEY,mnylow,CS_MONEY,mnylow);
      64             : TEST_ATTRIBUTE(t24,TDS_DATETIME,dtdays,CS_DATETIME,dtdays);
      65             : TEST_ATTRIBUTE(t25,TDS_DATETIME,dttime,CS_DATETIME,dttime);
      66             : TEST_ATTRIBUTE(t26,TDS_DATETIME4,days,CS_DATETIME4,days);
      67             : TEST_ATTRIBUTE(t27,TDS_DATETIME4,minutes,CS_DATETIME4,minutes);
      68             : TEST_ATTRIBUTE(t28,TDS_NUMERIC,precision,CS_NUMERIC,precision);
      69             : TEST_ATTRIBUTE(t29,TDS_NUMERIC,scale,CS_NUMERIC,scale);
      70             : TEST_ATTRIBUTE(t30,TDS_NUMERIC,array,CS_NUMERIC,array);
      71             : TEST_ATTRIBUTE(t30,TDS_NUMERIC,precision,CS_DECIMAL,precision);
      72             : TEST_ATTRIBUTE(t31,TDS_NUMERIC,scale,CS_DECIMAL,scale);
      73             : TEST_ATTRIBUTE(t32,TDS_NUMERIC,array,CS_DECIMAL,array);
      74             : #endif
      75             : 
      76             : static int
      77             : _ct_translate_severity(int tds_severity)
      78             : {
      79          12 :         switch (tds_severity) {
      80             :         case EXINFO:
      81             :                 return CS_SV_INFORM; /* unused */
      82             :         case EXUSER:
      83             :                 return CS_SV_CONFIG_FAIL;
      84             :         case EXNONFATAL:
      85             :                 return CS_SV_INTERNAL_FAIL; /* unused */
      86             :         case EXCONVERSION:
      87             :                 return CS_SV_API_FAIL;
      88             :         case EXSERVER:
      89             :                 return CS_SV_INTERNAL_FAIL; /* unused */
      90             :         case EXTIME:
      91             :                 return CS_SV_RETRY_FAIL;
      92             :         case EXPROGRAM:
      93             :                 return CS_SV_API_FAIL;
      94             :         case EXRESOURCE:
      95             :                 return CS_SV_RESOURCE_FAIL; /* unused */
      96             :         case EXCOMM:
      97             :                 return CS_SV_COMM_FAIL;
      98             :         case EXFATAL:
      99             :                 return CS_SV_FATAL; /* unused */
     100             :         case EXCONSISTENCY:
     101             :         default:
     102             :                 return CS_SV_INTERNAL_FAIL;
     103             :         }
     104             : }
     105             : 
     106             : /* 
     107             :  * error handler 
     108             :  * This callback function should be invoked only from libtds through tds_ctx->err_handler.  
     109             :  */
     110             : int
     111          12 : _ct_handle_client_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     112             : {
     113             :         CS_CLIENTMSG errmsg;
     114          12 :         CS_CONNECTION *con = NULL;
     115          12 :         CS_CONTEXT *ctx = NULL;
     116          12 :         int ret = (int) CS_SUCCEED;
     117             : 
     118          12 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_client_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     119             : 
     120          12 :         if (tds && tds_get_parent(tds)) {
     121          12 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     122             :         }
     123             : 
     124          12 :         memset(&errmsg, '\0', sizeof(errmsg));
     125          12 :         errmsg.msgnumber = msg->msgno;
     126          24 :         errmsg.severity = _ct_translate_severity(msg->severity);
     127          12 :         strlcpy(errmsg.msgstring, msg->message, sizeof(errmsg.msgstring));
     128          12 :         errmsg.msgstringlen = strlen(errmsg.msgstring);
     129          12 :         errmsg.osstring[0] = '\0';
     130          12 :         errmsg.osstringlen = 0;
     131             :         /* if there is no connection, attempt to call the context handler */
     132          12 :         if (!con) {
     133           0 :                 ctx = (CS_CONTEXT *) ctx_tds->parent;
     134           0 :                 if (ctx->_clientmsg_cb)
     135           0 :                         ret = ctx->_clientmsg_cb(ctx, con, &errmsg);
     136          12 :         } else if (con->_clientmsg_cb)
     137           0 :                 ret = con->_clientmsg_cb(con->ctx, con, &errmsg);
     138          12 :         else if (con->ctx->_clientmsg_cb)
     139           4 :                 ret = con->ctx->_clientmsg_cb(con->ctx, con, &errmsg);
     140             :                 
     141             :         /*
     142             :          * The return code from the error handler is either CS_SUCCEED or CS_FAIL.
     143             :          * This function was called by libtds with some kind of communications failure, and there are
     144             :          * no cases in which "succeed" could mean anything: In most cases, the function is going to fail
     145             :          * no matter what.  
     146             :          *
     147             :          * Timeouts are a different matter; it's up to the client to decide whether to continue
     148             :          * waiting or to abort the operation and close the socket.  ct-lib applications do their 
     149             :          * own cancel processing -- they can call ct_cancel from within the error handler -- so 
     150             :          * they don't need to return TDS_INT_TIMEOUT.  They can, however, return TDS_INT_CONTINUE
     151             :          * or TDS_INT_CANCEL.  We map the client's return code to those. 
     152             :          *
     153             :          * Only for timeout errors does TDS_INT_CANCEL cause libtds to break the connection. 
     154             :          */
     155          12 :         if (msg->msgno == TDSETIME) {
     156           0 :                 switch (ret) {
     157             :                 case CS_SUCCEED:        return TDS_INT_CONTINUE;
     158           0 :                 case CS_FAIL:           return TDS_INT_CANCEL;
     159             :                 }
     160             :         }
     161          12 :         return TDS_INT_CANCEL;
     162             : }
     163             : 
     164             : /* message handler */
     165             : TDSRET
     166        3220 : _ct_handle_server_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     167             : {
     168             :         CS_SERVERMSG_INTERNAL errmsg;
     169        3220 :         CS_CONNECTION *con = NULL;
     170             :         CS_CONTEXT *ctx;
     171             :         CS_SERVERMSG_COMMON2 *common2;
     172        3220 :         CS_RETCODE ret = CS_SUCCEED;
     173             : 
     174        3220 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_server_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     175             : 
     176        3220 :         if (tds && tds_get_parent(tds))
     177        3220 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     178             : 
     179        3220 :         ctx = con ? con->ctx : (CS_CONTEXT *) ctx_tds->parent;
     180             : 
     181        3220 :         memset(&errmsg, '\0', sizeof(errmsg));
     182        3220 :         errmsg.common.msgnumber = msg->msgno;
     183        3220 :         strlcpy(errmsg.common.text, msg->message, sizeof(errmsg.common.text));
     184        3220 :         errmsg.common.textlen = strlen(errmsg.common.text);
     185        3220 :         errmsg.common.state = msg->state;
     186        3220 :         errmsg.common.severity = msg->severity;
     187             : 
     188             : #define MIDDLE_PART(part) do { \
     189             :         common2 = (CS_SERVERMSG_COMMON2 *) &(errmsg.part.line); \
     190             :         if (msg->server) { \
     191             :                 errmsg.part.svrnlen = strlen(msg->server); \
     192             :                 strlcpy(errmsg.part.svrname, msg->server, sizeof(errmsg.part.svrname)); \
     193             :         } \
     194             :         if (msg->proc_name) { \
     195             :                 errmsg.part.proclen = strlen(msg->proc_name); \
     196             :                 strlcpy(errmsg.part.proc, msg->proc_name, sizeof(errmsg.part.proc)); \
     197             :         } \
     198             : } while(0)
     199             : 
     200        3220 :         if (ctx->use_large_identifiers)
     201        1610 :                 MIDDLE_PART(large);
     202             :         else
     203        1610 :                 MIDDLE_PART(small);
     204             : #undef MIDDLE_PART
     205             : 
     206        3220 :         common2->sqlstate[0] = 0;
     207        3220 :         if (msg->sql_state)
     208          64 :                 strlcpy((char *) common2->sqlstate, msg->sql_state, sizeof(common2->sqlstate));
     209        3220 :         common2->sqlstatelen = strlen((char *) common2->sqlstate);
     210        3220 :         common2->line = msg->line_number;
     211             : 
     212             :         /* if there is no connection, attempt to call the context handler */
     213        3220 :         if (!con) {
     214           0 :                 if (ctx->_servermsg_cb)
     215           0 :                         ret = ctx->_servermsg_cb(ctx, con, &errmsg.user);
     216        3220 :         } else if (con->_servermsg_cb) {
     217           8 :                 ret = con->_servermsg_cb(ctx, con, &errmsg.user);
     218        3212 :         } else if (ctx->_servermsg_cb) {
     219        3206 :                 ret = ctx->_servermsg_cb(ctx, con, &errmsg.user);
     220             :         }
     221        3214 :         return ret == CS_SUCCEED ? TDS_SUCCESS : TDS_FAIL;
     222             : }
     223             : 
     224             : /**
     225             :  * Check if a give version supports large identifiers.
     226             :  */
     227             : bool
     228        2160 : _ct_is_large_identifiers_version(CS_INT version)
     229             : {
     230        2160 :         switch (version) {
     231             :         case 112:
     232             :         case 1100:
     233             :         case 12500:
     234             :         case 15000:
     235             :                 return false;
     236             :         }
     237        1080 :         return true;
     238             : }
     239             : 
     240             : const CS_DATAFMT_COMMON *
     241        1770 : _ct_datafmt_common(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt)
     242             : {
     243        1770 :         if (!datafmt)
     244             :                 return NULL;
     245        1770 :         if (ctx->use_large_identifiers)
     246         885 :                 return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_LARGE *) datafmt)->datatype);
     247         885 :         return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_SMALL *) datafmt)->datatype);
     248             : }
     249             : 
     250             : /**
     251             :  * Converts CS_DATAFMT input parameter to CS_DATAFMT_LARGE.
     252             :  * @param ctx      CTLib context
     253             :  * @param datafmt  Input parameter
     254             :  * @param fmtbuf   Buffer to use in case conversion is required
     255             :  * @return parameter converted to large
     256             :  */
     257             : const CS_DATAFMT_LARGE *
     258         292 : _ct_datafmt_conv_in(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     259             : {
     260             :         const CS_DATAFMT_SMALL *small;
     261             : 
     262         292 :         if (!datafmt)
     263             :                 return NULL;
     264             : 
     265             :         /* read directly from input */
     266         292 :         if (ctx->use_large_identifiers)
     267             :                 return (CS_DATAFMT_LARGE *) datafmt;
     268             : 
     269             :         /* convert small format to large */
     270         146 :         small = (const CS_DATAFMT_SMALL *) datafmt;
     271             : 
     272         146 :         strlcpy(fmtbuf->name, small->name, sizeof(fmtbuf->name));
     273         146 :         fmtbuf->namelen = strlen(fmtbuf->name);
     274         146 :         *((CS_DATAFMT_COMMON *) &fmtbuf->datatype) = *((CS_DATAFMT_COMMON *) &small->datatype);
     275         146 :         return fmtbuf;
     276             : }
     277             : 
     278             : /**
     279             :  * Prepares to Convert CS_DATAFMT output 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             : CS_DATAFMT_LARGE *
     286         728 : _ct_datafmt_conv_prepare(CS_CONTEXT * ctx, CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     287             : {
     288         728 :         if (!datafmt)
     289             :                 return NULL;
     290             : 
     291             :         /* write directly to output */
     292         728 :         if (ctx->use_large_identifiers)
     293             :                 return (CS_DATAFMT_LARGE *) datafmt;
     294             : 
     295         364 :         return fmtbuf;
     296             : }
     297             : 
     298             : /**
     299             :  * Converts CS_DATAFMT output parameter to CS_DATAFMT_LARGE after setting it.
     300             :  * @param datafmt  Input parameter
     301             :  * @param fmtbuf   Buffer to use in case conversion is required. You should pass
     302             :  *                 value returned by _ct_datafmt_conv_prepare().
     303             :  */
     304             : void
     305         728 : _ct_datafmt_conv_back(CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     306             : {
     307             :         CS_DATAFMT_SMALL *small;
     308             : 
     309             :         /* already right format */
     310         728 :         if ((void *) datafmt == (void*) fmtbuf)
     311             :                 return;
     312             : 
     313             :         /* convert large format to small */
     314         364 :         small = (CS_DATAFMT_SMALL *) datafmt;
     315             : 
     316         364 :         strlcpy(small->name, fmtbuf->name, sizeof(small->name));
     317         364 :         small->namelen = strlen(small->name);
     318         364 :         *((CS_DATAFMT_COMMON *) &small->datatype) = *((CS_DATAFMT_COMMON *) &fmtbuf->datatype);
     319             : }
     320             : 
     321             : /**
     322             :  * Get length of a string buffer
     323             :  *
     324             :  * @return length of string or original negative value if error.
     325             :  */
     326             : static CS_INT
     327             : _ct_get_string_length(const char *buf, CS_INT buflen)
     328             : {
     329           0 :         if (buflen >= 0)
     330             :                 return buflen;
     331             : 
     332           0 :         if (buflen == CS_NULLTERM)
     333           0 :                 return (CS_INT) strlen(buf);
     334             : 
     335             :         return buflen;
     336             : }
     337             : 
     338             : CS_RETCODE
     339           0 : _ct_props_dstr(CS_CONNECTION * con, DSTR *s, CS_INT action, CS_VOID * buffer, CS_INT buflen, CS_INT * out_len)
     340             : {
     341           0 :         if (action == CS_SET) {
     342           0 :                 buflen = _ct_get_string_length(buffer, buflen);
     343           0 :                 if (buflen < 0) {
     344             :                         /* TODO what error ?? */
     345             :                         /* _ctclient_msg(NULL, con, "ct_con_props(SET,APPNAME)", 1, 1, 1, 5, "%d, %s", buflen, "buflen"); */
     346             :                         return CS_FAIL;
     347             :                 }
     348           0 :                 if (tds_dstr_copyn(s, buffer, buflen) != NULL)
     349             :                         return CS_SUCCEED;
     350           0 :         } else if (action == CS_GET) {
     351           0 :                 if (out_len)
     352           0 :                         *out_len = tds_dstr_len(s);
     353           0 :                 strlcpy((char *) buffer, tds_dstr_cstr(s), buflen);
     354           0 :                 return CS_SUCCEED;
     355           0 :         } else if (action == CS_CLEAR) {
     356           0 :                 tds_dstr_empty(s);
     357           0 :                 return CS_SUCCEED;
     358             :         }
     359             :         return CS_FAIL;
     360             : }

Generated by: LCOV version 1.13