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 : }
|