1 /*
2  * ProFTPD: mod_sql_odbc -- Support for connecting to databases via ODBC
3  * Copyright (c) 2003-2020 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders gives permission to link this program
20  * with OpenSSL, and distribute the resulting executable, without including
21  * the source code for OpenSSL in the source distribution.
22  */
23 
24 #include "conf.h"
25 #include "mod_sql.h"
26 
27 #define MOD_SQL_ODBC_VERSION    "mod_sql_odbc/0.3.4"
28 
29 /* Make sure the version of proftpd is as necessary. */
30 #if PROFTPD_VERSION_NUMBER < 0x0001030602
31 # error "ProFTPD 1.3.6rc2 or later required"
32 #endif
33 
34 #include "sql.h"
35 #include "sqlext.h"
36 
37 module sql_odbc_module;
38 
39 typedef struct db_conn_struct {
40   char *dsn;
41   char *user;
42   char *pass;
43 
44   HENV envh;
45   HDBC dbh;
46   HSTMT sth;
47 
48   unsigned int state;
49 
50 } db_conn_t;
51 
52 #define SQLODBC_HAVE_ENV_HANDLE         0x0001
53 #define SQLODBC_HAVE_DBC_HANDLE         0x0002
54 #define SQLODBC_HAVE_STMT_HANDLE        0x0004
55 #define SQLODBC_HAVE_INFO		0x0010
56 
57 typedef struct conn_entry_struct {
58   char *name;
59   void *data;
60 
61   /* Timer handling */
62   int timer;
63   int ttl;
64 
65   /* Connection handling */
66   unsigned int nconn;
67 
68 } conn_entry_t;
69 
70 #define DEF_CONN_POOL_SIZE 10
71 
72 static pool *conn_pool = NULL;
73 static array_header *conn_cache = NULL;
74 static int odbc_version = SQL_OV_ODBC3;
75 static const char *odbc_version_str = "ODBCv3";
76 
77 /* Default to using the LIMIT clause.  Some database drivers prefer
78  * ROWNUM (e.g. Oracle), and others prefer TOP (e.g. TDS).
79  */
80 static int use_limit_clause = TRUE;
81 static int use_rownum_clause = FALSE;
82 static int use_top_clause = FALSE;
83 
84 MODRET sqlodbc_close(cmd_rec *);
85 
sqlodbc_get_conn(char * name)86 static conn_entry_t *sqlodbc_get_conn(char *name) {
87   register unsigned int i = 0;
88 
89   if (!name)
90     return NULL;
91 
92   for (i = 0; i < conn_cache->nelts; i++) {
93     conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
94 
95     if (strcmp(name, entry->name) == 0)
96       return entry;
97   }
98 
99   return NULL;
100 }
101 
sqlodbc_add_conn(pool * p,char * name,db_conn_t * conn)102 static void *sqlodbc_add_conn(pool *p, char *name, db_conn_t *conn) {
103   conn_entry_t *entry = NULL;
104 
105   if (p == NULL ||
106       name == NULL ||
107       conn == NULL) {
108     errno = EINVAL;
109     return NULL;
110   }
111 
112   if (sqlodbc_get_conn(name) != NULL) {
113     errno = EEXIST;
114     return NULL;
115   }
116 
117   entry = (conn_entry_t *) pcalloc(p, sizeof(conn_entry_t));
118   entry->name = name;
119   entry->data = conn;
120 
121   *((conn_entry_t **) push_array(conn_cache)) = entry;
122   return entry;
123 }
124 
sqlodbc_timer_cb(CALLBACK_FRAME)125 static int sqlodbc_timer_cb(CALLBACK_FRAME) {
126   register unsigned int i = 0;
127 
128   for (i = 0; i < conn_cache->nelts; i++) {
129     conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
130 
131     if (entry->timer == p2) {
132       cmd_rec *cmd = NULL;
133 
134       sql_log(DEBUG_INFO, "timer expired for connection '%s'", entry->name);
135 
136       cmd = pr_cmd_alloc(conn_pool, 2, entry->name, "1");
137       sqlodbc_close(cmd);
138       destroy_pool(cmd->pool);
139 
140       entry->timer = 0;
141     }
142   }
143 
144   return 0;
145 }
146 
147 static SQLCHAR odbc_state[6];
148 static SQLCHAR odbc_errstr[SQL_MAX_MESSAGE_LENGTH];
149 
sqlodbc_errstr(SQLSMALLINT handle_type,SQLHANDLE handle,SQLCHAR ** statep)150 static const char *sqlodbc_errstr(SQLSMALLINT handle_type, SQLHANDLE handle,
151     SQLCHAR **statep) {
152   SQLSMALLINT odbc_errlen = 0;
153   SQLINTEGER odbc_errno;
154   SQLRETURN res;
155 
156   memset(odbc_state, '\0', sizeof(odbc_state));
157   memset(odbc_errstr, '\0', sizeof(odbc_errstr));
158 
159   /* Ideally, we'd keep calling SQLGetDiagRec() until it returned SQL_NO_DATA,
160    * in order to capture the entire error message stack.
161    */
162 
163   res = SQLGetDiagRec(handle_type, handle, 1, odbc_state, &odbc_errno,
164     odbc_errstr, sizeof(odbc_errstr), &odbc_errlen);
165 
166   if (res != SQL_NO_DATA) {
167     if (statep)
168       *statep = odbc_state;
169 
170     return (const char *) odbc_errstr;
171   }
172 
173   return "(no data)";
174 }
175 
176 /* Liberally borrowed from MySQL-3.23.55's libmysql.c file,
177  * mysql_odbc_escape_string() function.
178  */
sqlodbc_escape_string(char * to,const char * from,size_t fromlen)179 static void sqlodbc_escape_string(char *to, const char *from, size_t fromlen) {
180   const char *end;
181 
182   for (end = from + fromlen; from != end; from++) {
183     switch (*from) {
184       case 0:
185         *to++ = '\\';
186         *to++ = '0';
187         break;
188 
189       case '\n':
190         *to++ = '\\';
191         *to++ = 'n';
192         break;
193 
194       case '\r':
195         *to++ = '\\';
196         *to++ = 'r';
197         break;
198 
199       case '\\':
200         *to++ = '\\';
201         *to++ = '\\';
202         break;
203 
204       case '\'':
205         *to++ = '\'';
206         *to++ = '\'';
207         break;
208 
209       case '"':
210         *to++ = '\\';
211         *to++ = '"';
212         break;
213 
214       case '\032':
215         *to++ = '\\';
216         *to++ = 'Z';
217         break;
218 
219        default:
220          *to++ = *from;
221     }
222   }
223 }
224 
sqlodbc_typestr(SQLSMALLINT type)225 static const char *sqlodbc_typestr(SQLSMALLINT type) {
226   switch (type) {
227     case SQL_CHAR:
228       return "SQL_CHAR";
229 
230     case SQL_VARCHAR:
231       return "SQL_VARCHAR";
232 
233     case SQL_LONGVARCHAR:
234       return "SQL_LONGVARCHAR";
235 
236 #ifdef SQL_WCHAR
237     case SQL_WCHAR:
238       return "SQL_WCHAR";
239 #endif
240 
241 #ifdef SQL_WVARCHAR
242     case SQL_WVARCHAR:
243       return "SQL_WVARCHAR";
244 #endif
245 
246 #ifdef SQL_WLONGVARCHAR
247     case SQL_WLONGVARCHAR:
248       return "SQL_WLONGVARCHAR";
249 #endif
250 
251     case SQL_DECIMAL:
252       return "SQL_DECIMAL";
253 
254     case SQL_NUMERIC:
255       return "SQL_NUMERIC";
256 
257     case SQL_BIT:
258       return "SQL_BIT";
259 
260     case SQL_TINYINT:
261       return "SQL_TINYINT";
262 
263     case SQL_SMALLINT:
264       return "SQL_SMALLINT";
265 
266     case SQL_INTEGER:
267       return "SQL_INTEGER";
268 
269     case SQL_BIGINT:
270       return "SQL_BIGINT";
271 
272     case SQL_REAL:
273       return "SQL_REAL";
274 
275     case SQL_FLOAT:
276       return "SQL_FLOAT";
277 
278     case SQL_DOUBLE:
279       return "SQL_DOUBLE";
280 
281     case SQL_BINARY:
282       return "SQL_BINARY";
283 
284     case SQL_VARBINARY:
285       return "SQL_VARBINARY";
286 
287     case SQL_LONGVARBINARY:
288       return "SQL_LONGVARBINARY";
289 
290     case SQL_TYPE_DATE:
291       return "SQL_TYPE_DATE";
292 
293     case SQL_TYPE_TIME:
294       return "SQL_TYPE_TIME";
295 
296     case SQL_TYPE_TIMESTAMP:
297       return "SQL_TYPE_TIMESTAMP";
298 
299     case SQL_GUID:
300       return "SQL_GUID";
301   }
302 
303   return "[unknown]";
304 }
305 
sqlodbc_strerror(SQLSMALLINT odbc_error)306 static const char *sqlodbc_strerror(SQLSMALLINT odbc_error) {
307   switch (odbc_error) {
308     case SQL_SUCCESS:
309       return "Success";
310 
311     case SQL_SUCCESS_WITH_INFO:
312       return "Success with info";
313 
314 #ifdef SQL_NO_DATA
315     case SQL_NO_DATA:
316       return "No data";
317 #endif
318 
319     case SQL_ERROR:
320       return "Error";
321 
322     case SQL_INVALID_HANDLE:
323       return "Invalid handle";
324 
325     case SQL_STILL_EXECUTING:
326       return "Still executing";
327 
328     case SQL_NEED_DATA:
329       return "Need data";
330   }
331 
332   return "(unknown)";
333 }
334 
sqlodbc_get_error(cmd_rec * cmd,SQLSMALLINT handle_type,SQLHANDLE handle)335 static modret_t *sqlodbc_get_error(cmd_rec *cmd, SQLSMALLINT handle_type,
336     SQLHANDLE handle) {
337   SQLCHAR state[6], errstr[SQL_MAX_MESSAGE_LENGTH];
338   SQLSMALLINT errlen;
339   SQLINTEGER odbc_errno = 0;
340   SQLRETURN res;
341   unsigned int recno = 1;
342   char numstr[20];
343 
344   memset(errstr, '\0', sizeof(errstr));
345   pr_snprintf((char *) errstr, sizeof(errstr)-1, "%s", "(no data)");
346 
347   res = SQLGetDiagRec(handle_type, handle, recno++, state, &odbc_errno,
348     errstr, sizeof(errstr), &errlen);
349   while (res != SQL_NO_DATA
350 #ifdef SQL_ERROR
351          && res != SQL_ERROR
352 #endif /* SQL_ERROR */
353 #ifdef SQL_INVALID_HANDLE
354          && res != SQL_INVALID_HANDLE
355 #endif /* SQL_INVALID_HANDLE */
356         ) {
357     pr_signals_handle();
358 
359     sql_log(DEBUG_FUNC, "odbc error: [%d] %s", odbc_errno, errstr);
360 
361     res = SQLGetDiagRec(handle_type, handle, recno++, state, &odbc_errno,
362       errstr, sizeof(errstr), &errlen);
363   }
364 
365   /* This will return the last error retrieved.  This is OK, since we
366    * have logged all the previous errors.
367    */
368   memset(numstr, '\0', sizeof(numstr));
369   pr_snprintf(numstr, 20, "%d", (int) odbc_errno);
370 
371   return PR_ERROR_MSG(cmd, numstr, (char *) errstr);
372 }
373 
sqlodbc_get_data(cmd_rec * cmd,db_conn_t * conn)374 static modret_t *sqlodbc_get_data(cmd_rec *cmd, db_conn_t *conn) {
375   sql_data_t *sd = NULL;
376   array_header *dh = NULL;
377   SQLSMALLINT ncols;
378   SQLRETURN res;
379 
380   if (!conn)
381     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
382 
383   sd = (sql_data_t *) pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
384 
385   res = SQLNumResultCols(conn->sth, &ncols);
386   if (res != SQL_SUCCESS &&
387       res != SQL_SUCCESS_WITH_INFO) {
388     char *err;
389     SQLCHAR *state = NULL;
390 
391     err = (char *) sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, &state);
392 
393     sql_log(DEBUG_WARN, "error getting column count: %s", err);
394     return PR_ERROR_MSG(cmd, state ? (char *) state : "0", err);
395   }
396   sd->fnum = (unsigned long) ncols;
397   sd->rnum = 0;
398 
399   dh = (array_header *) make_array(cmd->tmp_pool, sd->fnum, sizeof(char *));
400 
401   /* NOTE: this could be optimised, caching the SQLDescribeCol() results
402    * (used mainly to get the column datatype) so that that call is not
403    * needed for subsequent rows, just the first row.
404    */
405   while (TRUE) {
406     int done_fetching = FALSE;
407     register unsigned int i;
408 
409     pr_signals_handle();
410 
411     res = SQLFetch(conn->sth);
412 
413     switch (res) {
414       case SQL_ERROR:
415         sql_log(DEBUG_WARN, "error fetching row %lu: %s", sd->rnum + 1,
416           sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
417         return PR_ERROR(cmd);
418 
419       case SQL_NO_DATA:
420         done_fetching = TRUE;
421         break;
422 
423       case SQL_SUCCESS_WITH_INFO:
424         /* Note: this deliberately falls through to the SQL_SUCCESS case. */
425         sql_log(DEBUG_WARN, "fetching row %lu: %s", sd->rnum + 1,
426           sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
427 
428       case SQL_SUCCESS:
429         sd->rnum++;
430 
431         for (i = 1; i <= sd->fnum; i++) {
432           SQLCHAR col_name[80];
433           SQLSMALLINT col_namelen, col_type, col_digits, col_nullable;
434           SQLULEN col_size;
435 
436           if (SQLDescribeCol(conn->sth, i, col_name, sizeof(col_name),
437               &col_namelen, &col_type, &col_size, &col_digits,
438               &col_nullable) == SQL_SUCCESS) {
439             SQLSMALLINT col_ctype;
440 
441             /* mod_sql expects to handle all returned data elements as strings
442              * (even though it converts some to numbers), so we need to
443              * stringify any numeric datatypes returned.
444              */
445             switch (col_type) {
446               case SQL_CHAR:
447               case SQL_LONGVARCHAR:
448               case SQL_VARCHAR:
449 #ifdef SQL_WVARCHAR
450               case SQL_WVARCHAR:
451 #endif
452                 col_ctype = SQL_C_CHAR;
453 
454                 if (col_size) {
455                   SQLLEN buflen;
456                   SQLCHAR *buf = pcalloc(cmd->tmp_pool, ++col_size);
457 
458                   if (SQLGetData(conn->sth, i, col_ctype, buf, col_size,
459                       &buflen) != SQL_SUCCESS) {
460                     sql_log(DEBUG_WARN, "error getting %s data for column %u, "
461                       "row %lu: %s", sqlodbc_typestr(col_ctype), i,
462                       sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
463                         NULL));
464                     *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
465                     continue;
466                   }
467 
468                   if (buflen == SQL_NO_TOTAL) {
469                     sql_log(DEBUG_WARN, "notice: unable to determine total "
470                       "number of bytes remaining for %s column %u, row %lu",
471                       sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
472                     *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
473                     break;
474 
475                   } else if (buflen == SQL_NULL_DATA) {
476                     sql_log(DEBUG_WARN, "notice: data is NULL for %s column "
477                       "%u, row %lu", sqlodbc_typestr(col_ctype), i,
478                       sd->rnum + 1);
479                     *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
480                     break;
481                   }
482 
483                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool,
484                     (char *) buf);
485 
486                 } else {
487                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
488                 }
489 
490                 break;
491 
492               /* Note: ODBC doesn't seem to provide a way of knowing whether
493                * the value in the column is supposed to be signed or unsigned.
494                * Could cause problems later on.
495                */
496 
497               case SQL_TINYINT: {
498                 char buf[64];
499                 short col_cval;
500                 SQLLEN ind;
501 
502                 col_ctype = SQL_C_TINYINT;
503 
504                 if (SQLGetData(conn->sth, i, col_ctype,
505                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
506                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
507                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
508                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
509                       NULL));
510                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
511                   break;
512                 }
513 
514                 if (ind == SQL_NO_TOTAL) {
515                   sql_log(DEBUG_WARN, "notice: unable to determine total "
516                     "number of bytes remaining for %s column %u, row %lu",
517                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
518                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
519                   break;
520 
521                 } else if (ind == SQL_NULL_DATA) {
522                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
523                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
524                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
525                   break;
526                 }
527 
528                 pr_snprintf(buf, sizeof(buf), "%hd", col_cval);
529                 buf[sizeof(buf)-1] = '\0';
530 
531                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
532                 break;
533               }
534 
535               case SQL_SMALLINT: {
536                 char buf[64];
537                 short col_cval;
538                 SQLLEN ind;
539 
540                 col_ctype = SQL_C_SHORT;
541 
542                 if (SQLGetData(conn->sth, i, col_ctype,
543                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
544                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
545                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
546                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
547                       NULL));
548                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
549                   break;
550                 }
551 
552                 if (ind == SQL_NO_TOTAL) {
553                   sql_log(DEBUG_WARN, "notice: unable to determine total "
554                     "number of bytes remaining for %s column %u, row %lu",
555                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
556                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
557                   break;
558 
559                 } else if (ind == SQL_NULL_DATA) {
560                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
561                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
562                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
563                   break;
564                 }
565 
566                 pr_snprintf(buf, sizeof(buf), "%hd", col_cval);
567                 buf[sizeof(buf)-1] = '\0';
568 
569                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
570                 break;
571               }
572 
573               case SQL_INTEGER: {
574                 char buf[64];
575                 int col_cval;
576                 SQLLEN ind;
577 
578                 col_ctype = SQL_INTEGER;
579 
580                 if (SQLGetData(conn->sth, i, col_ctype,
581                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
582                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
583                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
584                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
585                       NULL));
586                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
587                   break;
588                 }
589 
590                 if (ind == SQL_NO_TOTAL) {
591                   sql_log(DEBUG_WARN, "notice: unable to determine total "
592                     "number of bytes remaining for %s column %u, row %lu",
593                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
594                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
595                   break;
596 
597                 } else if (ind == SQL_NULL_DATA) {
598                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
599                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
600                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
601                   break;
602                 }
603 
604                 pr_snprintf(buf, sizeof(buf), "%d", col_cval);
605                 buf[sizeof(buf)-1] = '\0';
606 
607                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
608                 break;
609               }
610 
611               case SQL_BIGINT: {
612                 char buf[64];
613                 long col_cval;
614                 SQLLEN ind;
615 
616                 col_ctype = SQL_C_LONG;
617 
618                 if (SQLGetData(conn->sth, i, col_ctype,
619                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
620                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
621                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
622                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
623                       NULL));
624                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
625                   break;
626                 }
627 
628                 if (ind == SQL_NO_TOTAL) {
629                   sql_log(DEBUG_WARN, "notice: unable to determine total "
630                     "number of bytes remaining for %s column %u, row %lu",
631                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
632                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
633                   break;
634 
635                 } else if (ind == SQL_NULL_DATA) {
636                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
637                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
638                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
639                   break;
640                 }
641 
642                 pr_snprintf(buf, sizeof(buf), "%ld", col_cval);
643                 buf[sizeof(buf)-1] = '\0';
644 
645                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
646                 break;
647               }
648 
649               case SQL_REAL: {
650                 char buf[64];
651                 long col_cval;
652                 SQLLEN ind;
653 
654                 col_ctype = SQL_C_LONG;
655 
656                 if (SQLGetData(conn->sth, i, col_ctype,
657                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
658                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
659                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
660                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
661                       NULL));
662                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
663                   break;
664                 }
665 
666                 if (ind == SQL_NO_TOTAL) {
667                   sql_log(DEBUG_WARN, "notice: unable to determine total "
668                     "number of bytes remaining for %s column %u, row %lu",
669                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
670                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
671                   break;
672 
673                 } else if (ind == SQL_NULL_DATA) {
674                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
675                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
676                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
677                   break;
678                 }
679 
680                 pr_snprintf(buf, sizeof(buf), "%ld", col_cval);
681                 buf[sizeof(buf)-1] = '\0';
682 
683                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
684                 break;
685               }
686 
687               case SQL_FLOAT: {
688                 char buf[64];
689                 float col_cval;
690                 SQLLEN ind;
691 
692                 col_ctype = SQL_C_FLOAT;
693 
694                 if (SQLGetData(conn->sth, i, col_ctype,
695                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
696                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
697                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
698                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
699                       NULL));
700                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
701                   break;
702                 }
703 
704                 if (ind == SQL_NO_TOTAL) {
705                   sql_log(DEBUG_WARN, "notice: unable to determine total "
706                     "number of bytes remaining for %s column %u, row %lu",
707                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
708                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
709                   break;
710 
711                 } else if (ind == SQL_NULL_DATA) {
712                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
713                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
714                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
715                   break;
716                 }
717 
718                 pr_snprintf(buf, sizeof(buf), "%f", col_cval);
719                 buf[sizeof(buf)-1] = '\0';
720 
721                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
722                 break;
723               }
724 
725               case SQL_DOUBLE: {
726                 char buf[64];
727                 double col_cval;
728                 SQLLEN ind;
729 
730                 col_ctype = SQL_C_DOUBLE;
731 
732                 if (SQLGetData(conn->sth, i, col_ctype,
733                     (SQLPOINTER) &col_cval, 0, &ind) != SQL_SUCCESS) {
734                   sql_log(DEBUG_WARN, "error getting %s data for column %u, "
735                     "row %lu: %s", sqlodbc_typestr(col_ctype), i,
736                     sd->rnum + 1, sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth,
737                       NULL));
738                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
739                   break;
740                 }
741 
742                 if (ind == SQL_NO_TOTAL) {
743                   sql_log(DEBUG_WARN, "notice: unable to determine total "
744                     "number of bytes remaining for %s column %u, row %lu",
745                     sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
746                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
747                   break;
748 
749                 } else if (ind == SQL_NULL_DATA) {
750                   sql_log(DEBUG_WARN, "notice: data is NULL for %s column %u, "
751                     "row %lu", sqlodbc_typestr(col_ctype), i, sd->rnum + 1);
752                   *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, "");
753                   break;
754                 }
755 
756                 pr_snprintf(buf, sizeof(buf), "%f", col_cval);
757                 buf[sizeof(buf)-1] = '\0';
758 
759                 *((char **) push_array(dh)) = pstrdup(cmd->tmp_pool, buf);
760                 break;
761               }
762 
763               default:
764                 sql_log(DEBUG_WARN, "data type (%s) of column %u not handled",
765                   sqlodbc_typestr(col_type), i);
766             }
767 
768           } else {
769             sql_log(DEBUG_WARN, "error describing column %u: %s", i,
770               sqlodbc_errstr(SQL_HANDLE_STMT, conn->sth, NULL));
771           }
772         }
773         break;
774 
775       default:
776         sql_log(DEBUG_WARN, "SQLFetch returned unhandled result: %d", res);
777         break;
778     }
779 
780     if (done_fetching)
781       break;
782   }
783 
784   SQLFreeHandle(SQL_HANDLE_STMT, conn->sth);
785   conn->state &= ~SQLODBC_HAVE_STMT_HANDLE;
786 
787   /* We allocate an extra char *, for a terminating NULL. */
788   *((char **) push_array(dh)) = NULL;
789   sd->data = (char **) dh->elts;
790 
791   return mod_create_data(cmd, (void *) sd);
792 }
793 
sqlodbc_open(cmd_rec * cmd)794 MODRET sqlodbc_open(cmd_rec *cmd) {
795   conn_entry_t *entry = NULL;
796   db_conn_t *conn = NULL;
797   SQLSMALLINT res;
798 
799   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_open");
800 
801   if (cmd->argc < 1) {
802     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
803     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
804   }
805 
806   /* Get the named connection. */
807   entry = sqlodbc_get_conn(cmd->argv[0]);
808   if (entry == NULL) {
809     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
810     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
811       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
812   }
813 
814   conn = (db_conn_t *) entry->data;
815 
816   /* If we're already open (nconn > 0), increment the number of connections.
817    * Reset our timer if we have one, and return HANDLED.
818    */
819   if (entry->nconn > 0) {
820     entry->nconn++;
821 
822     if (entry->timer)
823       pr_timer_reset(entry->timer, &sql_odbc_module);
824 
825     sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
826       entry->nconn);
827     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
828     return PR_HANDLED(cmd);
829   }
830 
831   if (!(conn->state & SQLODBC_HAVE_ENV_HANDLE)) {
832     res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->envh);
833     if (res != SQL_SUCCESS) {
834       sql_log(DEBUG_WARN, "error allocating environment handle: %s",
835         sqlodbc_strerror(res));
836       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
837       return sqlodbc_get_error(cmd, SQL_HANDLE_ENV, conn->envh);
838     }
839 
840     res = SQLSetEnvAttr(conn->envh, SQL_ATTR_ODBC_VERSION,
841       (SQLPOINTER) odbc_version, 0);
842     if (res != SQL_SUCCESS) {
843       sql_log(DEBUG_WARN, "error setting SQL_ATTR_ODBC_VERSION %s: %s",
844         odbc_version_str, sqlodbc_strerror(res));
845       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
846       return sqlodbc_get_error(cmd, SQL_HANDLE_ENV, conn->envh);
847     }
848 
849     conn->state |= SQLODBC_HAVE_ENV_HANDLE;
850   }
851 
852   if (!(conn->state & SQLODBC_HAVE_DBC_HANDLE)) {
853     res = SQLAllocHandle(SQL_HANDLE_DBC, conn->envh, &conn->dbh);
854     if (res != SQL_SUCCESS) {
855       sql_log(DEBUG_WARN, "error allocating database handle: %s",
856         sqlodbc_strerror(res));
857       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
858       return sqlodbc_get_error(cmd, SQL_HANDLE_DBC, conn->dbh);
859     }
860 
861     conn->state |= SQLODBC_HAVE_DBC_HANDLE;
862   }
863 
864   res = SQLConnect(conn->dbh, (SQLCHAR *) conn->dsn, SQL_NTS,
865     (SQLCHAR *) conn->user, SQL_NTS, (SQLCHAR *) conn->pass, SQL_NTS);
866   if (res != SQL_SUCCESS &&
867       res != SQL_SUCCESS_WITH_INFO) {
868     sql_log(DEBUG_FUNC, "error connecting to dsn '%s': %s", conn->dsn,
869       sqlodbc_strerror(res));
870     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
871     return sqlodbc_get_error(cmd, SQL_HANDLE_DBC, conn->dbh);
872   }
873 
874   /* Add some DriverManager and Driver information to the logs via
875    * SQLGetInfo().
876    */
877   if (!(conn->state & SQLODBC_HAVE_INFO)) {
878     SQLCHAR info[SQL_MAX_MESSAGE_LENGTH];
879     SQLSMALLINT infolen;
880 
881     res = SQLGetInfo(conn->dbh, SQL_DBMS_NAME, info, sizeof(info), &infolen);
882     if (res == SQL_SUCCESS) {
883       info[infolen] = '\0';
884       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Product name: %s", info);
885 
886       /* Oracle does not like use of the LIMIT, and prefers ROWNUM instead.
887        * Thus we need to check to see if this driver is for Oracle.
888        */
889       if (strcasecmp((char *) info, "Oracle") == 0) {
890         sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
891           ": %s does not support LIMIT, using ROWNUM instead", info);
892         use_limit_clause = FALSE;
893         use_rownum_clause = TRUE;
894         use_top_clause = FALSE;
895       }
896 
897       /* FreeTDS (and SQL Server) does not like either LIMIT or ROWNUM, and
898        * prefers TOP. So we need to check for this as well.
899        */
900       if (strcasecmp((char *) info, "FreeTDS") == 0 ||
901           strstr((char *) info, "SQL Server") != NULL) {
902         sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
903           ": %s does not support LIMIT, using TOP instead", info);
904         use_limit_clause = FALSE;
905         use_rownum_clause = FALSE;
906         use_top_clause = TRUE;
907       }
908 
909     } else {
910       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
911         ": Product name: (unavailable)");
912     }
913 
914     res = SQLGetInfo(conn->dbh, SQL_DBMS_VER, info, sizeof(info), &infolen);
915     if (res == SQL_SUCCESS) {
916       info[infolen] = '\0';
917       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Product version: %s", info);
918 
919     } else {
920       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
921         ": Product version: (unavailable)");
922     }
923 
924     res = SQLGetInfo(conn->dbh, SQL_DM_VER, info, sizeof(info), &infolen);
925     if (res == SQL_SUCCESS) {
926       info[infolen] = '\0';
927       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver Manager version: %s",
928         info);
929 
930     } else {
931       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
932         ": Driver Manager version: (unavailable)");
933     }
934 
935     res = SQLGetInfo(conn->dbh, SQL_DRIVER_NAME, info, sizeof(info), &infolen);
936     if (res == SQL_SUCCESS) {
937       info[infolen] = '\0';
938       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver name: %s", info);
939 
940     } else {
941       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver name: (unavailable)");
942     }
943 
944     res = SQLGetInfo(conn->dbh, SQL_DRIVER_VER, info, sizeof(info), &infolen);
945     if (res == SQL_SUCCESS) {
946       info[infolen] = '\0';
947       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver version: %s", info);
948 
949     } else {
950       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
951         ": Driver version: (unavailable)");
952     }
953 
954     res = SQLGetInfo(conn->dbh, SQL_DRIVER_ODBC_VER, info, sizeof(info),
955       &infolen);
956     if (res == SQL_SUCCESS) {
957       info[infolen] = '\0';
958       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION ": Driver ODBC version: %s",
959         info);
960 
961     } else {
962       sql_log(DEBUG_INFO, MOD_SQL_ODBC_VERSION
963         ": Driver ODBC version: (unavailable)");
964     }
965 
966     conn->state |= SQLODBC_HAVE_INFO;
967   }
968 
969   entry->nconn++;
970 
971   if (pr_sql_conn_policy == SQL_CONN_POLICY_PERSESSION) {
972     /* If the connection policy is PERSESSION... */
973     if (entry->nconn == 1) {
974       /* ...and we are actually opening the first connection to the database;
975        * we want to make sure this connection stays open, after this first use
976        * (as per Bug#3290).  To do this, we re-bump the connection count.
977        */
978       entry->nconn++;
979     }
980 
981   } else if (entry->ttl > 0) {
982     /* Set up our timer, if necessary. */
983 
984     entry->timer = pr_timer_add(entry->ttl, -1, &sql_odbc_module,
985       sqlodbc_timer_cb, "odbc connection ttl");
986 
987     sql_log(DEBUG_INFO, "'%s' connection: %d second timer started",
988       entry->name, entry->ttl);
989 
990     /* Timed connections get re-bumped so they don't go away when
991      * sqlodbc_close() is called.
992      */
993     entry->nconn++;
994   }
995 
996   sql_log(DEBUG_INFO, "'%s' connection opened", entry->name);
997   sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
998     entry->nconn);
999   pr_event_generate("mod_sql.db.connection-opened", &sql_odbc_module);
1000 
1001   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_open");
1002   return PR_HANDLED(cmd);
1003 }
1004 
sqlodbc_close(cmd_rec * cmd)1005 MODRET sqlodbc_close(cmd_rec *cmd) {
1006   conn_entry_t *entry = NULL;
1007   db_conn_t *conn = NULL;
1008 
1009   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_close");
1010 
1011   if (cmd->argc < 1 || cmd->argc > 2) {
1012     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1013     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1014   }
1015 
1016   /* Get the named connection. */
1017   entry = sqlodbc_get_conn(cmd->argv[0]);
1018   if (entry == NULL) {
1019     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1020     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1021       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1022   }
1023 
1024   conn = (db_conn_t *) entry->data;
1025 
1026   /* If we're closed already (nconn == 0), return HANDLED. */
1027   if (entry->nconn == 0) {
1028     sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
1029       entry->nconn);
1030     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1031     return PR_HANDLED(cmd);
1032   }
1033 
1034   /* Decrement nconn. If our count is 0 or we received a second arg, close
1035    * the connection, explicitly set the counter to 0, and remove any timers.
1036    */
1037   if ((--entry->nconn) == 0 ||
1038       (cmd->argc == 2 && cmd->argv[1])) {
1039 
1040     if (conn->state & SQLODBC_HAVE_STMT_HANDLE) {
1041       SQLFreeHandle(SQL_HANDLE_STMT, conn->sth);
1042       conn->sth = NULL;
1043 
1044       conn->state &= ~SQLODBC_HAVE_STMT_HANDLE;
1045     }
1046 
1047     if (conn->state & SQLODBC_HAVE_DBC_HANDLE) {
1048       SQLDisconnect(conn->dbh);
1049       SQLFreeHandle(SQL_HANDLE_DBC, conn->dbh);
1050       conn->dbh = NULL;
1051 
1052       conn->state &= ~SQLODBC_HAVE_DBC_HANDLE;
1053     }
1054 
1055     if (conn->state & SQLODBC_HAVE_ENV_HANDLE) {
1056       SQLFreeHandle(SQL_HANDLE_ENV, conn->envh);
1057       conn->envh = NULL;
1058 
1059       conn->state &= ~SQLODBC_HAVE_ENV_HANDLE;
1060     }
1061 
1062     entry->nconn = 0;
1063 
1064     if (entry->timer) {
1065       pr_timer_remove(entry->timer, &sql_odbc_module);
1066       entry->timer = 0;
1067       sql_log(DEBUG_INFO, "'%s' connection timer stopped", entry->name);
1068     }
1069 
1070     sql_log(DEBUG_INFO, "'%s' connection closed", entry->name);
1071     pr_event_generate("mod_sql.db.connection-closed", &sql_odbc_module);
1072   }
1073 
1074   sql_log(DEBUG_INFO, "'%s' connection count is now %u", entry->name,
1075     entry->nconn);
1076   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_close");
1077 
1078   return PR_HANDLED(cmd);
1079 }
1080 
sqlodbc_def_conn(cmd_rec * cmd)1081 MODRET sqlodbc_def_conn(cmd_rec *cmd) {
1082   char *name = NULL;
1083   conn_entry_t *entry = NULL;
1084   db_conn_t *conn = NULL;
1085 
1086   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_defineconnection");
1087 
1088   if (cmd->argc < 4 ||
1089       cmd->argc > 10 ||
1090       !cmd->argv[0]) {
1091     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1092     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1093   }
1094 
1095   conn = (db_conn_t *) pcalloc(conn_pool, sizeof(db_conn_t));
1096 
1097   name = pstrdup(conn_pool, cmd->argv[0]);
1098   conn->user = pstrdup(conn_pool, cmd->argv[1]);
1099   conn->pass = pstrdup(conn_pool, cmd->argv[2]);
1100   conn->dsn = pstrdup(conn_pool, cmd->argv[3]);
1101 
1102   /* Insert the new conn_info into the connection hash */
1103   entry = sqlodbc_add_conn(conn_pool, name, (void *) conn);
1104   if (entry == NULL &&
1105       errno == EEXIST) {
1106     /* Log only connections named other than "default", for debugging
1107      * misconfigurations with multiple different SQLNamedConnectInfo
1108      * directives using the same name.
1109      */
1110     if (strcmp(name, "default") != 0) {
1111       sql_log(DEBUG_FUNC, "named connection '%s' already exists", name);
1112     }
1113 
1114     entry = sqlodbc_get_conn(name);
1115   }
1116 
1117   if (entry == NULL) {
1118     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1119     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1120       "error adding named connection");
1121   }
1122 
1123   if (cmd->argc >= 5) {
1124     entry->ttl = (int) strtol(cmd->argv[4], (char **) NULL, 10);
1125     if (entry->ttl >= 1) {
1126       pr_sql_conn_policy = SQL_CONN_POLICY_TIMER;
1127 
1128     } else {
1129       entry->ttl = 0;
1130     }
1131   }
1132 
1133   entry->timer = 0;
1134   entry->nconn = 0;
1135 
1136   sql_log(DEBUG_INFO, " name: '%s'", entry->name);
1137   sql_log(DEBUG_INFO, " user: '%s'", conn->user);
1138   sql_log(DEBUG_INFO, "  dsn: '%s'", conn->dsn);
1139 
1140   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_defineconnection");
1141   return PR_HANDLED(cmd);
1142 }
1143 
sqlodbc_select(cmd_rec * cmd)1144 MODRET sqlodbc_select(cmd_rec *cmd) {
1145   conn_entry_t *entry = NULL;
1146   db_conn_t *conn = NULL;
1147   modret_t *mr = NULL;
1148   char *query = NULL;
1149   cmd_rec *close_cmd;
1150 
1151   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_select");
1152 
1153   if (cmd->argc < 2) {
1154     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1155     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1156   }
1157 
1158   /* Get the named connection. */
1159   entry = sqlodbc_get_conn(cmd->argv[0]);
1160   if (entry == NULL) {
1161     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1162     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1163       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1164   }
1165 
1166   conn = (db_conn_t *) entry->data;
1167 
1168   mr = sqlodbc_open(cmd);
1169   if (MODRET_ERROR(mr)) {
1170     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1171     return mr;
1172   }
1173 
1174   /* Construct the query string. */
1175   if (cmd->argc == 2) {
1176     query = pstrcat(cmd->tmp_pool, "SELECT ", cmd->argv[1], NULL);
1177 
1178   } else {
1179     query = pstrcat(cmd->tmp_pool, cmd->argv[2], " FROM ", cmd->argv[1], NULL);
1180 
1181     if (cmd->argc > 3 &&
1182         cmd->argv[3])
1183       query = pstrcat(cmd->tmp_pool, query, " WHERE ", cmd->argv[3], NULL);
1184 
1185     if (cmd->argc > 4 &&
1186         cmd->argv[4]) {
1187 
1188       if (use_limit_clause) {
1189         query = pstrcat(cmd->tmp_pool, query, " LIMIT ", cmd->argv[4], NULL);
1190 
1191       } else if (use_rownum_clause) {
1192         query = pstrcat(cmd->tmp_pool, query, " AND ROWNUM = ", cmd->argv[4],
1193           NULL);
1194 
1195       } else if (use_top_clause) {
1196         /* Note that the TOP clause must be at the start of the query, not
1197          * the end.
1198          */
1199         query = pstrcat(cmd->tmp_pool, "TOP ", cmd->argv[4], " ", query, NULL);
1200       }
1201     }
1202 
1203     if (cmd->argc > 5) {
1204       register unsigned int i = 0;
1205 
1206       /* Handle the optional arguments -- they're rare, so in this case
1207        * we'll play with the already constructed query string, but in
1208        * general we should probably take optional arguments into account
1209        * and put the query string together later once we know what they are.
1210        */
1211 
1212       for (i = 5; i < cmd->argc; i++) {
1213         if (cmd->argv[i] &&
1214             strcasecmp("DISTINCT", cmd->argv[i]) == 0)
1215           query = pstrcat(cmd->tmp_pool, "DISTINCT ", query, NULL);
1216       }
1217     }
1218 
1219     query = pstrcat(cmd->tmp_pool, "SELECT ", query, NULL);
1220   }
1221 
1222   /* Log the query string */
1223   sql_log(DEBUG_INFO, "query \"%s\"", query);
1224 
1225   if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1226     if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1227         SQL_SUCCESS) {
1228       sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1229       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1230       return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1231     }
1232 
1233     conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1234   }
1235 
1236   /* Perform the query.  If it doesn't work, log the error, close the
1237    * connection, then return the error from the query processing.
1238    */
1239   switch (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query))) {
1240     case SQL_SUCCESS:
1241     case SQL_SUCCESS_WITH_INFO:
1242       break;
1243 
1244     default:
1245       mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1246 
1247       close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1248       sqlodbc_close(close_cmd);
1249       destroy_pool(close_cmd->pool);
1250 
1251       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1252       return mr;
1253   }
1254 
1255   switch (SQLExecute(conn->sth)) {
1256     case SQL_SUCCESS:
1257     case SQL_SUCCESS_WITH_INFO:
1258       break;
1259 
1260     default:
1261       mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1262 
1263       close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1264       sqlodbc_close(close_cmd);
1265       destroy_pool(close_cmd->pool);
1266 
1267       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1268       return mr;
1269   }
1270 
1271   /* Get the data. If it doesn't work, log the error, close the connection,
1272    * then return the error from the data processing.
1273    */
1274 
1275   mr = sqlodbc_get_data(cmd, conn);
1276   if (MODRET_ERROR(mr)) {
1277     close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1278     sqlodbc_close(close_cmd);
1279     destroy_pool(close_cmd->pool);
1280 
1281     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1282     return mr;
1283   }
1284 
1285   /* Close the connection, return the data. */
1286   close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1287   sqlodbc_close(close_cmd);
1288   destroy_pool(close_cmd->pool);
1289 
1290   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_select");
1291   return mr;
1292 }
1293 
sqlodbc_insert(cmd_rec * cmd)1294 MODRET sqlodbc_insert(cmd_rec *cmd) {
1295   conn_entry_t *entry = NULL;
1296   db_conn_t *conn = NULL;
1297   modret_t *mr = NULL;
1298   char *query = NULL;
1299   cmd_rec *close_cmd;
1300 
1301   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_insert");
1302 
1303   if (cmd->argc != 2 && cmd->argc != 4) {
1304     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1305     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1306   }
1307 
1308   /* Get the named connection. */
1309   entry = sqlodbc_get_conn(cmd->argv[0]);
1310   if (entry == NULL) {
1311     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1312     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1313       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1314   }
1315 
1316   conn = (db_conn_t *) entry->data;
1317 
1318   mr = sqlodbc_open(cmd);
1319   if (MODRET_ERROR(mr)) {
1320     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1321     return mr;
1322   }
1323 
1324   /* Construct the query string */
1325   if (cmd->argc == 2)
1326     query = pstrcat(cmd->tmp_pool, "INSERT ", cmd->argv[1], NULL);
1327 
1328   else
1329     query = pstrcat(cmd->tmp_pool, "INSERT INTO ", cmd->argv[1], " (",
1330       cmd->argv[2], ") VALUES (", cmd->argv[3], ")", NULL);
1331 
1332   /* Log the query string */
1333   sql_log(DEBUG_INFO, "query \"%s\"", query);
1334 
1335   if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1336     if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1337         SQL_SUCCESS) {
1338       sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1339       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1340       return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1341     }
1342 
1343     conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1344   }
1345 
1346   /* Perform the query.  If it doesn't work, log the error, close the
1347    * connection (and log any errors there, too) then return the error
1348    * from the query processing.
1349    */
1350   if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1351     mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1352 
1353     close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1354     sqlodbc_close(close_cmd);
1355     destroy_pool(close_cmd->pool);
1356 
1357     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1358     return mr;
1359   }
1360 
1361   switch (SQLExecute(conn->sth)) {
1362     case SQL_SUCCESS:
1363     case SQL_SUCCESS_WITH_INFO:
1364       break;
1365 
1366     default:
1367       mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1368 
1369       close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1370       sqlodbc_close(close_cmd);
1371       destroy_pool(close_cmd->pool);
1372 
1373       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1374       return mr;
1375   }
1376 
1377   /* Close the connection and return HANDLED. */
1378   close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1379   sqlodbc_close(close_cmd);
1380   destroy_pool(close_cmd->pool);
1381 
1382   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_insert");
1383   return PR_HANDLED(cmd);
1384 }
1385 
sqlodbc_update(cmd_rec * cmd)1386 MODRET sqlodbc_update(cmd_rec *cmd) {
1387   conn_entry_t *entry = NULL;
1388   db_conn_t *conn = NULL;
1389   modret_t *mr = NULL;
1390   char *query = NULL;
1391   cmd_rec *close_cmd;
1392 
1393   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_update");
1394 
1395   if (cmd->argc < 2 || cmd->argc > 4) {
1396     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1397     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1398   }
1399 
1400   /* Get the named connection. */
1401   entry = sqlodbc_get_conn(cmd->argv[0]);
1402   if (entry == NULL) {
1403     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1404     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1405       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1406   }
1407 
1408   conn = (db_conn_t *) entry->data;
1409 
1410   mr = sqlodbc_open(cmd);
1411   if (MODRET_ERROR(mr)) {
1412     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1413     return mr;
1414   }
1415 
1416   if (cmd->argc == 2) {
1417     query = pstrcat(cmd->tmp_pool, "UPDATE ", cmd->argv[1], NULL);
1418 
1419   } else {
1420     /* Construct the query string */
1421     query = pstrcat(cmd->tmp_pool, "UPDATE ", cmd->argv[1], " SET ",
1422       cmd->argv[2], NULL);
1423 
1424     if (cmd->argc > 3 && cmd->argv[3])
1425       query = pstrcat(cmd->tmp_pool, query, " WHERE ", cmd->argv[3], NULL);
1426   }
1427 
1428   /* Log the query string. */
1429   sql_log(DEBUG_INFO, "query \"%s\"", query);
1430 
1431   if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1432     if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1433         SQL_SUCCESS) {
1434       sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1435       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1436       return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1437     }
1438 
1439     conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1440   }
1441 
1442   /* Perform the query.  If it doesn't work close the connection, then
1443    * return the error from the query processing.
1444    */
1445   if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1446     mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1447 
1448     close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1449     sqlodbc_close(close_cmd);
1450     destroy_pool(close_cmd->pool);
1451 
1452     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1453     return mr;
1454   }
1455 
1456   switch (SQLExecute(conn->sth)) {
1457     case SQL_SUCCESS:
1458     case SQL_SUCCESS_WITH_INFO:
1459       break;
1460 
1461     default:
1462       mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1463 
1464       close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1465       sqlodbc_close(close_cmd);
1466       destroy_pool(close_cmd->pool);
1467 
1468       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1469       return mr;
1470   }
1471 
1472   /* Close the connection, return HANDLED.  */
1473   close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1474   sqlodbc_close(close_cmd);
1475   destroy_pool(close_cmd->pool);
1476 
1477   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_update");
1478   return PR_HANDLED(cmd);
1479 }
1480 
sqlodbc_procedure(cmd_rec * cmd)1481 MODRET sqlodbc_procedure(cmd_rec *cmd) {
1482   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_procedure");
1483 
1484   if (cmd->argc != 3) {
1485     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_procedure");
1486     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1487   }
1488 
1489   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_procedure");
1490   return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1491     "backend does not support procedures");
1492 }
1493 
sqlodbc_query(cmd_rec * cmd)1494 MODRET sqlodbc_query(cmd_rec *cmd) {
1495   conn_entry_t *entry = NULL;
1496   db_conn_t *conn = NULL;
1497   modret_t *mr = NULL;
1498   char *query = NULL;
1499   cmd_rec *close_cmd;
1500 
1501   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_query");
1502 
1503   if (cmd->argc != 2) {
1504     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1505     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1506   }
1507 
1508   /* Get the named connection. */
1509   entry = sqlodbc_get_conn(cmd->argv[0]);
1510   if (entry == NULL) {
1511     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1512     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1513       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1514   }
1515 
1516   conn = (db_conn_t *) entry->data;
1517 
1518   mr = sqlodbc_open(cmd);
1519   if (MODRET_ERROR(mr)) {
1520     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1521     return mr;
1522   }
1523 
1524   query = pstrcat(cmd->tmp_pool, cmd->argv[1], NULL);
1525 
1526   /* Log the query string */
1527   sql_log(DEBUG_INFO, "query \"%s\"", query);
1528 
1529   if (!(conn->state & SQLODBC_HAVE_STMT_HANDLE)) {
1530     if (SQLAllocHandle(SQL_HANDLE_STMT, conn->dbh, &conn->sth) !=
1531         SQL_SUCCESS) {
1532       sql_log(DEBUG_WARN, "%s", "error allocating statement handle");
1533       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1534       return sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1535     }
1536 
1537     conn->state |= SQLODBC_HAVE_STMT_HANDLE;
1538   }
1539 
1540   /* Perform the query.  If it doesn't work close the connection, then
1541    * return the error from the query processing.
1542    */
1543   if (SQLPrepare(conn->sth, (SQLCHAR *) query, strlen(query)) != SQL_SUCCESS) {
1544     mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1545 
1546     close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1547     sqlodbc_close(close_cmd);
1548     destroy_pool(close_cmd->pool);
1549 
1550     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1551     return mr;
1552   }
1553 
1554   switch (SQLExecute(conn->sth)) {
1555     case SQL_SUCCESS:
1556     case SQL_SUCCESS_WITH_INFO:
1557       break;
1558 
1559     default:
1560       mr = sqlodbc_get_error(cmd, SQL_HANDLE_STMT, conn->sth);
1561 
1562       close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1563       sqlodbc_close(close_cmd);
1564       destroy_pool(close_cmd->pool);
1565 
1566       sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1567       return mr;
1568   }
1569 
1570   /* Get data if necessary. If it doesn't work, log the error, close the
1571    * connection then return the error from the data processing.
1572    */
1573 
1574   mr = sqlodbc_get_data(cmd, conn);
1575   if (MODRET_ERROR(mr)) {
1576     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1577 
1578     close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1579     sqlodbc_close(close_cmd);
1580     destroy_pool(close_cmd->pool);
1581 
1582     return mr;
1583   }
1584 
1585   /* Close the connection, return the data. */
1586   close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1587   sqlodbc_close(close_cmd);
1588   destroy_pool(close_cmd->pool);
1589 
1590   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_query");
1591   return mr;
1592 }
1593 
1594 /* XXX need to provide an escapestring implementation, probably borrowed
1595  * from MySQL.  Make sure it's standards-compliant.
1596  */
1597 
sqlodbc_quote(cmd_rec * cmd)1598 MODRET sqlodbc_quote(cmd_rec *cmd) {
1599   conn_entry_t *entry = NULL;
1600   modret_t *mr = NULL;
1601   char *unescaped = NULL;
1602   char *escaped = NULL;
1603   cmd_rec *close_cmd;
1604 
1605   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_escapestring");
1606 
1607   if (cmd->argc != 2) {
1608     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1609     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION, "badly formed request");
1610   }
1611 
1612   /* Get the named connection. */
1613   entry = sqlodbc_get_conn(cmd->argv[0]);
1614   if (entry == NULL) {
1615     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1616     return PR_ERROR_MSG(cmd, MOD_SQL_ODBC_VERSION,
1617       pstrcat(cmd->tmp_pool, "unknown named connection: ", cmd->argv[0], NULL));
1618   }
1619 
1620   /* Make sure the connection is open. */
1621   mr = sqlodbc_open(cmd);
1622   if (MODRET_ERROR(mr)) {
1623     sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1624     return mr;
1625   }
1626 
1627   unescaped = cmd->argv[1];
1628   escaped = (char *) pcalloc(cmd->tmp_pool, sizeof(char) *
1629 			      (strlen(unescaped) * 2) + 1);
1630 
1631   sqlodbc_escape_string(escaped, unescaped, strlen(unescaped));
1632 
1633   close_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, entry->name);
1634   sqlodbc_close(close_cmd);
1635   destroy_pool(close_cmd->pool);
1636 
1637   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_escapestring");
1638   return mod_create_data(cmd, (void *) escaped);
1639 }
1640 
sqlodbc_exit(cmd_rec * cmd)1641 MODRET sqlodbc_exit(cmd_rec *cmd) {
1642   register unsigned int i = 0;
1643 
1644   sql_log(DEBUG_FUNC, "%s", "entering \todbc cmd_exit");
1645 
1646   for (i = 0; i < conn_cache->nelts; i++) {
1647     conn_entry_t *entry = ((conn_entry_t **) conn_cache->elts)[i];
1648 
1649     if (entry->nconn > 0) {
1650       cmd_rec *close_cmd = pr_cmd_alloc(conn_pool, 2, entry->name, "1");
1651       sqlodbc_close(close_cmd);
1652       destroy_pool(close_cmd->pool);
1653     }
1654   }
1655 
1656   sql_log(DEBUG_FUNC, "%s", "exiting \todbc cmd_exit");
1657   return PR_HANDLED(cmd);
1658 }
1659 
sqlodbc_identify(cmd_rec * cmd)1660 MODRET sqlodbc_identify(cmd_rec *cmd) {
1661   sql_data_t *sd = NULL;
1662 
1663   sd = (sql_data_t *) pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
1664   sd->data = (char **) pcalloc(cmd->tmp_pool, sizeof(char *) * 2);
1665 
1666   sd->rnum = 1;
1667   sd->fnum = 2;
1668 
1669   sd->data[0] = MOD_SQL_ODBC_VERSION;
1670   sd->data[1] = MOD_SQL_API_V1;
1671 
1672   return mod_create_data(cmd, (void *) sd);
1673 }
1674 
1675 /* mod_sql-specific command dispatch/handler table */
1676 static cmdtable sqlodbc_cmdtable[] = {
1677   { CMD, "sql_open",		G_NONE,	sqlodbc_open,	FALSE, FALSE },
1678   { CMD, "sql_close",		G_NONE, sqlodbc_close,	FALSE, FALSE },
1679   { CMD, "sql_defineconnection",G_NONE, sqlodbc_def_conn, FALSE, FALSE },
1680   { CMD, "sql_select",		G_NONE, sqlodbc_select,	FALSE, FALSE },
1681   { CMD, "sql_insert",		G_NONE, sqlodbc_insert,	FALSE, FALSE },
1682   { CMD, "sql_update",		G_NONE, sqlodbc_update,	FALSE, FALSE },
1683   { CMD, "sql_procedure",	G_NONE, sqlodbc_procedure, FALSE, FALSE },
1684   { CMD, "sql_query",		G_NONE, sqlodbc_query,	FALSE, FALSE },
1685   { CMD, "sql_escapestring",	G_NONE, sqlodbc_quote,	FALSE, FALSE },
1686   { CMD, "sql_exit",		G_NONE, sqlodbc_exit,	FALSE, FALSE },
1687   { CMD, "sql_identify",	G_NONE, sqlodbc_identify, FALSE, FALSE },
1688 
1689   { 0, NULL }
1690 };
1691 
1692 /* Configuration handlers
1693  */
1694 
1695 /* usage: SQLODBCVersion version */
set_sqlodbcversion(cmd_rec * cmd)1696 MODRET set_sqlodbcversion(cmd_rec *cmd) {
1697   int version = -1;
1698   const char *version_str;
1699   config_rec *c;
1700 
1701   CHECK_ARGS(cmd, 1);
1702   CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
1703 
1704   if (strcasecmp(cmd->argv[1], "2") == 0 ||
1705       strcasecmp(cmd->argv[1], "odbcv2") == 0) {
1706 #if defined(SQL_OV_ODBC2)
1707     version = SQL_OV_ODBC2;
1708     version_str = "ODBCv2";
1709 #endif /* ODBCv2 */
1710 
1711   } else if (strcasecmp(cmd->argv[1], "3") == 0 ||
1712              strcasecmp(cmd->argv[1], "odbcv3") == 0) {
1713 #if defined(SQL_OV_ODBC3)
1714     version = SQL_OV_ODBC3;
1715     version_str = "ODBCv3";
1716 #endif /* ODBCv3 */
1717 
1718   } else if (strcasecmp(cmd->argv[1], "3.80") == 0 ||
1719              strcasecmp(cmd->argv[1], "odbcv3.80") == 0) {
1720 #if defined(SQL_OV_ODBC3_80)
1721     version = SQL_OV_ODBC3_80;
1722     version_str = "ODBCv3.80";
1723 #endif /* ODBCv3.80 */
1724   }
1725 
1726   if (version < 0) {
1727     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1728       "unknown/supported ODBC API version: ", cmd->argv[1], NULL));
1729   }
1730 
1731   c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1732   c->argv[0] = palloc(c->pool, sizeof(int));
1733   *((int *) c->argv[0]) = version;
1734   c->argv[1] = pstrdup(c->pool, version_str);
1735 
1736   return PR_HANDLED(cmd);
1737 }
1738 
1739 /* Event handlers
1740  */
1741 
sqlodbc_mod_unload_ev(const void * event_data,void * user_data)1742 static void sqlodbc_mod_unload_ev(const void *event_data, void *user_data) {
1743 
1744   if (strcmp("mod_sql_odbc.c", (const char *) event_data) == 0) {
1745     /* Unegister ourselves with mod_sql. */
1746     if (sql_unregister_backend("odbc") < 0) {
1747       pr_log_pri(PR_LOG_NOTICE, MOD_SQL_ODBC_VERSION
1748         ": notice: error unregistering backend: %s", strerror(errno));
1749       pr_session_end(0);
1750     }
1751 
1752     /* Unregister ourselves from all events. */
1753     pr_event_unregister(&sql_odbc_module, NULL, NULL);
1754   }
1755 }
1756 
1757 /* Module initialization routines
1758  */
1759 
sqlodbc_init(void)1760 static int sqlodbc_init(void) {
1761 
1762   /* Register ourselves with mod_sql. */
1763   if (sql_register_backend("odbc", sqlodbc_cmdtable) < 0) {
1764     pr_log_pri(PR_LOG_NOTICE, MOD_SQL_ODBC_VERSION
1765       ": notice: error registering backend: %s", strerror(errno));
1766     errno = EPERM;
1767     return -1;
1768   }
1769 
1770   /* Register listeners for the unload event. */
1771   pr_event_register(&sql_odbc_module, "core.module-unload",
1772     sqlodbc_mod_unload_ev, NULL);
1773 
1774   return 0;
1775 }
1776 
sqlodbc_sess_init(void)1777 static int sqlodbc_sess_init(void) {
1778   config_rec *c;
1779 
1780   if (conn_pool != NULL) {
1781     destroy_pool(conn_pool);
1782     conn_cache = NULL;
1783   }
1784 
1785   conn_pool = make_sub_pool(session.pool);
1786   pr_pool_tag(conn_pool, "ODBC connection pool");
1787 
1788   if (conn_cache == NULL) {
1789     conn_cache = make_array(conn_pool, DEF_CONN_POOL_SIZE,
1790       sizeof(conn_entry_t *));
1791   }
1792 
1793   /* There is a very specific reason for using square brackets, rather than
1794    * parentheses, here.
1795    *
1796    * Users of the mod_sql_odbc module for talking to an Oracle database
1797    * via ODBC encountered a bug in the Oracle client library, where the Oracle
1798    * client library tries to find the name of the process calling the client,
1799    * and adds that name to the connection string used.  However, that process
1800    * name parsing will fail if the process name uses parentheses.  The
1801    * workaround, then, is to use square brackets.
1802    *
1803    * For those curious, this is Oracle Bug 3807408.  More discussions on
1804    * problem can be found at:
1805    *
1806    *  http://forums.oracle.com/forums/thread.jspa?threadID=296725
1807    *
1808    * The following comment indicates that more recent versions of Oracle
1809    * have this issue fixed; there is also a patch available:
1810    *
1811    *  "This issue is Oracle bug 3807408 and can be fixed by applying 10.2.0.1
1812    *   Patch 6. This can be downloaded from MetaLink if you have an account:
1813    *
1814    *   http://updates.oracle.com/ARULink/PatchDetails/process_form?patch_num=5059238"
1815    * This comment was found at:
1816    *
1817    *  http://www.topxml.com/forum/Database_Adapter_for_Oracle_on_64bit_Windows/m_3448/tm.htm
1818    */
1819 
1820   pr_proctitle_set("[accepting connections]");
1821 
1822   c = find_config(main_server->conf, CONF_PARAM, "SQLODBCVersion", FALSE);
1823   if (c != NULL) {
1824     odbc_version = *((int *) c->argv[0]);
1825     odbc_version_str = c->argv[1];
1826   }
1827 
1828   return 0;
1829 }
1830 
1831 /* Module API tables
1832  */
1833 
1834 static conftable sqlodbc_conftab[] = {
1835   { "SQLODBCVersion",	set_sqlodbcversion,	NULL },
1836   { NULL, NULL, NULL }
1837 };
1838 
1839 module sql_odbc_module = {
1840   NULL, NULL,
1841 
1842   /* Module API version */
1843   0x20,
1844 
1845   /* Module name */
1846   "sql_odbc",
1847 
1848   /* Module configuration directive table */
1849   sqlodbc_conftab,
1850 
1851   /* Module command handler table */
1852   NULL,
1853 
1854   /* Module authentication handler table */
1855   NULL,
1856 
1857   /* Module initialization */
1858   sqlodbc_init,
1859 
1860   /* Session initialization */
1861   sqlodbc_sess_init,
1862 
1863   /* Module version */
1864   MOD_SQL_ODBC_VERSION
1865 };
1866