LCOV - code coverage report
Current view: top level - src/ctlib - ctutil.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 90 105 85.7 %
Date: 2025-01-18 11:50:39 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          40 :         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          40 : _ct_handle_client_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     115             : {
     116             :         CS_CLIENTMSG errmsg;
     117          40 :         CS_CONNECTION *con = NULL;
     118          40 :         CS_CONTEXT *ctx = NULL;
     119          40 :         int ret = (int) CS_SUCCEED;
     120             : 
     121          40 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_client_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     122             : 
     123          40 :         if (tds && tds_get_parent(tds)) {
     124          40 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     125             :         }
     126             : 
     127          40 :         memset(&errmsg, '\0', sizeof(errmsg));
     128          40 :         errmsg.msgnumber = msg->msgno;
     129          80 :         errmsg.severity = _ct_translate_severity(msg->severity);
     130          40 :         strlcpy(errmsg.msgstring, msg->message, sizeof(errmsg.msgstring));
     131          40 :         errmsg.msgstringlen = strlen(errmsg.msgstring);
     132          40 :         errmsg.osstring[0] = '\0';
     133          40 :         errmsg.osstringlen = 0;
     134             :         /* if there is no connection, attempt to call the context handler */
     135          40 :         if (!con) {
     136           0 :                 ctx = (CS_CONTEXT *) ctx_tds->parent;
     137           0 :                 if (ctx->clientmsg_cb)
     138           0 :                         ret = ctx->clientmsg_cb(ctx, con, &errmsg);
     139          40 :         } else if (con->clientmsg_cb)
     140          28 :                 ret = con->clientmsg_cb(con->ctx, con, &errmsg);
     141          12 :         else if (con->ctx->clientmsg_cb)
     142           4 :                 ret = con->ctx->clientmsg_cb(con->ctx, con, &errmsg);
     143             :                 
     144             :         /*
     145             :          * The return code from the error handler is either CS_SUCCEED or CS_FAIL.
     146             :          * This function was called by libtds with some kind of communications failure, and there are
     147             :          * no cases in which "succeed" could mean anything: In most cases, the function is going to fail
     148             :          * no matter what.  
     149             :          *
     150             :          * Timeouts are a different matter; it's up to the client to decide whether to continue
     151             :          * waiting or to abort the operation and close the socket.  ct-lib applications do their 
     152             :          * own cancel processing -- they can call ct_cancel from within the error handler -- so 
     153             :          * they don't need to return TDS_INT_TIMEOUT.  They can, however, return TDS_INT_CONTINUE
     154             :          * or TDS_INT_CANCEL.  We map the client's return code to those. 
     155             :          *
     156             :          * Only for timeout errors does TDS_INT_CANCEL cause libtds to break the connection. 
     157             :          */
     158          40 :         if (msg->msgno == TDSETIME) {
     159          24 :                 switch (ret) {
     160             :                 case CS_SUCCEED:        return TDS_INT_CONTINUE;
     161           8 :                 case CS_FAIL:           return TDS_INT_CANCEL;
     162             :                 }
     163             :         }
     164          16 :         return TDS_INT_CANCEL;
     165             : }
     166             : 
     167             : /*
     168             :  * interrupt handler, installed on demand to avoid gratuitously interfering
     169             :  * with tds_select's optimization for the no-handler case */
     170             : int
     171          48 : _ct_handle_interrupt(void * ptr)
     172             : {
     173          48 :         CS_CONNECTION *con = (CS_CONNECTION *) ptr;
     174          48 :         if (con->interrupt_cb)
     175          48 :                 return (*con->interrupt_cb)(con);
     176           0 :         else if (con->ctx->interrupt_cb)
     177           0 :                 return (*con->ctx->interrupt_cb)(con);
     178             :         else
     179             :                 return TDS_INT_CONTINUE;
     180             : }
     181             : 
     182             : /* message handler */
     183             : TDSRET
     184        3292 : _ct_handle_server_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
     185             : {
     186             :         CS_SERVERMSG_INTERNAL errmsg;
     187        3292 :         CS_CONNECTION *con = NULL;
     188             :         CS_CONTEXT *ctx;
     189             :         CS_SERVERMSG_COMMON2 *common2;
     190        3292 :         CS_RETCODE ret = CS_SUCCEED;
     191             : 
     192        3292 :         tdsdump_log(TDS_DBG_FUNC, "_ct_handle_server_message(%p, %p, %p)\n", ctx_tds, tds, msg);
     193             : 
     194        3292 :         if (tds && tds_get_parent(tds))
     195        3292 :                 con = (CS_CONNECTION *) tds_get_parent(tds);
     196             : 
     197        3292 :         ctx = con ? con->ctx : (CS_CONTEXT *) ctx_tds->parent;
     198             : 
     199        3292 :         memset(&errmsg, '\0', sizeof(errmsg));
     200        3292 :         errmsg.common.msgnumber = msg->msgno;
     201        3292 :         strlcpy(errmsg.common.text, msg->message, sizeof(errmsg.common.text));
     202        3292 :         errmsg.common.textlen = strlen(errmsg.common.text);
     203        3292 :         errmsg.common.state = msg->state;
     204        3292 :         errmsg.common.severity = msg->severity;
     205             : 
     206             : #define MIDDLE_PART(part) do { \
     207             :         common2 = (CS_SERVERMSG_COMMON2 *) &(errmsg.part.line); \
     208             :         if (msg->server) { \
     209             :                 errmsg.part.svrnlen = strlen(msg->server); \
     210             :                 strlcpy(errmsg.part.svrname, msg->server, sizeof(errmsg.part.svrname)); \
     211             :         } \
     212             :         if (msg->proc_name) { \
     213             :                 errmsg.part.proclen = strlen(msg->proc_name); \
     214             :                 strlcpy(errmsg.part.proc, msg->proc_name, sizeof(errmsg.part.proc)); \
     215             :         } \
     216             : } while(0)
     217             : 
     218        3292 :         if (ctx->use_large_identifiers)
     219        1646 :                 MIDDLE_PART(large);
     220             :         else
     221        1646 :                 MIDDLE_PART(small);
     222             : #undef MIDDLE_PART
     223             : 
     224        3292 :         common2->sqlstate[0] = 0;
     225        3292 :         if (msg->sql_state)
     226          64 :                 strlcpy((char *) common2->sqlstate, msg->sql_state, sizeof(common2->sqlstate));
     227        3292 :         common2->sqlstatelen = strlen((char *) common2->sqlstate);
     228        3292 :         common2->line = msg->line_number;
     229             : 
     230             :         /* if there is no connection, attempt to call the context handler */
     231        3292 :         if (!con) {
     232           0 :                 if (ctx->servermsg_cb)
     233           0 :                         ret = ctx->servermsg_cb(ctx, con, &errmsg.user);
     234        3292 :         } else if (con->servermsg_cb) {
     235           8 :                 ret = con->servermsg_cb(ctx, con, &errmsg.user);
     236        3284 :         } else if (ctx->servermsg_cb) {
     237        3278 :                 ret = ctx->servermsg_cb(ctx, con, &errmsg.user);
     238             :         }
     239        3286 :         return ret == CS_SUCCEED ? TDS_SUCCESS : TDS_FAIL;
     240             : }
     241             : 
     242             : /**
     243             :  * Check if a give version supports large identifiers.
     244             :  */
     245             : bool
     246        2208 : _ct_is_large_identifiers_version(CS_INT version)
     247             : {
     248        2208 :         switch (version) {
     249             :         case 112:
     250             :         case 1100:
     251             :         case 12500:
     252             :         case 15000:
     253             :                 return false;
     254             :         }
     255        1104 :         return true;
     256             : }
     257             : 
     258             : const CS_DATAFMT_COMMON *
     259        2058 : _ct_datafmt_common(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt)
     260             : {
     261        2058 :         if (!datafmt)
     262             :                 return NULL;
     263        2042 :         if (ctx->use_large_identifiers)
     264        1021 :                 return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_LARGE *) datafmt)->datatype);
     265        1021 :         return (const CS_DATAFMT_COMMON *) &(((const CS_DATAFMT_SMALL *) datafmt)->datatype);
     266             : }
     267             : 
     268             : /**
     269             :  * Converts CS_DATAFMT input parameter to CS_DATAFMT_LARGE.
     270             :  * @param ctx      CTLib context
     271             :  * @param datafmt  Input parameter
     272             :  * @param fmtbuf   Buffer to use in case conversion is required
     273             :  * @return parameter converted to large
     274             :  */
     275             : const CS_DATAFMT_LARGE *
     276         292 : _ct_datafmt_conv_in(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     277             : {
     278             :         const CS_DATAFMT_SMALL *small;
     279             : 
     280         292 :         if (!datafmt)
     281             :                 return NULL;
     282             : 
     283             :         /* read directly from input */
     284         292 :         if (ctx->use_large_identifiers)
     285             :                 return (CS_DATAFMT_LARGE *) datafmt;
     286             : 
     287             :         /* convert small format to large */
     288         146 :         small = (const CS_DATAFMT_SMALL *) datafmt;
     289             : 
     290         146 :         strlcpy(fmtbuf->name, small->name, sizeof(fmtbuf->name));
     291         146 :         fmtbuf->namelen = strlen(fmtbuf->name);
     292         146 :         *((CS_DATAFMT_COMMON *) &fmtbuf->datatype) = *((CS_DATAFMT_COMMON *) &small->datatype);
     293         146 :         return fmtbuf;
     294             : }
     295             : 
     296             : /**
     297             :  * Prepares to Convert CS_DATAFMT output parameter to CS_DATAFMT_LARGE.
     298             :  * @param ctx      CTLib context
     299             :  * @param datafmt  Input parameter
     300             :  * @param fmtbuf   Buffer to use in case conversion is required
     301             :  * @return parameter converted to large
     302             :  */
     303             : CS_DATAFMT_LARGE *
     304         728 : _ct_datafmt_conv_prepare(CS_CONTEXT * ctx, CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     305             : {
     306         728 :         if (!datafmt)
     307             :                 return NULL;
     308             : 
     309             :         /* write directly to output */
     310         728 :         if (ctx->use_large_identifiers)
     311             :                 return (CS_DATAFMT_LARGE *) datafmt;
     312             : 
     313         364 :         return fmtbuf;
     314             : }
     315             : 
     316             : /**
     317             :  * Converts CS_DATAFMT output parameter to CS_DATAFMT_LARGE after setting it.
     318             :  * @param datafmt  Input parameter
     319             :  * @param fmtbuf   Buffer to use in case conversion is required. You should pass
     320             :  *                 value returned by _ct_datafmt_conv_prepare().
     321             :  */
     322             : void
     323         728 : _ct_datafmt_conv_back(CS_DATAFMT * datafmt, CS_DATAFMT_LARGE *fmtbuf)
     324             : {
     325             :         CS_DATAFMT_SMALL *small;
     326             : 
     327             :         /* already right format */
     328         728 :         if ((void *) datafmt == (void*) fmtbuf)
     329             :                 return;
     330             : 
     331             :         /* convert large format to small */
     332         364 :         small = (CS_DATAFMT_SMALL *) datafmt;
     333             : 
     334         364 :         strlcpy(small->name, fmtbuf->name, sizeof(small->name));
     335         364 :         small->namelen = strlen(small->name);
     336         364 :         *((CS_DATAFMT_COMMON *) &small->datatype) = *((CS_DATAFMT_COMMON *) &fmtbuf->datatype);
     337             : }
     338             : 
     339             : /**
     340             :  * Get length of a string buffer
     341             :  *
     342             :  * @return length of string or original negative value if error.
     343             :  */
     344             : CS_INT
     345        5104 : _ct_get_string_length(const char *buf, CS_INT buflen)
     346             : {
     347        5184 :         if (buflen >= 0)
     348             :                 return buflen;
     349             : 
     350        4982 :         if (buflen == CS_NULLTERM)
     351        4870 :                 return (CS_INT) strlen(buf);
     352             : 
     353             :         return buflen;
     354             : }
     355             : 
     356             : CS_RETCODE
     357          80 : _ct_props_dstr(CS_CONNECTION * con TDS_UNUSED, DSTR *s, CS_INT action, CS_VOID * buffer, CS_INT buflen, CS_INT * out_len)
     358             : {
     359          80 :         if (action == CS_SET) {
     360          80 :                 buflen = _ct_get_string_length(buffer, buflen);
     361          80 :                 if (buflen < 0) {
     362             :                         /* TODO what error ?? */
     363             :                         /* _ctclient_msg(NULL, con, "ct_con_props(SET,APPNAME)", 1, 1, 1, 5, "%d, %s", buflen, "buflen"); */
     364             :                         return CS_FAIL;
     365             :                 }
     366          80 :                 if (tds_dstr_copyn(s, buffer, buflen) != NULL)
     367             :                         return CS_SUCCEED;
     368           0 :         } else if (action == CS_GET) {
     369           0 :                 if (out_len)
     370           0 :                         *out_len = tds_dstr_len(s);
     371           0 :                 strlcpy((char *) buffer, tds_dstr_cstr(s), buflen);
     372           0 :                 return CS_SUCCEED;
     373           0 :         } else if (action == CS_CLEAR) {
     374           0 :                 tds_dstr_empty(s);
     375           0 :                 return CS_SUCCEED;
     376             :         }
     377             :         return CS_FAIL;
     378             : }

Generated by: LCOV version 1.13