1 /*
2    Copyright (C) 2008  Ulric Eriksson <ulric@siag.nu>
3 
4    Based on tsql.c from freetds:
5    FreeTDS - Library of routines accessing Sybase and Microsoft databases
6    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
7 
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library General Public
10    License as published by the Free Software Foundation; either
11    version 2 of the Licence, or (at your option) any later version.
12 
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17 
18    You should have received a copy of the GNU Library General Public
19    License along with this library; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21    MA 02111-1307, USA.
22  */
23 
24 #ifdef HAVE_LIBTDS
25 
26 # include <sys/time.h>
27 # include <time.h>
28 
29 #include <stdio.h>
30 #include <assert.h>
31 #include <ctype.h>
32 
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <locale.h>
38 
39 #ifdef HAVE_LANGINFO_H
40 #include <langinfo.h>
41 #endif /* HAVE_LANGINFO_H */
42 
43 #include <tds.h>
44 #include <tdsconvert.h>
45 #include "common.h"
46 #include "sdb.h"
47 
48 enum
49 {
50 	OPT_VERSION =  0x01,
51 	OPT_TIMER =    0x02,
52 	OPT_NOFOOTER = 0x04,
53 	OPT_NOHEADER = 0x08,
54 	OPT_QUIET =    0x10
55 };
56 
57 struct conn_info {
58 	TDSSOCKET *tds;
59 	TDSLOGIN *login;
60 	TDSCONTEXT *context;
61 	TDSCONNECTION *connection;
62 };
63 
64 #define QUIET (sdb_debuglevel == 0)
65 
sdb_handle_message(const TDSCONTEXT * context,TDSSOCKET * tds,TDSMESSAGE * msg)66 static int sdb_handle_message(const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg)
67 {
68 	if (msg->msgno == 0) {
69 		sdb_debug("%s", msg->message);
70 		return 0;
71 	}
72 
73 	if (msg->msgno != 5701 && msg->msgno != 5703
74 	    && msg->msgno != 20018) {
75 		sdb_debug("Msg %d, Level %d, State %d, Server %s, Line %d\n%s",
76 			msg->msgno, msg->severity, msg->state, msg->server, msg->line_number, msg->message);
77 	}
78 
79 	return 0;
80 }
81 
do_query(TDSSOCKET * tds,char * buf,int opt_flags,int (* callback)(int,char **,void *),void * closure)82 static int do_query(TDSSOCKET* tds, char *buf, int opt_flags,
83 		int (*callback)(int, char **, void *), void *closure)
84 {
85 	int rows = -1;
86 	int rc, i;
87 	TDSCOLUMN *col;
88 	int ctype;
89 	CONV_RESULT dres;
90 	unsigned char *src;
91 	TDS_INT srclen;
92 	TDS_INT resulttype;
93 	struct timeval start, stop;
94 	char message[128];
95 	char **p;
96 
97 	rc = tds_submit_query(tds, buf);
98 	if (rc != TDS_SUCCEED) {
99 		sdb_debug("tds_submit_query() failed");
100 		return -1;
101 	}
102 
103 	while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCEED) {
104 		if (opt_flags & OPT_TIMER) {
105 			gettimeofday(&start, NULL);
106 		}
107 		switch (resulttype) {
108 		case TDS_ROWFMT_RESULT:
109 			if (sdb_debuglevel && tds->current_results) {
110 				for (i = 0; i < tds->current_results->num_cols; i++) {
111 					sdb_debug("Column %d = '%s'", i, tds->current_results->columns[i]->column_name);
112 				}
113 			}
114 			break;
115 		case TDS_COMPUTE_RESULT:
116 		case TDS_ROW_RESULT:
117 			rows = 0;
118 			p = sdb_malloc(tds->current_results->num_cols * sizeof *p);
119 
120 			while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE)) == TDS_SUCCEED) {
121 				if (resulttype != TDS_ROW_RESULT && resulttype != TDS_COMPUTE_RESULT)
122 					break;
123 
124 				rows++;
125 
126 				if (!tds->current_results)
127 					continue;
128 
129 				for (i = 0; i < tds->current_results->num_cols; i++) {
130 					col = tds->current_results->columns[i];
131 					if (col->column_cur_size < 0) {
132 						if (sdb_debuglevel)
133 							sdb_debug("[%d,%d] %s", rows, i, "NULL");
134 						p[i] = NULL;
135 						continue;
136 					}
137 					ctype = tds_get_conversion_type(col->column_type, col->column_size);
138 
139 					src = &(tds->current_results->current_row[col->column_offset]);
140 					if (is_blob_type(col->column_type))
141 						src = (unsigned char *) ((TDSBLOB *) src)->textvalue;
142 					srclen = col->column_cur_size;
143 
144 
145 					if (tds_convert(tds->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBVARCHAR, &dres) < 0)
146 						continue;
147 					if (sdb_debuglevel)
148 						sdb_debug("[%d,%d] %s", rows, i, dres.c);
149 					p[i] = sdb_strdup(dres.c);
150 					free(dres.c);
151 				}
152 				if (sdb_debuglevel)
153 					sdb_debug("Call the callback here");
154 				(*callback)(tds->current_results->num_cols, p, closure);
155 				for (i = 0; i < tds->current_results->num_cols; i++) {
156 					sdb_free(p[i]);
157 				}
158 			}
159 			sdb_free(p);
160 			break;
161 		case TDS_STATUS_RESULT:
162 			sdb_debug("(return status = %d)", tds->ret_status);
163 			break;
164 		default:
165 			break;
166 		}
167 
168 		if (opt_flags & OPT_VERSION) {
169 			char version[64];
170 			int line = 0;
171 
172 			line = tds_version(tds, version);
173 			if (line) {
174 				sprintf(message, "using TDS version %s", version);
175 				tds_client_msg(tds->tds_ctx, tds, line, line, line, line, message);
176 			}
177 		}
178 		if (opt_flags & OPT_TIMER) {
179 			gettimeofday(&stop, NULL);
180 			sprintf(message, "Total time for processing %d rows: %ld msecs\n",
181 				rows, (long) ((stop.tv_sec - start.tv_sec) * 1000) + ((stop.tv_usec - start.tv_usec) / 1000));
182 			tds_client_msg(tds->tds_ctx, tds, 1, 1, 1, 1, message);
183 		}
184 	}
185 	return rows;
186 }
187 
populate_login(TDSLOGIN * login,char * url)188 static void populate_login(TDSLOGIN *login, char *url)
189 {
190 	char *hostname = sdb_url_value(url, "host");
191 	char *servername = sdb_url_value(url, "server");
192 	char *username = sdb_url_value(url, "uid");
193 	char *password = sdb_url_value(url, "pwd");
194 	char *confile = sdb_url_value(url, "cfg");
195 	char *portname = sdb_url_value(url, "port");
196 	int port = 1433;
197 	const char *locale = NULL;
198 	char *charset = NULL;
199 
200 	if (portname) port = atoi(portname);
201 
202 	setlocale(LC_ALL, "");
203 	locale = setlocale(LC_ALL, NULL);
204 
205 #if HAVE_LOCALE_CHARSET
206 	charset = locale_charset();
207 #endif
208 #if HAVE_NL_LANGINFO && defined(CODESET)
209 	if (!charset)
210 		charset = nl_langinfo(CODESET);
211 #endif
212 
213 	if (locale)
214 		if (!QUIET) sdb_debug("locale is \"%s\"", locale);
215 	if (charset) {
216 		if (!QUIET) sdb_debug("locale charset is \"%s\"", charset);
217 	} else {
218 		charset = "ISO-8859-1";
219 		if (!QUIET) sdb_debug("using default charset \"%s\"", charset);
220 	}
221 
222 	/* validate parameters */
223 	if (!servername && !hostname) {
224 		sdb_error("Missing server or host");
225 	}
226 	if (hostname && !port) {
227 		sdb_error("Missing port");
228 	}
229 	if (!username) {
230 		sdb_error("Missing user");
231 	}
232 	if (!password) {
233 		sdb_error("Missing password");
234 	}
235 
236 	/* all validated, let's do it */
237 
238 	/* if it's a servername */
239 
240 	if (servername) {
241 		tds_set_user(login, username);
242 		tds_set_app(login, "TSQL");
243 		tds_set_library(login, "TDS-Library");
244 		tds_set_server(login, servername);
245 		tds_set_client_charset(login, charset);
246 		tds_set_language(login, "us_english");
247 		tds_set_passwd(login, password);
248 		if (confile) {
249 			tds_set_interfaces_file_loc(confile);
250 		}
251 		/* else we specified hostname/port */
252 	} else {
253 		tds_set_user(login, username);
254 		tds_set_app(login, "TSQL");
255 		tds_set_library(login, "TDS-Library");
256 		tds_set_server(login, hostname);
257 		tds_set_port(login, port);
258 		tds_set_client_charset(login, charset);
259 		tds_set_language(login, "us_english");
260 		tds_set_passwd(login, password);
261 	}
262 
263 	sdb_free(hostname);
264 	sdb_free(servername);
265 	sdb_free(username);
266 	sdb_free(password);
267 	sdb_free(confile);
268 	sdb_free(portname);
269 }
270 
sdb_tds_open(char * url)271 static void *sdb_tds_open(char *url)
272 {
273 	struct conn_info *ci = sdb_malloc(sizeof *ci);
274 
275 	if (sdb_debuglevel) sdb_debug("sdb_tds_open(%s)", url);
276 	/* grab a login structure */
277 	ci->login = tds_alloc_login();
278 
279 	ci->context = tds_alloc_context(NULL);
280 	if (ci->context->locale && !ci->context->locale->date_fmt) {
281 		/* set default in case there's no locale file */
282 		ci->context->locale->date_fmt = strdup("%b %e %Y %I:%M%p");
283 	}
284 
285 	ci->context->msg_handler = sdb_handle_message;
286 	ci->context->err_handler = sdb_handle_message;
287 
288 	/* process all the command line args into the login structure */
289 	populate_login(ci->login, url);
290 
291 	/* Try to open a connection */
292 	ci->tds = tds_alloc_socket(ci->context, 512);
293 	tds_set_parent(ci->tds, NULL);
294 	ci->connection = tds_read_config_info(NULL, ci->login, ci->context->locale);
295 	if (!ci->connection || tds_connect(ci->tds, ci->connection) == TDS_FAIL) {
296 		tds_free_socket(ci->tds);
297 		tds_free_connection(ci->connection);
298 		sdb_debug("There was a problem connecting to the server");
299 		sdb_free(ci);
300 		return NULL;
301 	}
302 	tds_free_connection(ci->connection);
303 	return ci;
304 }
305 
sdb_tds_close(void * db)306 static int sdb_tds_close(void *db)
307 {
308 	struct conn_info *ci = db;
309 
310 	if (sdb_debuglevel) sdb_debug("sdb_tds_close(%p)", db);
311 
312 	/* close up shop */
313 	tds_free_socket(ci->tds);
314 	tds_free_login(ci->login);
315 	tds_free_context(ci->context);
316 
317 	sdb_free(ci);
318 	return 0;
319 }
320 
321 /* Parameters:
322    pdb = pointer to previously opened database connection, or NULL
323    url = url string
324    query = query string
325    callback = function to send the data to
326    closure = pointer to data passed from application
327 */
tds_driver(void * pdb,char * url,char * query,int (* callback)(int,char **,void *),void * closure)328 static int tds_driver(void *pdb, char *url, char *query,
329 	int (*callback)(int, char **, void *), void *closure)
330 {
331 	struct conn_info *ci;
332 	int opt_flags = 0;
333 	int rows;
334 
335 	if (sdb_debuglevel) sdb_debug("tds_driver(%p, %s, %s, %p, %p)",
336 				pdb, url, query, callback, closure);
337 	if (pdb == NULL) ci = sdb_tds_open(url);
338 	else ci = pdb;
339 	if (ci == NULL) {
340 		sdb_debug("Can't open database");
341 		return -1;
342 	}
343 
344 	rows = do_query(ci->tds, query, opt_flags, callback, closure);
345 
346 	if (pdb == NULL) sdb_tds_close(ci);
347 
348 	return rows;
349 }
350 
sdb_init_tds(void)351 void sdb_init_tds(void)
352 {
353 	sdb_register_driver("tds", tds_driver,
354 		sdb_tds_open, sdb_tds_close);
355 }
356 
357 #else
358 
sdb_init_tds(void)359 void sdb_init_tds(void)
360 {
361 	;
362 }
363 
364 #endif	/* HAVE_LIBTDS */
365